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

# Authentication

> Authenticate Paylead API calls with a JWT bearer token issued from Shift. One token per environment, carrying the scopes it needs.

export const PRODUCTION_BASE_URL = "https://api-{programRef}.paylead.eu";

export const SANDBOX_BASE_URL = "https://api-{programRef}.sandbox.paylead.tech";

Every Paylead API call carries two things:

1. A **bearer access token** in the `Authorization` header.
2. The mandatory **`X-Api-Version`** header (see [Versioning](/program/user-journey/versioning)).

**Why this matters.** Leaked credentials grant access to your [Program](/glossary#program) data. Treat your API client secret like a password: store it in a secret manager, and never commit it to source control.

## How authentication works

Authentication is a two-step model. You hold long-lived **client credentials** and exchange them for short-lived **access tokens**.

<Steps>
  <Step title="Create an API client in Shift">
    In Shift, create an API client for your service. Shift returns a `client_id` and a `client_secret` (prefixed `sk_…`). The secret is shown **once**; copy it into your secret manager immediately.
  </Step>

  <Step title="Exchange the credentials for an access token">
    Call the token endpoint (`POST /tokens`) with your `client_id` and `client_secret`. Paylead returns a short-lived **access token** plus an `expires_in` (about an hour).
  </Step>

  <Step title="Call the API with the token">
    Send the access token as a bearer credential, together with the `X-Api-Version` header. Refresh the token by calling the token endpoint again before it expires.
  </Step>
</Steps>

## Credentials vs. tokens

These are two different things; keep them straight:

|                | Client secret                          | Access token                                             |
| -------------- | -------------------------------------- | -------------------------------------------------------- |
| Looks like     | `sk_<env>_…` (prefixed)                | An **ES256 JWT** (`header.payload.signature`, no prefix) |
| Lifetime       | Long-lived; rotate on leak             | Short-lived (\~1 hour, see `expires_in`)                 |
| Where it lives | Your secret manager, never in code     | In memory; fetch on demand and cache until expiry        |
| Used to        | Obtain access tokens at `POST /tokens` | Authenticate each API call                               |

<Note>
  Never send your `client_secret` on a regular API call. It is only ever used against the token endpoint to mint an access token.
</Note>

## Get your API client credentials

Create an API client in Shift (one per environment) under **Settings > API Keys**. Shift returns a `client_id` and a `client_secret`. For the full walkthrough (signing in, creating, viewing, and revoking clients) see [API keys](/program/shift/api-keys) and [Sign in to Shift](/program/shift/sign-in-and-users).

<Warning>
  The `client_secret` is shown exactly once. Copy it into your secret manager immediately. If you close the dialog without copying it, delete the client and create a new one.
</Warning>

## Exchange credentials for an access token

Call the token endpoint with your `client_id` and `client_secret`. The response carries the access token and its lifetime (`expires_in`, in seconds).

<Note>
  **Code examples coming soon.** The Paylead API (v2) is still under construction. Request and response examples for this section will be published once the contract is finalized. In the meantime, contact your Paylead account manager for early-access details.
</Note>

<Tip>
  Cache the access token until shortly before `expires_in` elapses, then request a new one. Do not call the token endpoint on every API request.
</Tip>

## Call the API

Pass the access token as a bearer credential and set the mandatory `X-Api-Version` header. All calls must use HTTPS.

<Note>
  **Code examples coming soon.** The Paylead API (v2) is still under construction. Request and response examples for this section will be published once the contract is finalized. In the meantime, contact your Paylead account manager for early-access details.
</Note>

A call without a valid token returns `401 Unauthorized`. A call missing the `X-Api-Version` header is rejected; see [Versioning](/program/user-journey/versioning). See [Errors](/program/user-journey/errors) for the full list.

## Sandbox vs. production credentials

Sandbox and production credentials are separate. A sandbox client cannot mint tokens that work against production, and the reverse.

| Property      | Sandbox                                     | Production                         |
| ------------- | ------------------------------------------- | ---------------------------------- |
| Secret prefix | `sk_…` (sandbox)                            | `sk_…` (production)                |
| Base URL      | <code>{SANDBOX_BASE_URL}</code>             | <code>{PRODUCTION_BASE_URL}</code> |
| Access        | Granted on signup                           | Granted after sandbox validation   |
| Data          | Test entities created by the client, no PII | Real Consumers, real transactions  |

<Note>
  Use distinct secret-manager entries for each environment. Mixing them risks pointing your staging environment at production data.
</Note>

## Rotate credentials

Access tokens expire on their own (about an hour), so there is nothing to rotate there. What you protect and rotate is the **client secret**.

<Steps>
  <Step title="Create a new client">
    In Shift, create a new API client for the same environment. It returns a fresh `client_id` and `client_secret`.
  </Step>

  <Step title="Roll the new credentials to your service">
    Update the secret in your secret manager. Redeploy or hot-reload the services that read it, so they exchange the new secret at the token endpoint.
  </Step>

  <Step title="Verify traffic is on the new client">
    Watch your logs or Shift's API activity panel. Confirm requests are succeeding with tokens minted from the new secret.
  </Step>

  <Step title="Revoke the old client">
    Delete the old client in Shift. It can no longer mint tokens, and tokens already issued from it expire within the hour.
  </Step>
</Steps>

<Tip>
  Keep both clients active during rotation. Revoke the old one only after the new one is verified in production traffic. This avoids downtime if the rollout fails.
</Tip>

## Security checklist

* Store the `client_secret` in a secret manager (Vault, AWS Secrets Manager, GCP Secret Manager). Never in code, never in environment files committed to Git.
* Restrict who can create API clients in Shift. Audit the Program Manager role.
* Use a separate API client per service. If one service is compromised, you revoke one client, not all.
* Never log the `client_secret` or the access token.
* Log token usage on your side (request ID, timestamp). Correlate with Shift's audit log if you suspect misuse.

<Warning>
  If a `client_secret` leaks, delete that client in Shift immediately. Tokens it already issued expire within the hour. Then audit the previous 30 days of API activity for unexpected calls.
</Warning>

## What's next

<CardGroup cols={2}>
  <Card title="Versioning" icon="git-branch" href="/program/user-journey/versioning">
    The mandatory `X-Api-Version` header and the version policy.
  </Card>

  <Card title="Rate limits" icon="gauge" href="/program/user-journey/rate-limits">
    Per-token request limits and backoff strategy.
  </Card>

  <Card title="Errors" icon="triangle-alert" href="/program/user-journey/errors">
    Diagnose `401`, `403`, and other auth-related failures.
  </Card>

  <Card title="Environments" icon="layers" href="/program/user-journey/environments">
    Switch from sandbox to production safely.
  </Card>
</CardGroup>
