API reference
The ZadQ API is plain HTTPS with JSON bodies at https://api.zadq.net. This page is generated at build time from the service's OpenAPI contract, so every operation, parameter and schema below is the contract itself, rendered.
The verification path is free and public: it needs no account and no credential. Every integration keeps working when no verdict is available — the answer is unknown, and what to do with it stays the caller's decision.
Quickstart
Each operation in the reference carries a ready-to-run curl example; the three calls below are the ones most integrations start with. Values in angle brackets are placeholders to replace with your own.
1. Read the substrate status. Public, no credential: whether the identity substrate behind the verdicts is ok, degraded or unknown, and when a standing read last succeeded.
curl -sS "https://api.zadq.net/v0/status" -H "Accept: application/json"
2. Verify a seller endpoint. The free verification path: the current signed verdict for an x402 endpoint, or an explicit no-signal outcome. No account, no credential, never charged.
curl -sS "https://api.zadq.net/v0/verifications?chain=base&payTo=<wallet-address>&resourceUrl=<endpoint-url>" \
-H "Accept: application/json"
Signatures verify locally against the published keys at https://api.zadq.net/v0/verification-keys.
3. Open a session for the write operations. The dashboard's only credential is control of the payment wallet: request a challenge for the payTo address, sign it with the wallet, redeem the signature for a short-lived session credential and send it as a bearer on the write operations.
curl -sS -X POST "https://api.zadq.net/v0/auth/challenge" \
-H "Content-Type: application/json" \
-d '{"chain":"base","address":"<wallet-address>"}'
curl -sS -X POST "https://api.zadq.net/v0/auth/session" \
-H "Content-Type: application/json" \
-d '{"chain":"base","address":"<wallet-address>","nonce":"<challenge-nonce>","signature":"<wallet-signature>"}'
curl -sS "https://api.zadq.net/v0/sellers/<seller-id>" \
-H "Authorization: Bearer <session-credential>"
The exact request and response bodies of every call are in the reference below (AuthChallengeRequest, AuthSessionRequest, Verdict).
Overview
| Field | Value |
|---|---|
| Contract | Seller-Verification Service API |
| Version | 0.1.0 |
| Specification | OpenAPI 3.0.3 |
v0 seller-verification API: seller intake, admission state, attestation issuance metadata, and the free verification path.
The free verification path is never charged for, to anyone, ever.
Graceful degradation: consumers must treat an unreachable service or an absent verdict as "no signal", never as a failure of their own flow.
Authentication
Operations name the schemes they accept; an operation marked as public takes no credential at all.
walletSession
HTTP header Authorization: Bearer <credential>.
Session bearer from POST /v0/auth/session (wallet-proven payTo).
Other operations
POST /v0/auth/challenge
Wallet login, step 1 — challenge for a payTo address
Returns a one-time message for the seller's payment wallet to sign (EVM personal_sign or Solana message signing). Proving control of the payTo address is the only credential the dashboard uses — no account, no personal data. Challenges expire after 5 minutes.
Authentication: none — this operation is public.
Request body
application/json — required
Schema: AuthChallengeRequest
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
chain | Chain | yes | ||
address | string | yes |
Responses
201 — Challenge issued.
Content type application/json.
Schema: AuthChallenge
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
nonce | string | yes | ||
message | string | yes | Exact text to sign, byte for byte. | |
expiresAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/auth/challenge" \
-H "Content-Type: application/json" \
-d @request-body.json
POST /v0/auth/session
Wallet login, step 2 — redeem the signed challenge
Verifies the wallet signature over the challenge message and returns a bearer credential bound to {chain, address} (12 h). Send it as Authorization: Bearer <bearer> on the write operations.
Authentication: none — this operation is public.
Request body
application/json — required
Schema: AuthSessionRequest
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
chain | Chain | yes | ||
address | string | yes | ||
nonce | string | yes | ||
signature | string | yes | EVM — 0x-hex r||s||v from personal_sign; Solana — base58 (or hex) 64-byte signature. |
Responses
201 — Session issued.
Content type application/json.
Schema: AuthSession
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
bearer | string | yes | Opaque session credential for the Authorization header. | |
expiresAt | string (date-time) | yes | ||
chain | Chain | yes | ||
address | string | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/auth/session" \
-H "Content-Type: application/json" \
-d @request-body.json
GET /healthz
Liveness probe
Authentication: none — this operation is public.
Responses
200 — Service is up.
Content type application/json.
Schema: Health
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
status | string | yes | "ok" | |
version | string | no |
Example
curl -sS "<base-url>/healthz"
GET /v0/status
Substrate status (public)
What an explorer or a seller can check before asking: whether the identity substrate behind the verdicts is ok, degraded or unknown, when a standing read last succeeded, and the cadence promise. Read from the verifier's own status contract when wired (source: verifier); otherwise inferred from the cadence — degraded only after three consecutive cycles in which every attestation was withheld, never from a single unknown (source: cadence). No credentials, no identifiers, nothing personal (ZADQ-70). While the substrate is degraded every integration keeps working: verdicts read no_signal, badges read unknown, nothing blocks.
Authentication: none — this operation is public.
Responses
200 — The substrate's state as this service sees it.
Content type application/json.
Schema: SubstrateStatus
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
substrate | string | yes | "ok", "degraded", "unknown" | |
source | string | yes | Where the state comes from — the verifier's status read, or this service's cadence accounting. | "verifier", "cadence" |
reason | string | no | Why the substrate is not ok, in the verifier's standing vocabulary (for example height_stale, root_expired, state_unavailable). Absent when ok. | |
checkedAt | string (date-time) | yes | ||
lastSuccessfulStandingRead | string (date-time) | no | The last time a standing read answered with a known status. Absent before the first. | |
cadence | object | yes | ||
cadence.recheckEveryHours | integer | yes | The published cadence promise. | |
cadence.lastCycleAt | string (date-time) | no | ||
cadence.checked | integer | no | Attestations the last cycle asked the verifier about. | |
cadence.withheld | integer | no | Of those, how many came back with an unknown standing. |
Example
curl -sS "<base-url>/v0/status"
POST /v0/interest
Register interest (public web form target)
The target of the native HTML form on the public web's register-interest page (ZADQ-103): a vendor or an agent operator says which side of the trust perimeter it is on, optionally the x402 resource URL it sells at or the size band of its fleet, and nothing else. No contact data is accepted or stored here — the person sends their address through the mailbox from the page the browser is sent back to. Submissions are counted by side for the funnel; the optional details are cleared after twelve months, the count stays.
Answers with a redirect to the public web in every case, so a browser without scripts lands on a page: the "received" page (with the side as the fragment) on success, the "not received" page when the input is invalid, when the client is over its rate limit (a few submissions per client per ten minutes, and a global bound), or when storage fails. The page reached always shows the mailbox route, so nothing a person can do depends on this service being up. The field named website is a trap for automated submitters: when it carries a value the submission is dropped and the browser still lands on the "received" page.
Authentication: none — this operation is public.
Request body
application/x-www-form-urlencoded — required
Schema: InterestForm
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
side | string | yes | Which side of the trust perimeter the person stands on. | "vendor", "agent_operator" |
resource_url | string (uri) | no | The x402 resource URL a vendor sells at (optional, vendors). | |
agent_count_band | string | no | The size band of an operator's fleet (optional, agent operators). | "up_to_10", "up_to_100", "more_than_100" |
website | string | no | Trap field for automated submitters; a person leaves it empty. |
Responses
303 — The browser is sent back to the public web.
Example
curl -sS -X POST "<base-url>/v0/interest" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d @request-body.json
POST /v0/sellers
Seller intake (onboarding flow 1)
Registers a seller's x402 identifiers and segment declaration and opens a KYC session at the issuer-side KYC boundary. No identity data is accepted here. Requires a wallet session for the intake payTo address (401/403 otherwise): control of the address is proven from the first minute.
Authentication: walletSession.
Request body
application/json — required
Schema: SellerIntake
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
payTo | PayTo | yes | ||
resourceUrl | string (uri) | yes | ||
operatorType | string | yes | "person", "organisation" | |
segment | SegmentDeclaration | yes |
Responses
201 — Seller registered; KYC session opened.
Content type application/json.
Schema: Seller
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
payTo | PayTo | yes | ||
resourceUrl | string (uri) | yes | ||
operatorType | string | yes | "person", "organisation" | |
state | string | yes | kyc_expired: the proofing session ran past its deadline without an outcome; a new session can be opened (POST …/kyc/sessions). withdrawn: consent to the service withdrawn — terminal. | "pending_kyc", "kyc_expired", "admitted", "rejected", "withdrawn" |
billing | string | yes | Billing state at the operator's billing provider (ZADQ-54): none until a checkout starts, pending until the provider's signed event lands, active afterwards. The service holds no legal or payment data. | "none", "pending", "active" |
billingTier | string | no | The schedule tier chosen at checkout, by name. | "attested_vendor", "regulated_compliance" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
renewal | all of KycHandoff | no | The pending re-verification session, while one is open (ZADQ-91): the person completes it on handoffUrl; the approved session in kyc keeps backing the attestation meanwhile. | |
attestation | Attestation | no | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/sellers" \
-H "Authorization: Bearer <session-credential>" \
-H "Content-Type: application/json" \
-d @request-body.json
GET /v0/sellers/{sellerId}
Seller state (admission, attestation, billing)
Authentication: none — this operation is public.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Responses
200 — Current seller state.
Content type application/json.
Schema: Seller
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
payTo | PayTo | yes | ||
resourceUrl | string (uri) | yes | ||
operatorType | string | yes | "person", "organisation" | |
state | string | yes | kyc_expired: the proofing session ran past its deadline without an outcome; a new session can be opened (POST …/kyc/sessions). withdrawn: consent to the service withdrawn — terminal. | "pending_kyc", "kyc_expired", "admitted", "rejected", "withdrawn" |
billing | string | yes | Billing state at the operator's billing provider (ZADQ-54): none until a checkout starts, pending until the provider's signed event lands, active afterwards. The service holds no legal or payment data. | "none", "pending", "active" |
billingTier | string | no | The schedule tier chosen at checkout, by name. | "attested_vendor", "regulated_compliance" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
renewal | all of KycHandoff | no | The pending re-verification session, while one is open (ZADQ-91): the person completes it on handoffUrl; the approved session in kyc keeps backing the attestation meanwhile. | |
attestation | Attestation | no | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS "<base-url>/v0/sellers/<sellerId>"
GET /v0/sellers/{sellerId}/endpoints
The endpoints of this seller's operator
Every endpoint the same accountable operator registered on its approved verification (ZADQ-53). Requires the wallet session of this seller's payTo (401/403): the grouping is never a public read, so an operator's endpoints stay uncorrelatable to third parties.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Responses
200 — The operator's endpoints, oldest first.
Content type application/json.
Schema: OperatorEndpoints
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
endpoints | array of EndpointSummary | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS "<base-url>/v0/sellers/<sellerId>/endpoints" \
-H "Authorization: Bearer <session-credential>"
POST /v0/sellers/{sellerId}/endpoints
Add an endpoint on the operator's approved verification
Registers a further endpoint of the accountable operator behind this admitted seller, reusing its approved session inside the reuse window (ZADQ-53): no new KYC, the new seller reads admitted at once and can be attested. Requires the wallet session of this seller's payTo (401/403). When the new endpoint's payTo differs, proof must carry a signed auth challenge for the new address (POST /v0/auth/challenge for it, sign the message): control of every address is proven. 409 unless admitted with an open window (re-verify first), or when the endpoint already exists.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Request body
application/json — required
Schema: EndpointAddition
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
resourceUrl | string (uri) | yes | ||
payTo | PayTo | no | ||
proof | AddressProof | no |
Responses
201 — The new seller, admitted on the shared verification.
Content type application/json.
Schema: Seller
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
payTo | PayTo | yes | ||
resourceUrl | string (uri) | yes | ||
operatorType | string | yes | "person", "organisation" | |
state | string | yes | kyc_expired: the proofing session ran past its deadline without an outcome; a new session can be opened (POST …/kyc/sessions). withdrawn: consent to the service withdrawn — terminal. | "pending_kyc", "kyc_expired", "admitted", "rejected", "withdrawn" |
billing | string | yes | Billing state at the operator's billing provider (ZADQ-54): none until a checkout starts, pending until the provider's signed event lands, active afterwards. The service holds no legal or payment data. | "none", "pending", "active" |
billingTier | string | no | The schedule tier chosen at checkout, by name. | "attested_vendor", "regulated_compliance" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
renewal | all of KycHandoff | no | The pending re-verification session, while one is open (ZADQ-91): the person completes it on handoffUrl; the approved session in kyc keeps backing the attestation meanwhile. | |
attestation | Attestation | no | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/sellers/<sellerId>/endpoints" \
-H "Authorization: Bearer <session-credential>" \
-H "Content-Type: application/json" \
-d @request-body.json
POST /v0/sellers/{sellerId}/wallet
Rotate the payment wallet behind an attested endpoint
Changes this admitted seller's payTo address inside the approved session's reuse window (ZADQ-53): the attestation bound to the old identifiers is revoked upstream and a new one is issued for the new ones on the same verification — no new KYC. Requires the wallet session of the current payTo (401/403) and proof: a signed auth challenge for the new address. 409 unless admitted with an open window and acknowledged terms, or when the endpoint already exists.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Request body
application/json — required
Schema: WalletRotation
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
payTo | PayTo | yes | ||
proof | AddressProof | yes |
Responses
200 — The seller on its new address, with the new attestation.
Content type application/json.
Schema: Seller
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
payTo | PayTo | yes | ||
resourceUrl | string (uri) | yes | ||
operatorType | string | yes | "person", "organisation" | |
state | string | yes | kyc_expired: the proofing session ran past its deadline without an outcome; a new session can be opened (POST …/kyc/sessions). withdrawn: consent to the service withdrawn — terminal. | "pending_kyc", "kyc_expired", "admitted", "rejected", "withdrawn" |
billing | string | yes | Billing state at the operator's billing provider (ZADQ-54): none until a checkout starts, pending until the provider's signed event lands, active afterwards. The service holds no legal or payment data. | "none", "pending", "active" |
billingTier | string | no | The schedule tier chosen at checkout, by name. | "attested_vendor", "regulated_compliance" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
renewal | all of KycHandoff | no | The pending re-verification session, while one is open (ZADQ-91): the person completes it on handoffUrl; the approved session in kyc keeps backing the attestation meanwhile. | |
attestation | Attestation | no | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/sellers/<sellerId>/wallet" \
-H "Authorization: Bearer <session-credential>" \
-H "Content-Type: application/json" \
-d @request-body.json
POST /v0/sellers/{sellerId}/billing/checkout
Set up billing on the operator's hosted checkout
Opens the operator's hosted checkout page for this admitted seller, keyed by the seller id and the chosen tier only (ZADQ-54). The person enters legal and billing data there — none of it enters this service, which keeps the billing state, the tier name and an opaque customer reference. Billing reads pending until the provider's signed event lands. Requires the wallet session of the seller's payTo (401/403); 409 unless admitted, or while billing is already active. Manual bank transfer for the first contracts is agreed with us directly, outside the service.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Request body
application/json — required
Schema: BillingCheckout
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
tier | string | yes | The schedule tier, by name. Amounts are on the checkout page and in the operator agreement. | "attested_vendor", "regulated_compliance" |
Responses
201 — The hosted checkout to open, and the seller's billing state.
Content type application/json.
Schema: BillingHandoff
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
url | string (uri) | yes | The hosted checkout page — open it as-is. | |
billing | string | yes | "none", "pending", "active" |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/sellers/<sellerId>/billing/checkout" \
-H "Authorization: Bearer <session-credential>" \
-H "Content-Type: application/json" \
-d @request-body.json
POST /v0/billing/events
Billing provider event (signed)
Inbound event from the operator's billing provider, signed with the webhook secret (Stripe-Signature: t=<unix>,v1=<hmac-sha256>), applied once per event id. checkout.session.completed moves billing to active; checkout.session.expired and customer.subscription.deleted to none; anything else is acknowledged and ignored. The payload is read for its id, type, seller reference and customer reference only. 401 on a bad or stale signature.
Authentication: none — this operation is public.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
Stripe-Signature | header | yes | string |
Request body
application/json — required
Type: object.
Responses
200 — Acknowledged; whether the event changed a state.
Content type application/json.
Schema: BillingEventReceipt
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
applied | boolean | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/billing/events" \
-H "Stripe-Signature: <Stripe-Signature>" \
-H "Content-Type: application/json" \
-d @request-body.json
POST /v0/sellers/{sellerId}/credential/offer
Hand out the v1 credential offer, once
Creates the v1 credential offer for this admitted seller from its approved verification (ZADQ-83) and hands it out exactly once, over the wallet session of the seller's payTo (401/403). The offer is a one-shot pre-authorized code the seller's own middleware redeems at once with the holder library, in the seller's own infrastructure: this service never stores or logs it, never sees the credential, a key or a proof — the dashboard shows state, never secrets. The code and the credential it yields expire exactly with the verification (no grace); past the window the issuer answers 409 and the seller re-verifies. 409 while a credential is already held or a re-verification is pending; 501 when the substrate offers no v1 credential rail.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Responses
201 — The offer, handed out once, and when it expires.
Content type application/json.
Schema: CredentialOfferHandoff
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
offer | object | yes | The credential offer object, as the issuer returned it — opaque, one-shot; redeem it at once. | |
expiresAt | string (date-time) | yes | When the code and the credential it yields expire — the verification's own expiry at the latest. No grace. | |
credential | CredentialState | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/sellers/<sellerId>/credential/offer" \
-H "Authorization: Bearer <session-credential>"
POST /v0/sellers/{sellerId}/credential
Report the credential's state from the seller's infrastructure
The seller's middleware reports what it did with the offer: held (optionally with an opaque reference of the seller's choosing, and with the presentationUrl its holder answers this service's challenges at — that URL puts the seller on the v1 verdict path: on the cadence this service forwards its verifier's 10-second challenge there, byte for byte, and the verifier's decision on the holder's envelope governs the positive claim, ZADQ-84) or self_revoked (the holder revoked its own credential through the network's privacy relay; from the next status root on the credential presents nowhere; revoked is accepted as the older spelling of the same report; the presentation URL is cleared). Without a presentation URL the seller keeps the v0 standing path. Wallet session of the seller's payTo required (401/403). Nothing secret travels here; the reference is never a key, a code or a proof. presentationUrl must be an absolute https URL (http only to a loopback host, local development).
A self_revoked report is recorded as the state change on the seller's own record and nothing else: no event, no time, no log line, no notification — the reference, the hand-out time and the presentation URL are cleared. The consequence lands at the next verdict cadence, never at this moment: the v0 attestation is retired (statusReason: self_revoked) and the public verdict reads revoked from then on. The way back is a new verification (re-verify), which yields a fresh offer; another offer on the same verification answers 409.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Request body
application/json — required
Schema: CredentialReport
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
state | string | yes | held, or self_revoked (revoked is the older spelling of the same report). | "held", "self_revoked", "revoked" |
credentialRef | string | no | An opaque reference of the seller's choosing for the credential it holds. Never a key. | |
presentationUrl | string (uri) | no | Where the seller's holder answers this service's challenges (ZADQ-84): POST of the issued challenge as the verifier returned it ({request, signature_hex}), 200 with the envelope ({proof_hex, issuer_id, status_root, claimed_predicates}); any other status means the holder cannot present this time. Only with held; kept when omitted on a later report. |
Responses
200 — The seller with the reported credential state.
Content type application/json.
Schema: Seller
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
payTo | PayTo | yes | ||
resourceUrl | string (uri) | yes | ||
operatorType | string | yes | "person", "organisation" | |
state | string | yes | kyc_expired: the proofing session ran past its deadline without an outcome; a new session can be opened (POST …/kyc/sessions). withdrawn: consent to the service withdrawn — terminal. | "pending_kyc", "kyc_expired", "admitted", "rejected", "withdrawn" |
billing | string | yes | Billing state at the operator's billing provider (ZADQ-54): none until a checkout starts, pending until the provider's signed event lands, active afterwards. The service holds no legal or payment data. | "none", "pending", "active" |
billingTier | string | no | The schedule tier chosen at checkout, by name. | "attested_vendor", "regulated_compliance" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
renewal | all of KycHandoff | no | The pending re-verification session, while one is open (ZADQ-91): the person completes it on handoffUrl; the approved session in kyc keeps backing the attestation meanwhile. | |
attestation | Attestation | no | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/sellers/<sellerId>/credential" \
-H "Authorization: Bearer <session-credential>" \
-H "Content-Type: application/json" \
-d @request-body.json
POST /v0/sellers/{sellerId}/kyc/sessions
Start the identity verification again, or re-verify before the reuse window ends
Opens a new KYC session at the boundary. From kyc_expired: start again (the seller reads pending_kyc with the new handoff). From admitted: a re-verification that renews the approved session (ZADQ-91) — the seller stays admitted, its attestation keeps its standing, and the pending renewal is returned in renewal; once approved, the renewal is bound upstream to the attestation, which keeps its reference, and a rekyc_overdue suspension is lifted at the next root. 409 from any other state or while a renewal is pending. Requires the wallet session of the seller's payTo address (401/403 otherwise).
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Responses
201 — New session opened; the seller reads pending_kyc again.
Content type application/json.
Schema: Seller
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
payTo | PayTo | yes | ||
resourceUrl | string (uri) | yes | ||
operatorType | string | yes | "person", "organisation" | |
state | string | yes | kyc_expired: the proofing session ran past its deadline without an outcome; a new session can be opened (POST …/kyc/sessions). withdrawn: consent to the service withdrawn — terminal. | "pending_kyc", "kyc_expired", "admitted", "rejected", "withdrawn" |
billing | string | yes | Billing state at the operator's billing provider (ZADQ-54): none until a checkout starts, pending until the provider's signed event lands, active afterwards. The service holds no legal or payment data. | "none", "pending", "active" |
billingTier | string | no | The schedule tier chosen at checkout, by name. | "attested_vendor", "regulated_compliance" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
renewal | all of KycHandoff | no | The pending re-verification session, while one is open (ZADQ-91): the person completes it on handoffUrl; the approved session in kyc keeps backing the attestation meanwhile. | |
attestation | Attestation | no | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/sellers/<sellerId>/kyc/sessions" \
-H "Authorization: Bearer <session-credential>"
POST /v0/sellers/{sellerId}/consent/withdraw
Withdraw consent to the service (terminal)
The accountable party withdraws its consent to the service (ZADQ-91). The boundary records it on the approved session; every attestation it backs is revoked upstream with reason consent_withdrawn at the next root, the verdict reads revoked, and the seller is withdrawn — terminal (a new intake is a new session). Consent given to the identity provider itself is withdrawn at the provider and has no status effect here. Requires the wallet session of the seller's payTo address (401/403); 409 unless admitted; 501 when the substrate cannot record it (raised with the operator; nothing changes).
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Responses
200 — Consent withdrawn; the seller is terminal.
Content type application/json.
Schema: Seller
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
payTo | PayTo | yes | ||
resourceUrl | string (uri) | yes | ||
operatorType | string | yes | "person", "organisation" | |
state | string | yes | kyc_expired: the proofing session ran past its deadline without an outcome; a new session can be opened (POST …/kyc/sessions). withdrawn: consent to the service withdrawn — terminal. | "pending_kyc", "kyc_expired", "admitted", "rejected", "withdrawn" |
billing | string | yes | Billing state at the operator's billing provider (ZADQ-54): none until a checkout starts, pending until the provider's signed event lands, active afterwards. The service holds no legal or payment data. | "none", "pending", "active" |
billingTier | string | no | The schedule tier chosen at checkout, by name. | "attested_vendor", "regulated_compliance" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
renewal | all of KycHandoff | no | The pending re-verification session, while one is open (ZADQ-91): the person completes it on handoffUrl; the approved session in kyc keeps backing the attestation meanwhile. | |
attestation | Attestation | no | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/sellers/<sellerId>/consent/withdraw" \
-H "Authorization: Bearer <session-credential>"
GET /v0/terms
The terms-acknowledgement document in force (free path)
The versioned document a seller signs with its payment wallet before the first issuance (ZADQ-55): the version, its SHA-256, where the full Terms of Service are published, and the text itself — which carries the related-party disclosure at contract time. Public and cacheable, so anyone can check what an attested seller acknowledged.
Authentication: none — this operation is public.
Responses
200 — The current document.
Content type application/json.
Schema: TermsDocument
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
version | string | yes | Version name of the acknowledgement document. | |
sha256 | string | yes | Hex SHA-256 of text. | |
url | string (uri) | yes | Where the full Terms of Service are published. | |
text | string | yes | The acknowledgement text the wallet signs for. |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS "<base-url>/v0/terms"
GET /v0/sellers/{sellerId}/terms
Terms acknowledgement state of a seller, and the message to sign
Whether the seller's wallet has acknowledged the current terms version, and — if not — the exact message to sign, with the signing time the service will verify it against (messageSignedAt; submit it back unchanged within fifteen minutes). Requires the wallet session of the seller's payTo address (401/403).
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Responses
200 — Acknowledgement state.
Content type application/json.
Schema: SellerTerms
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
current | TermsDocument | yes | ||
acknowledged | boolean | yes | Whether the current version is acknowledged by this seller. | |
acknowledgedVersion | string | no | ||
signedAt | string (date-time) | no | When the current version was signed, if it was. | |
message | string | yes | The exact text to sign for the current version. | |
messageSignedAt | string (date-time) | yes | The signing time embedded in message; submit it back as signedAt. |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS "<base-url>/v0/sellers/<sellerId>/terms" \
-H "Authorization: Bearer <session-credential>"
POST /v0/sellers/{sellerId}/terms
Acknowledge the current terms with the seller's wallet
Records the wallet signature over the acknowledgement message for the current version (same challenge mechanism as the login, a different message). Stored against the seller: version, SHA-256, signature, signing time — no mailbox, no account, no identity data. Issuance requires an acknowledgement of the current version (409 terms not acknowledged otherwise). 401 on a signature that does not verify for the seller's payTo; 409 when the version or hash is not the current one; 400 when the signing time is outside the accepted window. A second acknowledgement of the same version changes nothing.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Request body
application/json — required
Schema: TermsAcknowledgement
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
version | string | yes | ||
sha256 | string | yes | ||
signedAt | string (date-time) | yes | ||
signature | string | yes | Wallet signature over the message (hex for EVM, base58 or hex for Solana). |
Responses
201 — Acknowledged.
Content type application/json.
Schema: SellerTerms
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
current | TermsDocument | yes | ||
acknowledged | boolean | yes | Whether the current version is acknowledged by this seller. | |
acknowledgedVersion | string | no | ||
signedAt | string (date-time) | no | When the current version was signed, if it was. | |
message | string | yes | The exact text to sign for the current version. | |
messageSignedAt | string (date-time) | yes | The signing time embedded in message; submit it back as signedAt. |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/sellers/<sellerId>/terms" \
-H "Authorization: Bearer <session-credential>" \
-H "Content-Type: application/json" \
-d @request-body.json
GET /v0/sellers/{sellerId}/webhooks
The seller's own receivers (secrets never shown again)
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Responses
200 — The seller's receivers.
Content type application/json.
Schema: SellerWebhooks
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
webhooks | array of Webhook | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS "<base-url>/v0/sellers/<sellerId>/webhooks" \
-H "Authorization: Bearer <session-credential>"
POST /v0/sellers/{sellerId}/webhooks
Register a receiver for the seller's own events
Owner-bound (ZADQ-51): the wallet session must prove the seller's payTo (401/403). The per-subscription secret is returned once; https is required except for loopback receivers (local development). Events and signing: see the webhooks note at the top of this document. 409 when the URL is already registered for this seller.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Request body
application/json — required
Schema: WebhookRegistration
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
url | string (uri) | yes |
Responses
201 — Receiver registered; secret shown once.
Content type application/json.
Schema: Webhook
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
url | string (uri) | yes | ||
secret | string | no | Hex signing secret for X-Signature. Returned once at registration. | |
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/sellers/<sellerId>/webhooks" \
-H "Authorization: Bearer <session-credential>" \
-H "Content-Type: application/json" \
-d @request-body.json
DELETE /v0/sellers/{sellerId}/webhooks/{webhookId}
Remove one of the seller's receivers
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string | |
webhookId | path | yes | string |
Responses
204 — Receiver removed.
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X DELETE "<base-url>/v0/sellers/<sellerId>/webhooks/<webhookId>" \
-H "Authorization: Bearer <session-credential>"
GET /v0/sellers/{sellerId}/events
What changed — the seller's recent events, newest first
The append-only event stream of the seller (intake, KYC outcomes and expiry, issuance, status changes, consent withdrawal), for the dashboard's "what changed" list (ZADQ-51). Wallet-bound; metadata only, never identity data.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string | |
limit | query | no | integer |
Responses
200 — Recent events.
Content type application/json.
Schema: SellerEvents
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
events | array of SellerEvent | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS "<base-url>/v0/sellers/<sellerId>/events" \
-H "Authorization: Bearer <session-credential>"
POST /v0/sellers/{sellerId}/kyc/result
KYC outcome callback (issuer-side boundary → this service)
Callback endpoint for the KYC boundary. Carries outcome metadata only — never identity data. HMAC-authenticated: X-Signature: sha256=<hex> over the raw body with the shared callback secret and X-Signature-Key (first 8 bytes of the secret's SHA-256, hex); 401 otherwise. Local fake mode without a secret leaves it open for the smoke.
Authentication: none — this operation is public.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Request body
application/json — required
Schema: KycResult
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
sessionId | string | yes | ||
outcome | string | yes | "approved", "rejected" | |
assuranceLevel | string | no | Assurance grade met by the proofing process (e.g. "G"). |
Responses
200 — Admission state updated.
Content type application/json.
Schema: Seller
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
payTo | PayTo | yes | ||
resourceUrl | string (uri) | yes | ||
operatorType | string | yes | "person", "organisation" | |
state | string | yes | kyc_expired: the proofing session ran past its deadline without an outcome; a new session can be opened (POST …/kyc/sessions). withdrawn: consent to the service withdrawn — terminal. | "pending_kyc", "kyc_expired", "admitted", "rejected", "withdrawn" |
billing | string | yes | Billing state at the operator's billing provider (ZADQ-54): none until a checkout starts, pending until the provider's signed event lands, active afterwards. The service holds no legal or payment data. | "none", "pending", "active" |
billingTier | string | no | The schedule tier chosen at checkout, by name. | "attested_vendor", "regulated_compliance" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
renewal | all of KycHandoff | no | The pending re-verification session, while one is open (ZADQ-91): the person completes it on handoffUrl; the approved session in kyc keeps backing the attestation meanwhile. | |
attestation | Attestation | no | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/sellers/<sellerId>/kyc/result" \
-H "Content-Type: application/json" \
-d @request-body.json
POST /v0/sellers/{sellerId}/attestations
Issue the seller-side attestation (flow 2)
Orchestrates issuance for an admitted seller via the issuer path and records attestation metadata. Fails for non-admitted sellers. Requires a wallet session for the seller's payTo address.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Responses
201 — Attestation issued.
Content type application/json.
Schema: Attestation
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
sellerId | string | yes | ||
assuranceLevel | string | yes | ||
status | string | yes | "active", "suspended", "revoked" | |
statusReason | string | no | Why a non-active status was set — never silent. rekyc_overdue: the reuse window ended; restored under the same reference by a bound re-verification. consent_withdrawn: terminal. self_revoked: the holder revoked its own credential and the attestation was retired at the next verdict cadence — terminal for this attestation; a new verification yields a new one. Absent for operator changes and while active. | "rekyc_overdue", "consent_withdrawn", "self_revoked" |
freshnessBand | string | no | Freshness band of the backing session, as the issuer reports it. | "F4", "F3", "F2", "F1" |
issuedAt | string (date-time) | yes | ||
updatedAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/sellers/<sellerId>/attestations" \
-H "Authorization: Bearer <session-credential>"
GET /v0/sellers/{sellerId}/attestations/{attestationId}/receipt
The issuer's signed receipt of the attestation (for X-PAYMENT-IDENTITY)
The issuer-signed receipt of the issuance, byte-shaped as the attestation-ref/v0 payload of the payment-identity companion header (ZADQ-92): the seller's middleware puts it on X-PAYMENT-IDENTITY beside X-PAYMENT, and a receiver may verify the issuer's Ed25519 signature locally against the issuer's registered status key. It is proof of issuance, never of standing: standing is the verdict's (GET /v0/verifications). Requires the wallet session of the seller's payTo address (401/403). 404 when no receipt is stored (local fakes, attestations recorded before receipts were kept).
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string | |
attestationId | path | yes | string |
Responses
200 — The receipt.
Content type application/json.
Schema: AttestationReceipt
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
attestation_ref | string | yes | 32-byte opaque reference, hex. | |
issuer_id | string | yes | ||
chain | string | yes | ||
pay_to | string | yes | ||
resource_url | string | yes | ||
assurance_grade | integer | yes | ||
issued_at_unix | integer | yes | ||
signature | string | yes | Ed25519 by the issuer's registered status signing key, hex (64 bytes). |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS "<base-url>/v0/sellers/<sellerId>/attestations/<attestationId>/receipt" \
-H "Authorization: Bearer <session-credential>"
POST /v0/attestations/{attestationId}/status
Status-mirror update (network status cycle → this service)
Applies a status change originating in the standard network status machinery. The service never revokes by its own say-so (seed §3.1 flow 4); this endpoint mirrors upstream state. HMAC-authenticated like the KYC callback (X-Signature, X-Signature-Key).
Authentication: none — this operation is public.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
attestationId | path | yes | string |
Request body
application/json — required
Schema: AttestationStatusUpdate
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
status | string | yes | "active", "suspended", "revoked" |
Responses
200 — Mirrored status applied.
Content type application/json.
Schema: Attestation
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
sellerId | string | yes | ||
assuranceLevel | string | yes | ||
status | string | yes | "active", "suspended", "revoked" | |
statusReason | string | no | Why a non-active status was set — never silent. rekyc_overdue: the reuse window ended; restored under the same reference by a bound re-verification. consent_withdrawn: terminal. self_revoked: the holder revoked its own credential and the attestation was retired at the next verdict cadence — terminal for this attestation; a new verification yields a new one. Absent for operator changes and while active. | "rekyc_overdue", "consent_withdrawn", "self_revoked" |
freshnessBand | string | no | Freshness band of the backing session, as the issuer reports it. | "F4", "F3", "F2", "F1" |
issuedAt | string (date-time) | yes | ||
updatedAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/attestations/<attestationId>/status" \
-H "Content-Type: application/json" \
-d @request-body.json
GET /v0/verification-keys
Verdict-verification public keys (free path)
The Ed25519 public keys verdict signatures are checked against. Free, unauthenticated, cacheable. keyId matches Verdict.keyId (first 8 bytes of the key's SHA-256, hex). Consumers without the key keep working: a signature they cannot check is "unverifiable", never an error.
Authentication: none — this operation is public.
Responses
200 — Current verification keys.
Content type application/json.
Schema: VerificationKeys
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
keys | array of VerificationKey | yes |
Example
curl -sS "<base-url>/v0/verification-keys"
GET /v0/metrics
Published metrics (free path)
The published metric series (ZADQ-106): sellers attested, 90-day retention, verifications served, independent registered verifiers, anchor-service revenue — computed from this service's own records, counts only, monthly, complete months only. Free, unauthenticated, cacheable. Every series carries relatedParty: true and its period: the operator runs a related-party pilot and nothing measured on it counts as independent adoption. A series the records hold no source for says so (available: false) rather than carrying a figure. Registry-based or aggregate throughout — no per-verification record exists — and nothing personal, no identifier of any kind.
Authentication: none — this operation is public.
Responses
200 — The current report.
Content type application/json.
Schema: MetricsReport
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
schema | string | yes | The wire shape's name (published-metrics/v1). | |
computedAt | string (date-time) | yes | ||
asOf | string (date) | yes | The cut-off — the last day of the last complete month (UTC). Partial months are never published. | |
period | MetricsPeriod | no | ||
cadence | object | yes | ||
cadence.publication | string | yes | monthly. | |
cadence.snapshot | string | yes | quarterly, aligned to the two-consecutive-quarters clock. | |
relatedParty | boolean | yes | Always true — the operator's position is a property of the dataset. | |
operator | MetricsOperator | yes | ||
series | array of MetricsSeries | yes | ||
contextNote | string | yes |
Example
curl -sS "<base-url>/v0/metrics"
GET /v0/verifications
Free verification path (explorers, buyers)
Returns the current signed verdict for an x402 endpoint, or a no-signal outcome. Free of charge, no authentication, ever.
Authentication: none — this operation is public.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
chain | query | yes | Chain | |
payTo | query | yes | string | |
resourceUrl | query | yes | string |
Responses
200 — Verdict (or explicit no-signal outcome).
Content type application/json.
Schema: Verdict
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
outcome | string | yes | "human_backed", "revoked", "stale", "no_signal" | |
subject | object | yes | ||
subject.chain | Chain | yes | ||
subject.payTo | string | yes | ||
subject.resourceUrl | string | yes | ||
assuranceLevel | string | no | ||
verifiedAt | string (date-time) | no | ||
recheckEveryHours | integer | yes | ||
signature | string | no | Ed25519 over the canonical verdict payload, base64. | |
keyId | string | no | ||
reason | string | no | Why the outcome is not human_backed: the standing read's reason code (closed vocabulary), or suspended when the witnessed status is suspended. Absent on human_backed; absent on revoked too. The policy reason of a suspension (rekyc_overdue) is the seller's own record, never part of the public verdict. On the v1 path (ZADQ-84) a verifier-side condition of the presentation decision is carried with the same code (issuer_not_active, status_root_stale, height_stale) or as state_unavailable; a presentation the verifier refused on the holder's side withholds the positive claim with no reason — the decision's exact code is the seller's own record (credential.lastPresentation). | "state_unavailable", "issuer_unknown", "issuer_not_active", "status_root_stale", "height_stale", "witness_unavailable", "witness_malformed", "witness_invalid", "witness_root_mismatch", "bond_insufficient", "suspended" |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS "<base-url>/v0/verifications?chain=<chain>&payTo=<payTo>&resourceUrl=<resourceUrl>"
POST /v0/webhooks
Register an explorer receiver for verdict.updated
Free and unauthenticated — the payload is the public verdict. The per-subscription secret is returned once; https is required except for loopback receivers (local development).
Authentication: none — this operation is public.
Request body
application/json — required
Schema: WebhookRegistration
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
url | string (uri) | yes |
Responses
201 — Subscription created; secret shown once.
Content type application/json.
Schema: Webhook
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
url | string (uri) | yes | ||
secret | string | no | Hex signing secret for X-Signature. Returned once at registration. | |
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/webhooks" \
-H "Content-Type: application/json" \
-d @request-body.json
DELETE /v0/webhooks/{webhookId}
Remove a receiver
Authentication: none — this operation is public.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
webhookId | path | yes | string |
Responses
204 — Subscription removed.
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X DELETE "<base-url>/v0/webhooks/<webhookId>"
GET /v0/sellers/{sellerId}/verdict
Latest verdict for a seller (dashboard view)
Authentication: none — this operation is public.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
sellerId | path | yes | string |
Responses
200 — Latest verdict.
Content type application/json.
Schema: Verdict
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
outcome | string | yes | "human_backed", "revoked", "stale", "no_signal" | |
subject | object | yes | ||
subject.chain | Chain | yes | ||
subject.payTo | string | yes | ||
subject.resourceUrl | string | yes | ||
assuranceLevel | string | no | ||
verifiedAt | string (date-time) | no | ||
recheckEveryHours | integer | yes | ||
signature | string | no | Ed25519 over the canonical verdict payload, base64. | |
keyId | string | no | ||
reason | string | no | Why the outcome is not human_backed: the standing read's reason code (closed vocabulary), or suspended when the witnessed status is suspended. Absent on human_backed; absent on revoked too. The policy reason of a suspension (rekyc_overdue) is the seller's own record, never part of the public verdict. On the v1 path (ZADQ-84) a verifier-side condition of the presentation decision is carried with the same code (issuer_not_active, status_root_stale, height_stale) or as state_unavailable; a presentation the verifier refused on the holder's side withholds the positive claim with no reason — the decision's exact code is the seller's own record (credential.lastPresentation). | "state_unavailable", "issuer_unknown", "issuer_not_active", "status_root_stale", "height_stale", "witness_unavailable", "witness_malformed", "witness_invalid", "witness_root_mismatch", "bond_insufficient", "suspended" |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS "<base-url>/v0/sellers/<sellerId>/verdict"
POST /v0/agent-operators
Agent-operator enrolment — the human or organisation behind an agent
Enrols the accountable party behind an agent: the wallet the operator's agent pays from, and the operator type (person → identity verification, organisation → business verification). Opens a KYC session at the issuer-side KYC boundary and returns the operator in pending_kyc with the identity provider's hosted page in kyc.handoffUrl. No identity data is accepted here. Requires the wallet session of that wallet (401/403 otherwise): control of the wallet is proven from the first minute. 409 when the wallet is already enrolled. An agent operator is not an x402 endpoint: it carries no resource URL and no attestation, and a wallet that is also a vendor's payTo is not linked to that vendor by this service.
Authentication: walletSession.
Request body
application/json — required
Schema: AgentOperatorEnrolment
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
wallet | all of PayTo | yes | The wallet the operator's agent pays from — the one the wallet session proves. | |
operatorType | string | yes | person is verified by identity verification, organisation by business verification; the boundary learns this and nothing else. | "person", "organisation" |
Responses
201 — Operator enrolled; KYC session opened.
Content type application/json.
Schema: AgentOperator
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | The operator's opaque reference (aop_…). | |
wallet | all of PayTo | yes | The wallet the operator's agent pays from. | |
operatorType | string | yes | "person", "organisation" | |
state | string | yes | pending_kyc → approved → credential_held (the principal's credential is held in the operator's infrastructure) → active (its first agent is minted). kyc_expired: the proofing deadline passed, or the approved verification's reuse window closed — the principal's credential expired with it; verify again (POST …/kyc/sessions). rejected and withdrawn are terminal. | "pending_kyc", "kyc_expired", "approved", "rejected", "credential_held", "active", "withdrawn" |
billing | string | yes | The one-off entry fee at the operator's billing provider: none until a checkout starts, pending until the provider's signed event lands, active once it is recorded. The service holds no legal or payment data. | "none", "pending", "active" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
agents | AgentCounts | no | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/agent-operators" \
-H "Authorization: Bearer <session-credential>" \
-H "Content-Type: application/json" \
-d @request-body.json
GET /v0/agent-operators/{agentOperatorRef}
Agent-operator state (verification, entry fee, the principal's credential)
The operator's state as the dashboard shows it — state, never keys. The read brings it up to date first: a pending session's outcome is read from the KYC boundary, a session past its deadline reads kyc_expired, and an approved verification whose reuse window closed reads kyc_expired too (the principal's credential expired with it, exactly — no grace). Wallet session of the operator's wallet required (401/403): an operator's record is never a public read.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agentOperatorRef | path | yes | string | The agent operator's opaque reference (aop_…). |
Responses
200 — Current agent-operator state.
Content type application/json.
Schema: AgentOperator
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | The operator's opaque reference (aop_…). | |
wallet | all of PayTo | yes | The wallet the operator's agent pays from. | |
operatorType | string | yes | "person", "organisation" | |
state | string | yes | pending_kyc → approved → credential_held (the principal's credential is held in the operator's infrastructure) → active (its first agent is minted). kyc_expired: the proofing deadline passed, or the approved verification's reuse window closed — the principal's credential expired with it; verify again (POST …/kyc/sessions). rejected and withdrawn are terminal. | "pending_kyc", "kyc_expired", "approved", "rejected", "credential_held", "active", "withdrawn" |
billing | string | yes | The one-off entry fee at the operator's billing provider: none until a checkout starts, pending until the provider's signed event lands, active once it is recorded. The service holds no legal or payment data. | "none", "pending", "active" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
agents | AgentCounts | no | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS "<base-url>/v0/agent-operators/<agentOperatorRef>" \
-H "Authorization: Bearer <session-credential>"
POST /v0/agent-operators/{agentOperatorRef}/kyc/sessions
Start the verification again
Opens a new KYC session at the boundary: from kyc_expired (the proofing deadline passed, or the approved verification's reuse window closed), or after the holder revoked the principal's credential — the way back is a new verification, which yields a fresh credential that carries nothing of the old one. The operator reads pending_kyc with the new handoff. 409 from any other state. Wallet session of the operator's wallet required (401/403).
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agentOperatorRef | path | yes | string | The agent operator's opaque reference (aop_…). |
Responses
201 — New session opened; the operator reads pending_kyc again.
Content type application/json.
Schema: AgentOperator
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | The operator's opaque reference (aop_…). | |
wallet | all of PayTo | yes | The wallet the operator's agent pays from. | |
operatorType | string | yes | "person", "organisation" | |
state | string | yes | pending_kyc → approved → credential_held (the principal's credential is held in the operator's infrastructure) → active (its first agent is minted). kyc_expired: the proofing deadline passed, or the approved verification's reuse window closed — the principal's credential expired with it; verify again (POST …/kyc/sessions). rejected and withdrawn are terminal. | "pending_kyc", "kyc_expired", "approved", "rejected", "credential_held", "active", "withdrawn" |
billing | string | yes | The one-off entry fee at the operator's billing provider: none until a checkout starts, pending until the provider's signed event lands, active once it is recorded. The service holds no legal or payment data. | "none", "pending", "active" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
agents | AgentCounts | no | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/agent-operators/<agentOperatorRef>/kyc/sessions" \
-H "Authorization: Bearer <session-credential>"
POST /v0/agent-operators/{agentOperatorRef}/kyc/result
KYC outcome callback for an agent operator (issuer-side boundary → this service)
Callback endpoint for the KYC boundary, the same contract as the sellers' one. Carries outcome metadata only — never identity data. HMAC-authenticated: X-Signature: sha256=<hex> over the raw body with the shared callback secret and X-Signature-Key (first 8 bytes of the secret's SHA-256, hex); 401 otherwise. Local fake mode without a secret leaves it open for the smoke.
Authentication: none — this operation is public.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agentOperatorRef | path | yes | string | The agent operator's opaque reference (aop_…). |
Request body
application/json — required
Schema: KycResult
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
sessionId | string | yes | ||
outcome | string | yes | "approved", "rejected" | |
assuranceLevel | string | no | Assurance grade met by the proofing process (e.g. "G"). |
Responses
200 — Verification state updated.
Content type application/json.
Schema: AgentOperator
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | The operator's opaque reference (aop_…). | |
wallet | all of PayTo | yes | The wallet the operator's agent pays from. | |
operatorType | string | yes | "person", "organisation" | |
state | string | yes | pending_kyc → approved → credential_held (the principal's credential is held in the operator's infrastructure) → active (its first agent is minted). kyc_expired: the proofing deadline passed, or the approved verification's reuse window closed — the principal's credential expired with it; verify again (POST …/kyc/sessions). rejected and withdrawn are terminal. | "pending_kyc", "kyc_expired", "approved", "rejected", "credential_held", "active", "withdrawn" |
billing | string | yes | The one-off entry fee at the operator's billing provider: none until a checkout starts, pending until the provider's signed event lands, active once it is recorded. The service holds no legal or payment data. | "none", "pending", "active" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
agents | AgentCounts | no | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/agent-operators/<agentOperatorRef>/kyc/result" \
-H "Content-Type: application/json" \
-d @request-body.json
POST /v0/agent-operators/{agentOperatorRef}/billing/checkout
Pay the entry fee on the operator's hosted checkout
Opens the hosted checkout of the one-off entry fee, keyed by the operator's reference and the tier of its operator type only (person or organisation; the amount is on the checkout page). The person enters legal and billing data there — none of it enters this service, which keeps the billing state and an opaque customer reference. billing reads pending until the provider's signed event lands on POST /v0/billing/events, active afterwards. The principal's credential is offered once the fee is recorded. Wallet session of the operator's wallet required (401/403); 409 for a rejected operator or once the fee is recorded.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agentOperatorRef | path | yes | string | The agent operator's opaque reference (aop_…). |
Responses
201 — The hosted checkout to open, and the operator's billing state.
Content type application/json.
Schema: BillingHandoff
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
url | string (uri) | yes | The hosted checkout page — open it as-is. | |
billing | string | yes | "none", "pending", "active" |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/agent-operators/<agentOperatorRef>/billing/checkout" \
-H "Authorization: Bearer <session-credential>"
POST /v0/agent-operators/{agentOperatorRef}/credential/offer
Hand out the offer of the principal's identity credential, once
Creates the offer of the operator's own identity credential — the principal its agents are minted from — from its approved verification, on the same issuer route as sellers' credentials, and hands it out exactly once over the wallet session of the operator's wallet (401/403). The offer is a one-shot pre-authorized code the operator's own infrastructure redeems at once with the holder library; the credential and its keys stay there — this service never stores or logs the offer and never sees the credential, a key or a proof. The operator's holder answers the issuer's mint challenge with it when an agent is minted. The code and the credential expire exactly with the verification (no grace). 409 until the entry fee is recorded, unless approved, while a credential is held, after the holder revoked it on this verification (verify again), or past the reuse window; 501 when the substrate offers no credential rail.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agentOperatorRef | path | yes | string | The agent operator's opaque reference (aop_…). |
Responses
201 — The offer, handed out once, and when it expires.
Content type application/json.
Schema: CredentialOfferHandoff
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
offer | object | yes | The credential offer object, as the issuer returned it — opaque, one-shot; redeem it at once. | |
expiresAt | string (date-time) | yes | When the code and the credential it yields expire — the verification's own expiry at the latest. No grace. | |
credential | CredentialState | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/agent-operators/<agentOperatorRef>/credential/offer" \
-H "Authorization: Bearer <session-credential>"
POST /v0/agent-operators/{agentOperatorRef}/credential
Report the principal's credential state from the operator's infrastructure
The operator's infrastructure reports what it did with the offer: held (optionally with an opaque reference of its choosing; the operator reads credential_held and can have agents minted) or self_revoked (the holder revoked the principal's credential through the network's privacy relay; from the next status root on it presents nowhere and the agents minted on it are revoked with it over the following roots; revoked is accepted as the older spelling of the same report). Nothing secret travels here; the reference is never a key, a code or a proof. Wallet session of the operator's wallet required (401/403).
A self_revoked report is recorded as the state change on the operator's own record and nothing else: no event, no time, no log line — the reference and the hand-out time are cleared. The way back is a new verification (POST …/kyc/sessions), which yields a fresh offer; another offer on the same verification answers 409.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agentOperatorRef | path | yes | string | The agent operator's opaque reference (aop_…). |
Request body
application/json — required
Schema: AgentOperatorCredentialReport
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
state | string | yes | held, or self_revoked (revoked is the older spelling of the same report). | "held", "self_revoked", "revoked" |
credentialRef | string | no | An opaque reference of the operator's choosing for the credential it holds. Never a key. |
Responses
200 — The operator with the reported credential state.
Content type application/json.
Schema: AgentOperator
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | The operator's opaque reference (aop_…). | |
wallet | all of PayTo | yes | The wallet the operator's agent pays from. | |
operatorType | string | yes | "person", "organisation" | |
state | string | yes | pending_kyc → approved → credential_held (the principal's credential is held in the operator's infrastructure) → active (its first agent is minted). kyc_expired: the proofing deadline passed, or the approved verification's reuse window closed — the principal's credential expired with it; verify again (POST …/kyc/sessions). rejected and withdrawn are terminal. | "pending_kyc", "kyc_expired", "approved", "rejected", "credential_held", "active", "withdrawn" |
billing | string | yes | The one-off entry fee at the operator's billing provider: none until a checkout starts, pending until the provider's signed event lands, active once it is recorded. The service holds no legal or payment data. | "none", "pending", "active" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
agents | AgentCounts | no | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/agent-operators/<agentOperatorRef>/credential" \
-H "Authorization: Bearer <session-credential>" \
-H "Content-Type: application/json" \
-d @request-body.json
GET /v0/agent-scopes
The delegation vocabulary an agent is minted with
The scope values and key-custody classes an agent's delegation draws from — the vocabulary carried on chain, read under its pinned content hash (contentSha256), never hard-coded. The dashboard builds the "Mint an agent" form from it. 503 when the content does not match its pin (fail closed: no offer is validated against it); 501 when the substrate supplies no vocabulary. Wallet session required (401).
Authentication: walletSession.
Responses
200 — The vocabulary and the hash it was read under.
Content type application/json.
Schema: ScopeVocabulary
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
contentSha256 | string | yes | The sha256 of the vocabulary content it was read under (hex). | |
version | string | no | ||
scopes | array of string | yes | ||
custodyClasses | array of string | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS "<base-url>/v0/agent-scopes" \
-H "Authorization: Bearer <session-credential>"
GET /v0/agent-operators/{agentOperatorRef}/agents
The operator's agents (pseudonymous handles, state, lease)
The agents minted for the operator, newest first: ZadQ's pseudonymous handle (agt_…), the delegation summary, the lease and the state — never a chain identifier, a key or the offer. Wallet session of the operator's wallet required (401/403).
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agentOperatorRef | path | yes | string | The agent operator's opaque reference (aop_…). |
Responses
200 — The operator's agents.
Content type application/json.
Schema: Agents
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
items | array of Agent | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS "<base-url>/v0/agent-operators/<agentOperatorRef>/agents" \
-H "Authorization: Bearer <session-credential>"
POST /v0/agent-operators/{agentOperatorRef}/agents
Mint an agent — hand out the agent offer, once
The operator declares the delegation (scope values from GET /v0/agent-scopes, a per-transaction limit and its unit, the key-custody class, a lease of 1..90 days). ZadQ records that it collected its fee (the fee declaration) and creates the agent offer on the issuer's operator route; the offer is handed out exactly once over the wallet session of the operator's wallet (401/403) and never stored or logged. The operator's agent client redeems it at once, answering the issuer's mint challenge with the principal's credential inside its 10-second window; the agent credential and its keys stay in the operator's infrastructure, and the link between the principal and the agent stays sealed with the issuer — this service never sees it. The agent expires at the end of its lease or with the principal's verification, whichever is first (expiresAt).
Refused here, before the issuer is asked: 400 for a delegation outside the vocabulary or the lease bounds; 409 until the entry fee is recorded, unless the principal's credential is held, or once the verification's window is closed. The issuer enforces at most 10 active agents per person and 5 mints per sliding hour, renewals included; ZadQ shows the answer, it does not re-count. Its refusals are 409 and terminal — the problem's title names the reason (active agent cap, mint rate, verification expired, fee declaration) and nothing is retried. 501 when the substrate offers no agent route.
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agentOperatorRef | path | yes | string | The agent operator's opaque reference (aop_…). |
Request body
application/json — required
Schema: AgentDelegation
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
scope | array of string | yes | Scope values of the vocabulary (GET /v0/agent-scopes), each once. | |
limit | AgentLimit | yes | ||
custodyClass | string | yes | The key-custody class of the agent's key, from the vocabulary. | |
leaseDays | integer | yes | The lease in days; the issuer allows at most 90. |
Responses
201 — The agent offer, handed out once, and the agent's record.
Content type application/json.
Schema: AgentOfferHandoff
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
offer | object | yes | The agent offer as the issuer returned it — opaque, one-shot; the operator's agent client redeems it at once. | |
expiresAt | string (date-time) | yes | When the agent's credential expires — its lease's end or the principal's verification, whichever is first. | |
agent | Agent | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/agent-operators/<agentOperatorRef>/agents" \
-H "Authorization: Bearer <session-credential>" \
-H "Content-Type: application/json" \
-d @request-body.json
GET /v0/agent-operators/{agentOperatorRef}/agents/{agentId}
One agent (state, reason, delegation, lease)
One agent of the operator. Another operator's agent reads 404. Wallet session of the operator's wallet required (401/403).
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agentOperatorRef | path | yes | string | The agent operator's opaque reference (aop_…). |
agentId | path | yes | string | The agent's pseudonymous handle (agt_…). |
Responses
200 — The agent.
Content type application/json.
Schema: Agent
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | The agent's pseudonymous handle (agt_…), assigned by ZadQ. | |
state | string | yes | "offered", "held", "revoked", "expired" | |
reason | string | no | "operator_revoked", "principal_revoked", "self_revoked", "lease_expired" | |
delegation | AgentDelegation | yes | ||
leaseUntil | string (date-time) | yes | The lease's end as the issuer set it. | |
generation | integer | yes | 1 at the mint, one more per renewal (replacement). | |
credentialRef | string | no | The operator's own opaque reference for the held credential. Never a key. | |
feeDeclared | boolean | yes | Whether ZadQ's fee declaration for the current generation is recorded. | |
offeredAt | string (date-time) | yes | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS "<base-url>/v0/agent-operators/<agentOperatorRef>/agents/<agentId>" \
-H "Authorization: Bearer <session-credential>"
POST /v0/agent-operators/{agentOperatorRef}/agents/{agentId}/credential
Report the agent credential's state from the operator's infrastructure
The operator's agent client reports what it did with the offer: held (optionally with an opaque reference of its choosing; the operator's first agent held makes it active) or self_revoked (the agent's holder revoked it through the network's privacy relay). A self_revoked report is recorded as the state and the reason and nothing else — no event, no time, no log line. Nothing secret travels here. Wallet session of the operator's wallet required (401/403).
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agentOperatorRef | path | yes | string | The agent operator's opaque reference (aop_…). |
agentId | path | yes | string | The agent's pseudonymous handle (agt_…). |
Request body
application/json — required
Schema: AgentCredentialReport
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
state | string | yes | "held", "self_revoked" | |
credentialRef | string | no | An opaque reference of the operator's choosing for the agent's credential. Never a key. |
Responses
200 — The agent with the reported state.
Content type application/json.
Schema: Agent
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | The agent's pseudonymous handle (agt_…), assigned by ZadQ. | |
state | string | yes | "offered", "held", "revoked", "expired" | |
reason | string | no | "operator_revoked", "principal_revoked", "self_revoked", "lease_expired" | |
delegation | AgentDelegation | yes | ||
leaseUntil | string (date-time) | yes | The lease's end as the issuer set it. | |
generation | integer | yes | 1 at the mint, one more per renewal (replacement). | |
credentialRef | string | no | The operator's own opaque reference for the held credential. Never a key. | |
feeDeclared | boolean | yes | Whether ZadQ's fee declaration for the current generation is recorded. | |
offeredAt | string (date-time) | yes | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/agent-operators/<agentOperatorRef>/agents/<agentId>/credential" \
-H "Authorization: Bearer <session-credential>" \
-H "Content-Type: application/json" \
-d @request-body.json
POST /v0/agent-operators/{agentOperatorRef}/agents/{agentId}/renew
Renew an agent — replacement, with a new offer handed out once
Renewal is replacement: a new credential with the same delegation and a new lease, the old one revoked at the next root. ZadQ records the renewal's fee declaration first, then asks the issuer; the new offer is handed out once, like a mint, and the agent reads offered (generation + 1) until the new credential is reported held. A renewal counts against the issuer's mint rate. A revoked or expired agent is not renewable (409): mint a new one. The issuer's refusals are 409 and terminal. Wallet session of the operator's wallet required (401/403).
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agentOperatorRef | path | yes | string | The agent operator's opaque reference (aop_…). |
agentId | path | yes | string | The agent's pseudonymous handle (agt_…). |
Responses
201 — The renewal offer, handed out once, and the agent's record.
Content type application/json.
Schema: AgentOfferHandoff
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
offer | object | yes | The agent offer as the issuer returned it — opaque, one-shot; the operator's agent client redeems it at once. | |
expiresAt | string (date-time) | yes | When the agent's credential expires — its lease's end or the principal's verification, whichever is first. | |
agent | Agent | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/agent-operators/<agentOperatorRef>/agents/<agentId>/renew" \
-H "Authorization: Bearer <session-credential>"
POST /v0/agent-operators/{agentOperatorRef}/agents/{agentId}/revoke
Revoke an agent (the operator's decision)
The operator revokes the agent: the issuer's status change, then the agent reads revoked with reason operator_revoked; from the next status root on its credential presents nowhere. 409 for an agent already revoked or expired. Wallet session of the operator's wallet required (401/403).
Authentication: walletSession.
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
agentOperatorRef | path | yes | string | The agent operator's opaque reference (aop_…). |
agentId | path | yes | string | The agent's pseudonymous handle (agt_…). |
Responses
200 — The revoked agent.
Content type application/json.
Schema: Agent
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | The agent's pseudonymous handle (agt_…), assigned by ZadQ. | |
state | string | yes | "offered", "held", "revoked", "expired" | |
reason | string | no | "operator_revoked", "principal_revoked", "self_revoked", "lease_expired" | |
delegation | AgentDelegation | yes | ||
leaseUntil | string (date-time) | yes | The lease's end as the issuer set it. | |
generation | integer | yes | 1 at the mint, one more per renewal (replacement). | |
credentialRef | string | no | The operator's own opaque reference for the held credential. Never a key. | |
feeDeclared | boolean | yes | Whether ZadQ's fee declaration for the current generation is recorded. | |
offeredAt | string (date-time) | yes | ||
createdAt | string (date-time) | yes |
default — RFC 9457 problem details.
Content type application/problem+json.
Schema: Problem
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |
Example
curl -sS -X POST "<base-url>/v0/agent-operators/<agentOperatorRef>/agents/<agentId>/revoke" \
-H "Authorization: Bearer <session-credential>"
Schemas
TermsDocument
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
version | string | yes | Version name of the acknowledgement document. | |
sha256 | string | yes | Hex SHA-256 of text. | |
url | string (uri) | yes | Where the full Terms of Service are published. | |
text | string | yes | The acknowledgement text the wallet signs for. |
SellerTerms
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
current | TermsDocument | yes | ||
acknowledged | boolean | yes | Whether the current version is acknowledged by this seller. | |
acknowledgedVersion | string | no | ||
signedAt | string (date-time) | no | When the current version was signed, if it was. | |
message | string | yes | The exact text to sign for the current version. | |
messageSignedAt | string (date-time) | yes | The signing time embedded in message; submit it back as signedAt. |
TermsAcknowledgement
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
version | string | yes | ||
sha256 | string | yes | ||
signedAt | string (date-time) | yes | ||
signature | string | yes | Wallet signature over the message (hex for EVM, base58 or hex for Solana). |
SellerWebhooks
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
webhooks | array of Webhook | yes |
SellerEvent
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | yes | Event name of the seller stream (e.g. intake, kyc_approved, attested, status_suspended, consent_withdrawn). | |
occurredAt | string (date-time) | yes |
SellerEvents
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
events | array of SellerEvent | yes |
Chain
The payment rail of a payTo address (Platform Decision 14, ZADQ-66): USDC on Base (base, an EVM address, stored and compared lowercase) or on Solana (solana, case-sensitive). Any other value is refused with a 400 problem; other EVM chains are not declared until a seller needs one.
Type: string, one of "base", "solana".
AuthChallengeRequest
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
chain | Chain | yes | ||
address | string | yes |
AuthChallenge
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
nonce | string | yes | ||
message | string | yes | Exact text to sign, byte for byte. | |
expiresAt | string (date-time) | yes |
AuthSessionRequest
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
chain | Chain | yes | ||
address | string | yes | ||
nonce | string | yes | ||
signature | string | yes | EVM — 0x-hex r||s||v from personal_sign; Solana — base58 (or hex) 64-byte signature. |
AuthSession
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
bearer | string | yes | Opaque session credential for the Authorization header. | |
expiresAt | string (date-time) | yes | ||
chain | Chain | yes | ||
address | string | yes |
Health
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
status | string | yes | "ok" | |
version | string | no |
CredentialOfferHandoff
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
offer | object | yes | The credential offer object, as the issuer returned it — opaque, one-shot; redeem it at once. | |
expiresAt | string (date-time) | yes | When the code and the credential it yields expire — the verification's own expiry at the latest. No grace. | |
credential | CredentialState | yes |
CredentialReport
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
state | string | yes | held, or self_revoked (revoked is the older spelling of the same report). | "held", "self_revoked", "revoked" |
credentialRef | string | no | An opaque reference of the seller's choosing for the credential it holds. Never a key. | |
presentationUrl | string (uri) | no | Where the seller's holder answers this service's challenges (ZADQ-84): POST of the issued challenge as the verifier returned it ({request, signature_hex}), 200 with the envelope ({proof_hex, issuer_id, status_root, claimed_predicates}); any other status means the holder cannot present this time. Only with held; kept when omitted on a later report. |
CredentialState
The v1 credential the seller holds in its own infrastructure, as this service knows it (ZADQ-83): state only. expired is derived — an offered or held credential past the verification's expiry — since a credential lasts exactly what the verification lasts. revoked: revoked by the issuer's policy (consent withdrawn). self_revoked (ZADQ-89): revoked by its holder through the network's privacy relay; terminal on this verification, with nothing recorded next to it — the way back is a new verification.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
state | string | yes | "none", "offered", "held", "expired", "revoked", "self_revoked" | |
offeredAt | string (date-time) | no | ||
expiresAt | string (date-time) | no | The verification's expiry, which is the credential's. | |
credentialRef | string | no | ||
presentationUrl | string (uri) | no | The holder's presentation endpoint registered with held (ZADQ-84); absent on the v0 path. | |
lastPresentation | PresentationOutcome | no |
PresentationOutcome
The outcome of the last cadence presentation of the held credential (ZADQ-84), as the verifier's own decision record keeps it: never the challenge, the envelope or a proof. stage says where it ended — challenge (no challenge issued: the verifier's proven state was unavailable), transport (the holder did not present: holder_unreachable, holder_refused, envelope_malformed) or decision (the verifier decided; reason is its code — accepted, or one of its closed vocabulary such as proof_invalid, predicate_missing, deadline_exceeded, status_root_stale, request_not_ours). A refused decision withholds the positive claim on the public verdict; the public reason stays in the standing vocabulary, so the exact code lives here and on the seller's channel.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
stage | string | yes | "challenge", "transport", "decision" | |
accepted | boolean | yes | ||
reason | string | yes | ||
sessionId | string | no | The verifier's session id of the decision record. | |
decidedAt | string (date-time) | yes |
SubstrateStatus
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
substrate | string | yes | "ok", "degraded", "unknown" | |
source | string | yes | Where the state comes from — the verifier's status read, or this service's cadence accounting. | "verifier", "cadence" |
reason | string | no | Why the substrate is not ok, in the verifier's standing vocabulary (for example height_stale, root_expired, state_unavailable). Absent when ok. | |
checkedAt | string (date-time) | yes | ||
lastSuccessfulStandingRead | string (date-time) | no | The last time a standing read answered with a known status. Absent before the first. | |
cadence | object | yes | ||
cadence.recheckEveryHours | integer | yes | The published cadence promise. | |
cadence.lastCycleAt | string (date-time) | no | ||
cadence.checked | integer | no | Attestations the last cycle asked the verifier about. | |
cadence.withheld | integer | no | Of those, how many came back with an unknown standing. |
BillingCheckout
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
tier | string | yes | The schedule tier, by name. Amounts are on the checkout page and in the operator agreement. | "attested_vendor", "regulated_compliance" |
BillingHandoff
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
url | string (uri) | yes | The hosted checkout page — open it as-is. | |
billing | string | yes | "none", "pending", "active" |
BillingEventReceipt
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
applied | boolean | yes |
AddressProof
Proof of control of a payment address: the nonce of an auth challenge issued for it and the wallet's signature over the challenge message. Consumed on use, valid or not, like a login.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
nonce | string | yes | ||
signature | string | yes |
EndpointAddition
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
resourceUrl | string (uri) | yes | ||
payTo | PayTo | no | ||
proof | AddressProof | no |
WalletRotation
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
payTo | PayTo | yes | ||
proof | AddressProof | yes |
OperatorEndpoints
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
endpoints | array of EndpointSummary | yes |
EndpointSummary
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
sellerId | string | yes | ||
payTo | PayTo | yes | ||
resourceUrl | string | yes | ||
state | string | yes | "pending_kyc", "kyc_expired", "admitted", "rejected", "withdrawn" | |
attestationStatus | string | no | "active", "suspended", "revoked" |
InterestForm
The register-interest form's fields; contact data is not among them.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
side | string | yes | Which side of the trust perimeter the person stands on. | "vendor", "agent_operator" |
resource_url | string (uri) | no | The x402 resource URL a vendor sells at (optional, vendors). | |
agent_count_band | string | no | The size band of an operator's fleet (optional, agent operators). | "up_to_10", "up_to_100", "more_than_100" |
website | string | no | Trap field for automated submitters; a person leaves it empty. |
SellerIntake
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
payTo | PayTo | yes | ||
resourceUrl | string (uri) | yes | ||
operatorType | string | yes | "person", "organisation" | |
segment | SegmentDeclaration | yes |
PayTo
Chain-qualified x402 payment address.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
chain | Chain | yes | ||
address | string | yes | The address that receives the payment; on base stored and compared lowercase. |
SegmentDeclaration
PARAM-29 declaration. Recorded, not enforced in v0.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
annualVolumeUsdAtLeast300 | boolean | yes | ||
regulatedB2bCounterparties | boolean | yes |
Seller
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
payTo | PayTo | yes | ||
resourceUrl | string (uri) | yes | ||
operatorType | string | yes | "person", "organisation" | |
state | string | yes | kyc_expired: the proofing session ran past its deadline without an outcome; a new session can be opened (POST …/kyc/sessions). withdrawn: consent to the service withdrawn — terminal. | "pending_kyc", "kyc_expired", "admitted", "rejected", "withdrawn" |
billing | string | yes | Billing state at the operator's billing provider (ZADQ-54): none until a checkout starts, pending until the provider's signed event lands, active afterwards. The service holds no legal or payment data. | "none", "pending", "active" |
billingTier | string | no | The schedule tier chosen at checkout, by name. | "attested_vendor", "regulated_compliance" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
renewal | all of KycHandoff | no | The pending re-verification session, while one is open (ZADQ-91): the person completes it on handoffUrl; the approved session in kyc keeps backing the attestation meanwhile. | |
attestation | Attestation | no | ||
createdAt | string (date-time) | yes |
KycHandoff
The person's side of the KYC session (ZADQ-52). handoffUrl is the identity provider's hosted page where the proofing is completed — open it as-is; it is present only while the session is pending. Nothing here is identity data.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
state | string | yes | "pending", "approved", "rejected", "expired", "withdrawn" | |
handoffUrl | string (uri) | no | ||
expiresAt | string (date-time) | no | Proofing deadline of the current session. | |
reusableUntil | string (date-time) | no | End of the approved session's reuse window (approval + the grade's validity — 12 months at grades 1–2, 6 at grade 3, 3 at grade 4). No grace and no extension: the attestation is suspended rekyc_overdue at the first root after it unless a re-verification was bound before. Re-verify ahead of it (POST …/kyc/sessions). | |
freshnessBand | string | no | Age of the last successful proofing as a band (F4 < 30 days, F3 < 90, F2 < 180, F1 < 365) — never a date. "Human-backed at G" means grade ≥ 2 and freshness ≥ F1; the issuer enforces it. | "F4", "F3", "F2", "F1" |
reverificationDueAt | string (date-time) | no | When "re-verification due" starts: reusableUntil minus the service's lead (ZADQ-79). Present while approved and no re-verification is pending. The issuer extends nothing; this is the whole of the notice. | |
reverificationDue | boolean | no | True from reverificationDueAt until the window ends: re-verify now (POST …/kyc/sessions) so the attestation keeps its standing and its reference. Past the window the issuer refuses every step with 409 session_reuse_expired — terminal: the seller reads kyc_expired and starts a new verification. |
KycResult
Outcome metadata only — the contract carries no identity data.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
sessionId | string | yes | ||
outcome | string | yes | "approved", "rejected" | |
assuranceLevel | string | no | Assurance grade met by the proofing process (e.g. "G"). |
Attestation
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
sellerId | string | yes | ||
assuranceLevel | string | yes | ||
status | string | yes | "active", "suspended", "revoked" | |
statusReason | string | no | Why a non-active status was set — never silent. rekyc_overdue: the reuse window ended; restored under the same reference by a bound re-verification. consent_withdrawn: terminal. self_revoked: the holder revoked its own credential and the attestation was retired at the next verdict cadence — terminal for this attestation; a new verification yields a new one. Absent for operator changes and while active. | "rekyc_overdue", "consent_withdrawn", "self_revoked" |
freshnessBand | string | no | Freshness band of the backing session, as the issuer reports it. | "F4", "F3", "F2", "F1" |
issuedAt | string (date-time) | yes | ||
updatedAt | string (date-time) | yes |
AttestationReceipt
The attestation-ref/v0 payload, field for field (snake_case as the issuer signs it): Ed25519 over `"mintid/seller-attestation/v0" || 0x00
|| SHA-256(canonical JSON of the seven fields but signature)`. Only opaque handles and public endpoint identifiers.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
attestation_ref | string | yes | 32-byte opaque reference, hex. | |
issuer_id | string | yes | ||
chain | string | yes | ||
pay_to | string | yes | ||
resource_url | string | yes | ||
assurance_grade | integer | yes | ||
issued_at_unix | integer | yes | ||
signature | string | yes | Ed25519 by the issuer's registered status signing key, hex (64 bytes). |
AttestationStatusUpdate
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
status | string | yes | "active", "suspended", "revoked" |
Verdict
The explorer-consumable signed verdict: "human-backed at assurance ≥ G, verified at T, re-checked every N hours." Staleness is policy-driven. Funded-recourse policy: the positive claim is published only while the issuer's guarantee deposit meets the published minimum; otherwise the outcome is no_signal with reason: bond_insufficient. Every non-positive outcome carries reason from the closed vocabulary (ZADQ-78); human_backed never does.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
outcome | string | yes | "human_backed", "revoked", "stale", "no_signal" | |
subject | object | yes | ||
subject.chain | Chain | yes | ||
subject.payTo | string | yes | ||
subject.resourceUrl | string | yes | ||
assuranceLevel | string | no | ||
verifiedAt | string (date-time) | no | ||
recheckEveryHours | integer | yes | ||
signature | string | no | Ed25519 over the canonical verdict payload, base64. | |
keyId | string | no | ||
reason | string | no | Why the outcome is not human_backed: the standing read's reason code (closed vocabulary), or suspended when the witnessed status is suspended. Absent on human_backed; absent on revoked too. The policy reason of a suspension (rekyc_overdue) is the seller's own record, never part of the public verdict. On the v1 path (ZADQ-84) a verifier-side condition of the presentation decision is carried with the same code (issuer_not_active, status_root_stale, height_stale) or as state_unavailable; a presentation the verifier refused on the holder's side withholds the positive claim with no reason — the decision's exact code is the seller's own record (credential.lastPresentation). | "state_unavailable", "issuer_unknown", "issuer_not_active", "status_root_stale", "height_stale", "witness_unavailable", "witness_malformed", "witness_invalid", "witness_root_mismatch", "bond_insufficient", "suspended" |
WebhookRegistration
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
url | string (uri) | yes |
Webhook
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | ||
url | string (uri) | yes | ||
secret | string | no | Hex signing secret for X-Signature. Returned once at registration. | |
createdAt | string (date-time) | yes |
VerificationKeys
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
keys | array of VerificationKey | yes |
VerificationKey
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
keyId | string | yes | First 8 bytes of the public key's SHA-256, hex. | |
algorithm | string | yes | "Ed25519" | |
publicKey | string | yes | Raw 32-byte Ed25519 public key, base64. | |
status | string | no | current signs new verdicts; previous is a key a rotation retired, kept published for at least the re-check window so verdicts signed before the rotation keep verifying (select the key by keyId, never by position). | "current", "previous" |
AgentOperatorEnrolment
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
wallet | all of PayTo | yes | The wallet the operator's agent pays from — the one the wallet session proves. | |
operatorType | string | yes | person is verified by identity verification, organisation by business verification; the boundary learns this and nothing else. | "person", "organisation" |
AgentOperator
The accountable party behind an agent, as this service keeps it (ZADQ-85): the wallet the operator's agent pays from, the operator type, the verification, the entry fee and the principal's identity credential — state only; the dashboard shows state, never keys. Agents present proof that an accountable operator stands behind them; they never disclose who.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | The operator's opaque reference (aop_…). | |
wallet | all of PayTo | yes | The wallet the operator's agent pays from. | |
operatorType | string | yes | "person", "organisation" | |
state | string | yes | pending_kyc → approved → credential_held (the principal's credential is held in the operator's infrastructure) → active (its first agent is minted). kyc_expired: the proofing deadline passed, or the approved verification's reuse window closed — the principal's credential expired with it; verify again (POST …/kyc/sessions). rejected and withdrawn are terminal. | "pending_kyc", "kyc_expired", "approved", "rejected", "credential_held", "active", "withdrawn" |
billing | string | yes | The one-off entry fee at the operator's billing provider: none until a checkout starts, pending until the provider's signed event lands, active once it is recorded. The service holds no legal or payment data. | "none", "pending", "active" |
kycSessionId | string | no | Opaque session handle at the KYC boundary. Not identity data. | |
kyc | KycHandoff | no | ||
credential | CredentialState | no | ||
agents | AgentCounts | no | ||
createdAt | string (date-time) | yes |
AgentOperatorCredentialReport
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
state | string | yes | held, or self_revoked (revoked is the older spelling of the same report). | "held", "self_revoked", "revoked" |
credentialRef | string | no | An opaque reference of the operator's choosing for the credential it holds. Never a key. |
ScopeVocabulary
The delegation vocabulary carried on chain, as read under its pinned content hash: the scope values and the key-custody classes an agent offer may name.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
contentSha256 | string | yes | The sha256 of the vocabulary content it was read under (hex). | |
version | string | no | ||
scopes | array of string | yes | ||
custodyClasses | array of string | yes |
AgentLimit
The per-transaction limit the issuer signs into the agent's delegation.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
amount | string | yes | A positive decimal amount (up to 8 decimals), as a string. | |
unit | string | yes | The limit's unit — a short currency code (for instance USDC). |
AgentDelegation
What the agent is allowed to do, as the operator declares it and the issuer signs it.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
scope | array of string | yes | Scope values of the vocabulary (GET /v0/agent-scopes), each once. | |
limit | AgentLimit | yes | ||
custodyClass | string | yes | The key-custody class of the agent's key, from the vocabulary. | |
leaseDays | integer | yes | The lease in days; the issuer allows at most 90. |
Agent
An agent minted for the operator, as this service keeps it: ZadQ's pseudonymous handle — never a chain identifier —, the delegation summary, the lease and the state; never a key, the offer or a link to a person. States: offered (handed out, not yet reported held; again after a renewal) → held; revoked and expired are terminal, with the reason: operator_revoked (the operator revoked it), principal_revoked (the principal stopped backing its agents — self-revoked, verification window closed or consent withdrawn — whatever triggered it), self_revoked (the agent's holder revoked it) or lease_expired (past its lease).
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | The agent's pseudonymous handle (agt_…), assigned by ZadQ. | |
state | string | yes | "offered", "held", "revoked", "expired" | |
reason | string | no | "operator_revoked", "principal_revoked", "self_revoked", "lease_expired" | |
delegation | AgentDelegation | yes | ||
leaseUntil | string (date-time) | yes | The lease's end as the issuer set it. | |
generation | integer | yes | 1 at the mint, one more per renewal (replacement). | |
credentialRef | string | no | The operator's own opaque reference for the held credential. Never a key. | |
feeDeclared | boolean | yes | Whether ZadQ's fee declaration for the current generation is recorded. | |
offeredAt | string (date-time) | yes | ||
createdAt | string (date-time) | yes |
Agents
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
items | array of Agent | yes |
AgentOfferHandoff
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
offer | object | yes | The agent offer as the issuer returned it — opaque, one-shot; the operator's agent client redeems it at once. | |
expiresAt | string (date-time) | yes | When the agent's credential expires — its lease's end or the principal's verification, whichever is first. | |
agent | Agent | yes |
AgentCredentialReport
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
state | string | yes | "held", "self_revoked" | |
credentialRef | string | no | An opaque reference of the operator's choosing for the agent's credential. Never a key. |
AgentCounts
The operator's live agents (offered or held), for display. The issuer enforces at most 10 active agents per person and 5 mints per sliding hour, renewals included; ZadQ shows the answer, it does not re-count.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
active | integer | yes |
MetricsReport
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
schema | string | yes | The wire shape's name (published-metrics/v1). | |
computedAt | string (date-time) | yes | ||
asOf | string (date) | yes | The cut-off — the last day of the last complete month (UTC). Partial months are never published. | |
period | MetricsPeriod | no | ||
cadence | object | yes | ||
cadence.publication | string | yes | monthly. | |
cadence.snapshot | string | yes | quarterly, aligned to the two-consecutive-quarters clock. | |
relatedParty | boolean | yes | Always true — the operator's position is a property of the dataset. | |
operator | MetricsOperator | yes | ||
series | array of MetricsSeries | yes | ||
contextNote | string | yes |
MetricsPeriod
Inclusive month range, YYYY-MM at both ends. Absent while no complete month holds a record.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
from | string | yes | ||
to | string | yes |
MetricsOperator
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
service | string | yes | ||
operator | string | yes | ||
relatedParty | boolean | yes | ||
position | string | yes | ||
disclosure | string | yes | The canonical metrics disclosure, embedded in every report. |
MetricsSeries
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
id | string | yes | "sellers_attested", "retention_r90", "verifications_free_path", "independent_verifiers", "anchor_service_revenue" | |
title | string | yes | ||
discipline | string | yes | "registry-based", "aggregate", "context" | |
definition | string | yes | ||
relatedParty | boolean | yes | Always true, on every series. | |
period | MetricsPeriod | no | ||
available | boolean | yes | False when the records hold no source for the series; unavailableReason says why and points is empty. | |
unavailableReason | string | no | ||
points | array of MetricsPoint | yes |
MetricsPoint
One month-end observation. Which fields carry a value depends on the series: cumulative, active, independent (sellers_attested); cohort, retained, r (retention_r90; r absent while the cohort is empty); count, registrations (independent_verifiers).
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
period | string | yes | YYYY-MM. | |
cumulative | integer | no | ||
active | integer | no | ||
independent | integer | no | ||
cohort | integer | no | ||
retained | integer | no | ||
r | number | no | Ratio, two decimals. | |
count | integer | no | ||
registrations | integer | no |
Problem
RFC 9457 problem details.
| Property | Type | Required | Description | Values |
|---|---|---|---|---|
type | string | no | ||
title | string | no | ||
status | integer | no | ||
detail | string | no | ||
instance | string | no |