SwitchLock Docs
Identity/Users

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.

User record

Stores mobile number, name, email, KYC level, and any custom metadata you pass. Scoped to your company.

National Identity

The normalized Zimbabwe ID (e.g. 631234567F15). One identity record is shared across all users with the same physical ID number.

Ledger + 3 accounts

A USER-type ledger with a USD wallet, a ZiG wallet, and a USD escrow reserve — all starting at zero.

Optional bank account

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"
Response — 201 Created
{
  "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

FieldTypeRequiredDescription
mobileNumberstringYesE.164 format. e.g. +263771234567
idNumberstringYesZimbabwe National ID. Normalised before storage — dashes and spaces are stripped.
companyIdstringVia headerRequired if x-company-id header is absent. Header takes precedence if both are sent.
firstNamestringNoCustomer's first name.
lastNamestringNoCustomer's last name.
emailstringNoCustomer's email address.
metadataJSON stringNoArbitrary key/value object for your own reference. Stored as-is.
bankAccountJSON stringNoObject: { institutionId, accountNumber, accountName?, branchName?, branchCode? }
customReferencestringNoUsed as the ledger reference when auto-assigning any pre-funded amounts.
documentfile (binary)NoIdentity 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"
Response — 200 OK
{
  "id": "usr_01HABC...",
  "identity": {
    "id": "nid_631234567F15",
    "id_number": "631234567F15",
    "status": "CLEAR",
    "document_url": "https://cdn.switchlock.co.zw/signed/new-file..."
  }
}
Accepted formats
JPEG, PNG, PDF
Max file size
10 MB recommended
Storage
Private — access via signed URLs only

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.

LevelLabelHow it's reachedAdmin filter status
1BasicNational ID linked. Document not yet required.PENDING (only when document is present)
2VerifiedDocument reviewed and approved by a SUPER_ADMIN via the admin API.APPROVED
BlacklistedSet 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.

Response — 200 OK
{
  "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).

Response — 200 OK
[
  {
    "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..."
Response
{
  "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