Customers who hold a Global Account must be KYC/KYB verified before any account funds can move from or to fiat rails. This quickstart picks up after KYC is complete.In sandbox, customers are automatically KYC approved on creation so you can skip straight to account setup.
You also need:
A platform configured with USDB in its supported currencies. In sandbox, USDB is enabled by default alongside USD and USDC.
Sandbox or production API credentials with access to the Embedded Wallet Auth and Internal Accounts endpoints.
Testing in sandbox. Sandbox accepts two fixed magic values so you can exercise the auth + signing flow without managing real OTP delivery or device-side keys:
Email OTP code: always 000000 for any EMAIL_OTP credential’s verify call.
Wallet signature: the Grid-Wallet-Signature header accepts the literal string sandbox-valid-signature for any signed account action. The encryptedSessionSigningKey returned by /verify in sandbox is a stub and is not meant to decrypt — skip the HPKE decrypt step entirely.
These shortcuts apply only on sandbox platforms. Production runs the real OTP, HPKE, and ECDSA flows described below.
The walkthrough below is the happy path: create a customer, find the auto-provisioned account, register a passkey, fund it, and withdraw to a bank account. Each step shows the HTTP request your integrator backend makes on behalf of the client.
Create the customer record. A Global Account is provisioned automatically whenever a customer is created on a platform that has USDB in its supported currencies — you don’t need to pass it on the customer.
Response:201 Created with the new Customer:... id. In sandbox, the customer is KYC-approved immediately; in production you would now run them through the KYC / KYB flow before any funds can move.
When a customer is created on a USDB-enabled platform, Grid automatically provisions a Global Account alongside their other internal accounts. Fetch it by filtering the customer’s internal accounts by type=EMBEDDED_WALLET.
curl -X GET "$GRID_BASE_URL/internal-accounts?customerId=Customer:019542f5-b3e7-1d02-0000-000000000001&type=EMBEDDED_WALLET" \ -u "$GRID_CLIENT_ID:$GRID_CLIENT_SECRET"
Global Accounts support three authentication credential types: passkey, OAuth (OIDC), and email OTP. A passkey is a user-friendly default: biometric, phishing-resistant, and usable across the user’s devices.Registration only binds the passkey to the account — it doesn’t issue a session. Sessions are created on-demand, when the customer initiates an action that needs a signature (step 7). The full flow with sequence diagram is documented in Authentication; the condensed version:
1
Your backend issues a WebAuthn challenge
Generate a random base64url challenge, store it short-lived in your session store, and return it to the client.
Grid verifies the attestation and replies 201 with the new AuthMethod:... id plus the first-authentication challenge, requestId, and expiresAt. Persist the auth method id against the customer — you’ll pass it to /challenge and /verify whenever the customer needs to sign.
The passkey is now bound to the account. You’ll use it to authorize the withdrawal in step 7.
Global Accounts behave like any other internal account on the way in — incoming funds do not need the customer’s signature. In sandbox, use the sandbox funding endpoint to skip straight to a funded state:
You will receive an INCOMING_PAYMENT webhook when the balance updates. The account now holds 1,000.00 USDB.
To fund from another currency (USD ACH, USDC on-chain, etc.), create a quote with destination.destinationType: "ACCOUNT" pointing at the Global Account’s InternalAccount id. The quote’s sourceCurrency can be any supported platform currency; Grid will convert into USDB on execute.
Create a quote with the Global Account as the source. Grid returns a payloadToSign in the quote’s payment instructions — this is what the client will sign to authorize the transfer.
The customer has an outstanding quote with a payloadToSign. Now we need a session signing key to sign it with — this is when the passkey actually gets used. The flow is keypair → challenge → assertion → verify → decrypt → sign.
1
Your backend requests a fresh challenge sealed to the client public key
The client generates a fresh P-256 client key pair and posts the public key (uncompressed hex) to your backend, which forwards it to Grid. Grid bakes the public key into the session-creation payload so the resulting session signing key is sealed to that device.
clientPublicKey is no longer required here — Grid already has it from the /challenge call. The Request-Id header ties this verify to that earlier challenge.Response (200):
Return encryptedSessionSigningKey and expiresAt to the client.
4
Client decrypts the session signing key and signs the payload
The client decryptsencryptedSessionSigningKey with the matching client private key, then signs the quote’s payloadToSign with the resulting session signing key. Return the base64 signature to your backend.
Sign the payloadToSign bytes exactly as Grid returned them. Do not parse, re-serialize, trim, or normalize the JSON — the signature must cover the same bytes Grid’s verifier hashes.
The session signing key is now valid for 15 minutes, so subsequent account actions within that window (for example, a second withdrawal) can reuse it without another /challenge + /verify round-trip.