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

# Identity and verification

> Which identity numbers a customer can hold, how they are stored, and what tier 1 does and does not permit.

This is the customer-specific integration guide: what you can put on a customer record by way of identity, and what follows from it downstream.

## Identity fields

Which fields apply depends on `type`, and sending one that does not apply is refused rather than ignored.

| Field | Applies to | Format | Notes |
| - | - | - | - |
| `bvn` | Individual | Exactly 11 digits | Nigerian Bank Verification Number. |
| `nin` | Individual | Exactly 11 digits | Nigerian National Identity Number. |
| `passport` | Individual | Up to 50 characters | Free-form; no jurisdiction is inferred from it. |
| `driversLicence` | Individual | Up to 50 characters | Free-form. |
| `taxId` | Both | Up to 50 characters | The only identity field available on both customer types. |
| `registrationNumber` | Business | Up to 50 characters | Company registration number. |

All six are optional. A customer with none of them is a valid customer.

<Note>
  `jurisdiction` is a two-letter country code and is not validated against the identity fields. A customer with `jurisdiction: "GH"` and a `bvn` is accepted; nothing cross-checks them.
</Note>

## They go in and do not come out

Identity numbers are **write-only**. No endpoint returns one, and no webhook payload carries one. The response reports presence instead:

```json theme={null}
{
  "hasBvn": true,
  "hasNin": false,
  "hasPassport": false,
  "hasDriversLicence": false,
  "hasTaxId": false,
  "hasRegistrationNumber": false
}
```

If you need to display or re-check a number, keep your own copy under your own controls. Sakawave will not give it back.

<Warning>
  These are regulated personal data. Keep them out of logs, error reports, analytics, and support tickets, and never send a real one to sandbox — every example in these pages uses a synthetic value.
</Warning>

## Uniqueness

Within your business, each identity number must be unique among customers that are not archived — one BVN cannot back two of your active customers. The same person may legitimately be a customer of two different Sakawave businesses, so uniqueness is scoped to you, not to the platform.

A collision returns `409 customer_already_exists`, the same code as a duplicate `externalReference`. The response does not say which value collided, so when you handle that error, check the reference first:

```ts theme={null}
// A 409 whose externalReference is not on file means an identity number
// collided — most often the same person onboarding under a second reference.
const existing = await get(`/v1/customers?externalReference=${reference}`);
const cause = existing.items.length ? 'duplicate_reference' : 'duplicate_identity';
```

## Correcting and clearing

Identity fields are patchable like any other. An explicit `null` clears one:

```bash theme={null}
curl --request PATCH \
  --url "$SAKAWAVE_BASE_URL/v1/customers/$CUSTOMER_ID" \
  --header "Authorization: Bearer $SAKAWAVE_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{ "bvn": "22212345678", "nin": null }'
```

Clearing a value frees it for another of your customers immediately.

## Tiers

Sakawave defines three verification tiers:

| Tier | Meaning |
| - | - |
| 1 | Contact details on file. No verified identity. |
| 2 | Identity verified — BVN and NIN, a passport or licence, or a registered entity. |
| 3 | Enhanced due diligence, including a mandatory tax identity. |

**Every customer is created at tier 1.** Supplying a BVN records the number; it does not verify it and does not raise the tier. The tier becomes 3 when the customer passes verification, and no endpoint sets it directly.

## Verification events

Each change in a customer's verification is sent as its own event:

| Event | When | Tier |
| - | - | - |
| `customer.kyc.approved` | The customer passed verification, including re-approval after a rejection or suspension. | Becomes 3 |
| `customer.kyc.rejected` | The customer failed verification, including after an earlier approval. | Unchanged |
| `customer.kyc.suspended` | An approval was withdrawn. New account details cannot be issued until the customer is approved again. | Unchanged |

Nothing is sent while a verification is still under review.

`data` is the customer object with one extra field:

```json theme={null}
{
  "kyc": {
    "status": "suspended",
    "tier": 3,
    "failureReason": "Periodic review",
    "resolvedAt": "2026-08-15T10:30:00.000Z"
  }
}
```

`failureReason` is the provider's reason on rejection or suspension, when it gives one, and always `null` on approval. A customer can send several of these over its lifetime, so act on the most recent by `resolvedAt` and deduplicate on the `Sakawave-Webhook-Id` header.

## What tier 1 means downstream

The tier is not decorative — it is what the account service reads when a customer wants a wallet. Two consequences follow, and both surprise people.

### Customer wallets are NGN only

A wallet account for a customer can only be denominated in NGN. Any other currency is refused:

```json theme={null}
{
  "statusCode": 403,
  "message": "International wallets for your customers are not available yet. Your own approval cannot be used for a customer wallet.",
  "code": "international_customer_wallet_not_supported",
  "currency": "USD"
}
```

This holds even when your own business is approved for that currency. Your approval covers your accounts, not your customers'.

### Tier 1 limits are low, by design

A customer's wallet inherits the customer's tier, and a tier-1 customer wallet in NGN is capped at:

| Limit | Tier 1 |
| - | -: |
| Wallet balance at any moment | ₦50,000 |
| Total outflow per calendar day | ₦50,000 |

A customer is deliberately held below an individual client: your business owns the relationship and the risk, and the customer has been verified by you rather than by Sakawave. If your product needs higher headroom per end user today, hold the balance on your own account and track your users' shares yourself.

## Practical guidance

* **Collect what you need, not what is available.** An unused identity number is a liability with no benefit; each one you store is one more thing to protect and one more uniqueness collision to handle.
* **Treat `has*` as an audit signal, not a verification signal.** It answers "did we send this?", not "is it real?".
* **Design for the limits you have.** Build against the ₦50,000 tier-1 ceiling and NGN-only wallets rather than an upgrade path that does not exist yet.
* **Keep your own verification record.** If you run KYC yourself, its outcome lives in your system. Sakawave has nowhere to put it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.