openapi: 3.1.0
info:
title: Grid Agent Management Cards API
description: 'API for managing global payments on the open Money Grid. Built by Lightspark. See the full documentation at https://docs.lightspark.com/.
'
version: '2025-10-13'
contact:
name: Lightspark Support
email: support@lightspark.com
license:
name: Proprietary
url: https://lightspark.com/terms
servers:
- url: https://api.lightspark.com/grid/2025-10-13
description: Production server
security:
- BasicAuth: []
- AgentAuth: []
tags:
- name: Cards
description: Card management endpoints. Issue debit cards against an internal account, freeze / unfreeze, close, manage card funding sources, and list card transactions.
paths:
/cards:
post:
summary: Issue a card
description: 'Issue a new card for a cardholder. Every card must be bound to at least one funding source at create time. The cardholder must have KYC status `APPROVED` before a card can be issued; otherwise the request is rejected with `CARDHOLDER_KYC_NOT_APPROVED`.
If any funding source is an Embedded Wallet internal account, the cardholder must authorize Grid to sign Spark token transactions for that card funding source by completing the delegated-key creation flow with `POST /auth/delegated-keys`. Until an active delegated key exists for that funding source, Authorization Decisioning cannot use it to fund card transactions.
New cards start in `state: "PROCESSING"` while the card issuer provisions the card. The `card.state_change` webhook fires on each state transition, including the transition to `ACTIVE` (or to `CLOSED` with `stateReason: "ISSUER_REJECTED"` if provisioning fails).
'
operationId: createCard
tags:
- Cards
security:
- BasicAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CardCreateRequest'
examples:
virtualCard:
summary: Issue a virtual card with one funding source
value:
cardholderId: Customer:019542f5-b3e7-1d02-0000-000000000001
platformCardId: card-emp-aary-001
form: VIRTUAL
fundingSources:
- InternalAccount:019542f5-b3e7-1d02-0000-000000000002
responses:
'201':
description: Card created successfully. Newly-created cards start in `PROCESSING` while the issuer provisions them. Cards funded by an Embedded Wallet internal account also require an active delegated key for that funding source before Authorization Decisioning can use it.
content:
application/json:
schema:
$ref: '#/components/schemas/Card'
'400':
description: Bad request. Returned with `CARDHOLDER_KYC_NOT_APPROVED` when the cardholder's KYC status is not `APPROVED`, with `FUNDING_SOURCE_INELIGIBLE` when the supplied funding source does not belong to the cardholder or is not denominated in a card-eligible currency, and for general invalid parameters.
content:
application/json:
schema:
$ref: '#/components/schemas/Error400'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error401'
'500':
description: Internal service error
content:
application/json:
schema:
$ref: '#/components/schemas/Error500'
'501':
description: Not implemented in this environment. Card issuance is not enabled for every Grid deployment; environments without a configured card issuer return `501 NOT_IMPLEMENTED`.
content:
application/json:
schema:
$ref: '#/components/schemas/Error501'
get:
summary: List cards
description: 'Retrieve a paginated list of cards. Cards can be filtered by cardholder, bound funding-source internal account, state, and platform-specific card identifier. If no filters are provided, returns all cards visible to the caller.
'
operationId: listCards
tags:
- Cards
security:
- BasicAuth: []
parameters:
- name: cardholderId
in: query
description: Filter by cardholder (customer) id.
required: false
schema:
type: string
- name: accountId
in: query
description: Filter by internal account id. Returns cards whose `fundingSources` array contains the given internal account id.
required: false
schema:
type: string
- name: platformCardId
in: query
description: Filter by platform-specific card identifier.
required: false
schema:
type: string
- name: state
in: query
description: Filter by card state.
required: false
schema:
$ref: '#/components/schemas/CardState'
- name: limit
in: query
description: Maximum number of results to return (default 20, max 100)
required: false
schema:
type: integer
minimum: 1
maximum: 100
default: 20
- name: cursor
in: query
description: Cursor for pagination (returned from previous request)
required: false
schema:
type: string
- name: sortOrder
in: query
description: Order to sort results in
required: false
schema:
type: string
enum:
- asc
- desc
default: desc
responses:
'200':
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/CardListResponse'
'400':
description: Bad request - Invalid parameters
content:
application/json:
schema:
$ref: '#/components/schemas/Error400'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error401'
'500':
description: Internal service error
content:
application/json:
schema:
$ref: '#/components/schemas/Error500'
'501':
description: Not implemented in this environment. Cards are not enabled for every Grid deployment; environments without a configured card issuer return `501 NOT_IMPLEMENTED`.
content:
application/json:
schema:
$ref: '#/components/schemas/Error501'
/cards/{id}:
parameters:
- name: id
in: path
description: System-generated unique card identifier
required: true
schema:
type: string
get:
summary: Get a card
description: Retrieve a card by its system-generated id. To display the card's full PAN, CVV, and expiry to the cardholder, request a reveal with `POST /cards/{id}/reveal` — the card resource itself never carries the reveal URL.
operationId: getCardById
tags:
- Cards
security:
- BasicAuth: []
responses:
'200':
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/Card'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error401'
'404':
description: Card not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error404'
'500':
description: Internal service error
content:
application/json:
schema:
$ref: '#/components/schemas/Error500'
'501':
description: Not implemented in this environment. Cards are not enabled for every Grid deployment; environments without a configured card issuer return `501 NOT_IMPLEMENTED`.
content:
application/json:
schema:
$ref: '#/components/schemas/Error501'
patch:
summary: Update a card
description: 'Update a card''s `state` and / or its bound `fundingSources`. At least one of the two fields must be supplied.
- `state` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible. Any other transition returns `409 INVALID_STATE_TRANSITION`.
- `fundingSources`, when supplied, fully replaces the card''s bound funding sources. Array order determines the priority Authorization Decisioning tries them in. Each id must belong to the cardholder and be denominated in the card''s currency; the list must contain at least one source. `fundingSources` cannot be supplied alongside `state: CLOSED`.
Because both updates are sensitive state changes, this endpoint uses Grid''s 202 → signed-retry pattern (same shape as `DELETE /auth/credentials/{id}` and `POST /internal-accounts/{id}/export`):
1. Call `PATCH /cards/{id}` with the target fields and no signing headers. The response is `202` with a `payloadToSign`, `requestId`, and `expiresAt`.
2. Sign the `payloadToSign` with the session private key of a verified authentication credential on the card''s owning internal account and retry with the signature as the `Grid-Wallet-Signature` header and the `requestId` echoed back as the `Request-Id` header. The signed retry returns `200` with the updated `Card`.
Effects:
- `state: FROZEN`: Authorization Decisioning declines new auths with `CARD_PAUSED`. Existing pulls and in-flight reconciliation continue — freezing does not pause the lifecycle of authorizations that already passed.
- `state: ACTIVE`: normal authorization behavior resumes.
- `state: CLOSED`: terminal close. The card transitions to `state: "CLOSED"` with `stateReason: "CLOSED_BY_PLATFORM"` and stays in the system for audit and reconciliation. All pending auths reconcile to a terminal state via the existing reconcile primitive. Inbound clearings received after close follow the standard force-post / late-presentment path — Lightspark absorbs the loss if a post-hoc pull on the now-unbound source fails. Funding-source bindings are detached. Refunds already in flight still complete because Lightspark holds the card-reserve keys.
- `fundingSources` change: emits `card.funding_source_change` reflecting the new ordered binding.
The `card.state_change` webhook fires on every successful `state` transition; the `card.funding_source_change` webhook fires whenever `fundingSources` is updated.
'
operationId: updateCardById
tags:
- Cards
security:
- BasicAuth: []
parameters:
- name: Grid-Wallet-Signature
in: header
required: false
description: Signature over the `payloadToSign` returned in a prior `202` response, produced with the session private key of a verified authentication credential on the card's owning internal account and base64-encoded. Required on the signed retry; ignored on the initial call.
schema:
type: string
example: MEUCIQDx7k2N0aK4p8f3vR9J6yT5wL1mB0sXnG2hQ4vJ8zYkCgIgZ4rP9dT7eWfU3oM6KjR1qSpNvBwL0tXyA2iG8fH5dE=
- name: Request-Id
in: header
required: false
description: The `requestId` returned in a prior `202` response, echoed back on the signed retry so the server can correlate it with the issued challenge. Required on the signed retry; must be paired with `Grid-Wallet-Signature`.
schema:
type: string
example: 7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CardUpdateRequest'
examples:
freeze:
summary: Freeze an active card
value:
state: FROZEN
unfreeze:
summary: Unfreeze a frozen card
value:
state: ACTIVE
updateFundingSources:
summary: Replace the card's bound funding sources
value:
fundingSources:
- InternalAccount:019542f5-b3e7-1d02-0000-000000000002
- InternalAccount:019542f5-b3e7-1d02-0000-000000000003
freezeAndUpdateSources:
summary: Freeze the card and replace its funding sources in one call
value:
state: FROZEN
fundingSources:
- InternalAccount:019542f5-b3e7-1d02-0000-000000000002
close:
summary: Permanently close the card
value:
state: CLOSED
responses:
'200':
description: Signed retry accepted. Returns the updated card.
content:
application/json:
schema:
$ref: '#/components/schemas/Card'
'202':
description: Challenge issued. The response contains a `payloadToSign` that must be signed with the session private key of a verified authentication credential on the card's owning internal account, along with a `requestId` that must be echoed back on the retry.
content:
application/json:
schema:
$ref: '#/components/schemas/SignedRequestChallenge'
'400':
description: Bad request. Returned with `FUNDING_SOURCE_INELIGIBLE` when a supplied funding source does not belong to the cardholder or is not denominated in the card's currency, and for general invalid parameters.
content:
application/json:
schema:
$ref: '#/components/schemas/Error400'
'401':
description: Unauthorized. Returned when the provided `Grid-Wallet-Signature` is missing, malformed, or does not match a pending update challenge for this card, or when the `Request-Id` does not match an unexpired pending challenge.
content:
application/json:
schema:
$ref: '#/components/schemas/Error401'
'404':
description: Card not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error404'
'409':
description: 'Conflict. Returned with `INVALID_STATE_TRANSITION` when the requested `state` transition is not one of `ACTIVE ⇄ FROZEN` or `ACTIVE | FROZEN → CLOSED` (e.g. trying to un-freeze a `CLOSED` card); with `CARD_ALREADY_CLOSED` when `state: CLOSED` is requested for a card that is already `CLOSED`; and with `CARD_NOT_MUTABLE` when the card is `CLOSED`.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error409'
'500':
description: Internal service error
content:
application/json:
schema:
$ref: '#/components/schemas/Error500'
'501':
description: Not implemented in this environment. Cards are not enabled for every Grid deployment; environments without a configured card issuer return `501 NOT_IMPLEMENTED`.
content:
application/json:
schema:
$ref: '#/components/schemas/Error501'
/cards/{id}/reveal:
parameters:
- name: id
in: path
description: System-generated unique card identifier
required: true
schema:
type: string
post:
summary: Reveal card details
description: 'Mint a signed, short-lived URL for the card processor''s iframe that displays the card''s full PAN, CVV, and expiry to the cardholder. This is the only way to obtain a reveal URL — the `Card` resource never carries one.
Request the reveal right before rendering the iframe and render the returned `panEmbedUrl` immediately; it expires at `expiresAt` (within minutes). Never store, cache, or log the URL — it is a bearer secret for the full card details. The card data renders inside the processor''s iframe and never crosses Grid''s or your servers.
Every reveal is audit-logged with the requesting actor.'
operationId: revealCard
tags:
- Cards
security:
- BasicAuth: []
responses:
'200':
description: Reveal URL minted.
content:
application/json:
schema:
$ref: '#/components/schemas/CardRevealResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error401'
'403':
description: Forbidden. The session has no attributable actor to audit the reveal against (for example, an impersonated dashboard session).
content:
application/json:
schema:
$ref: '#/components/schemas/Error403'
'404':
description: Card not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error404'
'500':
description: Internal service error
content:
application/json:
schema:
$ref: '#/components/schemas/Error500'
'501':
description: Not implemented in this environment. Cards are not enabled for every Grid deployment; environments without a configured card issuer return `501 NOT_IMPLEMENTED`.
content:
application/json:
schema:
$ref: '#/components/schemas/Error501'
components:
schemas:
SignedRequestChallenge:
title: Signed Request Challenge
type: object
required:
- payloadToSign
- requestId
- expiresAt
description: Common base for two-step signed-retry challenge responses on Embedded Wallet endpoints (credential registration or revocation, session refresh or revocation, wallet export, customer email updates, and similar). Holds the signing fields shared across every challenge shape; each variant composes this base via `allOf` and adds its own resource `id` (and `type`, when applicable) with variant-specific description and example.
properties:
payloadToSign:
type: string
description: Canonical payload for the retry authorization stamp. Build an API-key stamp over this exact value with the session API keypair, then send the full base64url-encoded stamp in `Grid-Wallet-Signature` on the retry that completes the original request.
example: '{"organizationId":"org_2m9F...","parameters":{"userId":"user_2m9F..."},"timestampMs":"1775681700000","type":"ACTIVITY_TYPE_EXAMPLE"}'
requestId:
type: string
description: Grid-issued `Request:<uuid>` identifier for this pending request. Echo this value exactly in the `Request-Id` header on the signed retry so the server can correlate the retry with the issued challenge.
example: Request:7c4a8d09-ca37-4e3e-9e0d-8c2b3e9a1f21
expiresAt:
type: string
format: date-time
description: Timestamp after which this challenge is no longer valid. The signed retry must be submitted before this time.
example: '2026-04-08T15:35:00Z'
Error501:
type: object
required:
- message
- status
- code
properties:
status:
type: integer
enum:
- 501
description: HTTP status code
code:
type: string
description: '| Error Code | Description |
|------------|-------------|
| UNRECOGNIZED_MANDATORY_PAYEE_DATA_KEY | Unrecognized mandatory payee data key |
| NOT_IMPLEMENTED | Feature not implemented |
'
enum:
- UNRECOGNIZED_MANDATORY_PAYEE_DATA_KEY
- NOT_IMPLEMENTED
message:
type: string
description: Error message
details:
type: object
description: Additional error details
additionalProperties: true
CardBrand:
type: string
enum:
- VISA
- MASTERCARD
description: 'Card network brand. Read-only — determined by Grid when the card is
provisioned with the issuer.
'
Error403:
type: object
required:
- message
- status
- code
properties:
status:
type: integer
enum:
- 403
description: HTTP status code
code:
type: string
description: '| Error Code | Description |
|------------|-------------|
| FORBIDDEN | Insufficient permissions |
| USER_NOT_READY | Customer exists but is not ready for operation |
| COUNTERPARTY_NOT_ALLOWED | Counterparty has not been enabled for your account |
| VELOCITY_LIMIT_EXCEEDED | Counterparty has exceeded velocity limits |
'
enum:
- FORBIDDEN
- USER_NOT_READY
- COUNTERPARTY_NOT_ALLOWED
- VELOCITY_LIMIT_EXCEEDED
message:
type: string
description: Error message
details:
type: object
description: Additional error details
additionalProperties: true
Error400:
type: object
required:
- message
- status
- code
properties:
status:
type: integer
enum:
- 400
description: HTTP status code
code:
type: string
description: '| Error Code | Description |
|------------|-------------|
| INVALID_INPUT | Invalid input provided |
| MISSING_MANDATORY_USER_INFO | Required customer information is missing |
| INVITATION_ALREADY_CLAIMED | Invitation has already been claimed |
| INVITATIONS_NOT_CONFIGURED | Invitations are not configured |
| INVALID_UMA_ADDRESS | UMA address format is invalid |
| INVITATION_CANCELLED | Invitation has been cancelled |
| QUOTE_REQUEST_FAILED | An issue occurred during the quote process; this is retryable |
| INVALID_PAYREQ_RESPONSE | Counterparty Payreq response was invalid |
| INVALID_RECEIVER | Receiver is invalid |
| PARSE_PAYREQ_RESPONSE_ERROR | Error parsing receiver PayReq response |
| CERT_CHAIN_INVALID | Counterparty certificate chain is invalid |
| CERT_CHAIN_EXPIRED | Counterparty certificate chain has expired |
| INVALID_PUBKEY_FORMAT | Counterparty Public key format is invalid |
| MISSING_REQUIRED_UMA_PARAMETERS | Counterparty required UMA parameters are missing |
| SENDER_NOT_ACCEPTED | Sender is not accepted |
| AMOUNT_OUT_OF_RANGE | Amount is out of range |
| INVALID_CURRENCY | Currency is invalid |
| INVALID_TIMESTAMP | Timestamp is invalid |
| INVALID_NONCE | Nonce is invalid |
| INVALID_REQUEST_FORMAT | Request format is invalid |
| INVALID_BANK_ACCOUNT | Bank account is invalid |
| SELF_PAYMENT | Self payment not allowed |
| LOOKUP_REQUEST_FAILED | Lookup request failed |
| PARSE_LNURLP_RESPONSE_ERROR | Error parsing LNURLP response |
| INVALID_AMOUNT | Amount is invalid |
| WEBHOOK_ENDPOINT_NOT_SET | Webhook endpoint is not set |
| WEBHOOK_DELIVERY_ERROR | Webhook delivery error |
| LOW_QUALITY | Document quality too low to process |
| DATA_MISMATCH | Document details don''t match provided information |
| EXPIRED | Document has expired |
| SUSPECTED_FRAUD | Document suspected of being forged or edited |
| UNSUITABLE_DOCUMENT | Document type is not accepted or not supported |
| INCOMPLETE | Document is missing pages or sides |
| EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS | An EMAIL_OTP credential is already registered on the target internal account; only one email OTP credential is supported per internal account at this time |
| SMS_OTP_CREDENTIAL_ALREADY_EXISTS | An SMS_OTP credential is already registered on the target internal account; only one SMS OTP credential is supported per internal account at this time |
| PASSKEY_CREDENTIAL_ALREADY_EXISTS | A PASSKEY credential with the same WebAuthn credentialId is already registered on the target internal account |
| STABLECOIN_PROVIDER_ACCOUNT_INVALID | The stablecoin provider account link is not usable |
| STABLECOIN_PROVIDER_ACCOUNT_REVOKED | The stablecoin provider account link has been revoked |
| STABLECOIN_PROVIDER_ACCOUNT_SELECTION_REQUIRED | Multiple active provider account links exist; pass `stablecoinProviderAccountId` to select one |
'
enum:
- INVALID_INPUT
- MISSING_MANDATORY_USER_INFO
- INVITATION_ALREADY_CLAIMED
- INVITATIONS_NOT_CONFIGURED
- INVALID_UMA_ADDRESS
- INVITATION_CANCELLED
- QUOTE_REQUEST_FAILED
- INVALID_PAYREQ_RESPONSE
- INVALID_RECEIVER
- PARSE_PAYREQ_RESPONSE_ERROR
- CERT_CHAIN_INVALID
- CERT_CHAIN_EXPIRED
- INVALID_PUBKEY_FORMAT
- MISSING_REQUIRED_UMA_PARAMETERS
- SENDER_NOT_ACCEPTED
- AMOUNT_OUT_OF_RANGE
- INVALID_CURRENCY
- INVALID_TIMESTAMP
- INVALID_NONCE
- INVALID_REQUEST_FORMAT
- INVALID_BANK_ACCOUNT
- SELF_PAYMENT
- LOOKUP_REQUEST_FAILED
- PARSE_LNURLP_RESPONSE_ERROR
- INVALID_AMOUNT
- WEBHOOK_ENDPOINT_NOT_SET
- WEBHOOK_DELIVERY_ERROR
- LOW_QUALITY
- DATA_MISMATCH
- EXPIRED
- SUSPECTED_FRAUD
- UNSUITABLE_DOCUMENT
- INCOMPLETE
- EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS
- SMS_OTP_CREDENTIAL_ALREADY_EXISTS
- PASSKEY_CREDENTIAL_ALREADY_EXISTS
- STABLECOIN_PROVIDER_ACCOUNT_INVALID
- STABLECOIN_PROVIDER_ACCOUNT_REVOKED
- STABLECOIN_PROVIDER_ACCOUNT_SELECTION_REQUIRED
message:
type: string
description: Error message
details:
type: object
description: Additional error details
additionalProperties: true
Error409:
type: object
required:
- message
- status
- code
properties:
status:
type: integer
enum:
- 409
description: HTTP status code
code:
type: string
description: '| Error Code | Description |
|------------|-------------|
| TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL | Transaction is not pending platform approval |
| UMA_ADDRESS_EXISTS | UMA address already exists |
| EMAIL_OTP_EMAIL_ALREADY_EXISTS | Email address is already associated with an EMAIL_OTP credential |
| EMAIL_OTP_CREDENTIAL_SET_CHANGED | Tied EMAIL_OTP credential set changed after the signed-retry challenge was issued |
| PASSKEY_ALREADY_ENROLLED | The customer already has an enrolled passkey factor; only one passkey per customer is supported. Delete the existing one before enrolling another |
| CONFLICT | Generic resource-state conflict. Returned, for example, when `platformCustomerId` on a customer create call collides with an existing active customer on the same platform |
'
enum:
- TRANSACTION_NOT_PENDING_PLATFORM_APPROVAL
- UMA_ADDRESS_EXISTS
- EMAIL_OTP_EMAIL_ALREADY_EXISTS
- EMAIL_OTP_CREDENTIAL_SET_CHANGED
- PASSKEY_ALREADY_ENROLLED
- CONFLICT
message:
type: string
description: Error message
details:
type: object
description: Additional error details
additionalProperties: true
CardRevealResponse:
type: object
required:
- panEmbedUrl
- expiresAt
properties:
panEmbedUrl:
type: string
format: uri
description: 'Signed URL of the card processor''s iframe that securely displays the PAN, CVV, and expiry to the cardholder. The full PAN and CVV never cross Grid''s servers — render this URL in an iframe in your client to reveal card details. The URL is a short-lived bearer secret: render it immediately and never store, cache, or log it.'
example: https://embed.lithic.com/iframe/...?t=...
expiresAt:
type: string
format: date-time
description: When the signed URL stops loading. Request a new reveal rather than re-rendering an expired URL.
example: '2026-05-08T14:16:00Z'
CardState:
type: string
enum:
- PENDING_KYC
- PROCESSING
- ACTIVE
- FROZEN
- CLOSED
description: 'Lifecycle state of a card.
| State | Description |
|-------|-------------|
| `PENDING_KYC` | The cardholder has not yet completed KYC. Cards in this state cannot transact. |
| `PROCESSING` | The card has been requested and is being provisioned with the issuer. |
| `ACTIVE` | The card is live and can authorize transactions. |
| `FROZEN` | The card is temporarily disabled by the platform. New authorizations are declined with `CARD_PAUSED`. Existing settlements and refunds continue to reconcile. |
| `CLOSED` | The card is permanently closed. Terminal, irreversible state. |
'
CardListResponse:
type: object
required:
- data
- hasMore
properties:
data:
type: array
description: List of cards matching the filter criteria
items:
$ref: '#/components/schemas/Card'
hasMore:
type: boolean
description: Indicates if more results are available beyond this page
nextCursor:
type: string
description: Cursor to retrieve the next page of results (only present if hasMore is true)
totalCount:
type: integer
description: Total number of cards matching the criteria (excluding pagination)
CardUpdateRequest:
type: object
description: Update request for `PATCH /cards/{id}`. At least one of `state` or `fundingSources` must be supplied. `state` transitions are limited to `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`; any other transition returns `409 INVALID_STATE_TRANSITION`. `CLOSED` is terminal and irreversible and cannot be combined with `fundingSources`. `fundingSources`, when supplied, fully replaces the card's bound funding sources — the array order determines the priority Authorization Decisioning tries them in.
properties:
state:
type: string
enum:
- ACTIVE
- FROZEN
- CLOSED
description: Target state for the card. Permitted transitions are `ACTIVE ⇄ FROZEN` and `ACTIVE | FROZEN → CLOSED`. `CLOSED` is terminal and irreversible; once closed, the card stays in the system for audit and reconciliation but cannot transact again.
example: FROZEN
fundingSources:
type: array
description: 'New ordered list of internal account ids to bind as funding sources. Fully replaces the previous binding. Each id must belong to the cardholder and be denominated in the card''s currency. The list must contain at least one source — to stop a card from spending without removing all sources, transition it to `FROZEN` instead. Cannot be supplied alongside `state: CLOSED`.'
minItems: 1
items:
type: string
example:
- InternalAccount:019542f5-b3e7-1d02-0000-000000000002
- InternalAccount:019542f5-b3e7-1d02-0000-00000000
# --- truncated at 32 KB (42 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/lightspark/refs/heads/main/openapi/lightspark-cards-api-openapi.yml