User Management
A user is your end customer — the person who holds a wallet inside your company. Creating one provisions a full ledger automatically: USD account, ZiG account, and an escrow reserve, ready to transact.
Every request requires x-api-key and x-company-id. Users are scoped to a company — a user created under one company cannot be accessed or transacted against from another.
What gets created when you register a user
User registration is a transactional operation. A single POST /v1/identity/users call creates the following records atomically — either all succeed or none do.
Stores mobile number, name, email, KYC level, and any custom metadata you pass. Scoped to your company.
The normalized Zimbabwe ID (e.g. 631234567F15). One identity record is shared across all users with the same physical ID number.
A USER-type ledger with a USD wallet, a ZiG wallet, and a USD escrow reserve — all starting at zero.
If you pass a bankAccount object at creation time, a linked bank account record is created in the same transaction.
Create a user
This endpoint accepts multipart/form-data so you can attach an identity document in the same call. If no file is uploaded now, you can upload it later via POST /v1/identity/users/:id/document.
curl -X POST "https://api.switchlock.co.zw/v1/identity/users" \
-H "x-api-key: sl_live_a1b2c3d4e5f6..." \
-H "x-company-id: cmp_01HXYZ..." \
-F "mobileNumber=+263771234567" \
-F "idNumber=63-1234567-F-15" \
-F "firstName=Tendai" \
-F "lastName=Moyo" \
-F "email=tendai@example.co.zw" \
-F 'metadata={"tier":"standard","source":"mobile_app"}' \
-F 'bankAccount={"institutionId":"inst_ecocash","accountNumber":"0771234567"}' \
-F "document=@/path/to/id-scan.jpg;type=image/jpeg"{
"id": "usr_01HABC...",
"mobileNumber": "+263771234567",
"firstName": "Tendai",
"lastName": "Moyo",
"email": "tendai@example.co.zw",
"kyc_level": 1,
"identity": {
"id": "nid_631234567F15",
"id_number": "631234567F15",
"status": "CLEAR",
"document_url": "https://cdn.switchlock.co.zw/signed/..."
},
"user_bank_accounts": [
{
"accountNumber": "0771234567",
"accountName": "Tendai Moyo",
"institution": { "id": "inst_ecocash", "name": "EcoCash" }
}
],
"company": { "id": "cmp_01HXYZ...", "name": "Acme Marketplace" }
}The document_url in the response is a time-limited signed URL — valid for a short window. Do not store it; fetch the user record again whenever you need to display the document.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| mobileNumber | string | Yes | E.164 format. e.g. +263771234567 |
| idNumber | string | Yes | Zimbabwe National ID. Normalised before storage — dashes and spaces are stripped. |
| companyId | string | Via header | Required if x-company-id header is absent. Header takes precedence if both are sent. |
| firstName | string | No | Customer's first name. |
| lastName | string | No | Customer's last name. |
| string | No | Customer's email address. | |
| metadata | JSON string | No | Arbitrary key/value object for your own reference. Stored as-is. |
| bankAccount | JSON string | No | Object: { institutionId, accountNumber, accountName?, branchName?, branchCode? } |
| customReference | string | No | Used as the ledger reference when auto-assigning any pre-funded amounts. |
| document | file (binary) | No | Identity document scan. Uploaded to private storage. JPEG / PNG / PDF. |
Uploading identity documents
Documents are stored in private, signed storage. To upload or replace a document on an existing user, use the dedicated document endpoint. The field name is always document.
curl -X POST "https://api.switchlock.co.zw/v1/identity/users/usr_01HABC.../document" \
-H "x-api-key: sl_live_a1b2c3d4e5f6..." \
-H "x-company-id: cmp_01HXYZ..." \
-F "document=@/path/to/id-front.jpg;type=image/jpeg"{
"id": "usr_01HABC...",
"identity": {
"id": "nid_631234567F15",
"id_number": "631234567F15",
"status": "CLEAR",
"document_url": "https://cdn.switchlock.co.zw/signed/new-file..."
}
}Uploading a new document replaces the existing one on the user's NationalIdentity record. The previous file is not deleted from storage immediately — but the user's active document URL will point to the new file.
KYC levels
Every user has a kyc_level — a number that reflects how much the user's identity has been verified. Higher levels unlock higher transaction limits.
| Level | Label | How it's reached | Admin filter status |
|---|---|---|---|
| 1 | Basic | National ID linked. Document not yet required. | PENDING (only when document is present) |
| 2 | Verified | Document reviewed and approved by a SUPER_ADMIN via the admin API. | APPROVED |
| — | Blacklisted | Set on the NationalIdentity record — not the user. Blocks all future accounts linked to that ID number, across any company. | BLACKLISTED |
KYC approval is manual and async — a SUPER_ADMIN reviews the uploaded document and calls POST /v1/identity/admin/users/:id/approve. Your integration should check kyc_level before permitting high-value transactions.
Retrieve a user
curl -X GET "https://api.switchlock.co.zw/v1/identity/users/usr_01HABC..." \
-H "x-api-key: sl_live_a1b2c3d4e5f6..." \
-H "x-company-id: cmp_01HXYZ..."Returns the full user object including their linked identity, bank accounts, and company. The document_url is replaced with a fresh signed URL on every fetch — do not cache it.
{
"id": "usr_01HABC...",
"mobileNumber": "+263771234567",
"firstName": "Tendai",
"lastName": "Moyo",
"email": "tendai@example.co.zw",
"kyc_level": 1,
"identity": {
"id": "nid_631234567F15",
"id_number": "631234567F15",
"status": "CLEAR",
"document_url": "https://cdn.switchlock.co.zw/signed/..."
},
"user_bank_accounts": [...]
}List all users for your company
curl -X GET "https://api.switchlock.co.zw/v1/identity/users" \
-H "x-api-key: sl_live_a1b2c3d4e5f6..." \
-H "x-company-id: cmp_01HXYZ..."Returns all users for the company, ordered by created_at DESC. For paginated admin access with search and status filters, use GET /v1/identity/admin/users (requires SUPER_ADMIN role).
[
{
"id": "usr_01HABC...",
"mobileNumber": "+263771234567",
"firstName": "Tendai",
"lastName": "Moyo",
"kyc_level": 1
},
{
"id": "usr_01HXYZ...",
"mobileNumber": "+263779876543",
"firstName": "Farai",
"lastName": "Ndlovu",
"kyc_level": 2
}
]Get a user's ledger
Each user has a single primary ledger per company. Use this to read balances across their USD account, ZiG account, and escrow reserve.
curl -X GET "https://api.switchlock.co.zw/v1/identity/users/usr_01HABC.../ledger" \
-H "x-api-key: sl_live_a1b2c3d4e5f6..." \
-H "x-company-id: cmp_01HXYZ..."{
"id": "ldg_01HABC...",
"type": "USER",
"accounts": [
{ "id": "acc_01HUSD...", "type": "USER", "currency": "USD", "balance": "5000" },
{ "id": "acc_01HZIG...", "type": "USER", "currency": "ZIG", "balance": "0" },
{ "id": "acc_01HESC...", "type": "ESCROW", "currency": "USD", "balance": "0" }
]
}Next steps
Document types, Zimbabwe ID format, upload flow, and how admin review works.
Generate a payment link tied to a seller, amount, and product list.
How the double-entry ledger works and what each account type represents.
Every identity endpoint with all request and response fields documented.