Accounts and identity
Headless provisioning without confusing app identity with custody.
Read Wallets and funds for custody, stable IDs and account-lifecycle boundaries. Your app owns login and user authorization.
Create the user's account
Before you start: configure the server client, authenticate your own user, and save their stable external ID and provisioning intent. Account access must be enabled for your integration. Example IDs and keys below are illustrative: use your own persisted values. HTTP tabs omit signing headers; see HTTP setup.
const result = await crisp.createAccount(
{
externalUserId: 'example:user-123',
externalIdentity: { label: 'Username', value: '@example' },
},
'onboard-example-user-123',
);POST /v1/accounts accepts:
| Field | Rules |
|---|---|
| externalUserId | Required stable string, 1–200 letters/digits or ., _, :, @, - |
| referrerExternalUserId | Optional existing account registered under your organization; attribution commits atomically with account onboarding |
| Optional contact email; encrypted, not verified or login-enabled | |
| externalWalletAddress | Optional 20-byte EVM address; encrypted, unverified, not managed wallet or default withdrawal destination |
| externalIdentity | Optional { label, value }; label starts with a letter, up to 64 letters/digits/spaces/underscores/hyphens; value up to 320 characters |
No generic attributes, hosted return URLs, provider credentials or arbitrary
metadata. externalIdentity is your operator-facing canonical display label,
not wallet authority. Keep external IDs opaque and stable; display names can change.
Successful create returns 202 with data.crispAccountId, externalUserId,
status, failureCode, createdAt and updatedAt. Persist the account mapping.
Repeated provisioning upserts the same external ID, rather than making a second
wallet. Display identity can be updated by provisioning again. Do not use this
endpoint as a general profile/contact editor. Provisioning replay resolves the
current account rather than freezing an obsolete asynchronous status.
For new referral integrations, supply the direct referrer here rather than
running a separate attribution worker. If the referrer does not exist, CRISP
returns 409 referrer_not_found and does not create the referred account/job.
Onboard the referrer first and retry the same intent. Referrer wallet readiness
is not required. Conflicting immutable edges, self-referrals and cycles fail
without partial writes. Omitting this field retains the existing behavior.
Wait until ready
await crisp.waitForAccount(externalUserId) polls for you. Inspect
result.data.status: ready, failed and revoked all stop the helper.
A deadline stops waiting, not provisioning. For HTTP integrations, poll
GET /v1/accounts/{externalUserId} with bounded backoff.
| Account state | What your product does |
|---|---|
| provisioning / wallet_pending | Wait and poll |
| polymarket_pending | Wallet exists, venue setup is incomplete; wait |
| ready | Enable eligible funding/trading capabilities |
| failed | Keep trading unavailable; send safe failure/request IDs to CRISP |
| revoked | Deny access to integration activity |
Reads: GET /v1/accounts/{externalUserId}, /profile, /wallet. Encode path
segments with encodeURIComponent. Profile can return profile_not_available
when no contact/display/profile data exists; account provisioning can still be
valid. Managed addresses only appear once created. Do not confuse an external
contact wallet with the actual trading/deposit address.
GET /v1/accounts?limit=50&offset=0 lists the accounts registered under the organization identified by your API key;
status optionally filters. General offset pagination allows limit 1–200.
Do not expose this organization-wide endpoint directly to every end user.
Your backend must authorize every account path. A valid API key can access the accounts registered under your organization and therefore is more privileged than a single user's login.
Done when: account status is ready. Next, fund the account.
Exact fields: createAccount and
getAccount.