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

# Authentication

> Authentication in sandbox is structurally identical to production.

## Overview

Sandbox authentication uses the same two layers as production:

1. **X.509 certificate (mTLS)** — issued by NTX Pay at onboarding.
2. **OAuth 2.0 `client_credentials`** — `clientId` + `clientSecret` received during signup.

Together they return a **JWT** (10-minute validity) used on the remaining endpoints as `Authorization: Bearer ...`.

<Info>
  Sandbox credentials are **distinct** from production. If you use production credentials against `https://sandbox.mx.ntxpay.com`, you will get `401`. The HTTP contract is identical — what changes is the certificate + clientId/clientSecret pair.
</Info>

## Get a Token

### POST /api/auth/token

```bash theme={"system"}
curl -X POST https://sandbox.mx.ntxpay.com/api/auth/token \
  -H "X-SSL-Client-Cert: $ENCODED_CERT" \
  -H "Content-Type: application/json" \
  -d '{
    "clientId": "qr-93-550e8400",
    "clientSecret": "a1b2c3d4e5f6g7h8"
  }'
```

#### Response (201)

```json theme={"system"}
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 600,
  "scope": "email profile"
}
```

## Use the Token

On a sandbox account, any authenticated call simulates the full pipeline without moving real money:

```bash theme={"system"}
curl -X POST https://sandbox.mx.ntxpay.com/api/spei/cash-out \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "amountCentavos": 15000,
    "destinationClabe": "012180001234567890",
    "beneficiaryName": "Maria Lopez",
    "externalId": "test-001"
  }'
```

The response is always `201 Created` with `status: PENDING`. The final outcome (confirmation or failure) arrives via webhook \~1 second later. See [Scenarios](/en/sandbox/scenarios) to force specific outcomes.

## Renewal

The token expires in **10 minutes (600s)**. There is no refresh token — get a new one via `POST /api/auth/token` before expiration.

<Warning>
  Under high load, mint a token per worker and renew every \~8 minutes to avoid `401` due to expiration.
</Warning>

## Common Errors

| Code  | Cause                             | Fix                                                              |
| ----- | --------------------------------- | ---------------------------------------------------------------- |
| `400` | `X-SSL-Client-Cert` missing       | Configure NGINX/ALB to forward the certificate                   |
| `401` | Invalid `clientId`/`clientSecret` | Double-check credentials; confirm you are using the sandbox ones |
| `401` | Certificate expired/revoked       | Request renewal from NTX Pay                                     |

## Detailed documentation

For the full step-by-step (certificate encoding, examples in multiple languages, etc.) see [Authentication](/en/guides/authentication) in the main guide — the only difference is the base URL `https://sandbox.mx.ntxpay.com`.
