v12

OpenAPI 3.0.3raw.githubusercontent.com2026-08-073710699.9 KB
Policies

Submit the owner's signature and persist the grant

Recomputes the grant from the echoed prepare fields, verifies the signature recovers to the authenticated owner, re-checks the entity allocation inside the write, and stores the policy as pending. The install itself rides the first workflow operation on this wallet — nothing reaches the chain here.

This REPLACES the runner's previous grants. A wallet carries at most one usable grant: the send path resolves exactly one and refuses to execute when it finds more, so grants do not stack. Every other usable grant on this runner is revoked as part of this call, and the ones revoked come back in supersededPolicyIds. There is no flag to opt out, and clients do not need to revoke the previous grant first.

Replacement is scoped to the runner, not to a capability: submitting a grant for one capability revokes the runner's grant for any other.

Off-chain only. The superseded grants' validation entities and ERC-20 spend caps stay installed on the account until the owner signs uninstallValidation — replacing a grant does not reduce what the account could authorize on chain, only what this gateway will use.

post/wallets/{address}/policies:submit

Request body

chainIdinteger required

Numeric chain ID (e.g. 11155111 for Sepolia, 8453 for Base). On chain-aware trigger/node configs this is required and must be a configured chain; on query/filter params it is optional.

policyIdstring required

ULID identifier (26-char Crockford base32).

entityIdinteger required
deadlineinteger required
validUntilinteger required

The ABSOLUTE expiry from prepare. It is baked into the signed calldata; recomputing it would change the digest.

agentLabelstring required
justificationstring
signaturestring required

The owner's 65-byte signature over the prepared digest.

Example request

{
  "chainId": 11155111,
  "policyId": "01JG2FE5MDVKBPHEG0PEYSDKAC",
  "allowedActions": [
    {
      "target": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
    }
  ],
  "erc20SpendCap": {
    "token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
    "amount": "500000000"
  }
}

Response

Grant stored; the gateway may now execute within it.

idstring required

ULID identifier (26-char Crockford base32).

runnerstring required

Lowercase or checksummed hex EOA / contract address.

chainIdinteger required

Numeric chain ID (e.g. 11155111 for Sepolia, 8453 for Base). On chain-aware trigger/node configs this is required and must be a configured chain; on query/filter params it is optional.

status'pending' | 'active' | 'revoked' required

pending = signed and stored, install not yet on-chain (revocable for free). active = install applied. revoked = grants nothing.

entityIdinteger required
sessionSignerstring required

Lowercase or checksummed hex EOA / contract address.

agentLabelstring required
justificationstring
validUntilinteger required

Unix milliseconds.

createdAtinteger required

Unix milliseconds.

supersededPolicyIdsUlid[] required

Grants revoked to keep this runner's authority a singleton — the previous grants this submit replaced. Their status now reads revoked; the array distinguishes a replacement the gateway performed from one the user asked for via DELETE .../policies/{policyId}.

Empty on a first grant. Non-empty means the user's earlier permission is gone, which is worth reflecting in the UI.

Example response

{
  "id": "01JG2FE5MDVKBPHEG0PEYSDKAC",
  "runner": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
  "chainId": 11155111,
  "sessionSigner": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
  "allowedActions": [
    {
      "target": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB"
    }
  ],
  "erc20SpendCap": {
    "token": "0x82F2Dd9a552a69f2ceD7Ff2D05c43aB8430158FB",
    "amount": "500000000"
  },
  "supersededPolicyIds": [
    "01JG2FE5MDVKBPHEG0PEYSDKAC"
  ]
}