- Host:
https://api.platform.sandbox.dakota.xyz. Cards is sandbox only for now. - Authenticate with your API key in the
x-api-keyheader. - Send an
X-Idempotency-Key(a UUID) on everyPOST,PATCH, andPUT, exceptPOST /cards/{card_id}/reveal_session. - Webhooks arrive on your existing stream, in the standard envelope. If your webhook target filters by
event_types, add the card event types first. See Cards webhooks.
Setup
1. Activate a customer for Cards
Once per customer. Dakota must have activated your account first. That step has no API; ask your Dakota contact.GET /customers/{customer_id}/capabilities. Find the entry withcapability: "cards".- If its
statusisaction_required, send the customer theurlof the requirement withkey: "cards_tos". The customer accepts the terms on a page Dakota hosts. - Wait for
customer.capability_status.updatedwithcapability: "cards"andstatus: "available".
403 cards-tos-not-accepted, or 403 cards-capability-unavailable when more than the terms is outstanding. Details: Activating customers.
2. Create a cardholder
Once per person. Pick the request shape that matches the customer:- For a business customer,
GET /customers/{customer_id}/persons. Pick a person whosecardholder_idisnull. Collect whatmissing_for_cardslists: alwaysphone, andemailwhen none is on file. POST /customers/{customer_id}/cardholders. Returns the cardholder withstatus: "pending".- Wait for
cardholder.updatedwithstatus: "active". This usually takes seconds.
409 cardholder-person-exists (the email belongs to a person already on the application; retry with the person_id it names) and 409 cardholder-already-exists. Details: Cardholders.
3. Enable a wallet for cards
Once per wallet, before its first card. The wallet must be an EVM wallet.- Have the customer sign the intent
{"type": "enable_card_settlement", "wallet_id": "…", "idempotency_key": "…"}, the same way they sign any endorsed request. POST /wallets/{wallet_id}/card_enablementwith the intent and signatures. Returnsstate: "attaching".- Wait for
wallet.card_enablement.completed. The state is thenactive. You can also readGET /wallets/{wallet_id}/card_enablement.
422 wallet-family-not-supported (not an EVM wallet) and 409 card-enablement-precondition (the wallet’s policies need fixing, or Dakota’s setup is incomplete). Details: Wallet enablement.
4. Issue a card
POST /customers/{customer_id}/cardswithcardholder_id,wallet_id, and optionallyspend_limit,nickname, andexternal_id. Returns the card withstatus: "pending".- Wait for
card.updatedwithstatus: "active". The card can now be revealed and spent.
403 cardholder-not-active, 403 wallet-not-card-enabled (do recipe 3 first), 400 limit-reached (the cardholder has too many active cards), and 409 cardholder-reenroll-required (delete the cardholder and create it again). Details: Issuing and managing cards.
Using cards
5. Show the card number to the cardholder
- In the browser, Cards.js calls your backend when the cardholder asks to see the card.
- Your backend authenticates the user and confirms they own the card. Then it calls
POST /cards/{card_id}/reveal_sessionwithsession_type: "card_details"and the page’sorigin. No idempotency key. - Your backend returns the session to Cards.js, which shows the details in a secure frame.
active. A session works once and expires within minutes. Details: Secure card data.
6. Freeze, unfreeze, or close a card
PATCH /cards/{card_id} with status set to frozen, active, or closed. Wait for card.updated.
Closing is permanent. Unfreezing can be refused: 403 card-freeze-held (a freeze you cannot lift, named in freeze_sources), 403 card-not-activated, 403 cardholder-suspended, or 403 card-closed.
7. Rename a card
PATCH /cards/{card_id} with nickname, up to 64 characters. A nickname can be replaced, but not removed.
8. Change a spend limit
PUT /cards/{card_id}/spend_limit with interval and amount in minor units. The new limit replaces the old one and applies from the next purchase.
9. Show the available balance
GET /wallets/{wallet_id}/balances. The card object carries total, held, outstanding, and available. Show available to the cardholder. A purchase can need slightly more than its own amount, so one for the entire available balance can be declined. Details: Card funding.
10. Track purchases
- Listen for
card_transaction.created(an approved or declined authorization) andcard_transaction.updated(every later change to the same purchase). Switch onstatus. - To backfill or reconcile,
GET /card_transactions, filtered bycustomer_id,card_id, orcardholder_id, andGET /card_transactions/{card_transaction_id}.
11. Find cards and cardholders
GET /customers/{customer_id}/cards, filtered bycardholder_id,status, orexternal_id.GET /cards/{card_id}also embeds the cardholder and the wallet.GET /customers/{customer_id}/cardholders, filtered byexternal_id, andGET /cardholders/{cardholder_id}.
external_id filters are exact and case-sensitive, and can match more than one record.
12. Report a dispute
POST /card_dispute_reports with the card_id, your client_reference, a reason, and when the cardholder told you. Dakota files the dispute with the network. Details: Disputes.
Offboarding
13. Remove a cardholder
- Close each of the cardholder’s cards with
PATCH /cards/{card_id}andstatus: "closed". The delete is refused while any of their cards is not closed. DELETE /cardholders/{cardholder_id}.- Wait for
cardholder.updatedwithstatus: "closed". The cardholder is no longer returned by the API.
PATCH /cardholders/{cardholder_id}.
Testing
14. Run a purchase in sandbox
POST /sandbox/wallets/{wallet_id}/faucetto fund the wallet. PollGET /sandbox/simulations/{simulation_id}until the funds land.POST /sandbox/cards/simulate/transactionto authorize a purchase.card_transaction.createdfires.POST /sandbox/cards/simulate/transaction/{card_transaction_id}with anactionsuch asclear,void, orreturn.card_transaction.updatedfires.

