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

# Overview

> Register, verify, and manage end-users within your partner scope.

The Partner API lets you provision end-users, run identity verification (KYC), and query account status — all from your own backend, using your own branded experience.

```
End User → Your App → Your Backend → Qash Partner API
                             ↑
                   X-Api-Key + X-Api-Secret
```

## Base URL

| Environment         | URL                       |
| ------------------- | ------------------------- |
| Production          | `https://api.qash.ai`     |
| Staging             | `https://staging.qash.ai` |
| Local (api-gateway) | `http://localhost:8080`   |

All partner endpoints are under `/api/v1/partner` or `/api/v1/user`.

## Authentication

There are two different credentials in this API, and each endpoint uses exactly one — never both.

**Before a user logs in** — user registration, profile, and KYC — you authenticate with your partner credentials, and pass `userId` in the request body (POST/PATCH) or as a query parameter (GET):

```http theme={null}
X-Api-Key:    qash_key_live_xxxxx
X-Api-Secret: qash_secret_live_xxxxx
```

**After a user logs in** — accounts, deposits, transfers, and card issuance — you authenticate with the QASH access token returned by [Verify login code](/api-reference/users/verify-otp):

```http theme={null}
Authorization: Bearer <user-access-token>
```

This is the same session token the QASH app itself uses. Your `X-Api-Key` is not sent on these calls — the token already identifies both the user and your partner account.

<Warning>
  `X-Api-Secret` is shown **only once** at generation time. If lost, revoke the key and create a new pair from **Qash Dashboard → Settings → API Keys**.
</Warning>

## Endpoints

### User management — API key only

| Method | Endpoint                        | Description                                 |
| ------ | ------------------------------- | ------------------------------------------- |
| `POST` | `/api/v1/partner/users`         | Register a new end-user                     |
| `GET`  | `/api/v1/partner/users`         | List your users with filters and pagination |
| `GET`  | `/api/v1/partner/users/:userId` | Get full details of a user                  |

### Login — API key in, access token out

| Method | Endpoint                      | Description                                                                     |
| ------ | ----------------------------- | ------------------------------------------------------------------------------- |
| `POST` | `/api/v1/partner/auth/send`   | Send a one-time email code to the user                                          |
| `POST` | `/api/v1/partner/auth/verify` | Verify the code — returns the user's access and refresh tokens                  |
| `POST` | `/api/v1/partner/auth/login`  | **Deprecated** — required the Privy SDK client-side. Use `send`/`verify` above. |
| `POST` | `/api/v1/partner/auth/token`  | **Removed** — no longer used. See note below.                                   |

### User profile — API key, before login

| Method  | Endpoint                                | Description                                              |
| ------- | --------------------------------------- | -------------------------------------------------------- |
| `POST`  | `/api/v1/user/profile`                  | Create the user's personal profile — required before KYC |
| `GET`   | `/api/v1/user/profile`                  | Get the user's personal profile                          |
| `PATCH` | `/api/v1/user/profile`                  | Update profile fields                                    |
| `GET`   | `/api/v1/user/profile/check-completion` | Check whether profile is ready for KYC                   |

<Note>
  Once the user has logged in, they can also read and update their own profile with their access token via `GET`/`POST`/`PATCH` `/api/v1/auth/profile` — the same underlying data, authenticated with `Authorization: Bearer` instead of your API key.
</Note>

### KYC — API key, before login

| Method | Endpoint                         | Description                                        |
| ------ | -------------------------------- | -------------------------------------------------- |
| `POST` | `/api/v1/partner/kyc`            | Initiate identity verification (server-to-server)  |
| `GET`  | `/api/v1/user/kyc/status`        | Get current KYC status                             |
| `POST` | `/api/v1/auth/kyc/verify`        | Re-sync KYC status against Persona                 |
| `GET`  | `/api/v1/auth/kyc/simple-status` | Get a lightweight KYC status string — always `200` |
| `GET`  | `/api/v1/user/kyc/document/me`   | Get document details from KYC verification         |
| `GET`  | `/api/v1/user/kyc/history`       | Get all KYC verification attempts                  |

<Note>
  **`POST /api/v1/partner/auth/token` was removed** — it exchanged a bare `userId` for a JWT with no proof the request came from the user. Login is now a two-step, email-verified exchange: [Send login code](/api-reference/users/send-otp) and [Verify login code](/api-reference/users/verify-otp). See [Exchange token](/api-reference/users/exchange-token) for the full history.
</Note>

## Onboarding flow

```mermaid theme={null}
sequenceDiagram
    participant PB as Partner Backend
    participant Q as Qash
    participant EU as End User

    PB->>Q: POST /api/v1/partner/users
    Q-->>PB: { id, status: "pending" }

    PB->>Q: POST /api/v1/user/profile (userId in body)
    Q-->>PB: { profile }

    PB->>Q: GET /api/v1/user/profile/check-completion?userId=...
    Q-->>PB: { isComplete: true }

    PB->>Q: POST /api/v1/partner/kyc (userId in body)
    Q-->>PB: { verificationUrl, inquiryId }

    PB->>EU: Redirect to verificationUrl

    EU->>EU: Completes identity verification via Persona

    Note over Q,EU: Persona notifies Qash automatically — status moves to active

    PB->>Q: POST /api/v1/partner/auth/send (email)
    Q-->>EU: Emails a 6-digit code

    PB->>Q: POST /api/v1/partner/auth/verify (email + code)
    Q-->>PB: { accessToken, refreshToken }

    Note over PB,EU: User active, logged in — ready to operate with accessToken

    PB->>Q: GET /api/v1/partner/users/:userId (poll)
    Q-->>PB: { status: "active" }

    Note over PB,EU: User active — ready to operate
```

## User status lifecycle

| Status         | Description                               | Can operate? |
| -------------- | ----------------------------------------- | ------------ |
| `pending`      | Registered, KYC not yet initiated         | No           |
| `kyc_required` | KYC initiated, awaiting user verification | No           |
| `active`       | KYC approved, fully operational           | Yes          |
| `suspended`    | Temporarily suspended                     | No           |
| `banned`       | Permanently blocked                       | No           |

## Data isolation

Your API key scopes all queries — you can only see and manage users you created. A `404` on any user endpoint means either the user doesn't exist or belongs to a different partner.

## Credential rotation

If your `X-Api-Secret` is compromised:

1. Go to **Qash Dashboard → Settings → API Keys**
2. Revoke the current key
3. Generate a new key pair
4. Update your backend with the new credentials

## Common errors

| HTTP status | Error                                  | Cause                                            |
| ----------- | -------------------------------------- | ------------------------------------------------ |
| `400`       | Validation message                     | Missing required field or invalid format         |
| `401`       | `Invalid partner credentials`          | Wrong or missing `X-Api-Key` / `X-Api-Secret`    |
| `403`       | `User does not belong to this partner` | `userId` belongs to a different partner          |
| `404`       | `User not found`                       | User doesn't exist or belongs to another partner |
| `409`       | `KYC already in progress`              | KYC was already initiated for this user          |
| `503`       | —                                      | Authentication service unavailable               |
