Nava Guardian API reference
Developer Preview
Nava Guardian integrations use Nava Service to submit a structured proposed action and read its policy verdict.
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 | Non-empty human-readable description of the action. |
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 a placeholder amount using the configured policy.",
"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 a placeholder amount using the configured policy.",
"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. |
evidence | object | decodedAction, 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, and optional triggering policy field. The table below is
the complete implemented and in-review 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 |
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 | Available | leverage_cap_exceeded |
sanctions | Sanctions | Available | sanctioned_address |
sink_recipient | Sink recipient | Available | sink_recipient |
per_tx_cap | Per-transaction USD cap | In review | per_tx_cap_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, and the failure codes listed in the check table above |
dataFreshness.overall | fresh, cached, mixed |
dataFreshness.sources[].status | fresh, cached |
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.
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.