> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nextlevelmca.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> API keys, roles, scopes, and how to send credentials.

Every request to `https://api.nextlevelmca.com/v1` needs a bearer credential: an API key you create in the app, or an OAuth access token issued to an MCP client. Both are checked the same way and both are bound to one location.

## Create an API key

<Steps>
  <Step title="Open Settings → Developers → API keys">
    You need settings access in the app; admins have it by default.
  </Step>

  <Step title="Choose a name, role, scopes and expiry">
    * **Name** is for you. It also appears in activity logs as `API key "name"` on anything the key changes.
    * **Role** sets what the key can see and do: `admin`, `manager` (the default) or `user`. See [Roles](#roles).
    * **Scopes** limit which resources the key can touch. Leave the list empty to grant every scope. See [Scopes](#scopes).
    * **Expiry** is optional. After `expires_at` the key returns `401`.
  </Step>

  <Step title="Copy the key">
    The full key is shown once. Afterwards the app only shows the prefix and the last four characters (`nlmca_live_ab12…wxyz`). If you lose it, revoke it and create a new one.
  </Step>
</Steps>

Keys look like `nlmca_live_` followed by 32 random base62 characters. Only a SHA-256 hash is stored; the key itself cannot be recovered.

<Warning>
  Treat keys like passwords. Send them only over HTTPS, keep them out of browser code and logs, and revoke any key that may have leaked.
</Warning>

## Send the key

```bash theme={null}
curl "https://api.nextlevelmca.com/v1/deals?limit=5" \
  -H "Authorization: Bearer nlmca_live_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
```

Keys are accepted **only** on `/v1`. The app's internal routes reject them, so a leaked key cannot reach anything that is not documented here. A missing or invalid credential returns `401` with `type: "authentication_error"`.

## Roles

A role sets the depth of access: which records the key can read, which actions it can perform and which fields are masked. Scopes then narrow the breadth. Both must pass on every request.

| Role                | What it can do                                                                                                                                                                                                           |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `admin`             | Everything an admin can do in the app, including team and settings.                                                                                                                                                      |
| `manager` (default) | Admin permissions minus team management and settings changes. It can still view the team and activity logs. Use this unless the integration must change settings.                                                        |
| `user`              | The standard user defaults: only deals it created, can create and edit deals, can submit but not withdraw, can log offers but not edit or accept them, no access to advances, lenders read-only, no reports or settings. |

Role also decides PII visibility: `dob` is returned only to `admin` and `manager`. SSNs are never returned by the API for any role.

## Scopes

A key with no scopes has all of them. Otherwise each endpoint requires the scope listed on its reference page, and a missing one returns `403` with `code: "missing_scope"` and `param` set to the scope you need.

| Scope               | Grants                                                |
| ------------------- | ----------------------------------------------------- |
| `businesses:read`   | Read businesses and their notes, tasks and activity   |
| `businesses:write`  | Create and update businesses, notes and tasks         |
| `people:read`       | Read people (contacts and owners)                     |
| `people:write`      | Create and update people                              |
| `deals:read`        | Read deals, submissions and the pipeline              |
| `deals:write`       | Create and update deals, move stages                  |
| `documents:read`    | Read documents and statement analysis                 |
| `documents:write`   | Upload documents and request documents from merchants |
| `lenders:read`      | Read lenders, criteria and lender matches             |
| `lenders:write`     | Create and update lenders and criteria                |
| `submissions:write` | Submit deals to lenders and withdraw submissions      |
| `offers:read`       | Read offers                                           |
| `offers:write`      | Log and update offers, set the primary offer          |
| `advances:read`     | Read funded advances and renewal flags                |
| `advances:write`    | Update advances                                       |
| `portal:send`       | Generate and send merchant portal links               |
| `webhooks:manage`   | Manage webhook subscriptions                          |

Two details worth knowing: reading submissions comes with `deals:read` (there is no `submissions:read`), and `PATCH /v1/tasks/{id}` accepts either `businesses:write` or `deals:write`.

## Revoke a key

Settings → Developers → API keys → Revoke. Revocation is immediate; the next request with that key gets `401` with `message: "API key has been revoked"`. Expired keys answer the same way with `"API key has expired"`.

## OAuth access tokens

Access tokens minted by the [authorization server](/mcp/oauth-and-security) for MCP clients are accepted on `/v1` with the same `Authorization: Bearer` header. They act as the user who approved them (that user's role and deal visibility) with the scopes shown on the consent screen. Tokens are audience-bound: a token minted for the MCP server also works on `/v1`, but a token minted for `/v1` (with `resource=https://api.nextlevelmca.com`) is not accepted on `/mcp`. If the user is deactivated or removed from the location, their tokens stop working immediately.


## Related topics

- [Conventions](/guides/conventions.md)
- [Overview](/getting-started/overview.md)
- [OAuth and security](/mcp/oauth-and-security.md)
