Nava Guardian API reference
Developer Preview
This page documents the lower-level Nava Service transport used to submit a structured proposed action and read its policy verdict. Most integrations should use the Nava MCP server or TypeScript SDK instead.
Authentication
Send one agent API key header on both operations:
x-api-key: <agent-api-key>Do not also send a bearer token. For least privilege, use an agent key with
verdicts:create:own. The status route requires authentication and confirms
that the caller owns the transaction.
Never send the key in a URL, persist it in request metadata, or log it.
POST /transactions
Submit a canonical action for policy evaluation. Agent keys default to policy
routing, but clients should send arbiter: "policy" explicitly.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
escrowAddress | string | Yes | The agent's registered wallet for the proposed chain. |
prompt | string | Yes | Short description for people and audit records. It is not a policy input. Keep it consistent with proposedTx and do not include secrets or personal data. |
proposedTx | object | Yes | Canonical structured action. Must include protocol and chainId, plus the venue-specific action/signing fields. |
metadata | object | No | Non-action context. Do not include secrets. |
contextLogs | object | No | Additional non-sensitive debugging context. |
executionSignature | string | No | Execution signature for flows that require it. |
arbiter | policy | No | Selects Nava Guardian policy evaluation. Set it explicitly to policy. |
{
"escrowAddress": "0x1111111111111111111111111111111111111111",
"prompt": "Swap WETH for USDC on Uniswap on Sepolia.",
"proposedTx": {
"protocol": "uniswap",
"chainId": 11155111,
"to": "0x2222222222222222222222222222222222222222",
"data": "0x",
"value": "0x0"
},
"metadata": {
"environment": "test"
},
"arbiter": "policy"
}The addresses and action data are placeholders. Replace them before use. Agent keys cannot use the pre-encrypted request shape.
201 response
The response is the created transaction record. The integration-critical field
is requestHash:
| Field | Type | Description |
|---|---|---|
id | string | Service transaction identifier. |
requestHash | string | Hash-bound request identifier used by the status route. |
description | string | User-facing description derived from prompt. |
status | string | Transaction lifecycle state; not the Guardian verdict. |
| Other transaction fields | varies | The service can return additional stored transaction fields. |
{
"id": "11111111-2222-3333-4444-555555555555",
"requestHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"description": "Swap WETH for USDC on Uniswap on Sepolia.",
"status": "PENDING"
}Reject a successful HTTP response that lacks a non-empty string requestHash.
GET /transactions/:requestHash/approval-status
Read the request's approval envelope and Nava Guardian verdict.
Path parameter
| Parameter | Type | Required | Description |
|---|---|---|---|
requestHash | string | Yes | The exact value returned by POST /transactions. |
Response envelope
| Field | Type | Description |
|---|---|---|
verdict | verdict or null | Guardian policy verdict when present. It may be absent or null before one is available. |
Verdict object
| Field | Type | Description |
|---|---|---|
schemaVersion | 1 | Literal verdict schema version. |
outcome | enum | Authoritative state: pending, approved, or rejected. |
reasonCode | enum | Top-level machine-readable reason. Must agree with the outcome. |
humanReason | string | Human-readable explanation. |
triggeringPolicyField | string or null | Dotted path to the policy field responsible for a rejection, when applicable. |
checks | check[] | Per-check results. Multi-leg actions can include a zero-based legIndex on leg-specific results. |
evidence | object | decodedAction, optional multi-leg decodedActions, concrete observations, and source references. |
policyVersion | integer or null | Policy version evaluated; may be null while pending or before policy resolution. |
latencyMs | non-negative integer | Evaluation latency. |
dataFreshness | object | Overall and per-source freshness. |
trust | object | Trust level and optional notes. |
redaction | object | Whether fields were redacted and which fields. |
usdNotional | non-negative number or null | Action notional when applicable. |
timestamp | ISO 8601 string | Verdict timestamp. |
Check results
Each checks[] item contains a checkId, status, optional failure code,
human-readable reason, optional triggering policy field, and an optional
non-negative legIndex for a decoded multi-leg action. The table below is
the complete published preview check contract. See Policy checks
for human-readable behavior and applicability.
checkId | Human-readable check | Availability | Failure code |
|---|---|---|---|
venue | Venue | Available | venue_not_allowed |
chain | Network | Available | chain_not_allowed |
contract_selector | Contract and function | Available | contract_or_selector_not_allowed |
asset | Asset | Available | asset_not_in_universe |
recipient | Recipient | Available | recipient_not_allowed or data_unavailable |
side | Trade side | Available | side_not_allowed |
slippage_guard | Slippage protection | Available | no_slippage_guard |
execution_price_bound | Execution price bound | Available | no_execution_price_bound |
leverage | Leverage update | Available | leverage_cap_exceeded |
sanctions | Sanctions | Available | sanctioned_address or data_unavailable |
sink_recipient | Sink recipient | Available | sink_recipient |
per_tx_cap | Per-transaction USD cap | Available | per_tx_cap_exceeded or data_unavailable |
outflow_24h | Rolling 24-hour outflow | Available | outflow_24h_exceeded or data_unavailable |
portfolio_leverage | Projected portfolio leverage | Available | leverage_cap_exceeded or data_unavailable |
protocol_exposure | Projected protocol exposure | Available | protocol_exposure_exceeded or data_unavailable |
checks[].status | Meaning |
|---|---|
passed | The check ran and the action satisfied it. |
failed | The check ran and rejected the action. |
not_checked | The check did not apply or its optional policy constraint was not configured. |
Other enum values
| Field | Values |
|---|---|
outcome | pending, approved, rejected |
reasonCode | allowed, pending, decode_failure, data_unavailable, asset_identity_unresolved, and the failure codes listed in the check table above |
dataFreshness.overall | fresh, cached, mixed |
dataFreshness.sources[].status | fresh, cached, unavailable |
trust.level | high, medium, low |
An approved verdict must use reasonCode: "allowed"; a pending verdict must
use reasonCode: "pending"; and a rejected verdict must use a violation code.
Except for cross-cutting decode_failure and data_unavailable, a rejected
reason code must match a failed check. asset_identity_unresolved is also
cross-cutting and can be returned with no failed check. A data-dependent check
can use data_unavailable in place of its policy-violation code.
See Failure modes and recovery for the definition and builder response for every reason code, degraded-state examples, and API-key incident handling.
Approved response
{
"verdict": {
"schemaVersion": 1,
"outcome": "approved",
"reasonCode": "allowed",
"humanReason": "The action satisfies the configured policy.",
"triggeringPolicyField": null,
"checks": [
{
"checkId": "venue",
"status": "passed",
"code": null,
"humanReason": "The venue is configured.",
"triggeringPolicyField": null
}
],
"evidence": {
"decodedAction": {
"protocol": "uniswap",
"chainId": 11155111
},
"observations": {},
"sources": []
},
"policyVersion": 3,
"latencyMs": 42,
"dataFreshness": {
"overall": "fresh",
"sources": []
},
"trust": {
"level": "high",
"notes": null
},
"redaction": {
"redacted": false,
"fields": []
},
"usdNotional": null,
"timestamp": "2026-07-20T12:00:00.000Z"
}
}Rejected response
{
"verdict": {
"schemaVersion": 1,
"outcome": "rejected",
"reasonCode": "chain_not_allowed",
"humanReason": "The proposed chain is not allowed by the active policy.",
"triggeringPolicyField": "venues.uniswap.chainAllowlist",
"checks": [
{
"checkId": "chain",
"status": "failed",
"code": "chain_not_allowed",
"humanReason": "Chain 11155111 is not in the allowlist.",
"triggeringPolicyField": "venues.uniswap.chainAllowlist"
}
],
"evidence": {
"decodedAction": {
"protocol": "uniswap",
"chainId": 11155111
},
"observations": {
"chainId": 11155111
},
"sources": []
},
"policyVersion": 3,
"latencyMs": 37,
"dataFreshness": {
"overall": "fresh",
"sources": []
},
"trust": {
"level": "high",
"notes": null
},
"redaction": {
"redacted": false,
"fields": []
},
"usdNotional": null,
"timestamp": "2026-07-20T12:00:00.000Z"
}
}Status-code handling
| Status | Operation | Handling |
|---|---|---|
200 | Status | Validate the envelope and verdict; poll only a missing, null, or pending verdict. |
201 | Submit | Validate and retain requestHash. |
400 | Submit | Correct the DTO or action; do not retry unchanged. |
401 | Both | Authentication failed. Stop and replace or re-authenticate the key. |
403 | Both | Scope, ownership, agent, or registered-wallet mismatch. Stop and correct configuration. |
404 | Status | Unknown request. Stop and reconcile the captured hash/base URL. |
429 | Both | Rate limited. A bounded GET retry is safe; do not blindly repeat a POST. |
5xx or network failure | Both | Keep the action paused. Retry GET within a bounded deadline; reconcile an uncertain POST before resubmitting. |
Polling, retries, and idempotency
Use bounded exponential backoff for a missing, null, or pending verdict and
for transient status-read failures. Cap both the delay and total attempts, pass
an abort signal to every request and delay, and fail closed when the bound is
reached.
The Developer Preview submit DTO and controller expose no idempotency key. Do not
automatically retry POST /transactions: a repeat can create another request.
GET /transactions/:requestHash/approval-status is read-only and can be retried
within the polling bound after 429, transient 5xx, or network interruption.
Malformed JSON, an unknown outcome, inconsistent required verdict fields, or a
persistently unavailable service is not approval. Stop the action and surface a
non-sensitive error alongside the requestHash when available.