> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dakota.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Create a cardholder

> Create a cardholder for the given customer, gated on the cards capability being available.
The cardholder is created synchronously and returns in `pending` status, then transitions to
`active` via a `cardholder.updated` webhook.

The request body is one of three shapes, chosen by which fields are present:

- `CreateCardholderForIndividualCustomerRequest` (`phone`, optional `email`): for an
  INDIVIDUAL customer. Name is taken from the individual on the customer's latest approved
  application; email too, unless the individual has none on file, in which case the request
  `email` is required.
- `CreateCardholderFromPersonRequest` (`person_id` + `phone`, optional `email`): for a
  BUSINESS customer, creates a cardholder from an existing person returned by
  `GET /customers/{customer_id}/persons`. Name comes from that person; email too, unless the
  person has none on file, in which case the request `email` is required.
- `CreateCardholderForNewPersonRequest` (`first_name`, `last_name`, `email`, `phone`): for a
  BUSINESS customer, creates a cardholder from contact details supplied directly, without an
  existing person to reference.

An INDIVIDUAL customer only accepts the first shape; a BUSINESS customer only accepts the
second or third; sending `person_id` together with `first_name`/`last_name` is ambiguous and
refused. Shared email addresses between persons are not supported: whenever a shape takes its
email from the request (a new-person request's `email`, or one supplied for a person with none
on file), an email matching a person already on the application is refused with
`#cardholder-person-exists` rather than silently creating a second person under the same
address.

`person_id` is validated the same way whether it fails to parse into any application person or
the customer has no approved application at all: both are the same `400` field violation on
`person_id`, so a caller can never use the response to probe for another customer's application
or persons.


<Warning>**Sandbox only.** This endpoint is available in sandbox only while we finish development. It is not available in production yet, and its request and response shapes may change before release.</Warning>


## OpenAPI

````yaml /openapi.yaml post /customers/{customer_id}/cardholders
openapi: 3.0.3
info:
  title: Dakota Platform API
  version: 1.0.0
  description: >-
    Combined API specification for Dakota Platform services:

    - Issuance API: Asset minting and burning operations

    - Onboarding API: Know Your Business/Customer verification

    - On/Off Ramp API: Managing on-ramp and off-ramp accounts

    - Recipients API: Managing destinations for KYB'd entities

    - Transactions API: Viewing transaction history across platform operations


    ## Authentication and API Headers


    All API endpoints require the following headers:


    - `X-Idempotency-Key`: Required for all POST endpoints to ensure request
    idempotency

    - `x-api-key`: Required for authentication across all endpoints


    Note: On /applications endpoints you need a token for authentication instead
    of a x-api-key

    - `x-application-token`: Required for authentication on public /applications
    endpoints (alternative to `x-api-key` where documented)



    ## Rate Limits


    Requests are rate limited per API key. Every response includes the following
    headers:


    | Header | Description |

    | --- | --- |

    | `X-RateLimit-Limit` | Maximum requests allowed in the current one-minute
    window. |

    | `X-RateLimit-Remaining` | Requests remaining in the current window. |

    | `X-RateLimit-Reset` | Absolute Unix timestamp (seconds since epoch) when
    the current rate-limit window resets. |


    When a request is throttled (`429`), responses also include `Retry-After`
    with seconds to wait before retrying.
servers:
  - url: https://api.platform.dakota.xyz
    description: Production environment
  - url: https://api.platform.sandbox.dakota.xyz
    description: Sandbox — safe for testing with simulated data
security:
  - ApiKeyAuth: []
tags:
  - name: Agentic Payments
    x-beta: true
    description: >-
      Beta — agent-driven payments: provision agents, draft and approve spending
      mandates, accept reviewed instructions, and manage scheduled payments.


      **Prerequisites:** Customer onboarded; signer groups attached for
      recognition.

      **Related:** Wallets, Signer Groups, Transactions
  - name: Mandates
    x-beta: true
    description: >-
      Beta — spending mandates: signed, signer-bound authorizations governing
      what may be spent, approved or cancelled by a second recognized signer (§8
      — the dual-control rule that every mandate mutation must be signed by a
      recognized signer OTHER than the bound one). Independent of agents and
      scheduled payments.


      **Prerequisites:** Signer groups attached for recognition.

      **Related:** Signer Groups, Transactions
  - name: Insights
    x-beta: true
    description: >-
      Beta — read-only insight: a deterministic report over a customer's agentic
      activity (funding balances, upcoming obligations, failures, mandate
      headroom and expiry), or — via `GET /insights` — over the whole client's
      book, with portfolio KPIs, daily series and a per-customer roll-up. Never
      moves money, never creates or changes anything.


      **Prerequisites:** Customer onboarded; insight is computed from the
      customer's scheduled payments, mandates, and wallets.

      **Related:** Agentic Payments, Mandates
  - name: Customers
    description: >-
      Manage customer entities representing businesses and organizations
      onboarded to Dakota.


      **Prerequisites:** Complete KYB via Onboarding endpoints before initiating
      money movement.

      **Related:** Onboarding, Recipients, Transactions, Accounts, Wallets
  - name: Wallets
    description: >-
      Manage wallets, balances, and wallet-to-signer-group relationships for
      custody and movement controls.


      **Prerequisites:** Customer must exist. Configure signer groups before
      policy-enforced workflows.

      **Related:** Signer Groups, Policies, Transactions, Customers
  - name: Transactions
    description: >-
      Create, cancel, and retrieve transaction records across account and wallet
      flows.


      **Prerequisites:** Accounts or destinations must be configured based on
      flow type.

      **Related:** Accounts, Recipients, Policies, Events
  - name: Recipients
    description: >-
      Manage recipient entities and destination rails used by customers for
      payouts and transfers.


      **Prerequisites:** Customer must be onboarded and active.

      **Related:** Customers, Transactions, Accounts, Onboarding
  - name: Cards
    description: >-
      Issue and manage cardholders and cards, and follow card transactions.
      Every change is also delivered as a webhook event.


      **Prerequisites:** Customer must be onboarded and the cards capability
      must be available.

      **Related:** Customers, Wallets, Events


      ### Webhook event catalog (v1)


      Every card-family event a client can subscribe to, in one place:


      | Event | Fires when |

      | -- | -- |

      | `cardholder.created` | A cardholder is created. |

      | `cardholder.updated` | Any cardholder change, including every
      review-status transition and deletion (`status: closed`). |

      | `cardholder.information_requested` | A reviewer asks for more
      information; the payload lists the open requirements. Not yet emitted; the
      review pipeline that opens a request for information is still to land. |

      | `card.created` | A card is created. |

      | `card.updated` | Any card change: status, spend limit, last4, freeze
      sources. A close arrives here with `status: closed`. |

      | `card_transaction.created` | The first event for a transaction, normally
      an authorization (`status: authorized`) placing a hold. A declined
      authorization also arrives here, with `status: declined`. |

      | `card_transaction.updated` | Every later change to the same transaction
      (see below). |

      | `wallet.card_enablement.completed` | A wallet's card-settlement
      enablement became active. |


      **Card event ordering.** `card.created` and `card.updated` carry the card
      as it stands after

      the change, including `version` and `freeze_sources` (always an array,
      empty when nothing

      holds the card). `version` is strictly increasing per card and matches the
      `version` on the

      card resource. Deliveries can arrive out of order, so keep the highest
      `version` you have

      applied for each card and drop any event whose `version` is not greater
      than it.


      **One transaction stream.** Holds, releases, partial clearings,
      settlement, returns,

      disputes and force posts are not separate event types. They are `status`
      transitions on

      the transaction, delivered as `card_transaction.updated` with the same
      payload shape as

      `card_transaction.created`:


      | What happened | `status` on the event |

      | -- | -- |

      | Transaction known before its first authorization event | `pending` (on
      `card_transaction.created`) |

      | Hold placed (authorization) | `authorized` (on
      `card_transaction.created`, or `card_transaction.updated` after a
      `pending` start) |

      | Hold released without clearing (merchant, issuer or network reversal) |
      `auth_reversed` |

      | Hold expired unused | `expired` |

      | Part of the hold settled | `partially_cleared` (with the new
      `cleared_amount`) |

      | Fully settled | `cleared` |

      | Settled with no prior hold | `force_posted` |

      | Merchant refund | `returned`, on a new card transaction for the refund;
      the original purchase stays `cleared`. The network returned the money;
      read `refund_state` for whether it reached the customer's wallet
      (`pending` then `paid`). `returned` alone is not proof of payout. |

      | Chargeback opened | `disputed` (reserved; not emitted yet) |

      | Authorization refused | `declined` (on `card_transaction.created`, or
      `card_transaction.updated` when the transaction already exists). Moves no
      money; read `decline_reason` and `decline_code` for why. |


      `outstanding_amount` is the part of `cleared_amount` that no authorization
      covered, less any refunds: non-zero after a force post or an over-capture.


      **Naming.** A dotted segment names a sub-resource of the resource before
      it, so

      `wallet.card_enablement.*` is a wallet's card enablement.
      `card_transaction.*` carries an

      underscore because the resource is `card_transactions`, a top-level
      resource, not a

      sub-resource of a card. It is not a typo, and there is no
      `card.transaction.*` family, and

      no separate settlement event: settlement is a `status` on
      `card_transaction.updated`.
  - name: Accounts
    description: >-
      Manage account resources used for onramp, offramp, and swap operations.


      **Prerequisites:** Customer must be created and network/asset constraints
      must be known.

      **Related:** Customers, Transactions, Auto Transactions, Info
  - name: Auto Transactions
    description: >-
      Manage automated transaction configurations and execution history for
      account automation workflows.


      **Prerequisites:** Source account must exist and be configured for
      automation.

      **Related:** Accounts, Transactions, Events
  - name: Onboarding
    description: >-
      Manage KYB/KYC onboarding lifecycle, application documents, attestations,
      and verification steps.


      **Prerequisites:** Customer context and required entity/application
      metadata.

      **Related:** Customers, Exceptions, Recipients, Transactions
  - name: Policies
    description: >-
      Define and manage policy objects and rules used for transaction governance
      and risk controls.


      **Prerequisites:** Wallet and signer group resources should be configured
      for enforcement scenarios.

      **Related:** Wallets, Signer Groups, Transactions
  - name: Signer Groups
    description: >-
      Manage signer groups and signer assignments for multi-party authorization
      models.


      **Prerequisites:** Wallets should exist before linking signer groups.

      **Related:** Wallets, Policies, Transactions
  - name: Authentication
    description: >-
      Manage API authentication credentials and key lifecycle for platform
      access.


      **Prerequisites:** Client organization must be provisioned.

      **Related:** Users, Info
  - name: Users
    description: >-
      Manage client users, roles, and identity metadata for platform access
      control.


      **Prerequisites:** Auth credentials and client context must be
      established.

      **Related:** Authentication
  - name: Webhooks
    description: >-
      Manage outbound webhook targets and delivery configuration for event
      notifications.


      **Prerequisites:** Subscriber endpoint must be reachable and secured.

      **Related:** Events, Authentication
  - name: Payouts
    description: >-
      Manage where Dakota sends your accrued developer-fee payouts.


      **Prerequisites:** Auth credentials and client context must be
      established.

      **Related:** Events
  - name: RD Marketing Fee
    description: >-
      Everything behind your RD marketing fee: read what a month came to,
      declare the wallets you hold outside Dakota so the RD in them counts, and
      say where the fee should be sent.


      **Prerequisites:** Client must be in the RD marketing-fee programme. A
      month is readable once it has closed.

      **Related:** Wallets, Events
  - name: Self Serve
    description: >-
      Buy and track prepaid credits, and read the tiers and pricing they are
      sold at.


      **Prerequisites:** Auth credentials and client context must be
      established.

      **Related:** Billing
  - name: Events
    description: >-
      Retrieve event records emitted by platform operations for audit and
      troubleshooting.


      **Prerequisites:** Requesting client must have access to referenced
      resources.

      **Related:** Webhooks, Transactions, Onboarding
  - name: Info
    description: >-
      Read platform capability metadata, such as supported rails, networks, and
      assets.

      These operations are served under `/capabilities/*` - `GET
      /capabilities/countries`

      and `GET /capabilities/networks`. The tag name does not appear in the
      request paths.


      **Prerequisites:** Valid authentication headers.

      **Related:** Accounts, Transactions
  - name: Sandbox
    description: >-
      Trigger sandbox-only simulation endpoints for safe end-to-end integration
      testing with synthetic data. The sandbox host
      (`https://api.platform.sandbox.dakota.xyz`) also accepts a family of
      `X-Sandbox-*` request headers on most write endpoints (`Customers`,
      `Accounts`, `Transactions`, simulate endpoints) that let integrators drive
      deterministic failure modes — pick a preset via `X-Sandbox-Scenario`, or
      compose a custom one with
      `X-Sandbox-Error-Step`/`X-Sandbox-Error-Status`/`X-Sandbox-Error-Message`.
      `X-Sandbox-Instant-Completion` collapses async flows to a single
      synchronous step, and `X-Sandbox-Skip-Auto-Approval` keeps newly created
      KYB applications in `pending` for manual-review testing. All `X-Sandbox-*`
      headers are ignored in production.


      **Prerequisites:** Sandbox environment and test customer data.

      **Related:** Customers, Accounts, Transactions, Onboarding
  - name: Legal
    description: |-
      The legal documents customers accept — terms of service, privacy policy,
      e-sign notice, and partner agreements.

      Dakota publishes these here, and this is the authoritative source: the
      hosted onboarding flow, the dakota.xyz website, and your own integration
      all read the same revisions. Present the current revision to your customer
      before capturing their acceptance so the record reflects the text they
      actually saw.
paths:
  /customers/{customer_id}/cardholders:
    parameters:
      - name: customer_id
        in: path
        required: true
        schema:
          $ref: '#/components/schemas/KSUID'
    post:
      tags:
        - Cards
      summary: Create a cardholder
      description: >
        Create a cardholder for the given customer, gated on the cards
        capability being available.

        The cardholder is created synchronously and returns in `pending` status,
        then transitions to

        `active` via a `cardholder.updated` webhook.


        The request body is one of three shapes, chosen by which fields are
        present:


        - `CreateCardholderForIndividualCustomerRequest` (`phone`, optional
        `email`): for an
          INDIVIDUAL customer. Name is taken from the individual on the customer's latest approved
          application; email too, unless the individual has none on file, in which case the request
          `email` is required.
        - `CreateCardholderFromPersonRequest` (`person_id` + `phone`, optional
        `email`): for a
          BUSINESS customer, creates a cardholder from an existing person returned by
          `GET /customers/{customer_id}/persons`. Name comes from that person; email too, unless the
          person has none on file, in which case the request `email` is required.
        - `CreateCardholderForNewPersonRequest` (`first_name`, `last_name`,
        `email`, `phone`): for a
          BUSINESS customer, creates a cardholder from contact details supplied directly, without an
          existing person to reference.

        An INDIVIDUAL customer only accepts the first shape; a BUSINESS customer
        only accepts the

        second or third; sending `person_id` together with
        `first_name`/`last_name` is ambiguous and

        refused. Shared email addresses between persons are not supported:
        whenever a shape takes its

        email from the request (a new-person request's `email`, or one supplied
        for a person with none

        on file), an email matching a person already on the application is
        refused with

        `#cardholder-person-exists` rather than silently creating a second
        person under the same

        address.


        `person_id` is validated the same way whether it fails to parse into any
        application person or

        the customer has no approved application at all: both are the same `400`
        field violation on

        `person_id`, so a caller can never use the response to probe for another
        customer's application

        or persons.
      operationId: createCardholder
      parameters:
        - $ref: '#/components/parameters/IdempotencyKeyHeader'
      requestBody:
        description: Cardholder details
        required: true
        content:
          application/json:
            examples:
              from_person:
                summary: 'Business customer: from an existing person'
                value:
                  person_id: 33KqV8cN2pLmR5tW7xYzA1bC3dE
                  phone: '+15555550123'
                  external_id: emp-0042
              new_person:
                summary: 'Business customer: a new person'
                value:
                  first_name: Ada
                  last_name: Lovelace
                  email: ada@example.com
                  phone: '+15555550123'
                  external_id: emp-0042
              individual_customer:
                summary: 'Individual customer: the customer themself'
                value:
                  phone: '+15555550123'
            schema:
              $ref: '#/components/schemas/CreateCardholderRequest'
      responses:
        '201':
          description: Cardholder created successfully
          content:
            application/json:
              example:
                id: 31TgvufZK3gDXBcA3BnSeLWiSn7
                customer_id: 2tQRvD3xFcJ7bKpW9qNsT4hZmYr
                first_name: Ada
                last_name: Lovelace
                email: ada@example.com
                phone: '+15555550123'
                status: pending
                open_requirements:
                  count: 0
                  types: []
                external_id: emp-0042
                created_at: 1758211200
                updated_at: 1758211200
              schema:
                $ref: '#/components/schemas/CardholderResponse'
        '400':
          description: Invalid request
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '403':
          description: >
            Forbidden — the cards capability is not available for this customer.
            The `type` is

            `#cards-capability-unavailable` in the general case (not in rollout,
            or more than the

            Cards ToS outstanding), or the more specific
            `#cards-tos-not-accepted` when the sole

            unmet requirement is the Cards ToS `terms_acceptance`. When the
            capability check finds

            outstanding requirements, they ride in `errors`. A
            `#cards-capability-unavailable` because

            the customer's bank record is not active carries no `errors`: it
            usually clears once the

            customer's bank onboarding completes, and if it persists, contact
            Dakota support.

            A `#cards-capability-unavailable` because the customer's card
            account needs Dakota support

            also carries no `errors`: contact Dakota support; retrying does not
            help until then.

            `#cardholder-base-not-enabled` means the client is not activated to
            issue cards to this

            customer's type (business or individual); contact Dakota.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
              example:
                type: >-
                  https://docs.dakota.xyz/api-reference/errors#cards-capability-unavailable
                title: Cards Capability Unavailable
                status: 403
                detail: The cards capability is not available for this customer.
                errors:
                  - field: cards_tos
                    message: >-
                      Cards Terms of Service must be accepted before creating a
                      cardholder.
                    code: terms_acceptance
        '404':
          description: Not found
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '409':
          description: >
            Conflict. `type` distinguishes the cause:


            - `#cardholder-person-exists` — an email supplied in the request (a
            new-person request's
              `email`, or one supplied for a person with none on file) matches a person already on the
              customer's latest approved application. `person_id` names the match; a BUSINESS customer
              retries as a `CreateCardholderFromPersonRequest` with that id (an INDIVIDUAL customer's
              cardholder is the customer themself, so the email must change). The same type without
              `person_id` means the bank reports that another person of this customer already uses the
              email; use a different email, or create the cardholder from that person if the persons
              list shows them. Two persons who share one email address are not supported.
            - `#cardholder-already-exists` — a cardholder of this customer
            blocks this one: the
              resolved person's live cardholder, a live cardholder that already uses this email, or an
              INDIVIDUAL customer's cardholder (an individual customer can hold one cardholder that is
              not deleted, whatever its status). `cardholder_id` names it when Dakota has it on record.
            - `#cardholder-person-unavailable` — there is no usable record to
            source the cardholder
              from, and changing the request cannot fix it: an INDIVIDUAL customer has no approved
              application, no individual on it, or the individual's record is missing a name; or the
              bank has no usable record for the named person (contact Dakota support).
            - `#conflict` — a replay of an already-processed request (same
            idempotency key) whose body
              no longer agrees with the cardholder already created for it.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/CreateCardholderConflictProblem'
              example:
                type: >-
                  https://docs.dakota.xyz/api-reference/errors#cardholder-person-exists
                title: Cardholder Person Exists
                status: 409
                detail: >-
                  A person with this email already exists on this application.
                  Send person_id 2abc123 instead.
                person_id: 2abc123
        '501':
          description: Not yet implemented
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '502':
          description: Card provider unavailable
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '503':
          description: Cards is not available in this environment
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
      servers:
        - url: https://api.platform.sandbox.dakota.xyz
          description: Sandbox
components:
  schemas:
    KSUID:
      type: string
      title: KSUID
      description: >-
        KSUID is a 27-character globally unique ID that combines a timestamp
        with a random component. Used for all entity identifiers in the Dakota
        platform.
      pattern: ^[0-9A-Za-z]{27}$
      minLength: 27
      maxLength: 27
      example: 1NFHrqBHb3cTfLVkFSGmHZqdDPi
    CreateCardholderRequest:
      title: Create Cardholder Request
      description: >-
        Request for creating a cardholder. One of three shapes, distinguished by
        which fields are present rather than by a discriminator property:
        `person_id` present means `CreateCardholderFromPersonRequest`; absent,
        and either of `first_name`/`last_name` present means
        `CreateCardholderForNewPersonRequest`; otherwise
        `CreateCardholderForIndividualCustomerRequest`. `email` is never the
        signal -- it is optional on both
        `CreateCardholderForIndividualCustomerRequest` and
        `CreateCardholderFromPersonRequest`, so its presence alone cannot
        distinguish either from `CreateCardholderForNewPersonRequest`. Which of
        the three a given customer accepts depends on its type -- see
        `createCardholder`'s description.
      oneOf:
        - $ref: '#/components/schemas/CreateCardholderForIndividualCustomerRequest'
        - $ref: '#/components/schemas/CreateCardholderFromPersonRequest'
        - $ref: '#/components/schemas/CreateCardholderForNewPersonRequest'
    CardholderResponse:
      type: object
      title: Cardholder Response
      description: Response containing cardholder details.
      required:
        - id
        - customer_id
        - first_name
        - last_name
        - email
        - phone
        - status
        - created_at
        - updated_at
      properties:
        id:
          $ref: '#/components/schemas/KSUID'
        customer_id:
          $ref: '#/components/schemas/KSUID'
        first_name:
          type: string
        last_name:
          type: string
        email:
          type: string
        phone:
          type: string
        status:
          type: string
          description: >-
            Current status of the cardholder. Returns `pending` on create; on
            the clean path it flips to `active` within seconds via a
            `cardholder.updated` webhook.


            An enrollment flagged during screening takes a longer route:
            `pending` → `under_review` (a reviewer holds it) →
            `request_for_information` (the reviewer needs something from you) →
            `active` or `declined`. You answer information requests through the
            API; there is never a cardholder-facing link. Every transition emits
            `cardholder.updated`, and opening a request also emits
            `cardholder.information_requested`.


            `suspended` and `closed` are lifecycle rather than review states.
            `closed` is terminal and appears only in the `cardholder.updated`
            webhook emitted when a cardholder is deleted — deleted cardholders
            are not returned by the REST endpoints.
          enum:
            - pending
            - under_review
            - request_for_information
            - active
            - declined
            - suspended
            - closed
          example: pending
        open_requirements:
          $ref: '#/components/schemas/CardholderOpenRequirements'
        external_id:
          type: string
          nullable: true
        created_at:
          type: integer
          description: Unix timestamp (seconds) of creation.
        updated_at:
          type: integer
          description: Unix timestamp (seconds) of last update.
    ProblemDetails:
      type: object
      required:
        - type
        - title
        - status
      description: |
        Error response following RFC 9457 Problem Details.
        Public API error responses use this format.
      example:
        type: https://docs.dakota.xyz/api-reference/errors#not-found
        title: Customer Not Found
        status: 404
        detail: Customer cst_2abc123 was not found in your organization.
        instance: https://api.platform.dakota.xyz/customers/cst_2abc123
        request_id: req_7f3a8b2c
      properties:
        type:
          type: string
          format: uri
          description: |
            URI reference identifying the problem type.
            Resolves to human-readable documentation.
          example: https://docs.dakota.xyz/api-reference/errors#not-found
        title:
          type: string
          description: >-
            Short, human-readable summary of the problem type. Stable across
            occurrences.
          example: Customer Not Found
        status:
          type: integer
          description: HTTP status code for this occurrence.
          example: 404
        detail:
          type: string
          description: Human-readable explanation specific to this occurrence.
          example: Customer cst_2abc123 was not found in your organization.
        instance:
          type: string
          format: uri
          description: The request path that triggered this error.
          example: https://api.platform.dakota.xyz/customers/cst_2abc123
        request_id:
          type: string
          description: Unique request identifier. Include when contacting support.
          example: req_7f3a8b2c
        errors:
          type: array
          description: Field-level validation errors (present for validation failures).
          items:
            $ref: '#/components/schemas/ValidationError'
        resolution_url:
          type: string
          format: uri
          description: |
            A link the customer can follow to CLEAR this error, present only on
            problems with a concrete self-service remedy.

            Today this is returned by
            `#terms-not-accepted`, where it points at the hosted flow in which
            the outstanding agreement can be signed. The link is token-gated and
            usable as-is — send the customer to it directly rather than parsing
            it out of `detail`.
          example: >-
            https://onboarding.dakota.xyz/applications/2abc123?token=tok_7f3a8b2c
        user_message:
          type: string
          description: |
            A plain-language rendition of `detail` written for the end
            customer, present when one exists for this error. `detail` names
            request fields and actions so a machine caller (such as a payment
            agent drafting proposals) can self-correct; `user_message` says the
            same thing without API vocabulary. Clients that relay errors into a
            human surface (chat, email, UI) should show `user_message` when
            present and fall back to `detail`.
          example: >-
            ACH payments pay out USD, so a USDC payout isn't possible on this
            rail. Change the payout currency to USD and try again.
    CreateCardholderConflictProblem:
      title: Create Cardholder Conflict Problem
      description: >-
        The 409 response of `createCardholder`. Carries ProblemDetails plus,
        depending on `type`, one of two RFC 9457 extension members naming the
        specific conflict -- scoped here rather than added to the shared
        ProblemDetails schema, since no other operation returns them.
      allOf:
        - $ref: '#/components/schemas/ProblemDetails'
        - type: object
          properties:
            person_id:
              type: string
              description: >-
                Only on `#cardholder-person-exists`, and only when the match is
                a person on the customer's latest approved application: the id
                of the person whose email the request's `email` matched. A
                business customer retries the create as a
                `CreateCardholderFromPersonRequest` naming this id. Absent when
                the bank reports that another person uses the email.
              example: 2abc123
            cardholder_id:
              type: string
              description: >-
                Only on `#cardholder-already-exists`: the id of the cardholder
                of this customer that blocks this one.
              example: 2xyz789
    CreateCardholderForIndividualCustomerRequest:
      type: object
      title: Create Cardholder For Individual Customer Request
      description: >-
        Creates a cardholder for an INDIVIDUAL customer. Name is not supplied
        here -- it is taken from the individual on the customer's latest
        approved onboarding application. `email` follows the same on-file rule
        as `CreateCardholderFromPersonRequest`: required only when the person
        has none on file, and otherwise rejected if it disagrees with the one
        already there.
      required:
        - phone
      additionalProperties: false
      properties:
        phone:
          type: string
          description: >
            The cardholder's own mobile number, E.164-formatted. It is used to
            verify

            the cardholder at online checkouts that ask for 3-D Secure, so do
            not send

            a shared or placeholder number.
          example: '+15555550123'
        email:
          type: string
          format: email
          maxLength: 254
          description: >-
            Required only when the individual has no email on file. Otherwise
            omit it; sending a different email than the one already on file is
            rejected.
          example: ada@example.com
        external_id:
          type: string
          nullable: true
          description: Optional client-supplied lookup key.
        address:
          $ref: '#/components/schemas/Address'
          description: >-
            Cardholder's residential address. Required by the card provider;
            when omitted, the customer's address is used.
    CreateCardholderFromPersonRequest:
      type: object
      title: Create Cardholder From Person Request
      description: >-
        Creates a cardholder for a BUSINESS customer from an existing person --
        one returned by `GET /customers/{customer_id}/persons`. Name comes from
        that person; `email` here is only used when the person has no email on
        file, and is otherwise rejected if it disagrees with the one already
        there. Shared email addresses between persons are not supported.
      required:
        - person_id
        - phone
      additionalProperties: false
      properties:
        person_id:
          type: string
          description: >-
            A person's `id` from `GET /customers/{customer_id}/persons`, on the
            customer's latest approved application.
        phone:
          type: string
          description: >
            The cardholder's own mobile number, E.164-formatted. It is used to
            verify

            the cardholder at online checkouts that ask for 3-D Secure, so do
            not send

            a shared or placeholder number.
          example: '+15555550123'
        email:
          type: string
          format: email
          description: >-
            Required only when the person has no email on file (see
            `missing_for_cards` on `GET /customers/{customer_id}/persons`).
            Otherwise omit it; sending a different email than the one already on
            file is rejected.
          example: ada@example.com
        external_id:
          type: string
          nullable: true
          description: Optional client-supplied lookup key.
        address:
          $ref: '#/components/schemas/Address'
          description: >-
            Cardholder's residential address. Required by the card provider;
            when omitted, the customer's address is used.
    CreateCardholderForNewPersonRequest:
      type: object
      title: Create Cardholder For New Person Request
      description: >-
        Creates a cardholder for a BUSINESS customer from contact details
        supplied directly, with no existing person to reference. Shared email
        addresses between persons are not supported: an `email` matching a
        person already on the customer's latest approved application is refused
        with `#cardholder-person-exists` rather than creating a second person
        under the same address.
      required:
        - first_name
        - last_name
        - email
        - phone
      additionalProperties: false
      properties:
        first_name:
          type: string
          description: Cardholder's first name.
          example: Ada
        last_name:
          type: string
          description: Cardholder's last name.
          example: Lovelace
        email:
          type: string
          format: email
          maxLength: 254
          description: The cardholder's own email address.
          example: ada@example.com
        phone:
          type: string
          description: >
            The cardholder's own mobile number, E.164-formatted. It is used to
            verify

            the cardholder at online checkouts that ask for 3-D Secure, so do
            not send

            a shared or placeholder number.
          example: '+15555550123'
        external_id:
          type: string
          nullable: true
          description: Optional client-supplied lookup key.
        address:
          $ref: '#/components/schemas/Address'
          description: >-
            Cardholder's residential address. Required by the card provider;
            when omitted, the customer's address is used.
    CardholderOpenRequirements:
      type: object
      title: Cardholder Open Requirements
      description: >-
        Summary of what a reviewer is currently waiting on for this cardholder.
        `count` is `0` and `types` is empty unless the cardholder is in
        `request_for_information`.


        This is a summary only. Read the individual items, with their
        descriptions, from `GET /cardholders/{cardholder_id}/application`.
      required:
        - count
        - types
      properties:
        count:
          type: integer
          description: >-
            Number of requirements still outstanding, that is those with status
            `missing`. Requirements already satisfied (`on_file`) are excluded,
            so this can be lower than the length of `requested_information` on
            the application.
          example: 0
        types:
          type: array
          description: >-
            The distinct kinds of information still outstanding, deduplicated.
            Satisfied requirements do not contribute a type.
          items:
            $ref: '#/components/schemas/CardholderRequirementType'
          example: []
    ValidationError:
      type: object
      required:
        - field
        - message
      properties:
        field:
          type: string
          description: Field path using dot notation for nested fields.
          example: bank_account.routing_number
        message:
          type: string
          description: Human-readable description of the field error.
          example: Routing number must be exactly 9 digits
        code:
          type: string
          description: Machine-readable error code for this field.
          example: invalid_format
    Address:
      type: object
      title: Address
      description: >-
        Standardized physical address format used throughout the Dakota platform
        for user and entity addresses.
      required:
        - street1
        - city
        - country
      properties:
        street1:
          type: string
          description: Primary street address line
          example: 123 Main St
        street2:
          type: string
          description: >-
            Secondary address information such as apartment, suite, or unit
            number
          example: Apt 4B
        street3:
          type: string
          description: Additional address information like building name or floor
          example: Building C
        city:
          type: string
          description: City or locality name
          example: San Francisco
        region:
          type: string
          description: Full name of state, province, or region
          example: California
        postal_code:
          type: string
          description: Postal or ZIP code
          example: '94105'
        country:
          type: string
          description: ISO 3166-1 alpha-2 country code (two-letter country code)
          example: US
          minLength: 2
          maxLength: 2
    CardholderRequirementType:
      type: string
      title: Cardholder Requirement Type
      description: >-
        The kind of information a reviewer has asked for. `field` is a value on
        the cardholder that must be corrected, `question` is free text a
        reviewer asked, and `document` is a file that must be supplied.
      enum:
        - document
        - field
        - question
      example: document
  parameters:
    IdempotencyKeyHeader:
      name: x-idempotency-key
      in: header
      required: true
      description: >-
        Unique key to ensure request idempotency. If the same key is used within
        a certain time window, the original response will be returned instead of
        executing the request again.
      schema:
        type: string
        format: uuid
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````