Skip to content

Account Provisioning

How it works

Provisioning creates a Regini USDC account for one of your users. You do this once per user. It is idempotent, so calling it twice for the same external_user_id returns the same account_id without creating a duplicate.

What happens when you call POST /accounts:

  1. Regini registers the user and opens a USDC account, returning an account_id immediately
  2. In the background, a Turnkey sub-wallet is provisioned on-chain for the account (takes a few seconds; the account is usable for deposits and withdrawals before this completes)
  3. You store the account_id on your side. This is the identifier for every subsequent API call

The user's phone, first_name, and last_name are collected at this step because they are required by M-Pesa for STK push and payout. You can update them later via PATCH /accounts/{account_id} if they change.

KYC: Regini does not perform identity verification. You run your own KYC and pass the result as kyc_status: "verified". Unverified accounts cannot initiate withdrawals.


List accounts

GET /accounts

Returns all accounts you have provisioned, sorted newest first.

Query parameters:

Parameter Default Description
limit 20 Results per page (max 100)
page 1 Page number

Response:

{
  "total_count": 2,
  "page": 1,
  "limit": 20,
  "accounts": [
    {
      "account_id": "3f8a1b2c-...",
      "status": "active",
      "external_user_id": "your-internal-user-id",
      "yield_enabled": false,
      "created_at": "2026-07-21T10:00:00Z"
    }
  ]
}

Create an account

POST /accounts

Creates a Regini USDC account for one of your users. Returns an account_id that you store on your side and use for all subsequent API calls.

This call is idempotent on your external_user_id; calling it twice for the same user returns the same account_id.

Request:

{
  "external_user_id": "your-internal-user-id",
  "phone": "+254700000000",
  "first_name": "Jane",
  "last_name": "Doe",
  "program_id": "your-program-uuid",
  "kyc_status": "verified",
  "yield_enabled": true
}
Field Required Description
external_user_id Yes Your internal user identifier. Used for idempotency.
phone Yes E.164 format (e.g. +254700000000). Used for M-Pesa.
first_name Yes User's first name
last_name Yes User's last name
program_id Yes Your Regini program UUID (provided during onboarding)
kyc_status Yes Pass "verified" when your user has passed KYC. Unverified accounts cannot withdraw.
yield_enabled No Whether this account earns yield on its USDC balance. Defaults to your tenant's yield setting, if yield is enabled for your tenant, this is true unless you explicitly pass false. Phase 2 feature; has no effect in Phase 1.

Response:

{
  "account_id": "3f8a1b2c-...",
  "created": true
}
Field Description
account_id Store this; used for all subsequent calls
created true if a new account was provisioned; false if the external_user_id was already registered (idempotent return)

Wallet provisioning

The account is immediately active for KES deposits and withdrawals. A Turnkey sub-org (on-chain wallet) is provisioned asynchronously in the background. Check wallet_provisioning_status via GET /accounts/{account_id} if you need to verify wallet readiness. In sandbox, the wallet is marked active immediately; no async step.


Get account details

GET /accounts/{account_id}

Returns account status and the stored user profile.

{
  "account_id": "3f8a1b2c-...",
  "status": "active",
  "wallet_provisioning_status": "active",
  "yield_enabled": false,
  "user": {
    "external_user_id": "your-internal-user-id",
    "first_name": "Jane",
    "last_name": "Doe",
    "phone_number": "+254700000000",
    "kyc_verified": true
  }
}
Field Description
status Account status: active, inactive, or suspended. inactive is set via POST /accounts/{id}/deactivate. suspended is set by Regini (e.g. for compliance reasons), contact support.
wallet_provisioning_status pending, active, or failed; tracks on-chain wallet setup. If failed, contact Regini support, the account remains usable for M-Pesa deposits and withdrawals but on-chain operations will be unavailable.
yield_enabled Whether the account participates in yield pools (Phase 2). Reflects the value set at provisioning, subject to the tenant-level yield flag. Migration to yield for existing accounts is handled by Regini, not via the partner API.

Update an account

PATCH /accounts/{account_id}

Updates the phone number or name stored on the account. At least one field must be provided. Only the supplied fields are changed.

Request:

{
  "phone": "+254711111111",
  "first_name": "Jane",
  "last_name": "Smith"
}
Field Required Description
phone No New M-Pesa number in E.164 format. Used for all future STK pushes and payouts.
first_name No Updated first name
last_name No Updated last name

Response: The updated account object (same shape as GET /accounts/{account_id}).

Warning

Updating phone affects all future M-Pesa transactions on this account. Confirm the new number is correct and belongs to the same user before calling this endpoint.


Deactivate an account

POST /accounts/{account_id}/deactivate

Sets the account status to inactive. Inactive accounts cannot initiate new deposits or withdrawals.

No request body required.

Response:

{
  "account_id": "3f8a1b2c-...",
  "status": "inactive"
}

Returns the same response if the account is already inactive (idempotent).

Errors:

Code HTTP When
DEACTIVATION_BLOCKED 409 A withdrawal is currently in-flight; wait for it to complete

Info

Re-activation requires Regini support. Deactivation does not affect funds, the user's USDC balance is preserved and can be viewed via GET /accounts/{account_id}/balance.