Register the client's agentic policy (ALPHA)
Alpha — early access.
Registers (or fully replaces) this client’s client_policy, so the client declares its vocabulary ONCE instead of resending it in every POST /payment-agents/{payment_agent_id}/proposals body. Sending it per request still works and still wins, as a development override — but forgetting to send it fails SILENTLY: the agent simply narrates in platform’s nouns again (“destination”, “mandate”) and nothing errors. A registration removes that failure mode.
The body is the SAME client_policy object the proposals request takes, so an existing integration registers by moving the object it already sends. It goes through the SAME validation: an unknown key, an unknown value, or a label for a concept the server does not implement is a 400 HERE, 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.
A client may register its OWN policy; writing another client’s is a 403.
Authorizations
Headers
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.
Path Parameters
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.
27^[0-9A-Za-z]{27}$"1NFHrqBHb3cTfLVkFSGmHZqdDPi"
Body
ALPHA — 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 ONCE at PUT /clients/{client_id}/agentic-policy.
DELIVERY: sent in a POST .../proposals body this object is a DEVELOPMENT OVERRIDE — it wins for that one turn and the server logs that it did. Prefer the registration: forgetting to send the body copy fails SILENTLY, and the agent simply starts narrating in platform's nouns again with no error anywhere. Resolution per request is: a non-empty body policy, else this client's registration, else nothing at all.
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.
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.
, nested, flat "flat"
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.
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.
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.
, external_only "external_only"
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.
, conversion_account_only, bank_only "conversion_account_only"
Response
The registered policy, normalized as it will be applied.
A client's registered client_policy (ALPHA) 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).
ALPHA — 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 ONCE at PUT /clients/{client_id}/agentic-policy.
DELIVERY: sent in a POST .../proposals body this object is a DEVELOPMENT OVERRIDE — it wins for that one turn and the server logs that it did. Prefer the registration: forgetting to send the body copy fails SILENTLY, and the agent simply starts narrating in platform's nouns again with no error anywhere. Resolution per request is: a non-empty body policy, else this client's registration, else nothing at all.
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.
Unix time this client first registered a policy.
Unix time the registration was last replaced.

