Validation API
Validation API
This guide covers how to call the Arbiter validation API, interpret results, and handle errors.
When to Use This
- You have a user intent (free-form text).
- You have a candidate transaction payload.
- You want a verification result (
PASSorREJECT) with node-level reasoning.
Prerequisites
- Arbiter-Core dependencies installed.
- API server running.
Start locally:
python -m api.server
Default URL: http://localhost:8000
Endpoints
GET /healthreturns basic server and LLM status.POST /validateruns full validation.
POST /validate Request
{
"human_intent": "string (required)",
"proposed_tx": {
"protocol": "optional but strongly recommended",
"to": "0x...",
"data": "0x...",
"value": "string-or-number",
"gas": "string-or-number",
"metadata": {},
"context": {},
"order": {},
"transactions": []
},
"metadata": {},
"preferences": {
"slippage_bps": 0,
"aggressiveness": 0.5,
"protocol_allowlist": ["uniswap"]
},
"return_prompts": false
}
Field notes:
human_intentis required.proposed_txdefaults to{}if omitted, but practical validation needs real transaction fields.proposed_tx.protocolis optional (auto-detection exists), but sending it reduces ambiguity.- Top-level
metadatais passed into validation context. preferencesis accepted by the API schema but not actively enforced in validator logic yet.
POST /validate Response
{
"decision": "PASS | REJECT",
"reason": "string",
"protocol": "string|null",
"primary_failure_node": "string|null",
"llm_calls": 0,
"results": {
"node.id": {
"status": "PASS|FAIL|SKIP",
"confidence": 0.0,
"reasoning": "string",
"details": {}
}
},
"extracted_elements": {},
"context": {},
"explanation": {
"summary": "string",
"decision": "PASS|REJECT",
"key_issues": [],
"affected_nodes": {},
"confidence_summary": {},
"recommendations": [],
"metadata": {}
}
}
Interpreting Results
decisionis the final verdict:PASSorREJECT.reasonis the top-level explanation from the first relevant failure (or a pass summary).resultscontains per-node verdicts and details.primary_failure_nodeidentifies the key failing node used for the decision narrative.llm_callsshows semantic-node call count, useful for debugging and cost visibility.
Node Statuses
Each node in results reports one of three statuses:
- PASS: the check succeeded.
- FAIL: the check failed.
- SKIP: the check was intentionally skipped or lacked the required context.
primary_failure_node points to the main failing node used for top-level reasoning.
Examples
ERC-20 Transfer (curl)
curl -X POST "http://localhost:8000/validate" \
-H "Content-Type: application/json" \
-d '{
"human_intent": "Transfer 100 USDC to 0x000000000000000000000000000000000000dEaD",
"proposed_tx": {
"protocol": "transfer",
"to": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"data": "0xa9059cbb000000000000000000000000000000000000000000000000000000000000dead0000000000000000000000000000000000000000000000000000000005f5e100",
"value": "0",
"gas": "65000"
}
}'
Native ETH Transfer (curl)
curl -X POST "http://localhost:8000/validate" \
-H "Content-Type: application/json" \
-d '{
"human_intent": "Send 0.1 ETH to 0x000000000000000000000000000000000000dEaD",
"proposed_tx": {
"protocol": "transfer",
"to": "0x000000000000000000000000000000000000dEaD",
"value": "100000000000000000",
"gas": "21000"
}
}'
Python
import requests
url = "http://localhost:8000/validate"
payload = {
"human_intent": "Swap 500 USDC for ETH with max slippage 0.5%",
"proposed_tx": {
"protocol": "uniswap",
"to": "0x...",
"data": "0x...",
"value": "0"
},
"metadata": {"from_address": "0x..."}
}
resp = requests.post(url, json=payload, timeout=30)
print(resp.status_code)
print(resp.json())
Error Handling
There are two categories of failure to handle.
API/Transport Errors (HTTP Error Codes)
The request could not be processed by the server.
| Status Code | Meaning |
|---|---|
422 | Request schema or type validation failed. |
503 | Validator not ready (startup or initialization issue). |
500 | Unexpected server exception. |
Error response shape:
{
"detail": "error message"
}
Validation Rejections (HTTP 200 with REJECT)
The API call succeeded, but the proposed transaction did not satisfy validation rules. This is a product-level or domain-level error, not a network or server failure.
Example response:
{
"decision": "REJECT",
"reason": "Operation mismatch ...",
"primary_failure_node": "intent_alignment.operation_matching",
"results": {
"intent_alignment.operation_matching": {
"status": "FAIL",
"confidence": 0.9,
"reasoning": "...",
"details": {}
}
}
}
Client Handling Strategy
- If the HTTP status is not 2xx, handle by status code (422, 503, 500). Retry only on transient server conditions such as 503 or intermittent 500 errors.
- If the HTTP status is 200, read
decision. If it isREJECT, inspectprimary_failure_node,reason, and node details. Show remediation guidance to the user, or rebuild the transaction and re-validate.
Retry Guidance
Retry with backoff for:
503when the Arbiter is not ready- Transient
500conditions
Do not retry for:
422malformed request errorsREJECTdecisions caused by deterministic rule failures
Remediation by Failure Type
intent_alignment.*failures: Make the intent and transaction consistent in action, token, amount, deadline, and slippage.technical_invariants.*failures: Fix transaction encoding, format, protocol parameters, or gas.legal_compliance.*failures: Use supported assets and addresses, and ensure policy-compliant amounts.adversarial_detection.*failures: Tighten parameters, remove suspicious patterns, and add explicit user constraints.
Operational Notes
- The LLM may be disabled. In that case, semantic capability is reduced, but the API still works.
llm_callshelps monitor semantic workload.explanationmay be absent (null) if explanation construction fails. Rely onreasonandresultsas a fallback.
Logging Recommendations
Persist at least the following for each validation call:
- Request ID or transaction hash (if available)
decisionreasonprotocolprimary_failure_node- Failing node details from
results - HTTP status and retry count
This makes support triage and rule tuning significantly easier.