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

# Customers (WaaS)

> One customer record per end-user — the same record used for wallets and cards. Create with your own externalId to stay idempotent.

A **customer** is the single, canonical record for one of your end-users. The same customer is used for wallets **and** cards — you never create the customer twice. Provide the fields a card would need up front and the one record satisfies both.

<Note>
  This is the canonical customer endpoint. It supersedes the older `POST /api/v1/business/customers` create call — use `POST /api/v1/customers` for all new integrations.
</Note>

## Create or update a customer

```
POST /api/v1/customers
```

**Signature required:** Yes · **Scope:** `customers:write`

Upsert semantics: if you pass an `externalId` (your own stable id) that already exists, the record is updated in place — so a repeat is safe and stays in sync with your system.

### Request body

| Field                                                  | Type      | Required | Description                                                                                       |
| ------------------------------------------------------ | --------- | -------- | ------------------------------------------------------------------------------------------------- |
| `externalId`                                           | string    | No       | Your own stable id for this customer — makes upserts idempotent                                   |
| `firstName` / `lastName`                               | string    | No       | Customer's name                                                                                   |
| `email`                                                | string    | No       | Customer's email                                                                                  |
| `phone`                                                | string    | No       | Customer's phone                                                                                  |
| `dateOfBirth`                                          | date      | No       | ISO date (`1990-05-01`)                                                                           |
| `country`                                              | string    | No       | ISO-3166 alpha-2 (e.g. `NG`)                                                                      |
| `bvn`                                                  | string    | No       | 11-digit BVN — stored as an **unverified claim** until Numero verifies it (see KYC below)         |
| `address1` / `address2` / `city` / `state` / `zipcode` | string    | No       | Address — needed for tier 2 (payout / card)                                                       |
| `idType` / `idNumber`                                  | string    | No       | Government ID — needed for tier 2                                                                 |
| `idDocumentFrontUrl` / `idDocumentBackUrl`             | string    | No       | Hosted URLs of the ID document images                                                             |
| `metadataJson`                                         | string    | No       | Free-form JSON string you control                                                                 |
| `profiles`                                             | string\[] | No       | Which profiles to attach — e.g. `["wallet","card"]`. The same identity fields serve every profile |

<Warning>
  Supplying a `bvn` (or any ID field) does **not** raise the customer's KYC tier. A tier only moves when Numero itself verifies the evidence — see below. This is deliberate: a provider accepting a payload is not proof anyone checked it.
</Warning>

### Request example

```bash theme={null}
curl -X POST "https://api.usenumero.com/numeroaccount/api/v1/customers" \
  -H "Content-Type: application/json" \
  -H "X-Numero-Api-Key: your_api_key" \
  -H "X-Numero-Signature: $SIGNATURE" \
  -H "X-Numero-Signature-Version: v2" \
  -d '{
    "externalId": "user_10231",
    "firstName": "Ada",
    "lastName": "Obi",
    "email": "ada@example.com",
    "phone": "08031234567",
    "country": "NG",
    "profiles": ["wallet","card"]
  }'
```

### Response

```json theme={null}
{
  "data": {
    "id": 4021,
    "externalId": "user_10231",
    "firstName": "Ada",
    "lastName": "Obi",
    "email": "ada@example.com",
    "phone": "08031234567",
    "country": "NG",
    "kycTier": 0,
    "kycStatus": null,
    "capabilities": [
      "hold_wallet",
      "receive_internal_transfer"
    ],
    "status": "Active",
    "isTest": false,
    "createdAt": "2026-07-24T09:12:00Z"
  },
  "error": null,
  "meta": {
    "request_id": "req_365123ebc73323f1482508560ce6400e",
    "pagination": null
  }
}
```

`id` is **our** canonical customer id. Your `externalId` and the per-provider issuer ids (for cards) all resolve to this one record.

## Read a customer

```
GET /api/v1/customers/{id}
GET /api/v1/customers/by-external-id/{externalId}
```

**Scope:** `customers:write`. Returns the same customer object as the upsert, including the live `kycTier` and `capabilities`.

## Submit KYC (verify identity, raise the tier)

```
POST /api/v1/customers/{id}/kyc
```

**Signature required:** Yes · **Scope:** `kyc:write`

Submit one identity item at a time. Numero verifies it through a real provider check (billed to your account), records the pass, and **recomputes the customer's tier** from everything verified so far. A tier only rises when a check genuinely passes — never from a value you merely supply.

| `type`                      | Verifies                                                             | Counts toward |
| --------------------------- | -------------------------------------------------------------------- | ------------- |
| `BVN`                       | Bank Verification Number                                             | tier 1        |
| `NIN`                       | National Identity Number                                             | tier 2        |
| `PASSPORT` / `FRSC` / `PVC` | Government ID — passport / driver's licence / voter's card (any one) | tier 2        |
| `ADDRESS`                   | Residential address                                                  | tier 3        |

Default ladder (admin-configurable): **BVN** → tier 1 (receive external); **BVN + NIN + a government ID** → tier 2 (pay out, card); **+ address** → tier 3 (elevated limits).

```bash theme={null}
# BVN (also accepts the legacy bare { "bvn": "..." } body)
curl -X POST ".../api/v1/customers/4021/kyc" -H "X-Numero-Api-Key: your_api_key" \
  -H "X-Numero-Signature: $SIGNATURE" -H "X-Numero-Signature-Version: v2" \
  -d '{ "type": "BVN", "payload": { "bvn": "22222222222" } }'

# NIN
curl -X POST ".../api/v1/customers/4021/kyc" -H "X-Numero-Api-Key: your_api_key" \
  -H "X-Numero-Signature: $SIGNATURE" -H "X-Numero-Signature-Version: v2" \
  -d '{ "type": "NIN", "payload": { "nin": "12345678901" } }'

# Government ID (e.g. passport)
curl -X POST ".../api/v1/customers/4021/kyc" -H "X-Numero-Api-Key: your_api_key" \
  -H "X-Numero-Signature: $SIGNATURE" -H "X-Numero-Signature-Version: v2" \
  -d '{ "type": "PASSPORT", "payload": { "passportNumber": "A01234567" } }'
```

Each response is the updated customer with the new `kycTier` and `capabilities`. Submitting an item whose category is already verified is **idempotent — it is not re-charged**.

<Note>
  The tiers, their requirements, and their limits are configured by your Numero admin and may differ from the defaults shown here. Always read `kycTier` and `capabilities` from the response rather than hard-coding the ladder.
</Note>
