Skip to content
Version v0.1 (draft)
ChangelogContributing

Objects

Decision and signing

The authorizer's signed verdict, how it is signed, verified and versioned.

A decision is the authorizer’s answer to an intent. It is signed with Ed25519 over canonical JSON, and it is the only thing an executor will act on.

Verdict Executable Meaning
ACCEPT yes Every rule passed. The buying power the order needs is reserved.
RESIZE yes Accepted at a smaller qty because the mandate has allow_resize. requested_qty keeps the original.
REJECT no A rule failed. code and message say which. Signed, so the refusal is attributable.
PENDING_APPROVAL no Above approval_above_notional. Carries an approval_id. After a human approves, every rule is re-run and an executable decision is issued.
CHECK_PASS no The answer of a dry run (/v1/check). Never executable.
Field Notes
v Payload version, 2
decision_id, intent_id
verdict See above
agent_id, user_id, mandate_id Who asked, on whose behalf, under which mandate
instrument, side, qty, order_type, limit_price, venue The order exactly as it may be sent
requested_qty RESIZE only
account_id, account_ref The user’s account at the venue, and the venue’s own account reference passed to the adapter
protected_unit The venue and product class whose rules applied
reference_price, price_collar_bps Used to bound market orders
reservation_id, reserved_notional, currency The hold taken on buying power
reduce_only
time_in_force Canonical value (post_only, not alo)
enforcement Who stands between this decision and the venue. See below
expires_at Short-lived. The reference implementation defaults to 30 seconds
client_order_id The idempotency and reconciliation key at the venue
approval_id PENDING_APPROVAL only
code, message Reason for a rejection
key_id Identifies the signing key
  1. Build the payload above, including key_id.
  2. Serialise it as canonical JSON.
  3. Sign the bytes with Ed25519. The signature is hex-encoded and travels next to the decision, not inside it.

Verifiers fetch public keys from GET /v1/keys, which returns entries of key_id, public_key (hex) and alg. A verifier selects the key by the payload’s key_id. An unknown key_id SHOULD trigger a bounded refresh of the key set, so keys can rotate without restarts.

Any change to a signed field makes verification fail. The executor answers A2M_BAD_SIGNATURE with HTTP 422.

Value Meaning Status
custodial_fairx The executor holds the venue credential and is the only path to the venue. Enforced. Implemented. Every decision issued today.
client_hook A hook in the agent’s own client asks the authorizer first. Bypassable. Planned
watcher A read-only connection reconciles the account afterwards and flags orders with no decision. Detects, does not prevent. Planned
venue_verified The venue verifies the authorizer’s signature itself. Planned

An executor MUST refuse an enforcement mode it does not implement (A2M_ENFORCEMENT).

  • Adding a signed field, or changing the meaning of one, is a version bump.
  • A verifier MUST refuse a version it does not know, and MUST refuse a payload that carries a field its version does not define.
  • v: 1 (legacy) has no time_in_force and no enforcement. It is still accepted so a decision issued before an upgrade stays usable until it expires, and executes as gtc with custodial_fairx. A v1 payload carrying either v2 field is rejected.
  • v: 2 (current) requires both fields.

A decision is issued, then either consumed by the executor or voided. It cannot be sent twice (A2M_DECISION_CONSUMED). A decision that expires before it is sent is refused (A2M_DECISION_EXPIRED) and its reservation is released.

The authorizer runs these rules in this order and stops at the first failure. Rules after kya.mandate may assume exactly one live mandate.

# Rule Layer
1 kya.agent kya
2 kya.mandate kya
3 allowlist.instrument user
4 allowlist.venue user
5 market.instrument market
6 broker.unit broker
7 account.open account
8 market.session market
9 price.reference market
10 market.lot_tick market
11 market.price_band market
12 price.collar user
13 broker.notional broker
14 fat_finger.qty user
15 fat_finger.notional user
16 kill.halt user
17 direction user
18 position.limit user
19 throttle.open_orders user
20 throttle.rate user
21 buying_power account
22 budget account
23 account.cash account

The authorize response carries the rule results as hits (each with rule, layer, passed, code, message), next to verdict, decision, signature and key_id.

Shape only; identifiers are placeholders.

{
"verdict": "REJECT",
"code": "A2M_FAT_FINGER_QTY",
"message": "quantity exceeds the fat-finger cap",
"decision": {
"v": 2,
"decision_id": "dec_...",
"intent_id": "demo-0002",
"verdict": "REJECT",
"instrument": "ETH",
"side": "buy",
"qty": "5",
"venue": "hyperliquid",
"enforcement": "custodial_fairx",
"time_in_force": "gtc",
"code": "A2M_FAT_FINGER_QTY",
"key_id": "...",
"...": "..."
},
"signature": "<hex>",
"key_id": "..."
}