EMBEDDED_WALLET
internal account that holds USDB and that only the customer can sign for.
Grid cannot debit that account the way it debits a custodial funding source,
and a card authorization has to be answered in seconds, so the cardholder
cannot sign each purchase. Instead the cardholder authorizes a delegated
key once. Grid custodies the key and uses it to sign the pull from the
wallet at every authorization.
This page covers issuing the card, creating the delegated key, what happens
when an authorization arrives, and what fails when the card has no active
key.
Prerequisites
- Cards are enabled for your platform, and your Lightspark contact has enabled Embedded Wallet card funding for it.
- The cardholder is a
CustomerwithkycStatus: APPROVEDand has anEMBEDDED_WALLETinternal account. To find it, list the customer’s internal accounts withGET /internal-accounts?customerId=...and pick the account withtype: "EMBEDDED_WALLET". - The wallet is provisioned. Grid provisions the wallet’s signer the first time a credential on the account is verified, so the cardholder must have completed at least one credential verification. See Authentication.
- Your client can hold a session signing key and stamp a
payloadToSign. Creating the delegated key needs two stamps from the cardholder’s active session. See Client keys. - The wallet holds USDB on Spark. Each authorization spends the wallet’s on-chain balance at authorization time.
How the pieces fit
- You issue the card with the wallet account as its
fundingSource. - The cardholder authorizes a delegated key for that card and account through a three-leg signed-retry flow. Grid generates the keypair, registers it as a signer on the wallet, and keeps the private key.
- When an authorization arrives, Grid runs its usual decisioning, then pays the hold from the wallet: it builds a USDB transfer for the authorized amount, signs it with the delegated key, and broadcasts it on Spark.
- Clearings capture the held USDB. Reversals and expiries return it to the wallet. Neither needs the cardholder.
Issue the card
Issue the card the same way as any other, with the Embedded Wallet account as the funding source:currency is USD. The wallet holds USDB, which Grid converts
1:1, so a $12.50 authorization pulls 12.50 USDB.
The card comes back in status: "PROCESSING" and activates through the
CARD.STATUS_CHANGE webhook as described in
Issuing cards. An ACTIVE card with
no active delegated key declines every authorization, so complete the next
section before you show the card to the cardholder.
Set maxSpendPerTransaction and maxSpendPerDay. Spend controls work the same
way for a wallet-funded card as for any other card: the values on the card cap
what that card can authorize, and cardConfigs in your platform config can set
platform-wide caps that apply to every card you issue. When both are set, Grid
enforces the lower of the two.
Create the delegated key
POST /auth/delegated-keys is a three-leg flow. Every leg sends the same
body; the second and third legs add a stamp from the cardholder’s session
signing key. Your backend relays each payloadToSign to the client and the
resulting stamp back to Grid, the same shape as
passkey registration.
Leg 1: request the challenge
Send the request with no signature headers.cardId and
internalAccountId identify the card funding source; the account must be
the card’s current fundingSource.
202:
Leg 2: authorize the signer
The client stampspayloadToSign byte for byte with the session signing
key. Retry the same request with the stamp in Grid-Wallet-Signature and
the requestId in Request-Id:
202
with a new payloadToSign and requestId. The key now exists in
PENDING status: the signer is registered but holds no signing policy yet,
so it cannot sign.
Leg 3: grant the signing policy
The client stamps the newpayloadToSign. Retry once more with the new
Request-Id. The response is 201 with the key in ACTIVE status:
publicKey identifies the credential and
is not needed for anything else.
Rules
- A card funding source has at most one
ACTIVEkey. A second create returns409 CONFLICTuntil you revoke the first. - A flow abandoned after leg 2 leaves a
PENDINGkey. It cannot sign and does not block creating another key. - The key does not expire. It stays
ACTIVEuntil you revoke it. - The key is bound to the pair of card and funding account. If you replace
the card’s
fundingSourcewith a different account, create a new key for the new pairing and revoke the old one.
Errors
What happens at authorization
When the network asks Grid to authorize a purchase on the card:- Grid applies the same checks as for any card: the card is
ACTIVE, the amount is within the card’s and the platform’s spend limits, and program rules allow the merchant. - Grid builds a USDB transfer from the wallet for the authorized amount, signs it with the delegated key, and broadcasts it on Spark. The wallet’s balance drops by the authorized amount now, not at clearing.
- Grid approves the authorization once the broadcast is accepted and sends
CARD_TRANSACTION.AUTHORIZED.accountIdon the transaction is the wallet account.
- Clearing. Grid captures the held USDB. The wallet is not touched again for the authorized amount. If the merchant clears for more than the authorization, Grid pulls the difference from the wallet the same way it pulled the hold, signed with the delegated key.
- Reversal or expiry. Grid returns the held USDB to the wallet on Spark. The return is a separate on-chain transfer into the wallet and does not use the delegated key.
- The balance check is real and synchronous. Grid selects the wallet’s USDB outputs before it signs. If the wallet cannot cover the amount, the authorization is declined, and no hold is placed.
- Approval waits on the broadcast. The authorization is approved only after the signed transfer has been accepted by the Spark network.
What fails without an active key
Grid checks for the delegated key when it goes to sign the pull, not before. If the card’s funding source has noACTIVE key (never created, still
PENDING, or revoked), the signing step fails and Grid declines the
authorization. Nothing is pulled from the wallet.
The card transaction is recorded with status: "DECLINED" and
cardDeclinedReason: "OTHER", and a CARD_TRANSACTION.DECLINED webhook
fires. The decline does not name the missing key, so check for it directly
when you investigate a declined card funded from a wallet:
data array, or one with no ACTIVE entry, means every
authorization on that card declines until the cardholder completes the
three-leg flow again.
The same decline path applies when the cardholder revokes the key: the card
stays ACTIVE, and every authorization declines. To stop a card from
spending without touching the key, freeze it with PATCH /cards/{id} and
status: "FROZEN".
Manage the key
List the keys for an account or a funding source. At least one filter is required.PENDING and REVOKED keys are included:
204. Revoking a key that is not ACTIVE returns
400 INVALID_INPUT. Revocation is permanent. To let the card spend again,
run the three-leg flow to create a new key.
Revoke the key when the card is closed or when the cardholder asks to
withdraw Grid’s authority over the wallet. Closing the card does not revoke
the key.
Testing in Sandbox
The flow is the same in Sandbox. Sandbox wallets run on Spark regtest, and a sandbox authorization pulls regtest USDB from the wallet into a sandbox treasury.- Sandbox accepts a real session stamp on the delegated-key legs. The
legacy value
Grid-Wallet-Signature: sandbox-valid-signatureis also accepted. POST /sandbox/internal-accounts/{id}/fundcredits the account’s ledger balance only. A card hold spends the wallet’s balance on Spark, so fund the wallet with a quote whose destination is the wallet account, as described in the Global Accounts walkthrough, before you simulate an authorization.POST /sandbox/cards/{id}/simulate/authorizationwith any non-magic descriptor drives the pull. Run it once with the key in place and once after revoking the key to see both outcomes.