The Verity public network accepts VerityReceipt objects from any XAP-compatible implementation. Submitted receipts become publicly verifiable — any party can paste a receipt hash at verityengine.io/verify and independently confirm the outcome. No ZexRail account required.
This document specifies everything needed to connect your implementation:
- How to register your implementation
- How to format and sign a receipt for submission
- The submission API
- What gets accepted and what gets rejected
- Rate limits and abuse controls
- The federation model for v1.1
Prerequisite: Your system must produce valid VerityReceipt objects per the XAP v0.2 schema. See github.com/agentra-commerce/xap-protocol/xap/schemas/verity-receipt.json.
Every system that submits receipts to the Verity network must register. Registration creates a verified implementation identity — a public key pair that signs every submission and identifies which system produced the receipt.
Registration is handled through a GitHub issue in the verity-engine repo. This is intentional: it keeps the registry auditable, community-visible, and free of gatekeeping infrastructure.
Step 1: Generate an Ed25519 key pair
# Using the verity-engine CLI (once installed)
cargo install verity-cli
verity-cli keygen --output my-implementation
# Produces:
# my-implementation.pub (share this — your public key)
# my-implementation.key (keep this secret — your signing key)Or using any Ed25519 implementation — the key format is standard.
Step 2: Open a registration issue
Go to github.com/agentra-commerce/verity-engine/issues/new?template=register-implementation.md
Required fields:
implementation_name: "Your System Name"
implementation_url: "https://your-system.example.com"
contact_email: "you@example.com"
public_key: "base64url-encoded Ed25519 public key"
xap_version: "0.2"
description: "One sentence describing your system"Step 3: Receive your implementation_id
A maintainer reviews the registration within 48 hours. If approved, you receive:
{
"implementation_id": "impl_a1b2c3d4",
"implementation_name": "Your System Name",
"public_key": "your-public-key",
"registered_at": "2026-03-17T00:00:00Z",
"status": "active"
}Your implementation_id is permanent and public. It appears on every receipt you submit and on the Observatory page.
Registration is not a trust certification. It is an identity binding. It means:
- Receipts submitted with your
implementation_idare verifiably from your system - If your system submits invalid receipts, they are rejected and logged against your
implementation_id - If your system submits receipts that fail replay verification, the network flags them
- You can revoke your key and re-register with a new key at any time
Registration does not mean Agentra Labs has reviewed or endorses your system. The math does that.
Every submission must contain a valid VerityReceipt object. The canonical schema is at xap-protocol/xap/schemas/verity-receipt.json. This section summarizes the required fields and their constraints.
{
"verity_id": "vrt_[64 hex chars]",
"xap_version": "0.2",
"implementation_id": "impl_a1b2c3d4",
"settlement_id": "stl_[8+ chars]",
"issued_at": "2026-03-17T10:42:00Z",
"outcome": {
"state": "SUCCESS",
"confidence_bps": 10000,
"finality_class": "reversible"
},
"input_state": {
"settlement_intent_hash": "sha256-of-settlement-intent",
"agent_identities": ["agnt_...", "agnt_..."],
"condition_values": {},
"elapsed_ms": 1240
},
"rules_applied": {
"policy_version": "xap-v0.2",
"condition_type": "deterministic",
"verifier": "ENGINE"
},
"computation": {
"steps": ["condition_check", "split_cascade", "adapter_dispatch"],
"evidence_refs": []
},
"replay_hash": "sha256([input_state_canonical] + [rules_applied_canonical] + [outcome_state])",
"chain": {
"previous_hash": "vrt_[prior receipt hash or GENESIS for first receipt]",
"sequence": 1
},
"signature": {
"algorithm": "Ed25519",
"public_key": "your-public-key",
"value": "base64url-encoded-signature"
}
}| State | Meaning | confidence_bps |
|---|---|---|
SUCCESS |
Conditions met. Funds released. | 10000 |
FAIL |
Conditions not met. Funds returned. | 10000 |
UNKNOWN |
Verification ambiguous. Pre-declared resolution ran. | 0–9999 |
DISPUTED |
One party challenged. Arbitration engaged. | any |
REVERSED |
Settlement was final. Now reversed via journal entry. | 10000 |
UNKNOWN is a valid outcome, not an error. If your condition verifier could not reach a confident result, UNKNOWN with a lower confidence_bps is the correct and honest representation.
The replay_hash is the fingerprint that makes independent verification possible. It is computed as:
replay_hash = SHA-256(
canonical_json(input_state) +
canonical_json(rules_applied) +
outcome.state
)
Where canonical_json means: keys sorted alphabetically, no whitespace, UTF-8 encoded. This ensures identical computation across all implementations.
The Verity network verifies this hash on every submission. If replay_hash does not match a recomputation from the provided input_state, rules_applied, and outcome.state, the submission is rejected.
Every implementation maintains an append-only chain of receipts per agent. The chain.previous_hash field links to the verity_id of the prior receipt for that agent. For the first receipt an agent ever produces, use the literal string GENESIS.
Chain integrity is verified on submission. A previous_hash that does not match a known receipt in your chain causes the submission to be rejected with ERR_CHAIN_BROKEN.
A VerityReceipt submitted to the Verity Network may carry up to seven independently verifiable trust properties. All five v0.3 fields are optional — v0.2 receipts are accepted and carry properties 1-4.
| Property | Required? | Field | How to get it |
|---|---|---|---|
| 1. Existence | Always | verity_id |
Auto-generated |
| 2. Integrity | Always | chain.previous_hash |
Track your chain |
| 3. Correctness | Optional | rules_applied.policy_content_hash |
Hash the policy doc |
| 4. Determinism | Always | replay_hash |
Compute per Appendix A |
| 5. Attribution | Optional | signature.key_id |
Register key history |
| 6. Causality | Optional | causality.workflow_id |
Track your workflows |
| 7. Third-party | Optional | verifier_attestation.signature |
Have verifier sign |
import json, hashlib
def policy_content_hash(policy_rules: dict) -> str:
canonical = json.dumps(policy_rules, sort_keys=True, separators=(',', ':'))
return "sha256:" + hashlib.sha256(canonical.encode()).hexdigest()The xap-v0.2 policy document and its hash are available at:
https://api.zexrail.com/xap/v1/policies/xap-v0.2
Base URL: https://api.verityengine.io/v1
All requests require an Authorization header containing your implementation_id and a request signature.
Authorization: Verity impl_id="impl_a1b2c3d4", signature="base64url-encoded-sig"
The signature covers the request body (the VerityReceipt JSON, canonicalized). The signing key is the same Ed25519 key registered in Section 1.
import json
import base64
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
def sign_submission(receipt: dict, private_key: Ed25519PrivateKey) -> str:
canonical = json.dumps(receipt, sort_keys=True, separators=(',', ':'))
signature = private_key.sign(canonical.encode('utf-8'))
return base64.urlsafe_b64encode(signature).rstrip(b'=').decode()POST /v1/receipts
Content-Type: application/json
Authorization: Verity impl_id="impl_a1b2c3d4", signature="..."
{
"receipt": { ...VerityReceipt object... }
}
Success response (201 Created):
{
"verity_id": "vrt_a1b2c3d4...",
"accepted_at": "2026-03-17T10:42:01Z",
"replay_verified": true,
"chain_position": 847,
"public_url": "https://verityengine.io/verify/vrt_a1b2c3d4..."
}Error response (400 Bad Request):
{
"error": "ERR_REPLAY_HASH_MISMATCH",
"detail": "Provided replay_hash does not match recomputation from input_state + rules_applied + outcome",
"expected_hash": "sha256:...",
"provided_hash": "sha256:..."
}For high-volume implementations, receipts can be submitted in batches of up to 100:
POST /v1/receipts/batch
Content-Type: application/json
Authorization: Verity impl_id="impl_a1b2c3d4", signature="..."
{
"receipts": [ ...up to 100 VerityReceipt objects... ]
}
Response:
{
"accepted": 98,
"rejected": 2,
"results": [
{ "verity_id": "vrt_...", "status": "accepted" },
{ "verity_id": "vrt_...", "status": "accepted" },
{ "verity_id": "vrt_...", "status": "rejected", "error": "ERR_SCHEMA_INVALID" }
]
}Partial batch acceptance is normal. Each receipt is processed independently. A single invalid receipt does not block the rest.
This endpoint requires no authentication. It is the endpoint behind verityengine.io/verify.
GET /v1/receipts/{verity_id_or_hash}
Response:
{
"verity_id": "vrt_a1b2c3d4...",
"implementation_id": "impl_a1b2c3d4",
"implementation_name": "Your System Name",
"settlement_id": "stl_...",
"outcome": "SUCCESS",
"confidence_bps": 10000,
"replay_hash": "sha256:...",
"issued_at": "2026-03-17T10:42:00Z",
"replay_verified": true,
"public": true
}Private fields (agent identities, condition values, evidence refs) are not returned in the public endpoint. They are available to authenticated parties who can prove ownership of the relevant agent IDs.
GET /v1/receipts?implementation_id=impl_a1b2c3d4&outcome=SUCCESS&limit=20&cursor=...
Returns the most recent public receipts matching the filters. Used by the Observatory page live feed.
| Error Code | Meaning | Action |
|---|---|---|
ERR_SCHEMA_INVALID |
Receipt does not validate against verity-receipt.json |
Fix the object structure |
ERR_REPLAY_HASH_MISMATCH |
replay_hash does not match recomputation |
Recompute using canonical JSON per Section 2.3 |
ERR_CHAIN_BROKEN |
previous_hash does not match a known receipt |
Submit missing prior receipts first |
ERR_SIGNATURE_INVALID |
Ed25519 signature does not verify | Check signing key matches registered public key |
ERR_IMPL_NOT_FOUND |
implementation_id not registered |
Register per Section 1 |
ERR_IMPL_SUSPENDED |
Implementation suspended due to abuse | Contact hello@agentralabs.tech |
ERR_DUPLICATE |
verity_id already exists in the network |
Idempotent — this is safe to ignore |
ERR_FUTURE_TIMESTAMP |
issued_at is more than 5 minutes in the future |
Check system clock |
ERR_XAP_VERSION |
xap_version not supported |
Use "0.2" |
ERR_OUTCOME_INVALID |
Outcome state not in allowed set | Use SUCCESS/FAIL/UNKNOWN/DISPUTED/REVERSED |
ERR_RATE_LIMITED |
Too many requests | Back off per Section 5 |
| Endpoint | Limit | Window |
|---|---|---|
POST /v1/receipts (single) |
100 per minute | Per implementation_id |
POST /v1/receipts/batch |
10 per minute | Per implementation_id |
GET /v1/receipts/{id} (public) |
1000 per minute | Per IP |
GET /v1/receipts (query) |
60 per minute | Per IP |
Rate limit headers are included on every response:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1710677460
When rate limited, back off with exponential jitter. The Retry-After header gives the exact seconds to wait.
For implementations with higher volume requirements, contact hello@agentralabs.tech with your implementation_id and expected volume. Raised limits are available for legitimate high-volume systems.
The Verity network is append-only and public. This creates abuse vectors that the submission layer must guard against.
Replay hash farming — submitting receipts with artificially high confidence scores that don't reflect real settlement outcomes. Detected by: cross-referencing claimed settlement_id patterns against known agent behaviors over time.
Chain poisoning — submitting a valid receipt then submitting a receipt with a previous_hash that references a different chain. Detected by: chain integrity verification on every submission.
Sybil implementations — registering many implementation_ids to artificially inflate network statistics. Detected by: IP correlation during registration, submission pattern analysis.
Receipt flooding — submitting large volumes of receipts to pollute the public feed. Controlled by: rate limits, batch limits, and suspension policy.
Receipts that pass schema validation but fail behavioral checks are accepted but flagged as UNVERIFIED in the public explorer. Implementations that repeatedly submit suspicious receipts are suspended. Suspension is logged publicly against the implementation_id with the reason.
We do not delete receipts. The record of what was submitted is permanent. This is intentional.
For implementations that need data sovereignty, high volume, or air-gapped operation, running a local Verity instance is fully supported. The verity-engine crates are MIT licensed and contain everything needed.
git clone https://github.com/agentra-commerce/verity-engine
cd verity-engine
cargo build --workspace --release
# Run the submission server
./target/release/verity-server --port 8080 --storage ./dataA local instance behaves identically to the hosted network but stores receipts locally. Receipts submitted to a local instance are not automatically visible on verityengine.io.
Federation (v1.1): Local instances can opt into the federation network, making their receipts visible on the public Observatory and allowing cross-instance receipt verification. The federation protocol is defined in Section 8 below and will be implemented in v1.1.
Federation allows multiple Verity instances to form a network where any receipt submitted to any instance is verifiable from any other instance. This section documents the design so that implementations building today build toward compatibility.
Every federated Verity instance has an instance_id and a signing key pair, registered at verityengine.io/instances. The instance signs every receipt it stores, providing a chain of custody: the submitting implementation signed the receipt, and the storing instance counter-signed it.
When the public Observatory at verityengine.io receives a receipt hash it does not know, it queries known federated instances:
GET https://other-instance.example.com/v1/receipts/{hash}
X-Verity-Instance: inst_verityengine_main
X-Verity-Signature: Ed25519 signature of the request
The queried instance returns the receipt if it has it. The response includes the instance's counter-signature so the querying node can verify the response is authentic.
Implementations building today should:
- Store their
instance_idasverityengine_hosted(or their own instance ID if running locally) - Include
instance_idin their receipt objects as an optional field — it is ignored by v0.1 but parsed by v1.1 - Design their storage to support the federation query endpoint (GET by hash) — this is what v1.1 will call
No action required today beyond awareness. The federation protocol will be backward compatible with receipts that do not include an instance_id.
Everything you need to go from zero to first accepted receipt in 30 minutes.
cargo install verity-cliverity-cli register --name "My System" --url https://my-system.example.com
# Opens a browser to the GitHub registration issue template
# Pre-fills your public key automaticallyuse verity_kernel::{VerityId, VerityReceipt};
use verity_outcomes::Outcome;
use verity_integrity::HashChain;
let receipt = VerityReceipt::builder()
.verity_id(VerityId::generate())
.settlement_id("stl_test_001")
.implementation_id("impl_a1b2c3d4")
.outcome(Outcome::Success { confidence_bps: 10000 })
.input_state(input_state)
.rules_applied(rules)
.sign_with(&signing_key)
.build()?;verity-cli submit --receipt receipt.json --key my-implementation.key
# Output:
# Accepted: vrt_a1b2c3d4...
# Public URL: https://verityengine.io/verify/vrt_a1b2c3d4...verity-cli verify vrt_a1b2c3d4...
# Output:
# Outcome: SUCCESS
# Replay hash: sha256:...
# Replay verified: true
# Implementation: My SystemOr paste the hash at verityengine.io/verify — no account needed.
The xap-sdk Python package includes a VeritySubmitter class for implementations using Python:
from xap.verity import VeritySubmitter
submitter = VeritySubmitter(
implementation_id="impl_a1b2c3d4",
signing_key_path="my-implementation.key",
network="https://api.verityengine.io/v1",
)
# Submit a single receipt
result = await submitter.submit(receipt)
print(f"Accepted: {result.verity_id}")
print(f"Public: {result.public_url}")
# Submit a batch
results = await submitter.submit_batch(receipts)
print(f"Accepted: {results.accepted}/{len(receipts)}")Registered implementations appear on the Observatory page at verityengine.io. Each implementation shows:
- Name and URL
- Total receipts submitted
- Success rate across all receipts
- Registration date
- Status (active / suspended)
To update your implementation's name, URL, or public key, open a new GitHub issue with the label update-implementation and your implementation_id.
Canonical JSON is used for computing replay_hash and request signatures. The rules are:
- Keys sorted alphabetically at every level of nesting
- No whitespace (no spaces, no newlines)
- Strings encoded as UTF-8
- Numbers with no trailing zeros
- Booleans as
true/false - Null as
null - Arrays preserve insertion order (no sorting)
Reference implementation in Rust:
use verity_kernel::canonical::to_canonical_json;
let canonical = to_canonical_json(&receipt_value)?;
let hash = sha256(canonical.as_bytes());Reference implementation in Python:
import json
def canonical_json(obj: dict) -> str:
return json.dumps(obj, sort_keys=True, separators=(',', ':'), ensure_ascii=False)Use these to verify your replay_hash computation is correct before submitting.
Test vector 1 — deterministic SUCCESS:
input_state: {"settlement_intent_hash":"abc123","agent_identities":["agnt_001","agnt_002"],"condition_values":{"check":"http_status_200","result":true},"elapsed_ms":1240}
rules_applied: {"policy_version":"xap-v0.2","condition_type":"deterministic","verifier":"ENGINE"}
outcome_state: "SUCCESS"
canonical_payload: {"agent_identities":["agnt_001","agnt_002"],"condition_values":{"check":"http_status_200","result":true},"elapsed_ms":1240,"settlement_intent_hash":"abc123"}{"condition_type":"deterministic","policy_version":"xap-v0.2","verifier":"ENGINE"}SUCCESS
expected_replay_hash: sha256:d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5Test vector 2 — probabilistic UNKNOWN:
input_state: {"settlement_intent_hash":"def456","agent_identities":["agnt_003"],"condition_values":{"check":"quality_score","score_bps":7200,"threshold_bps":8500},"elapsed_ms":3100}
rules_applied: {"policy_version":"xap-v0.2","condition_type":"probabilistic","verifier":"ENGINE"}
outcome_state: "UNKNOWN"
expected_replay_hash: sha256:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2If your implementation produces different hashes for these test vectors, your canonical JSON implementation is not compliant.
Registration questions: Open an issue at github.com/agentra-commerce/verity-engine
Integration support: hello@agentralabs.tech
Security issues: security@agentralabs.tech — see SECURITY.md for responsible disclosure
Verity Network Submission Specification · verityengine.io · v0.1 · March 2026 The truth engine is open. The network is open. The receipts are permanent.