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

# Amend a mandate — append a new version (ALPHA)

> > **Alpha** — early access.

Appends a NEW immutable version to an ACTIVE mandate, carrying a changed rule into force with ONE signature and WITHOUT resetting the spend already made in the current window.

Mandates are immutable, so a limit could previously only be changed by cancel-then-create — which cost two signing ceremonies AND silently gave the agent a fresh budget, because usage is recorded per mandate and a replacement starts with empty buckets. Versions fix that: usage keeps accruing to the MANDATE, so an agent that has spent 9,000 of a 10,000 monthly limit and is amended to 20,000 has 11,000 left — not 20,000.

Versioning is append-only. The outgoing version's rule is never rewritten and stays readable at GET /mandates/{mandate_id}/versions; the newest version governs from the moment it lands.

**Only the amount fields (`max_per_tx` and the window caps) and `targets` may change.** `target_type`, `window`, `asset` and `network_id` are frozen — changing one is rejected with a 400 telling you to cancel this mandate and create a new one. The window in particular is frozen because usage rows carry no window label, so changing it would retroactively re-bucket every past spend.

**ADDING a payee to `targets` additionally requires the resulting rule to carry an aggregate ceiling** — `max_amount_in_window` or `max_count_in_window`. Without one, each payee has its own separate budget, so a new payee brings a new budget and the payee list alone would move what the agent can spend in total, with no amount you approved having changed. Set the ceiling in the SAME amendment that adds the payee: the amount fields are amendable, so this costs no second signature and no budget reset. A count-preserving swap counts as adding, because the incoming payee starts with an empty budget. REMOVING payees is always allowed — it can only lower what the agent may spend.

§8 applies as it does to every mandate mutation: the amender must be a recognized signer of the mandate's customer OTHER than the bound one. The signature covers the "amend" verb, the new rule, AND the version number being created, so a signature for v2 cannot be replayed to create v3.

Note that LOWERING a limit below what has already been spent leaves zero remaining, not a negative balance — the agent is frozen for the rest of the window. That is correct (spend cannot be undone) but it is worth saying out loud to a user, because "reduce my limit" and "stop all payments this month" feel like different actions.




## OpenAPI

````yaml /openapi.yaml post /mandates/{mandate_id}/amend
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-alpha: true
    description: >-
      Alpha — 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-alpha: true
    description: >-
      Alpha — 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-alpha: true
    description: >-
      Alpha — read-only account insight: a deterministic report over a
      customer's agentic activity (funding balances, upcoming obligations,
      failures, mandate headroom and expiry) plus an advisory chat that narrates
      it. 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.


      **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
paths:
  /mandates/{mandate_id}/amend:
    post:
      tags:
        - Mandates
      summary: Amend a mandate — append a new version (ALPHA)
      description: >
        > **Alpha** — early access.


        Appends a NEW immutable version to an ACTIVE mandate, carrying a changed
        rule into force with ONE signature and WITHOUT resetting the spend
        already made in the current window.


        Mandates are immutable, so a limit could previously only be changed by
        cancel-then-create — which cost two signing ceremonies AND silently gave
        the agent a fresh budget, because usage is recorded per mandate and a
        replacement starts with empty buckets. Versions fix that: usage keeps
        accruing to the MANDATE, so an agent that has spent 9,000 of a 10,000
        monthly limit and is amended to 20,000 has 11,000 left — not 20,000.


        Versioning is append-only. The outgoing version's rule is never
        rewritten and stays readable at GET /mandates/{mandate_id}/versions; the
        newest version governs from the moment it lands.


        **Only the amount fields (`max_per_tx` and the window caps) and
        `targets` may change.** `target_type`, `window`, `asset` and
        `network_id` are frozen — changing one is rejected with a 400 telling
        you to cancel this mandate and create a new one. The window in
        particular is frozen because usage rows carry no window label, so
        changing it would retroactively re-bucket every past spend.


        **ADDING a payee to `targets` additionally requires the resulting rule
        to carry an aggregate ceiling** — `max_amount_in_window` or
        `max_count_in_window`. Without one, each payee has its own separate
        budget, so a new payee brings a new budget and the payee list alone
        would move what the agent can spend in total, with no amount you
        approved having changed. Set the ceiling in the SAME amendment that adds
        the payee: the amount fields are amendable, so this costs no second
        signature and no budget reset. A count-preserving swap counts as adding,
        because the incoming payee starts with an empty budget. REMOVING payees
        is always allowed — it can only lower what the agent may spend.


        §8 applies as it does to every mandate mutation: the amender must be a
        recognized signer of the mandate's customer OTHER than the bound one.
        The signature covers the "amend" verb, the new rule, AND the version
        number being created, so a signature for v2 cannot be replayed to create
        v3.


        Note that LOWERING a limit below what has already been spent leaves zero
        remaining, not a negative balance — the agent is frozen for the rest of
        the window. That is correct (spend cannot be undone) but it is worth
        saying out loud to a user, because "reduce my limit" and "stop all
        payments this month" feel like different actions.
      operationId: amendMandate
      parameters:
        - name: mandate_id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AmendMandateRequest'
            example:
              signer_public_key: BHkSignerKeyQm
              signature: MEUCIQExampleSignature
              rule:
                target_type: recipient
                targets:
                  - 2vWxRecipient000000000000000
                asset: USDC
                window: MONTHLY
                max_amount_in_window: '20000'
      responses:
        '200':
          description: >
            The mandate at its new version. `version` is the version just
            created; `rule` is now that version's rule.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Mandate'
              example:
                id: 2vWxMandate00000000000000000
                status: active
                version: 2
        '400':
          description: Invalid request
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
              example:
                type: https://docs.dakota.xyz/api-reference/errors#invalid-request
                title: Invalid Request
                status: 400
                detail: >-
                  window cannot change between mandate versions (currently
                  "MONTHLY", requested "WEEKLY") — cancel this mandate and
                  create a new one
        '404':
          description: Agentic payments not enabled, or the resource was not found
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
              example:
                type: https://docs.dakota.xyz/api-reference/errors#not-found
                title: Not Found
                status: 404
                detail: agentic payments are not enabled
components:
  schemas:
    AmendMandateRequest:
      type: object
      required:
        - signer_public_key
        - signature
        - rule
      description: >
        A new version of an active mandate's rule, signed by a recognized signer
        of the mandate's customer other than the bound one (§8).


        The `rule` below is taken VERBATIM — it is stored, and the signature is
        verified, exactly as sent. Unlike mandate creation, this endpoint does
        NOT normalize it: a rule that is not already canonical is refused with
        400 naming the offending field, rather than being rewritten into
        something you cannot reproduce. So `window` must be present and
        non-empty (send "NONE" for a lifetime window), `targets` must be
        recipient ids and never payee names, and `asset` must already be
        uppercase. This is what lets you compute the signed bytes from the body
        you are about to send.
      properties:
        signer_public_key:
          type: string
          description: >-
            The amending signer's public key. Must differ from the mandate's
            bound signer.
        signature:
          type: string
          description: >
            Signature over the JCS-canonical amend payload — the same shape as
            the approve/cancel payload with `action` set to "amend", `rule` set
            to the NEW rule below EXACTLY as you send it, and one extra key,
            `version`, set to the version being created (the mandate's current
            `version` + 1). Including the version is what stops a signature for
            v2 from being replayed to create v3. Because the rule is never
            normalized on this path, the bytes you sign are the bytes the server
            verifies.
        rule:
          allOf:
            - $ref: '#/components/schemas/MandateRule'
          description: >
            The complete NEW rule, not a patch, in canonical form — it is stored
            and signed verbatim, never normalized. `window` is REQUIRED here
            (use "NONE" for a lifetime window) even though it is optional when
            creating; `targets` must be recipient ids, not payee names; `asset`
            must be uppercase. Anything else is refused with 400 naming the
            field, not silently rewritten. `target_type`, `window`, `asset` and
            `network_id` must additionally match the current version exactly;
            only the amount fields and `targets` may differ.
    Mandate:
      type: object
      properties:
        id:
          type: string
        status:
          type: string
          enum:
            - pending
            - active
            - expired
            - rejected
            - revoked
            - done
          description: >
            expired is DERIVED, never stored - a pending or active mandate whose
            valid_until has passed. It cannot authorize payments and cannot be
            approved; it can still be cancelled.
        bound_signer_id:
          type: string
        customer_id:
          type: string
          description: >
            The customer this mandate is anchored to - approval requires a
            recognized signer of this customer other than the bound one (§8),
            and recipient targets belong to it. Absent only on rows created
            before the anchor existed.
        target_names:
          type: array
          items:
            type: string
          description: >
            DERIVED, display-only - the rule's recipient targets resolved to
            names, parallel to rule.targets (raw id on a miss). Absent for
            address/any target kinds. The ids in the rule remain the grant.
        instruction_id:
          type: string
        version:
          type: integer
          description: >
            The CURRENT version number (1 for a mandate that has never been
            amended). `rule` below is this version's rule — the one the gate
            governs on. Each version is immutable and independently signed; the
            full history is at GET /mandates/{mandate_id}/versions.
        rule:
          $ref: '#/components/schemas/MandateRule'
        valid_from:
          type: integer
          format: int64
        valid_until:
          type: integer
          format: int64
        approved_by_signer_id:
          type: string
        approved_at:
          type: integer
          format: int64
          description: Unix time of the §8 approval; absent until approved.
        rejected_by_signer_id:
          type: string
          description: >-
            The signer that cancelled the mandate while it was still pending
            (§8); absent otherwise.
        revoked_by_signer_id:
          type: string
          description: >-
            The signer that cancelled the mandate after activation (§8); absent
            otherwise.
    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'
    MandateRule:
      type: object
      required:
        - target_type
        - asset
      properties:
        target_type:
          type: string
          enum:
            - any
            - recipient
            - address
        targets:
          type: array
          items:
            type: string
          description: >-
            One or more targets of the declared kind (recipient ids or
            addresses; empty for any). Max 32.
        network_id:
          type: string
          description: >-
            The network the funding wallet pays on (e.g. base-sepolia). Empty
            authorizes any network — the other dimensions still apply.
        asset:
          type: string
          description: >
            The asset the funding wallet SENDS — deposit-denominated, e.g. USDC
            — compared case-insensitively against the firing payment. Write the
            crypto deposit asset even when the recipient ultimately receives
            fiat via a bank offramp (stablecoin conversion is 1:1): a fiat
            symbol such as USD can never match a send, so the mandate would deny
            every payment.
        max_per_tx:
          type: string
          description: >-
            Per-payment cap — a decimal string in the rule's (deposit) asset.
            Empty means no per-payment cap; the window caps may still bound
            total spend.
        window:
          type: string
          enum:
            - NONE
            - DAILY
            - WEEKLY
            - MONTHLY
          description: >
            Spend-cap window shared by every window limit below (calendar, not
            rolling; all boundaries in UTC). NONE = lifetime; DAILY = calendar
            day from 00:00 UTC; WEEKLY = calendar week from Monday 00:00 UTC;
            MONTHLY = calendar month from the 1st, 00:00 UTC.
        max_amount_per_target_in_window:
          type: string
          description: >-
            Cumulative cap over the window, PER TARGET (never shared across
            targets).
        max_count_per_target_in_window:
          type: integer
          description: Up to N times PER TARGET in the window (window NONE = lifetime).
        max_amount_in_window:
          type: string
          description: >
            Cumulative cap over the window ACROSS ALL TARGETS — the ceiling on
            total spend under this mandate, whoever it pays. Use this for "the
            agent may spend up to X per month": a per-target cap alone
            MULTIPLIES by the number of targets (a 3-payee allowlist capped at
            10000 per target permits 30000). Set both and both are enforced —
            the tighter one binds. Empty means no aggregate amount ceiling.
        max_count_in_window:
          type: integer
          description: >
            Up to N payments in the window ACROSS ALL TARGETS, whoever they go
            to (window NONE = lifetime). The aggregate twin of
            max_count_per_target_in_window, which caps each target separately
            and so permits targets x N payments. Omit or 0 for no aggregate
            count ceiling.
    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

````