← Documentation

API reference

Every endpoint, generated from the OpenAPI spec — so this page cannot drift from the contract your client is built against. Download the spec to generate a client or import it into Postman.

Base URLs

https://sandbox.identityinc.io
Sandbox — free test verifications (real Shufti test account), simulated chain. Keys are bi_test_… and must not be sent to production. Mint via POST /v1/billing/checkout on this host (see Quickstart).
https://api.identityinc.io
Production — the base URL for the API key issued at signup (bi_…)

All endpoints take Authorization: Bearer <key> unless marked Public. See Authentication.

Accounts

POST/v1/accounts/freeSponsor free provisional account

Request body

ownerPublicKeyrequiredstring
activePublicKeystring
passkeyCredentialIdstring
preferredNamestringRequested account name. Permanent once created. Conflicts with an existing name return 409 and hand the kycCreateToken back, so the caller can retry with a different name without repeating KYC. Pattern: ^[a-z1-5.]{1,12}$
deviceFingerprintstring
personDidstring
kycCreateTokenstringOne-time token from GET /v1/kyc/status after approval.
emailProofTokenstringFrom POST /v1/email/otp/confirm. Binds the verified address to the new account. Required when the organization sets requireEmailProofBeforeFreeCreate.
phoneProofTokenstringFrom POST /v1/phone/otp/confirm.

Responses

201Created
409Requested name, email, or number already taken
GET/v1/accounts/name-availableCheck whether an account name can be claimed

Advisory only. Nothing is reserved: the chain is the authority and /v1/accounts/free re-checks, so a name can still be lost between this call and create.

Parameters

namerequiredstring · query

Responses

200Availabilityname: string · available: boolean · reason: TAKEN | INVALID · message: string
POST/v1/accounts/unlockRemove faucet cosigner after bind

Responses

200Unlocked
GET/v1/accounts/{did}Account status, lifecycle, and active bindings

Parameters

didrequiredstring · pathAccount DID, URL-encoded (`did:antelope:<chain>:<name>`)

Responses

200Account with non-revoked bindings
404Account not found in this organization

KYC

POST/v1/kyc/startStart KYC; returns verification URL / access token

Person-first by default: omit `accountDid` to create a Person before any chain account (required when the organization gates free-create on KYC). Identity checks are document + face via Shufti Pro — BoundIdentity does not enable Shufti phone OTP for KYC. On sandbox, complete `verificationUrl` with a Shufti test ID (free on the test account).

Request body

accountDidstringOptional. Omit for person-first KYC before free-create.
levelNamestring

Responses

200Token issued
GET/v1/kyc/statusKYC status for a person

Parameters

personDidrequiredstring · query

Responses

200Status, level, provider, and country
404Person not found in this organization

KYB

POST/v1/kyb/startStart Know-Your-Business for a legal entity

Responses

200Organization pending

Bind

POST/v1/bind/challengeIssue bind nonce for wallet signature

Responses

200Challenge
POST/v1/bind/confirmConfirm bind with signature; issue AccountBinding VC

Responses

200Bound

Compliance

POST/v1/aml/screen-applicantScreen applicant id hash (sanctions / denylist)

Responses

200Screen result
POST/v1/risk/assessAnti-abuse risk score before free create / bind

Responses

200Assessment
POST/v1/compliance/exportExport hash-chained audit log for a date range

Responses

200Export job
GET/v1/compliance/exportsList recent compliance export jobs

Responses

200Jobs
GET/v1/compliance/export/{jobId}Fetch completed export artifact

Parameters

jobIdrequiredstring · path

Responses

200Job + artifact JSON
POST/v1/compliance/audit/verifyVerify audit hash-chain integrity

Responses

200Verification result

Admin

POST/v1/admin/accounts/suspendSuspend an account (compliance action)

Responses

200Suspended
GET/v1/admin/dashboardOrganization dashboard aggregates (lifecycle, AML, recent audit)

Responses

200Dashboard payload
GET/v1/admin/accountsList chain accounts for the organization

Responses

200Account list
GET/v1/admin/peopleList people / KYC subjects

Responses

200People list
GET/v1/admin/organizationsList KYB businesses

Responses

200Businesses
GET/v1/admin/auditHash-chained audit log entries

Responses

200Audit entries
GET/v1/admin/membersOrganization console memberships (requires admin scope)

Responses

200Members
POST/v1/admin/membersAdd a console member, or restore and re-role a revoked one

The membership roster decides who may sign in to the admin console and what they may do there. Roles, weakest first: `viewer`, `operator`, `compliance_officer`, `admin`, `owner`. Re-adding an existing address updates its role and reactivates it, so a returning colleague does not collide with their old row. While an organization has no members at all, the console accepts any address with the correct password and treats it as an `owner` — adding the first member closes that path.

Request body

emailrequiredstring (email)
rolerequiredviewer | operator | compliance_officer | admin | owner

Responses

201Member added or updated
PATCH/v1/admin/members/{email}Change a member's role or reactivate them

Parameters

emailrequiredstring (email) · path

Request body

roleviewer | operator | compliance_officer | admin | owner
activeboolean

Responses

200Updated
404No such member in this organization
DELETE/v1/admin/members/{email}Revoke a member's console access

Deactivates rather than deletes. The address remains the recorded actor on every audit entry the member produced, and removing the row that explains who they were would leave those entries unresolvable.

Parameters

emailrequiredstring (email) · path

Responses

200Revoked
404No such member in this organization
GET/v1/admin/amlPEP / sanctions alerts + AML audit events

Responses

200Screening feed
GET/v1/admin/riskAnti-abuse posture (velocity, shared devices, risk events)

Responses

200Risk summary
GET/v1/admin/tenantCurrent organization + policy (scoped by API key)

Responses

200Organization detail
GET/v1/admin/api-keysList API keys (prefixes only)

Responses

200Keys
POST/v1/admin/api-keysCreate or rotate API key (raw returned once)

Request body

namerequiredstringMax length 64.
revokeKeyIdstring (uuid)Deactivate this key after minting the new one
scopesread | accounts_write | kyc_write | bind_write | unlock_write | admin | compliance_export | webhook_manage[]Least-privilege scope set. **Omitting this grants every scope, including `admin`** — the historical behaviour of this endpoint. Pass a narrower set for a key that only needs part of the API, for example `["read"]` for a dashboard or `["accounts_write","kyc_write"]` for an onboarding service.

Responses

201Created — the raw key is returned once and never againid: string (uuid) · prefix: string · apiKey: string · scopes: string[] · revokedKeyId: string · warning: string · scopeNotice: string
DELETE/v1/admin/api-keys/{id}Revoke API key

Parameters

idrequiredstring · path

Responses

200Revoked
POST/v1/admin/signing/enableEnable HMAC request signing (secret once)

Responses

200HMAC secret
POST/v1/admin/signing/disableDisable HMAC request signing

Responses

200Disabled
PUT/v1/admin/ip-allowlistReplace organization IP allowlist

Responses

200Allowlist
POST/v1/admin/dpa/acceptAccept Data Processing Agreement (sets dpaAcceptedAt)

Responses

200Acceptance recorded
GET/v1/admin/webhooksList outbound webhooks

Responses

200Webhooks
POST/v1/admin/webhooksRegister outbound webhook (signing secret once)

Responses

201Registered
DELETE/v1/admin/webhooks/{id}Deactivate outbound webhook

Parameters

idrequiredstring · path

Responses

200Deactivated
GET/v1/admin/webhooks/deliveriesRecent outbound delivery attempts

Diagnostics for a receiver that is rejecting events: status, attempt count, and the last error per delivery. Scoped to your own webhooks.

Parameters

statuspending | failed | delivered | dead · query
limitinteger · query

Responses

200Delivery attempts
POST/v1/admin/webhooks/deliveries/{id}/replayResend a delivery that gave up

Re-queues a `failed` or `dead` delivery and resets its attempt counter, for use once the receiving system has been repaired. The signed body is stored with the delivery, so the replay carries the original payload and signature inputs rather than a regenerated approximation. Deliveries that already succeeded are refused — deduplicate on `X-BI-Delivery-Id` if you need to reprocess one.

Parameters

idrequiredstring · path

Responses

200Re-queued
409Already delivered, or the payload is no longer available
GET/v1/tenantCurrent organization posture (signing, allowlist, policy)

Responses

200Organization
PUT/v1/admin/country-policySet the organization's jurisdiction policy

Enforced when binding a person to an account: a country on `blockedCountries`, or absent from a non-empty `allowedCountries`, is refused. The blocklist takes precedence. When either list is non-empty a subject whose country is unknown is also refused, so the control cannot be bypassed by an unresolvable jurisdiction. Empty lists mean no restriction. Omit a field to leave it unchanged.

Request body

allowedCountriesstring[]ISO-3166 alpha-2 codes
blockedCountriesstring[]

Responses

200Policy appliedallowedCountries: string[] · blockedCountries: string[]
400A code was not a valid alpha-2

AML

POST/v1/aml/screen-accountKYT-style screen of a chain account

Request body

chainAccountrequiredstring

Responses

200Score and labels
POST/v1/aml/clearClear PEP / sanctions flags on a person

Compliance-officer action. Resets `pepFlag`, `sanctionsFlag`, and `riskScore`, and writes an `aml.cleared` audit entry with the reason. Requires `compliance_export`.

Request body

subjectDidrequiredstring
reasonrequiredstringMax length 500.

Responses

200Flags cleared
404Person not found in this organization

Billing

POST/v1/billing/chain/confirmPublicSettle an on-chain A payment for a Growth checkout

Verifies a transfer to the treasury carrying this checkout's memo, then provisions the organization. A transaction id can only ever pay for one checkout. Returns 425 while the transfer is still reversible — retry shortly rather than treating it as a rejection.

Request body

sessionIdrequiredstring
transactionIdrequiredstring
accountstringOptional payer; when set the transfer must originate from it

Responses

201Organization set up (API key returned once)
400Payment not verified
409Already provisioned
425Transfer seen but not yet irreversible — retry
GET/v1/billing/checkout/{sessionId}PublicPoll checkout status

Parameters

sessionIdrequiredstring · path

Responses

200Status with a masked email; never includes the API key
404Unknown session

Phone

POST/v1/phone/otp/startSend an SMS one-time code

Rate limited per phone and per client IP. Requires `accounts_write`. On hosted sandbox and production the code is delivered by SMS only — it is never returned in the response body.

Request body

phonerequiredstringE.164, or a local Ghana number

Responses

200Challenge issuedchallengeId: string (uuid) · expiresAt: string (date-time) · phoneMasked: string
429Too many codes for this phone or IP
POST/v1/phone/otp/confirmConfirm an SMS code, returning a short-lived phone proof

Capped at 5 attempts per challenge. The returned `phoneProofToken` is accepted by `POST /v1/accounts/free` for 15 minutes.

Request body

challengeIdrequiredstring (uuid)
coderequiredstring

Responses

200Proof issuedphoneProofToken: string · expiresInSec: integer · personDid: string · softIdentity: boolean
400Invalid or expired code
429Attempt cap reached for this challenge

Contact

Verified email and phone bound to an account. Contact channels are never identity evidence - KYC owns that.

POST/v1/email/otp/startEmail a verification code

Confirming an email never creates or promotes a Person. Email is a contact channel; KYC remains the only identity authority.

Request body

emailrequiredstring (email)

Responses

200Challenge issuedchallengeId: string (uuid) · expiresAt: string (date-time) · emailMasked: string · devCode: string
409Address already bound to an account
POST/v1/email/otp/confirmConfirm an emailed code; returns a short-lived emailProofToken

Request body

challengeIdrequiredstring (uuid)
coderequiredstring

Responses

200VerifiedemailProofToken: string · expiresInSec: integer · emailMasked: string
429Too many attempts on this challenge
GET/v1/accounts/{did}/contactMasked view of the contacts bound to an account

Parameters

didrequiredstring · pathAccount DID, or the bare chain name.

Responses

200Masked contacts; raw values are never returned
POST/v1/accounts/contact/challengeIssue a nonce the account's owner key must sign to change a contact

Contact changes are authorized by the account key, not by the session — whoever can change the address controls where account notices go. The nonce is single-use and consumed by the matching start call.

Request body

accountDidrequiredstring
channelrequiredemail | phone

Responses

200Challengenonce: string · payloadToSign: string · expiresAt: string (date-time)
POST/v1/accounts/contact/email/startSend a code to a proposed new email

Nothing is unbound until the code is confirmed.

Request body

accountDidrequiredstring
emailrequiredstring (email)
noncerequiredstring
signaturerequiredstring

Responses

200Challenge issued
400INVALID_SIGNATURE - the signature does not verify against the account's active key.
409Address already bound to another account
POST/v1/accounts/contact/email/confirmConfirm the code and swap the bound email

Detach and attach run in one transaction, so the account is never left without a verified email. The previous address is notified.

Request body

challengeIdrequiredstring (uuid)
coderequiredstring

Responses

200Changed
POST/v1/accounts/contact/phone/startSend a code to a proposed new phone number

Request body

accountDidrequiredstring
phonerequiredstring
noncerequiredstring
signaturerequiredstring

Responses

200Challenge issued
POST/v1/accounts/contact/phone/confirmConfirm the code and swap the bound phone number

Request body

challengeIdrequiredstring (uuid)
coderequiredstring

Responses

200Changed

Resolution

GET/.well-known/did.jsonPublicIssuer DID document (W3C did:web)

Publishes the Ed25519 `assertionMethod` key used to sign issued credentials. A relying party needs only this document to verify a credential offline.

Responses

200DID document
GET/people/{personId}/did.jsonPublicPerson DID document (W3C did:web)

Parameters

personIdrequiredstring (uuid) · path

Responses

200DID document exposing KYC status only
404Unknown person
GET/orgs/{orgId}/did.jsonPublicOrganization DID document (W3C did:web)

Parameters

orgIdrequiredstring (uuid) · path

Responses

200DID document exposing KYB status only
404Unknown organization

Ops

GET/v1/healthPublicLiveness

Responses

200OK
GET/v1/whoamiPublicThe source IP this API attributes to you

Returns the address tenant IP allowlists are compared against. Call it from the server that will hold your API key before configuring an allowlist: NAT and egress gateways mean the address we see is often not the one you expect, and an allowlist that does not match fails closed — every request returns 403. Unauthenticated, and returns nothing beyond your own address.

Responses

200The caller's resolved source addressclientIp: string
GET/v1/readyPublicReadiness

Responses

200Ready
503Not ready
GET/v1/metricsMetrics (Prometheus text exposition)

Requires `read` scope.

Responses

200text/plain exposition
401Missing or invalid API key
GET/v1/billing/plansPublicPublic Identity Inc. plan catalog

Responses

200Plans
POST/v1/billing/ai/advisePublicAI checkout advisor (rate-limited)

Responses

200Recommendation
429Rate limited
POST/v1/billing/checkoutPublicBuilder signup, Growth Stripe Checkout, or Institutional lead

Responses

200Redirect or sales lead
201Builder provisioned
429Rate limited
POST/v1/billing/stripe/webhookPublicStripe lifecycle + checkout.session.completed

Responses

200Received
POST/v1/billing/checkout/completePublicComplete a pending Growth checkout (sandbox / test settlement)

Responses

200Provisioned or upgraded
POST/v1/billing/checkout/revealPublicReveal API key once after Stripe Checkout (rate-limited)

Responses

200Key payload
GET/v1/billing/usagePlan + usage meters for the authenticated organization

Responses

200Usage snapshot
POST/v1/billing/upgradeBuilder → Growth Checkout (keeps organization + keys)

Responses

200Checkout redirect
POST/v1/billing/portalStripe Customer Portal session

Responses

200Portal URL
API reference · Identity Inc.