Terms
terms is the object the human approves and the relying party enforces. The server
validates its shape on create and answers 422 when a field is missing, blank, or the
wrong type. Every field is also a promise to the approver, because the YID app renders
them on the approval screen — and now because the approver’s own key signs it. See
user-signed approvals.
Required string fields must hold at least one non-whitespace character. The whole object
is capped at 4 KB of compact JSON, and the server answers 413 above that.
| Field | Required | Type | Meaning |
|---|---|---|---|
schema_version |
yes | integer | Exactly 1. An absent, non-integer, or different value is a 422 |
action |
yes | string | What kind of action this is, such as purchase. Keep it short, lowercase, and identical across identical operations so a relying party can branch on it |
approval_title |
yes | string | The headline the YID app shows the approver. Name the action, not your product |
summary |
yes | string | One sentence stating the whole action, including the amount when one exists |
merchant |
yes | string | The seller, counterparty, or service name the approver recognizes |
details |
yes | array | The rows the app renders as a table, described below |
currency |
no | string | ISO 4217 alpha-3 code from the server’s configured allowlist. Required whenever amount is present |
amount |
no | object | What gets charged. Omit for non-financial actions |
For an action such as signing a document, granting access, or publishing content, omit
both amount and currency. YanezYID then omits the Amount row entirely. Do not send a
zero-dollar placeholder.
Extra keys are allowed at every level. Domain fields the relying party matches on, such
as an item id, a resource id, or a scope list, go alongside the required ones; the
server stores them untouched, the approver’s signature covers them, and they are
compared with everything else at enforcement time. Any number among them is subject to
the same integer rule and the same bound as minor_units. details is for what the human reads, extra keys are for what the executor
checks.
amount
amount is optional. When it is present, top-level currency is required and must
match amount.currency.
| Field | Type | Meaning |
|---|---|---|
minor_units |
integer | The amount as a whole number of the currency’s minor unit, which is the smallest denomination the currency charges in. Under USD the minor unit is the cent, so 18000 is $180.00. Under a zero-decimal currency such as JPY the minor unit is the yen, so 18000 is ¥18,000. Must be a non-negative integer no greater than 9007199254740991, which is 2^53 - 1. Never a float and never a decimal string |
currency |
string | Must equal the top-level currency exactly |
display was removed. The app formats the amount itself, from minor_units and the
currency’s own exponent. Two fields describing one amount can disagree, and the one the
human read was the one that could lie — the server had no way to tell you. Send the
number; let the app render it.
The 2^53 - 1 bound is the largest integer a double round-trips exactly, so a JavaScript
verifier and a Python one cannot disagree about the value they are comparing. The same
bound applies to every number anywhere in terms, and every one of them must be an
integer.
details
Each entry requires label and value as non-blank strings. emphasized is optional;
set it to true to render a row with visual emphasis, or omit it for standard emphasis.
When supplied, it must be a boolean. The app renders the entries as a two-column table
in array order, so the array order is the reading order. The app doesn’t sort, merge,
or drop rows.
The array can be empty, but give it rows. The table is where the approver checks the specifics of what they’re agreeing to, and a screen carrying only a title and a summary leaves them less to check.
agent_name
agent_name names the agent doing the asking, and the YID app shows it on the approval
screen:
{"agent_name": "Shopping agent"}
Omit it, or send null, and the app falls back to the label on the agent key. A blank
string is a 422 rather than a fallback, because the app reads null as “use the key’s
label” and a blank string as “show nothing”, so the server refuses the one that renders
an empty name. Any non-string value is a 422 as well.
Financial example
{
"schema_version": 1,
"action": "purchase",
"approval_title": "Purchase running shoes",
"summary": "Buy running shoes for $180.00 at Example Store",
"merchant": "Example Store",
"currency": "USD",
"amount": {"minor_units": 18000, "currency": "USD"},
"details": [
{"label": "Merchant", "value": "Example Store", "emphasized": false},
{"label": "Item", "value": "Running shoes, model X, size 10", "emphasized": false},
{"label": "Amount", "value": "$180.00", "emphasized": true}
]
}
Non-financial example
Omit amount, currency, and unnecessary presentation hints for an action with no
monetary component:
{
"schema_version": 1,
"action": "document.signature.authorize",
"approval_title": "Sign mutual NDA",
"summary": "Authorize your signature on the mutual NDA with Yanez Pulse.",
"merchant": "Documenso",
"details": [
{"label": "Document", "value": "Mutual Non-Disclosure Agreement"},
{"label": "Counterparty", "value": "Yanez Pulse"},
{"label": "Signing as", "value": "Yanez AI"},
{"label": "Agreement ID", "value": "NDA-2026-0914"},
{"label": "Governing law", "value": "California"}
]
}
terms travels in the create body next to decision_window_seconds, which is how long
the approver has to answer and is not part of the terms:
HTTP quickstart.
Profiles per action type (purchase, disclosure, permission): terms guidance.
Terms are compared whole
Every field travels into the receipt as yanez_terms, and the same object travels
inside the bytes the approver signed. Both are compared structurally at enforcement
time. Re-titling an approval screen produces terms that no longer match what the action
executor expects, so build the object once and hand the same object to both the create
call and the executor: receipts.
“Structurally” has precise rules — a boolean is never a number, -0 equals 0, and
array order matters. Use the SDK’s terms_equal / termsEqual rather than a stock deep
-equality helper, which will disagree with the other language on at least one of those:
comparing terms.
If any material field changes after approval — counterparty, resource, amount, currency, destination, scope, deadline — the old receipt must not be used. New terms mean a new authorization request.