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

# Direct Control: Mandates & Scheduled Payments

> Create and manage the agentic artifacts straight from the API — no proposals or instructions required

The conversation flow — proposals drafted from a prompt, accepted via instructions — is optional. Every artifact it creates has a plain API surface, and when your application already knows exactly what to create, you can drive it directly. Same objects, same guarantees, no LLM in the loop.

Two invariants never relax, whichever path you take:

* **Dual control**: a mandate activates only when a recognized signer other than the bound one signs it ([Mandate signing](/documentation/agentic-payments/mandate-signing)).
* **Fire-time coverage**: the mandate gate matches every payment against active mandates at the moment it fires — nothing is bound in advance.

## Mandates

### Create

[`POST /mandates`](/api-reference/mandates/create-a-mandate-directly-alpha) drafts a `pending` mandate bound to a signer — see [choosing targets](/documentation/agentic-payments/examples#choosing-targets) for the three target kinds. Exactly **one binding form** names that signer:

* **`payment_agent_id`** — the payment agent convenience: binds the agent's signer and anchors the mandate to the agent's customer.
* **`signer_id` + `customer_id`** — binds **any of your signers**. Mandates were always signer-level at the gate; this form just names the signer directly. `customer_id` is required alongside because the customer anchors two things the agent used to supply: the recipient-target scope (names resolve against *this* customer's payees) and the approver set (approval takes a recognized signer of *this* customer, other than the bound one). The signer must be recognized for that customer — a member of a signer group attached to one of its wallets.

The hosted-agent form:

```bash theme={null}
curl -X POST https://api.platform.dakota.xyz/mandates \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "payment_agent_id": "2vWxAgent0000000000000000000",
    "rule": {
      "target_type": "recipient",
      "targets": ["Alice"],
      "network_id": "base-sepolia",
      "asset": "USDC",
      "max_per_tx": "100",
      "window": "MONTHLY",
      "max_count_per_target_in_window": 1
    },
    "valid_until": 1798675200
  }'
```

The same mandate bound to a signer named directly:

```bash theme={null}
curl -X POST https://api.platform.dakota.xyz/mandates \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "signer_id": "2vWxSigner000000000000000000",
    "customer_id": "2vWxCustomer0000000000000000",
    "rule": {
      "target_type": "recipient",
      "targets": ["Alice"],
      "network_id": "base-sepolia",
      "asset": "USDC",
      "max_per_tx": "100",
      "window": "MONTHLY",
      "max_count_per_target_in_window": 1
    },
    "valid_until": 1798675200
  }'
```

Either way it stays `pending` — and covers nothing — until the customer approves it with a signed payload (`POST /mandates/{mandate_id}/approve`, walked through in [Mandate signing](/documentation/agentic-payments/mandate-signing)). The returned mandate carries `bound_signer_id` and `customer_id`, whichever form created it.

The rule's `asset` is validated at create: it must be a **sendable** asset — deployed on the pinned `network_id` (or on at least one network when the rule leaves the network open). A symbol that can never match a payment (a fiat output like `USD`, a typo) is a 400 here, not a mandate that silently denies every fire.

### List, inspect, cancel

```bash theme={null}
# Active mandates for one signer
curl "https://api.platform.dakota.xyz/mandates?signer_id=2vWxSigner000000000000000000&status=active" \
  -H "X-API-Key: $DAKOTA_API_KEY"

# One mandate, full wire shape (rule, validity, audit columns)
curl "https://api.platform.dakota.xyz/mandates/2vWxMandate00000000000000000" \
  -H "X-API-Key: $DAKOTA_API_KEY"
```

Cancelling is a mandate mutation, so it is §8-signed like approval — same canonical payload, `"action": "cancel"`, signed by a recognized signer other than the bound one. A pending mandate becomes `rejected`, an active one `revoked`:

```bash theme={null}
curl -X POST https://api.platform.dakota.xyz/mandates/2vWxMandate00000000000000000/cancel \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "signer_public_key": "BHkApproverKeyQm",
    "signature": "MEUCIQExampleSignature"
  }'
```

## Scheduled payments

### Create

[`POST /scheduled-payments`](/api-reference/agentic-payments/schedule-a-payment-directly-alpha) schedules one or more payments for a signer, without the proposal flow. Name the payee **either** with `destination_id` (an existing crypto destination of the wallet's customer) **or** with a raw `address` + `network_id` — exactly one of the two. The schedule is explicit `dates` (one payment per timestamp), or `count` × `interval_seconds` from `start_at` (`0` = now).

One payment to an existing destination:

```bash theme={null}
curl -X POST https://api.platform.dakota.xyz/scheduled-payments \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "signer_id": "2vWxSigner000000000000000000",
    "wallet_id": "2vWxWallet000000000000000000",
    "destination_id": "2vWxDestination0000000000000",
    "amount": "10",
    "asset": "USDC",
    "dates": [1784073600]
  }'
```

Four weekly payments to a raw address, starting now:

```bash theme={null}
curl -X POST https://api.platform.dakota.xyz/scheduled-payments \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "signer_id": "2vWxSigner000000000000000000",
    "wallet_id": "2vWxWallet000000000000000000",
    "address": "0xa11ce00000000000000000000000000000000001",
    "network_id": "base-sepolia",
    "amount": "10",
    "asset": "USDC",
    "count": 4,
    "interval_seconds": 604800,
    "start_at": 0
  }'
```

The response is one row per date, each `scheduled`.

What is validated **at create**: the signer must be permitted to spend on the funding wallet, the wallet must belong to the calling client, the destination must belong to the wallet's customer, and dates must be plausible (not past, not absurdly far). What is **not** validated at create: mandate coverage — that is decided at fire time by the gate, so a payment with no covering active mandate **fails at fire** (emitting [`scheduled_payment.failed`](/documentation/agentic-payments/webhooks)) rather than being rejected here. Schedule first and sign the mandate later, or the other way round — order doesn't matter, coverage at the fire moment does.

### List and cancel

```bash theme={null}
# Everything still pending for one wallet
curl "https://api.platform.dakota.xyz/scheduled-payments?wallet_id=2vWxWallet000000000000000000&status=scheduled" \
  -H "X-API-Key: $DAKOTA_API_KEY"

# Cancel one payment while it is still scheduled
curl -X POST https://api.platform.dakota.xyz/scheduled-payments/2vWxScheduled000000000000000/cancel \
  -H "X-API-Key: $DAKOTA_API_KEY" \
  -H "X-Idempotency-Key: $(uuidgen)"
```

List filters compose: `customer_id`, `signer_id`, `wallet_id`, `mandate_id` (which mandate covered an executed payment), and comma-separated `status` values (`scheduled`, `executed`, `cancelled`, `failed`). Executed rows carry `mandate_id` and `wallet_transaction_id` for audit.

## Mixing the two paths

Direct creates and agent-drafted instructions produce the same rows — a mandate signed after an instruction covers direct schedules too, and vice versa. Use the agent where natural language earns its keep, and this surface where your application already speaks the schema.
