# Limit a key or an agent

`POST /spend-policies` · Skerry · Limits, agents and approvals

Limit a key or an agent: a mandate. `scope` is required, `family` or `currency`, and decides the unit of every amount limit (see the group note); a request without it is refused with `invalid_request` naming `scope`. `operations` may name payment.charge, refund.create, payout.create, conversion.create, settlement.create, checkout.create, payment_link.create, subscription.create, standing_order.create, payee.create and wallet.create. `max_count` caps operations per `count_window` (day or month, rolling). `approve_operations` always wait for a person. `checkout_above` is the amount above which checkout sessions and payment links are checked. `merchant_allow_list` applies to operations that name a merchant; `wallet_allow_list` to the wallet money leaves. `evidence` records consent; with `consent_required` the mandate is inactive until verified evidence exists. The answer carries `mandate_id`, `summary` (the rule in sentences, generated from it) and `active`.

- Scope: policies:write
- Moves money: no
- Idempotency: optional
- Untrusted: name, purpose, approved_by, summary, revoked_reason

## Parameters

- `Idempotency-Key` (header), string: A repeat with the same key and body returns the first answer instead of acting twice.

## Request body

`application/json`

```json
{
  "type": "object"
}
```

## Responses

- `200`: Success.
- `202`: requires_approval — Not an error: the operation is held for a person. The body names the approval.
- `400`: price_below_minimum — The price is below the smallest single payment for a method (`detail.method`, `detail.minimum`), or has a part of a cent. Set `credit_only`, raise the price, or drop the method.
- `401`: dpop_invalid — The token is bound to a key and the DPoP proof was missing, wrong for this request, or already used.
- `402`: payment_failed — The payer's bank or card refused. `detail.reason` says why in plain terms.
- `403`: agent_payment_over_max — The merchant asks for more than `max_amount`. Nothing was signed. `detail.asked_minor` is the lowest price asked, in US cents.
- `404`: not_found — No such object in this project — including one that belongs to somebody else.
- `409`: token_not_issued — A delegated payment token could not be issued ready to use, for instance because the payer would have to act. Nothing was sent to the merchant.
- `429`: too_many_waiters — This key already has as many requests waiting (`?wait=`) as it may. Let one finish, or read without wait.
- `500`: internal_error — Ours. Quote the `request_id` and we can find the call.
- `502`: platform_error — A provider failed. Safe to retry with the same Idempotency-Key.
- `503`: oauth_unavailable — The authorisation server's keys could not be read, so tokens cannot be checked. API keys still work.

## Example

```sh
curl -X POST 'https://api.skerry.xyz/spend-policies' \
  -H 'Authorization: Bearer $KEY' \
  -H 'Content-Type: application/json' \
  -d '"{ customer_id, approved_by, scope: family | currency, key_id? | agent_id?, purpose?, max_amount?, daily_max?, monthly_max?, max_count?, count_window?, approval_above?, second_approval_above?, approvers_required?: 1 | 2, approval_evidence?: none | passkey, approval_expires_minutes?, approve_operations?, checkout_above?, currencies?, operations?, payee_allow_list?, merchant_allow_list?, wallet_allow_list?, valid_from?, expires_at?, consent_required?, evidence?: { method, subject, at, assertion?, attestation? } }"'
```
