Source: https://docs.debitura.com/referral-partners/getting-started/authentication

# API keys and authentication

|  | What you send | Starter Integration | Full Integration |
| --- | --- | --- | --- |
| **API key** | `XApiKey` header on the Referral Partner API | Everything | Onboarding, tokens, reporting, webhooks |
| **Bearer token** | `Authorization: Bearer` on the Customer API, per client | Only if you add Customer API calls | Case submission and management |

The Starter Integration needs only the API key. See [How the Full Integration works](https://docs.debitura.com/referral-partners/getting-started/how-it-works.md) for why the Full Integration uses two APIs.

<a id="your-api-key-both-integrations"></a>

## Your API key (both integrations)

Include your API key in the `XApiKey` header for all Referral Partner API requests:

```http
GET /clients HTTP/1.1
Host: referral-api.debitura.com
XApiKey: YOUR_API_KEY
```

Contact [partnerships@debitura.com](mailto:partnerships@debitura.com) to obtain your API key.

For base URLs (including test environment), see [Environments](https://docs.debitura.com/referral-partners/getting-started/environments.md).

<a id="bearer-tokens-full-integration"></a>

## Bearer tokens (Full Integration)

To submit cases on behalf of referred clients, generate a JWT token using the OAuth endpoint:

```http
POST /oauth/token HTTP/1.1
Host: referral-api.debitura.com
XApiKey: YOUR_API_KEY
Content-Type: application/json

{
  "externalTenantId": "your-tenant-identifier"
}
```

Response:

```json
{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "tokenType": "Bearer",
  "expiresIn": 1800,
  "externalTenantId": "your-tenant-identifier",
  "creditorId": "uuid-of-linked-creditor"
}
```

<a id="token-details"></a>

### Token Details

| Property | Value |
| --- | --- |
| Algorithm | HS256 |
| Expiry | 30 minutes (1800 seconds) |
| Refresh | Not supported — request a new token when expired |

<a id="what-minting-a-token-requires"></a>

### What Minting a Token Requires

The only requirement is an **active client link** for that `externalTenantId` under your partner account — a link that has not been withdrawn, reset, or otherwise archived.

SDCA completion is not required to mint a token

You can mint a token for a client who has not yet signed the Standard Debt Collection Agreement. Contract requirements are enforced when you **submit a case**, not when you request a token: an unsigned client's case submission is rejected with `422` and a `solutionUrl` pointing at the signing flow (see [White-Label UI](https://docs.debitura.com/referral-partners/concepts/white-label-ui.md)). Minting a token early is therefore a normal part of the `202 Accepted` onboarding path.

Failure responses:

| Response | Meaning |
| --- | --- |
| `400` | `externalTenantId` is missing, empty, or whitespace-only |
| `404` | No active client link exists for that `externalTenantId` under your partner account — including a client you have withdrawn or, in test, [reset](https://docs.debitura.com/referral-partners/getting-started/environments.md#reset-test-clients) |
| `500` | Unexpected server error — retry |

A `404` here is an ordinary business outcome, not an error to alert on. If you expect the client to be linked, call `POST /clients` again with the same `externalTenantId` to re-establish the link before retrying the token request.

<a id="using-the-token"></a>

### Using the Token

Include the JWT in Customer API requests via the `Authorization` header:

```http
POST /cases HTTP/1.1
Host: customer-api.debitura.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

{
  "currencyCode": "EUR",
  "amountToRecover": 5000.00,
  "creditorReference": "INV-2025-001",
  "debtor": { ... }
}
```

<a id="token-expiry-handling"></a>

## Token Expiry Handling

Tokens expire after 30 minutes. Unlike typical OAuth flows, there is **no refresh token** — you must mint a new token when the current one expires.

This is different from most OAuth implementations

Most OAuth providers issue a refresh token alongside the access token. Debitura does not. Plan your implementation to handle token expiry by re-minting.

**Recommended pattern:** wrap Customer API calls in a retry-on-401 handler:

```javascript
async function callCustomerApi(method, path, body, externalTenantId) {
  let token = await getOrMintToken(externalTenantId);

  let response = await fetch(`https://customer-api.debitura.com${path}`, {
    method,
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: body ? JSON.stringify(body) : undefined,
  });

  // Token expired — mint a new one and retry once
  if (response.status === 401) {
    token = await mintNewToken(externalTenantId);
    response = await fetch(`https://customer-api.debitura.com${path}`, {
      method,
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: body ? JSON.stringify(body) : undefined,
    });
  }

  return response;
}
```

**Key points:**

-   Cache tokens per `externalTenantId` and reuse them until they expire
-   On `401 Unauthorized`, mint a fresh token and retry the request **once**
-   Do not retry more than once — a second 401 means the client link is invalid, not just an expired token

For rate limits, see [Rate Limiting](https://docs.debitura.com/developer-concepts/rate-limiting.md). For full endpoint details, see the [Referral Partner API Reference](https://docs.debitura.com/api/referral-partner.md) and [Customer API Reference](https://docs.debitura.com/api/customer.md).
