Metadata
| Field | Value |
|---|---|
| Name | payments.initiate |
| Category | write |
| Required scope | payments:initiate |
| Idempotency key required | yes |
Annotations
| Annotation | Value |
|---|---|
| Title | Initiate Payment |
| Read-only | no |
| Destructive | yes |
| Idempotent | yes |
| Open-world | no |
| Requires human approval | yes (step-up) |
Input schema
Output schema
Auth
Caller’s grant must include thepayments:initiate scope. Grants whose scope set is a superset of the required scope are accepted.
Request examples
Response examples
Success — payment accepted and queued for broadcast:step_up_amount_cents in the envelope):
Errors
| Code | Name | Cause | Remediation |
|---|---|---|---|
-32600 | Invalid request | Malformed JSON-RPC envelope | Check method, jsonrpc, and id fields |
-32602 | Invalid params | Missing required field, chain not in enum, or idempotency_key too short | Validate against the input schema before calling |
-32001 | Unauthorized | Missing/expired grant token | Refresh via agent.grant.refresh |
-32002 | Policy denied | Amount exceeds per-tx / daily / lifetime cap; counterparty not on allowlist; chain blocked; MCC blocked; geo blocked; sanctions screen hit | Run payments.simulate first to identify the blocking axis |
-32003 | Step-up required | Amount exceeds step_up_amount_cents in the policy envelope | Follow the step-up flow below, then retry with step_up_sigil |
-32004 | Rate limited | Too many initiate calls in a short window | Back off by retry_after_seconds in the error data |
-32005 | Vault contention | Another payment is being broadcast from the same vault | Retry after the in-flight broadcast completes |
-32603 | Internal error | Server-side error during enqueueing | Retry with the same idempotency_key; the call is idempotent |
Step-up flow
When the amount exceeds thestep_up_amount_cents threshold in the policy envelope, the server returns a -32003 error with data.step_up_url and data.reason_id: "step_up_required". The payment row is already written in pending state — you do not need to resend the full payload.
TypeScript