Lightspark Cards API

Card management endpoints. Issue debit cards against an internal account, freeze / unfreeze, close, manage card funding sources, and list card transactions.

OpenAPI Specification

lightspark-cards-api-openapi.yml Raw ↑
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