Members are your users. We provide an identity and a ledger; pricing, limits, and risk controls are yours to manage.
Member Creation and Lifecycle
Members Belong to You
We do only two things for a member: provide an identity unique within your merchant, and a ledger organized by asset.
What we do not decide, because it is your business:
- How much to charge them (your retail pricing)
- How large each transaction may be (your member limits)
- How to manage their risk (your member risk controls)
Want to see the whole sequence first—member creation → KYC → service access?
See the diagram in Member Creation and the Complete KYC Flow.
Save the Payee
POST /v1/members
{ "external_member_id": "u_88123", "email": "…" }
external_member_id is the user ID in your system. We use it for idempotency: creating with the same value again returns 200 and the original member. It does not create another member or emit a member.created event.
⚠ An idempotent match returns 200; initial creation returns 201. The status code reliably identifies a new member,
as does a
member.createdevent. Do not infer it from a combination of the two—events are asynchronous.
Identifiers: Both Work, but Do Not Confuse Them
| Identifier received | Format |
|---|---|
id | mem_<uuid>—with its prefix, not a bare UUID |
external_member_id | The string you supplied |
Which location accepts which identifier:
| Location | Accepted identifier |
|---|---|
x-on-behalf-of | Either type |
GET /v1/members/{id}, /v1/members/{id}/limits | Either type |
POST /v1/members/{id}/sessions/revoke | Only external_member_id |
⚠ Do not strip the prefix. If you remove
mem_before storing the ID and then send the bare UUID,it is looked up as an
external_member_id—404member_not_found,an error that looks like “this member does not exist” but actually means “you used the wrong identifier.”
⚠ The prefix selects one lookup, without fallback. Therefore, **do not use an
external_member_idstarting with
mem_**—that member will always return 404 in the locations above.
Change Email: email-sessions
POST /v1/members/{id}/email-sessions → { "hosted_url": "…" }
Pass the link to the end user. They enter the new address on our page, and we send one verification code to each of the old and new addresses. Both must be entered correctly to make the change.
Why this is not a PATCH field: email is this member's only step-up authentication factor on our platform. They have no platform password (creation uses a random placeholder), no passkeys, and no trusted devices. An endpoint that directly changes email would let you redirect any member's factor to your own mailbox and subsequently pass step-up authentication on their behalf to reveal card credentials or add withdrawal addresses. That would be a complete account takeover path.
The two codes prove different things:
| Code | Proves |
|---|---|
| New address | They actually own this new mailbox (preventing a typo that makes the account permanently unreachable) |
| Current address | They are the owner of this account |
⚠ A successful change revokes all of the member's platform sessions, trusted devices, and passkeys. This does not directly affect you (your members do not use our sessions), but if you cache their email, refresh it.
uid Is the Member Number, Available at Creation
uid in the POST /v1/members response is nine digits—the default internal transfer recipient_type refers to it.
⚠ Before 2026-08-14 this was always an empty string (allocation happened only during member-side registration).
Rather than an immediate error, the symptom was persistent “recipient not found” when transferring with the default value;
operations staff also could not find the member by number. This is fixed; all newly created members have one.
Suspend and Restore
POST /v1/members/{id}/suspend { "suspended": true }
This flag only affects the member's availability under your merchant. Suspension and restoration share one event, member.suspended, distinguished by data.status.
⚠ This event can arrive again (suspend → restore → suspend again), and
status_versionis always 0.Do not skip equal versions—that would discard restoration and leave the member permanently suspended in your system.
⚠⚠ This is not the only event with a constant
status_versionof 0. Other recurring events include
kyc.result.updated(sent across rejection → supplements → approval) and wealth interest payments.Skipping versions less than or equal to the current one would respectively cause
KYC results to remain stuck at the first event and all interest payments from the second day onward to be lost.
Branch by event type: use
status_versionto merge forward only for events carrying object state (orders, cards);process constant-0 events in
created_atorder.See Webhook Overview for the constant-0 types.
Platform-level bans are separate and not visible to you. We may suspend a member for violating platform rules; that is independent of your flag and is not lifted when you restore them.