Skip to main content
The Corporate Card API lets a business issue and manage spend cards for its own employees. An Employee is the true card holder — created once per person, KYC’d, funded from a balance the business assigns to them, and then able to hold any number of cards with no artificial limit. All endpoints below require the same authenticated, per-business request context used across the rest of the API (API key + bearer token). Routes carry no customer_id/employee_id in the URL — both are passed as request fields (query string on GET, request body on POST). Every customer_id must belong to the authenticated business account, and every employee_id must belong to that same customer_id — requests for data outside your own business return 404.

Access requirements

Business Spend Cards must be enabled for your account before any of these endpoints can be used. Two checks apply to every Corporate Card request, and to the employee portal and /api/v1/spend/* and /api/v1/business/spend-card/* endpoints:
  1. Your account is approved for Business Spend Cards. Approval is granted by Yativo — contact support to request it. Until then every request returns 403: Your account is not yet approved for Business Spend Cards. Please contact support.
  2. The customer holds an approved faster_payments endorsement. Checked for whichever customer the request targets — directly via customer_id, or through an employee_id or card_id that belongs to it. Otherwise: This customer does not have an approved Faster Payments endorsement. (403)
Requests that don’t name a customer (for example, an unfiltered card list) only need check 1. Access is also blocked (403) while the employee is suspended or terminated, the customer is banned, or your user account is suspended.

Concepts

  • Customer — the business’s own registered customer record (already used elsewhere in the API for KYC, virtual accounts, etc). Corporate Card is layered on top of an existing, fully-KYC’d customer.
  • Employee — the card holder. One employee can hold multiple cards. Created explicitly via the Employees endpoints below, or automatically the first time a card is requested for a customer that has none yet.
  • Employee KYC — a lightweight KYC record captured per employee (identity, address, government ID, payslip/work contract, proof of address) required before a card can be fully activated for spend.
  • Memo / Invoice — a note and/or invoice an employee attaches to their card activity, which the business then approves or rejects.
  • USDC balance — the business’s own funding wallet. Funding an employee’s card balance debits this wallet; withdrawing credits it back. Every fee is also charged to this balance.
  • Employee card balance — the money an employee can spend. It belongs to the employee, and every card they hold spends from it. You fund and withdraw it; the employee can only view it (see the Employee Portal). It exists once the employee’s first card is issued.
  • Reserve — a minimum amount that must stay in your USDC balance at all times while your account is approved for Business Spend Cards. See Reserve.
  • Corporate Card wallet — a dedicated USDC (Solana) deposit address a business can provision once to fund its USDC balance by depositing crypto directly, separate from any other wallets on the account.

Customers

Create a customer

Required fields: customer_name, customer_email, customer_phone, customer_type (individual or business), customer_country. Before the customer can be used for Corporate Cards it also needs an approved faster_payments endorsement — see Access requirements.

Employees

Create an employee

Fields Selfie image (optional) — supply one of: When both are present the uploaded file takes precedence. Omit both to skip. Uploaded selfies are stored privately; see Private document links. The upload happens before the employee is created: if it fails, the request returns 502 (Unable to upload the selfie image. Please try again.) and no employee is created, so it is safe to retry.

Update an employee

All fields are optional — send only the ones you want to change:
Updatable fields A failed selfie upload returns 502 and leaves the employee unchanged.

List employees

Returns every employee across all of your customers, newest first. Each employee includes its customer (customer_id, customer_name, customer_email) and cards_count. Optional query filters:

Get an employee

Returns the employee record including its metadata and current kyc sub-object.

Terminate an employee

Sets status to terminated, suspends all cards held by that employee, and soft-deletes the record. This action is not reversible via the API.

Employee KYC

Submit or update KYC

employee_id is a required field in the request body (multipart or JSON), alongside the KYC data. Accepts either multipart/form-data with file fields, or JSON with hosted URLs — both are supported for each document: All three document fields are optional — supply at least the file or the URL for any document you want to attach; omit both to skip it. A file field also accepts a base64 string (data:<type>;base64,... or raw base64) instead of a multipart file. Accepted types: images (jpg, png, webp, gif, heic), PDF, Word, Excel, CSV and plain text; max 10 MB. The type is detected from the file’s content, not its name. If a file upload fails, the request returns 422 naming the field (e.g. Unable to upload gov_id_image. Please try again.). Uploaded selfies and KYC documents are stored privately. The URL saved on the employee is a Yativo link (https://<api-host>/files/private/...) that only opens for your account (bearer token) or Yativo staff. To display one in a browser (<img>, new tab), exchange it for a 15-minute signed link:
The response is { "url": "<signed link>", "expires_in": 900 }. Only your own files can be signed; anything else returns 404. Required identity/address fields: employee_id, full_name, id_type, id_number, dob, street, city, postal_code, gov_id_type, gov_id_number. Optional identity/address fields: state_id, country_id, nationality, occupation.
  • country_id — the country’s ISO2 code (e.g. US), taken from the iso2 field of the countries list below. Despite its name, this is not the numeric country id.
  • nationality — also an ISO2 code (e.g. NG).
  • state_id — the numeric id of a state from the states list below. An unknown state_id is rejected with 422 Invalid state_id.
KYC fee. The first KYC submission for each employee charges the employee_kyc fee to your USDC balance (Business Spend Card wallet only — this wallet is not used for payouts, swaps, or other products). Resubmissions and updates for the same employee are free. If the fee can’t be charged the request returns 402, the KYC is not saved, and the error explains that you need sufficient USDC to pay the employee KYC fee (for example: Insufficient USDC balance to pay the employee KYC fee (0.50 USD). Fund your Business Spend Card wallet and try again.).

Looking up countries and states

Use these endpoints to populate country and state pickers and to resolve the values sent in the KYC request. They are public, need no authentication, and return cached reference data.
{country_id} here is the numeric id from the countries list (e.g. 101), not the ISO2 code. GET /api/v1/locations/states without a country returns every state and is not recommended, as the response is large. Example — list countries:
Example — list states for a country:
Typical flow: fetch countries, and from the one the employee picks send its iso2 as country_id in the KYC request. Then fetch /states/{id} with that country’s numeric id, and send the chosen state’s id as state_id.

Get KYC status

Returns status (pending, approved, rejected) and, when rejected, a reject_reason. KYC approval/rejection itself is performed internally after review — it is not a customer-facing action.

Memos / Invoices

Employees (or the business, on an employee’s behalf) can attach a memo and/or invoice to card activity, which the business then approves or rejects.
POST .../memos requires employee_id in the body, plus note (text), and either an invoice file upload or an invoice_url, plus an optional card_id to tie the memo to a specific card. Rejecting a memo requires a reject_reason. A failed invoice upload returns 502 (Unable to upload the invoice. Please try again.) and no memo is created.

Employee card balance

Each employee has one card balance that all of their cards spend from. You manage it; employees can view theirs in the employee portal but can’t move it.

Get an employee’s card balance

  • available_balance is what the employee can spend right now. ledger_balance also includes authorizations that haven’t settled yet.
  • has_cardholder is false until the employee’s first card is issued. Until then the balance is zero and can’t be funded.
  • balance_unavailable is true if the balance couldn’t be read in full right now. Retry later.
  • accounts usually has a single entry. Employees whose cards were issued before employee card balances existed can have more than one; the totals above cover all of them.

Fund or withdraw

  • Fund debits your USDC balance and credits the employee’s card balance. It can’t use the reserve.
  • Withdraw moves money from the employee’s card balance back to your USDC balance and charges the card_withdrawal fee. It is refused with 422 if it exceeds the employee’s available balance. Withdrawing everything doesn’t close any cards.
  • An employee with no card yet returns 404.
  • Send an Idempotency-Key header to make retries safe.
The response is the employee’s updated card balance, in the same shape as GET .../balance.

Cards

POST .../cards requires customer_id in the body. employee_id is optional: pass it to issue the card to a specific employee (it must already belong to that customer_id); omit it and the API resolves — or, the first time, creates — that customer’s employee automatically. Either way, an employee can hold any number of cards, with no per-employee limit. Issuance fees are charged to your USDC balance before the card is requested, and refunded automatically if the card can’t be issued: Terminated or deleted virtual cards still count towards “first”, so terminating and reissuing a card is not free. If a fee can’t be charged the request returns 402 and no card is issued. Cards an employee creates for themselves in the employee portal follow the same rules and are charged to your balance. GET .../cards returns every card you own: cards issued by your account, and any card belonging to one of your customers or to an employee of one of your customers. It accepts optional customer_id and employee_id query filters, plus status.

Managing an employee’s cards

You can see and manage every card that belongs to your customers and their employees (you → customer → employee → card), including cards an employee created for themselves in the employee portal. Cards are referenced by card_id (the opaque id returned in card responses).
  • .../{card_id} returns a single card.
  • To view full card details, call GET /api/v1/spend/cards/{card_id}/widget-token. It returns {"iframe": "<url>"}, a short-lived link to a secure page showing the full card number, expiry and CVV. Embed it as an <iframe src>; request a new one each time.
  • status, pin and limits take customer_id and card_id in the body, plus: action (activate, suspend, terminate) for status; pin for PIN; a limits array for limits.
  • To add or remove money, use the employee’s card balance, which all of their cards share.
The same operations are also available under /api/v1/spend/cards (POST /spend/cards, GET /spend/cards, POST /spend/cards/status, POST /spend/cards/pin, PUT /spend/cards/limits) with identical fields and behaviour. POST /spend/cards/wallet (fund or withdraw by card_id) remains for existing integrations. New integrations should use the employee card balance instead. Every endpoint only sees cards you own. A card_id that belongs to another account returns 404.

Card transactions & disputes

Every card authorization is recorded in your transaction history, where you can add notes, attach receipts, and raise disputes. transaction_id in the business_spend_card.transaction.* webhooks is the id used here. On sandbox you can simulate a real spend-card authorization (it is written to the card’s activity history) with POST /api/v1/sandbox/spend-cards/{cardId}/simulate, or send a sample event without creating a record with POST /api/v1/demo/webhook/spendcard. See Testing on sandbox.
  • The list accepts optional status and card_id filters. A single transaction includes its notes and invoices.
  • Notes take { "note": "..." }. Invoices are multipart/form-data with an invoice file (jpg, jpeg, png or pdf, max 10 MB). Every file is virus-scanned before it is stored, and a file that fails the scan is rejected with 422.

Raise a dispute

reason is required (max 500 characters). Raising a dispute charges the dispute fee to your USDC balance, marks the transaction as disputed, and sends a business_spend_card.transaction.disputed webhook. Our operations team then files the dispute with the card network.
A transaction can also be disputed by customer with POST /api/v1/spend/cards/transactions/{transaction_id}/dispute, which takes customer_id and reason in the body and behaves the same way.

Corporate Card funding wallet (USDC deposits)

A business can provision a dedicated Solana/USDC deposit address to top up its USDC balance directly, separate from any other crypto wallets on the account.
Both take customer_id (body on POST, query string on GET) so the request can be authorized against a customer you own. The wallet itself is per business, not per customer — every customer on the same account shares one Solana/USDC deposit address. POST is idempotent; calling it again, even with a different customer_id, returns that same wallet. GET returns the wallet address and status, plus your balance split into what is reserved and what you can spend:
All three balances are in USD (major units, two decimal places), not ledger cents. Deposits to this address are detected automatically and credited to the business’s USDC balance — no manual reconciliation is required. They do not load any employee card; assign balance with POST /api/v1/corporate-card/employees/balance or from the dashboard. Only send USDC on Solana to this address. Always read the current wallet.wallet_address from GET /api/v1/corporate-card/wallet before depositing rather than reusing a stored copy: Yativo may move a wallet to a new address. When that happens, deposits that still arrive at the previous address continue to be credited, but new deposits should go to the current one. The wallet object contains wallet_address, wallet_currency (USDC_SOL) and wallet_status.

Reserve

While your account is approved for Business Spend Cards, a minimum amount must stay in your USDC balance at all times. The default is 2,000 USD; Yativo may agree a different amount for your account.
  • The reserve can’t be spent — card balance funding and other spend-card debits stop at it (payouts and transfers cannot use the USDC wallet at all).
  • Fees can use it. That is what it is held for, so a fee is still charged when your available balance is zero.
  • A debit that would go below the reserve returns an error such as: Insufficient available balance. A minimum balance of 2,000.00 USD must be kept in your USDC balance for Business Spend Cards; 150.00 USD is available to spend.
  • If access is revoked, the reserve is released.
Use usdc_available_balance from GET /api/v1/corporate-card/wallet to see how much you can spend.

Fees

All Business Spend Card fees are charged to your USDC balance. Amounts come from your pricing agreement; contact your account manager for your rates. Each fee is charged at most once per event, so retries and repeated webhooks never double-charge. A transaction is cross-border when the merchant is outside the card’s home country or, if the merchant country isn’t known, when it is in a currency other than the card’s home currency. Fees you trigger through the API (card issuance, KYC, disputes, withdrawals) are charged before the action runs: if the fee can’t be charged the request returns 402 and nothing happens. Fees triggered by card activity or the monthly cycle are retried automatically if your balance can’t cover them when they fall due.

Errors

All endpoints return the standard API error envelope on failure. When the card service rejects a request, data.error gives the reason (for example an invalid date of birth). When it can’t complete the request at all, you get 502 with The card service could not complete this request right now. Please try again later. Responses never include the card service’s raw response.