> For the complete documentation index, see [llms.txt](https://docs.moderncollections.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.moderncollections.io/getting-started/authentication.md).

# Authentication

The Modern Collections platform exposes three independent authentication surfaces. As an integrator you will almost always touch only one — the **creditor API key**. This page documents all three for reference.

## At a glance

| Surface               | What it gates                                              | Format                                     | When you use it                                                                                        |
| --------------------- | ---------------------------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------ |
| **Creditor API key**  | REST API for creditor-owned data                           | `ca_{prefix}_{secret}`                     | Almost all integrations. The default.                                                                  |
| **Debtor portal JWT** | Debtor self-service (view own balance, pay, dispute, etc.) | JWT, issued via the debtor magic-link flow | Rare for integrators. The debtor portal is its own product surface.                                    |
| **Operator session**  | Internal cross-tenant operations                           | Internal staff sign-in                     | Not exposed to partners; documented only so you understand what admin actions can affect your account. |

The remainder of this page focuses on the **creditor API key** flow because it covers almost all integrations.

## Creditor API keys

### Getting a key

There are two issuance paths. Both return the plaintext key **exactly once** — the platform stores only a bcrypt hash plus the 8-character public prefix.

**Self-serve onboarding wizard:**

1. `POST /v1/creditors` — public signup. Returns a short-lived (24h) onboarding JWT.
2. Driven by that JWT, the wizard moves the account through the verification state machine (`pending_signup` → `pending_verification` → `pending_bank_link` → `pending_agreement` → `verified`): KYB submission, a Stripe Connect bank link, and click-through MSA acceptance.
3. `POST /v1/creditors/me/issue-api-key` mints the key once the full gate clears (verified status + payouts enabled + accepted MSA on file). If a key was already issued this returns `409` — a lost key is rotated, never retrieved.

**Operator-provisioned:**

1. A prospective creditor POSTs to `/v1/auth/signup` (or is provisioned by an admin).
2. On approval, the admin endpoint (`POST /v1/admin/creditors/{creditor_id}/approve`) mints the API key and returns the plaintext once.

### Format

```
ca_a3f8c0d1_qY8...long-random-secret...
```

* `ca_` — fixed prefix identifying a Modern Collections creditor key.
* `a3f8c0d1` — 8-character public prefix. Indexed on the creditors table for O(1) lookup.
* `qY8...` — high-entropy secret. Stored only as a bcrypt hash.

Any deviation from `ca_<prefix>_<secret>` is rejected at the auth layer with `401`.

### Using the key

Every REST API request sends the key as a **Bearer token** in the `Authorization` header.

{% tabs %}
{% tab title="curl" %}

```bash
curl https://<api-host>/v1/placements \
  -H "Authorization: Bearer ca_a3f8c0d1_yourSecretHere"
```

{% endtab %}

{% tab title="Python" %}

```python
import httpx

client = httpx.Client(
    base_url="https://<api-host>/v1",
    headers={"Authorization": "Bearer ca_a3f8c0d1_yourSecretHere"},
)
response = client.get("/placements")
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
const response = await fetch(
  "https://<api-host>/v1/placements",
  {
    headers: {
      Authorization: "Bearer ca_a3f8c0d1_yourSecretHere",
    },
  },
);
```

{% endtab %}
{% endtabs %}

Substitute `<api-host>` with the base URL for your provisioned environment — production is `api.moderncollections.io`. See [Environments](/getting-started/environments.md) for the full base-URL table.

### Key hygiene

* **Never commit to a repository.** Use a secret manager.
* **Revoke / rotate** by contacting support. Rotation is immediate — the old key stops working the moment the new one is issued, with no grace period, so coordinate rotation with your deploy.
* A creditor whose account status is not `verified` (e.g. `pending_*`, `suspended`, `rejected`) is rejected at the auth layer with `403 Account not active`, even if the key itself is valid.

### Permissions and scopes

Creditor API keys grant **full access to the creditor account they belong to** with no cross-tenant access. There are no per-key permission scopes today.

## Debtor portal JWTs

The debtor portal is reached via magic link. After OTP verification, the JWT (sent as a bearer token to `/v1/portal/*` routes) carries the bound `placement_id`, `debtor_id`, and `creditor_id` claims. Server-side, three Postgres GUCs (`app.current_creditor_id`, `app.current_debtor_id`, `app.current_placement_id`) are set so portal-self RLS policies apply.

State-changing portal routes (e.g. `POST /v1/portal/payment-intents`, `POST /v1/portal/disputes`) additionally require the session row to be **OTP-verified**.

## Admin sessions

Admin endpoints under `/v1/admin/*` are restricted to Modern Collections operator sessions. They are not exposed to partner integrators.

## Security expectations on the integrator side

* Use TLS 1.2 or higher.
* Treat the API key as you would a database password.
* When logging requests on your side, **never log the `Authorization` header.**

## See also

* [Environments](/getting-started/environments.md) — production and demo base URLs.
* [Error handling](/getting-started/error-handling.md) — what 401s vs. 403s mean.
* [Data handling and PII](/compliance-for-integrators/data-handling-pii.md) — what we send you and what you can store.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.moderncollections.io/getting-started/authentication.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
