> ## 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.

# Register the client's agentic policy (BETA)

> > **Beta** — early access.

Registers (or fully replaces) this client's `client_policy` — the ONLY way to set one. A client declares its vocabulary once, and every drafting turn and every accept then resolve it from here.

Registration is deliberately the only path. The policy was once also accepted in the `POST /payment-agents/{payment_agent_id}/proposals` and `POST /instructions` bodies, and that made a two-call conversation able to disagree with itself: a proposal drafted under one policy and accepted without it is judged by different rules, so a legal draft was refused at the customer's approval click. A policy is a property of the CLIENT, not of a request, and it now lives in exactly one place.

A full replace, not a merge: send the whole policy every time. Changing one takes effect on the next turn — there is no cache — so this is also how you TEST a policy. Register, run a conversation, register something else. Validation happens HERE: an unknown key, an unknown value, or a label for a concept the server does not implement is a 400 at registration, not a surprise on a customer's first conversation.

FULL REPLACE, not a merge: the registration IS the client's declared vocabulary, so an omitted field means the client no longer wants it. An empty body (`{}`) therefore clears the registration back to platform defaults.




## OpenAPI

````yaml /openapi.yaml put /agentic-policy
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 account insight: a deterministic report over a customer's
      agentic activity (funding balances, upcoming obligations, failures,
      mandate headroom and expiry). 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: 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: 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:
  /agentic-policy:
    put:
      tags:
        - Agentic Payments
      summary: Register the client's agentic policy (BETA)
      description: >
        > **Beta** — early access.


        Registers (or fully replaces) this client's `client_policy` — the ONLY
        way to set one. A client declares its vocabulary once, and every
        drafting turn and every accept then resolve it from here.


        Registration is deliberately the only path. The policy was once also
        accepted in the `POST /payment-agents/{payment_agent_id}/proposals` and
        `POST /instructions` bodies, and that made a two-call conversation able
        to disagree with itself: a proposal drafted under one policy and
        accepted without it is judged by different rules, so a legal draft was
        refused at the customer's approval click. A policy is a property of the
        CLIENT, not of a request, and it now lives in exactly one place.


        A full replace, not a merge: send the whole policy every time. Changing
        one takes effect on the next turn — there is no cache — so this is also
        how you TEST a policy. Register, run a conversation, register something
        else. Validation happens HERE: an unknown key, an unknown value, or a
        label for a concept the server does not implement is a 400 at
        registration, not a surprise on a customer's first conversation.


        FULL REPLACE, not a merge: the registration IS the client's declared
        vocabulary, so an omitted field means the client no longer wants it. An
        empty body (`{}`) therefore clears the registration back to platform
        defaults.
      operationId: updateClientAgenticPolicy
      parameters:
        - $ref: '#/components/parameters/IdempotencyKeyHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgenticClientPolicy'
            example:
              payee_model: flat
              mandate_strategy: external_only
              payout_route: bank_only
              labels:
                limit: spending limit
                limit_unit: USD
                payee: recipient
      responses:
        '200':
          description: The registered policy, normalized as it will be applied.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RegisteredAgenticClientPolicy'
              example:
                policy:
                  payee_model: flat
                  mandate_strategy: external_only
                  payout_route: bank_only
                  labels:
                    limit: spending limit
                    limit_unit: USD
                    payee: recipient
                created_at: 1761600000
                updated_at: 1761686400
        '400':
          description: >
            The policy is not one this server can enforce — an unknown key, an
            unsupported value, or a label for an unimplemented concept.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
        '404':
          description: Not Found
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
components:
  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
  schemas:
    AgenticClientPolicy:
      type: object
      description: >
        BETA — how THIS client's product speaks, and what the agent may propose
        for it. It reshapes what the drafting model SEES (tool results, tool
        descriptions, prompt sections) and constrains what it may PROPOSE, so
        the agent narrates in the client's own nouns instead of platform ones.


        SCOPE: this is a per-CLIENT policy — it belongs to the `client_id`
        behind the API key, never to a key (api keys are N:1 to clients, so a
        per-key policy would fragment for a client running one service key per
        deployment). REGISTER IT at `PUT /agentic-policy`, which resolves the
        client from the API key: there is no id to pass and no other client's
        policy to address.


        DELIVERY: registration only. A request body never carries this object —
        a conversation is two calls (draft, then accept) judged independently,
        and a per-request policy let them disagree, so a draft that was legal
        under one could be refused at the customer's approval click.


        STRICT: an unknown key, an unknown value, or a label for a concept the
        server does not implement is a 400 — "accepted" always means "enforced".
        Absent (or every field empty) ⇒ platform defaults, byte-for-byte the
        behaviour of a request that never mentioned it.
      additionalProperties: true
      properties:
        payee_model:
          type: string
          enum:
            - ''
            - nested
            - flat
          description: >
            How payees are shaped in tool results. `nested` (the default, and
            what an absent policy means) returns ONE payee carrying N payout
            methods. `flat` returns one entry PER payout method — a payee is a
            name plus one way of being paid — each with `payee_ref` (the id to
            pass straight through wherever a payout method is referenced),
            `entity_ref` (the person behind the entry; several entries can share
            one, and it is what binds a new payout method to an existing person
            instead of duplicating them), `name`, and `paid_by` (a rendered
            human phrase such as `bank account at Chase ****4321` or
            `base-sepolia 0x1234…cdef`). A payout method never pins an asset, so
            `paid_by` never names one.
          example: flat
        payout_assets:
          type: array
          items:
            type: string
          description: >
            The assets a PAYEE may receive — what a conversion may output, and
            what a direct payment may send. Absent or empty means unrestricted,
            which is the platform default.


            State it when your product's FUNDING asset is not something a payee
            is ever paid in. Platform's asset registry only knows which assets
            settle on which chain, so it cannot tell that your funding
            stablecoin is a category error as a payout — it will happily draft
            one if a customer names it, and because that route needs no
            conversion account it also creates a duplicate payee for someone
            already saved. Declaring the allowlist turns both into a refusal the
            model sees before the customer does.


            Case-insensitive and de-duplicated; the order you send is the order
            quoted back to the customer.
          example:
            - USDC
            - USDT
        labels:
          type: object
          additionalProperties:
            type: string
          description: >
            Platform concept → the noun THIS client's customers use for it. The
            noun replaces the platform word in the tool results the agent reads
            AND in what it writes to the customer. Implemented concepts: `limit`
            (the spending limit; platform calls it a mandate), `payee` (the
            person being paid), and `limit_unit` (the UNIT the limit's amounts
            are quoted in, e.g. `USD`). Any other key is a 400 — a label for a
            concept the server does not implement would be accepted and then
            ignored.


            `limit` and `payee` are nouns and must match `^[a-z][a-z0-9
            _-]{0,30}$`. `limit_unit` is a unit code and must match
            `^[A-Za-z][A-Za-z0-9]{0,9}$`, so it may be uppercase. Never a
            phrase, never a sentence.


            `limit_unit` matters most under a `payout_route` that forces a
            conversion account: the limit governs the DEPOSIT leg, so its own
            asset is the funding asset the customer has never heard of. With the
            unit set the agent quotes the caps and remaining budget in it ("your
            spending limit is 10 USD per month, 0.98 left") instead of reaching
            for the funding asset.


            Labelling `payee` also renames the flat view's keys: the container
            becomes the label plus `s` and each entry's ref becomes the label
            plus `_ref` — so `{"payee": "recipient"}` yields `recipients[]` with
            `recipient_ref`. `entity_ref` is NOT renamed; it is an opaque handle
            to the person behind the entry, not the labelled concept. The plural
            is a literal `+s` (the charset restricts labels to simple lowercase
            nouns), so an irregular noun gets an odd but harmless plural.
          example:
            limit: spending limit
            limit_unit: USD
            payee: recipient
        mandate_strategy:
          type: string
          enum:
            - ''
            - external_only
          description: >
            `external_only`: spending limits live entirely OUTSIDE this
            conversation — the client creates and amends them in its own limit
            editor through the customer-signed amend API. The agent never drafts
            one; a drafted payment must fit an existing active limit's remaining
            budget, and when nothing covers it the agent says so and points the
            customer at the app instead of proposing a limit. Absent ⇒ the agent
            drafts limits as usual.
          example: external_only
        payout_route:
          type: string
          enum:
            - ''
            - conversion_account_only
            - bank_only
          description: >
            Restricts how the money may leave. `conversion_account_only`: every
            scheduled payment must fund a conversion account — no direct
            wallet-to-address send. `bank_only`: on top of that the payout must
            land in a bank account — an outbound bank rail is required and no
            crypto payout method may be created. Absent ⇒ any supported route.


            Either value also makes the FUNDING leg system-chosen, which changes
            two things. The payment has two legs and the customer only names
            one: what they ask for is what ARRIVES (the conversion's output),
            while what LEAVES the wallet is the conversion deposit. The spending
            limit governs the DEPOSIT leg, so (a) the asset or chain the
            customer names can never put a payment outside the limit — coverage
            is judged on the per-payment cap and the remaining budget alone —
            and (b) the deposit's asset and network move out of the limit's
            `rule` into a `funding_leg_internal` block in the `active_mandates`
            result, which the agent copies into the conversion account and never
            says out loud. Pair this with `labels.limit_unit` so the agent has a
            unit to quote the limit in.
          example: conversion_account_only
    RegisteredAgenticClientPolicy:
      type: object
      description: >
        A client's registered `client_policy` (BETA) with its registration
        timestamps.


        `policy` is the NORMALIZED form — what the server will actually apply,
        not an echo of what was sent. Values that mean "the default" are
        normalized away (`payee_model: nested` becomes absent, because an
        explicit nested and an absent policy have to be the same value rather
        than two values that merely behave alike today).
      required:
        - policy
      properties:
        policy:
          $ref: '#/components/schemas/AgenticClientPolicy'
        created_at:
          type: integer
          format: int64
          description: Unix time this client first registered a policy.
        updated_at:
          type: integer
          format: int64
          description: Unix time the registration was last replaced.
    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.
    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
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````