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

FieldValue
ContractSeller-Verification Service API
Version0.1.0
SpecificationOpenAPI 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

PropertyTypeRequiredDescriptionValues
chainChainyes
addressstringyes

Responses

201 — Challenge issued.

Content type application/json.

Schema: AuthChallenge

PropertyTypeRequiredDescriptionValues
noncestringyes
messagestringyesExact text to sign, byte for byte.
expiresAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

PropertyTypeRequiredDescriptionValues
chainChainyes
addressstringyes
noncestringyes
signaturestringyesEVM — 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

PropertyTypeRequiredDescriptionValues
bearerstringyesOpaque session credential for the Authorization header.
expiresAtstring (date-time)yes
chainChainyes
addressstringyes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

PropertyTypeRequiredDescriptionValues
statusstringyes"ok"
versionstringno

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

PropertyTypeRequiredDescriptionValues
substratestringyes"ok", "degraded", "unknown"
sourcestringyesWhere the state comes from — the verifier's status read, or this service's cadence accounting."verifier", "cadence"
reasonstringnoWhy the substrate is not ok, in the verifier's standing vocabulary (for example height_stale, root_expired, state_unavailable). Absent when ok.
checkedAtstring (date-time)yes
lastSuccessfulStandingReadstring (date-time)noThe last time a standing read answered with a known status. Absent before the first.
cadenceobjectyes
cadence.recheckEveryHoursintegeryesThe published cadence promise.
cadence.lastCycleAtstring (date-time)no
cadence.checkedintegernoAttestations the last cycle asked the verifier about.
cadence.withheldintegernoOf 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

PropertyTypeRequiredDescriptionValues
sidestringyesWhich side of the trust perimeter the person stands on."vendor", "agent_operator"
resource_urlstring (uri)noThe x402 resource URL a vendor sells at (optional, vendors).
agent_count_bandstringnoThe size band of an operator's fleet (optional, agent operators)."up_to_10", "up_to_100", "more_than_100"
websitestringnoTrap 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

PropertyTypeRequiredDescriptionValues
payToPayToyes
resourceUrlstring (uri)yes
operatorTypestringyes"person", "organisation"
segmentSegmentDeclarationyes

Responses

201 — Seller registered; KYC session opened.

Content type application/json.

Schema: Seller

PropertyTypeRequiredDescriptionValues
idstringyes
payToPayToyes
resourceUrlstring (uri)yes
operatorTypestringyes"person", "organisation"
statestringyeskyc_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"
billingstringyesBilling 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"
billingTierstringnoThe schedule tier chosen at checkout, by name."attested_vendor", "regulated_compliance"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
renewalall of KycHandoffnoThe 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.
attestationAttestationno
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Responses

200 — Current seller state.

Content type application/json.

Schema: Seller

PropertyTypeRequiredDescriptionValues
idstringyes
payToPayToyes
resourceUrlstring (uri)yes
operatorTypestringyes"person", "organisation"
statestringyeskyc_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"
billingstringyesBilling 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"
billingTierstringnoThe schedule tier chosen at checkout, by name."attested_vendor", "regulated_compliance"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
renewalall of KycHandoffnoThe 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.
attestationAttestationno
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Responses

200 — The operator's endpoints, oldest first.

Content type application/json.

Schema: OperatorEndpoints

PropertyTypeRequiredDescriptionValues
endpointsarray of EndpointSummaryyes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Request body

application/json — required

Schema: EndpointAddition

PropertyTypeRequiredDescriptionValues
resourceUrlstring (uri)yes
payToPayTono
proofAddressProofno

Responses

201 — The new seller, admitted on the shared verification.

Content type application/json.

Schema: Seller

PropertyTypeRequiredDescriptionValues
idstringyes
payToPayToyes
resourceUrlstring (uri)yes
operatorTypestringyes"person", "organisation"
statestringyeskyc_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"
billingstringyesBilling 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"
billingTierstringnoThe schedule tier chosen at checkout, by name."attested_vendor", "regulated_compliance"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
renewalall of KycHandoffnoThe 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.
attestationAttestationno
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Request body

application/json — required

Schema: WalletRotation

PropertyTypeRequiredDescriptionValues
payToPayToyes
proofAddressProofyes

Responses

200 — The seller on its new address, with the new attestation.

Content type application/json.

Schema: Seller

PropertyTypeRequiredDescriptionValues
idstringyes
payToPayToyes
resourceUrlstring (uri)yes
operatorTypestringyes"person", "organisation"
statestringyeskyc_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"
billingstringyesBilling 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"
billingTierstringnoThe schedule tier chosen at checkout, by name."attested_vendor", "regulated_compliance"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
renewalall of KycHandoffnoThe 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.
attestationAttestationno
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Request body

application/json — required

Schema: BillingCheckout

PropertyTypeRequiredDescriptionValues
tierstringyesThe 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

PropertyTypeRequiredDescriptionValues
urlstring (uri)yesThe hosted checkout page — open it as-is.
billingstringyes"none", "pending", "active"

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
Stripe-Signatureheaderyesstring

Request body

application/json — required

Type: object.

Responses

200 — Acknowledged; whether the event changed a state.

Content type application/json.

Schema: BillingEventReceipt

PropertyTypeRequiredDescriptionValues
appliedbooleanyes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Responses

201 — The offer, handed out once, and when it expires.

Content type application/json.

Schema: CredentialOfferHandoff

PropertyTypeRequiredDescriptionValues
offerobjectyesThe credential offer object, as the issuer returned it — opaque, one-shot; redeem it at once.
expiresAtstring (date-time)yesWhen the code and the credential it yields expire — the verification's own expiry at the latest. No grace.
credentialCredentialStateyes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Request body

application/json — required

Schema: CredentialReport

PropertyTypeRequiredDescriptionValues
statestringyesheld, or self_revoked (revoked is the older spelling of the same report)."held", "self_revoked", "revoked"
credentialRefstringnoAn opaque reference of the seller's choosing for the credential it holds. Never a key.
presentationUrlstring (uri)noWhere 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

PropertyTypeRequiredDescriptionValues
idstringyes
payToPayToyes
resourceUrlstring (uri)yes
operatorTypestringyes"person", "organisation"
statestringyeskyc_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"
billingstringyesBilling 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"
billingTierstringnoThe schedule tier chosen at checkout, by name."attested_vendor", "regulated_compliance"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
renewalall of KycHandoffnoThe 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.
attestationAttestationno
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Responses

201 — New session opened; the seller reads pending_kyc again.

Content type application/json.

Schema: Seller

PropertyTypeRequiredDescriptionValues
idstringyes
payToPayToyes
resourceUrlstring (uri)yes
operatorTypestringyes"person", "organisation"
statestringyeskyc_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"
billingstringyesBilling 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"
billingTierstringnoThe schedule tier chosen at checkout, by name."attested_vendor", "regulated_compliance"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
renewalall of KycHandoffnoThe 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.
attestationAttestationno
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Responses

200 — Consent withdrawn; the seller is terminal.

Content type application/json.

Schema: Seller

PropertyTypeRequiredDescriptionValues
idstringyes
payToPayToyes
resourceUrlstring (uri)yes
operatorTypestringyes"person", "organisation"
statestringyeskyc_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"
billingstringyesBilling 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"
billingTierstringnoThe schedule tier chosen at checkout, by name."attested_vendor", "regulated_compliance"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
renewalall of KycHandoffnoThe 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.
attestationAttestationno
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

PropertyTypeRequiredDescriptionValues
versionstringyesVersion name of the acknowledgement document.
sha256stringyesHex SHA-256 of text.
urlstring (uri)yesWhere the full Terms of Service are published.
textstringyesThe acknowledgement text the wallet signs for.

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Responses

200 — Acknowledgement state.

Content type application/json.

Schema: SellerTerms

PropertyTypeRequiredDescriptionValues
currentTermsDocumentyes
acknowledgedbooleanyesWhether the current version is acknowledged by this seller.
acknowledgedVersionstringno
signedAtstring (date-time)noWhen the current version was signed, if it was.
messagestringyesThe exact text to sign for the current version.
messageSignedAtstring (date-time)yesThe signing time embedded in message; submit it back as signedAt.

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Request body

application/json — required

Schema: TermsAcknowledgement

PropertyTypeRequiredDescriptionValues
versionstringyes
sha256stringyes
signedAtstring (date-time)yes
signaturestringyesWallet signature over the message (hex for EVM, base58 or hex for Solana).

Responses

201 — Acknowledged.

Content type application/json.

Schema: SellerTerms

PropertyTypeRequiredDescriptionValues
currentTermsDocumentyes
acknowledgedbooleanyesWhether the current version is acknowledged by this seller.
acknowledgedVersionstringno
signedAtstring (date-time)noWhen the current version was signed, if it was.
messagestringyesThe exact text to sign for the current version.
messageSignedAtstring (date-time)yesThe signing time embedded in message; submit it back as signedAt.

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Responses

200 — The seller's receivers.

Content type application/json.

Schema: SellerWebhooks

PropertyTypeRequiredDescriptionValues
webhooksarray of Webhookyes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Request body

application/json — required

Schema: WebhookRegistration

PropertyTypeRequiredDescriptionValues
urlstring (uri)yes

Responses

201 — Receiver registered; secret shown once.

Content type application/json.

Schema: Webhook

PropertyTypeRequiredDescriptionValues
idstringyes
urlstring (uri)yes
secretstringnoHex signing secret for X-Signature. Returned once at registration.
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring
webhookIdpathyesstring

Responses

204 — Receiver removed.

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring
limitquerynointeger

Responses

200 — Recent events.

Content type application/json.

Schema: SellerEvents

PropertyTypeRequiredDescriptionValues
eventsarray of SellerEventyes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Request body

application/json — required

Schema: KycResult

PropertyTypeRequiredDescriptionValues
sessionIdstringyes
outcomestringyes"approved", "rejected"
assuranceLevelstringnoAssurance grade met by the proofing process (e.g. "G").

Responses

200 — Admission state updated.

Content type application/json.

Schema: Seller

PropertyTypeRequiredDescriptionValues
idstringyes
payToPayToyes
resourceUrlstring (uri)yes
operatorTypestringyes"person", "organisation"
statestringyeskyc_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"
billingstringyesBilling 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"
billingTierstringnoThe schedule tier chosen at checkout, by name."attested_vendor", "regulated_compliance"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
renewalall of KycHandoffnoThe 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.
attestationAttestationno
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Responses

201 — Attestation issued.

Content type application/json.

Schema: Attestation

PropertyTypeRequiredDescriptionValues
idstringyes
sellerIdstringyes
assuranceLevelstringyes
statusstringyes"active", "suspended", "revoked"
statusReasonstringnoWhy 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"
freshnessBandstringnoFreshness band of the backing session, as the issuer reports it."F4", "F3", "F2", "F1"
issuedAtstring (date-time)yes
updatedAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring
attestationIdpathyesstring

Responses

200 — The receipt.

Content type application/json.

Schema: AttestationReceipt

PropertyTypeRequiredDescriptionValues
attestation_refstringyes32-byte opaque reference, hex.
issuer_idstringyes
chainstringyes
pay_tostringyes
resource_urlstringyes
assurance_gradeintegeryes
issued_at_unixintegeryes
signaturestringyesEd25519 by the issuer's registered status signing key, hex (64 bytes).

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
attestationIdpathyesstring

Request body

application/json — required

Schema: AttestationStatusUpdate

PropertyTypeRequiredDescriptionValues
statusstringyes"active", "suspended", "revoked"

Responses

200 — Mirrored status applied.

Content type application/json.

Schema: Attestation

PropertyTypeRequiredDescriptionValues
idstringyes
sellerIdstringyes
assuranceLevelstringyes
statusstringyes"active", "suspended", "revoked"
statusReasonstringnoWhy 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"
freshnessBandstringnoFreshness band of the backing session, as the issuer reports it."F4", "F3", "F2", "F1"
issuedAtstring (date-time)yes
updatedAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

PropertyTypeRequiredDescriptionValues
keysarray of VerificationKeyyes

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

PropertyTypeRequiredDescriptionValues
schemastringyesThe wire shape's name (published-metrics/v1).
computedAtstring (date-time)yes
asOfstring (date)yesThe cut-off — the last day of the last complete month (UTC). Partial months are never published.
periodMetricsPeriodno
cadenceobjectyes
cadence.publicationstringyesmonthly.
cadence.snapshotstringyesquarterly, aligned to the two-consecutive-quarters clock.
relatedPartybooleanyesAlways true — the operator's position is a property of the dataset.
operatorMetricsOperatoryes
seriesarray of MetricsSeriesyes
contextNotestringyes

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

NameInRequiredTypeDescription
chainqueryyesChain
payToqueryyesstring
resourceUrlqueryyesstring

Responses

200 — Verdict (or explicit no-signal outcome).

Content type application/json.

Schema: Verdict

PropertyTypeRequiredDescriptionValues
outcomestringyes"human_backed", "revoked", "stale", "no_signal"
subjectobjectyes
subject.chainChainyes
subject.payTostringyes
subject.resourceUrlstringyes
assuranceLevelstringno
verifiedAtstring (date-time)no
recheckEveryHoursintegeryes
signaturestringnoEd25519 over the canonical verdict payload, base64.
keyIdstringno
reasonstringnoWhy 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

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

PropertyTypeRequiredDescriptionValues
urlstring (uri)yes

Responses

201 — Subscription created; secret shown once.

Content type application/json.

Schema: Webhook

PropertyTypeRequiredDescriptionValues
idstringyes
urlstring (uri)yes
secretstringnoHex signing secret for X-Signature. Returned once at registration.
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
webhookIdpathyesstring

Responses

204 — Subscription removed.

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
sellerIdpathyesstring

Responses

200 — Latest verdict.

Content type application/json.

Schema: Verdict

PropertyTypeRequiredDescriptionValues
outcomestringyes"human_backed", "revoked", "stale", "no_signal"
subjectobjectyes
subject.chainChainyes
subject.payTostringyes
subject.resourceUrlstringyes
assuranceLevelstringno
verifiedAtstring (date-time)no
recheckEveryHoursintegeryes
signaturestringnoEd25519 over the canonical verdict payload, base64.
keyIdstringno
reasonstringnoWhy 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

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

PropertyTypeRequiredDescriptionValues
walletall of PayToyesThe wallet the operator's agent pays from — the one the wallet session proves.
operatorTypestringyesperson 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

PropertyTypeRequiredDescriptionValues
idstringyesThe operator's opaque reference (aop_…).
walletall of PayToyesThe wallet the operator's agent pays from.
operatorTypestringyes"person", "organisation"
statestringyespending_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"
billingstringyesThe 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"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
agentsAgentCountsno
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
agentOperatorRefpathyesstringThe agent operator's opaque reference (aop_…).

Responses

200 — Current agent-operator state.

Content type application/json.

Schema: AgentOperator

PropertyTypeRequiredDescriptionValues
idstringyesThe operator's opaque reference (aop_…).
walletall of PayToyesThe wallet the operator's agent pays from.
operatorTypestringyes"person", "organisation"
statestringyespending_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"
billingstringyesThe 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"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
agentsAgentCountsno
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
agentOperatorRefpathyesstringThe agent operator's opaque reference (aop_…).

Responses

201 — New session opened; the operator reads pending_kyc again.

Content type application/json.

Schema: AgentOperator

PropertyTypeRequiredDescriptionValues
idstringyesThe operator's opaque reference (aop_…).
walletall of PayToyesThe wallet the operator's agent pays from.
operatorTypestringyes"person", "organisation"
statestringyespending_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"
billingstringyesThe 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"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
agentsAgentCountsno
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
agentOperatorRefpathyesstringThe agent operator's opaque reference (aop_…).

Request body

application/json — required

Schema: KycResult

PropertyTypeRequiredDescriptionValues
sessionIdstringyes
outcomestringyes"approved", "rejected"
assuranceLevelstringnoAssurance grade met by the proofing process (e.g. "G").

Responses

200 — Verification state updated.

Content type application/json.

Schema: AgentOperator

PropertyTypeRequiredDescriptionValues
idstringyesThe operator's opaque reference (aop_…).
walletall of PayToyesThe wallet the operator's agent pays from.
operatorTypestringyes"person", "organisation"
statestringyespending_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"
billingstringyesThe 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"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
agentsAgentCountsno
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
agentOperatorRefpathyesstringThe 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

PropertyTypeRequiredDescriptionValues
urlstring (uri)yesThe hosted checkout page — open it as-is.
billingstringyes"none", "pending", "active"

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
agentOperatorRefpathyesstringThe agent operator's opaque reference (aop_…).

Responses

201 — The offer, handed out once, and when it expires.

Content type application/json.

Schema: CredentialOfferHandoff

PropertyTypeRequiredDescriptionValues
offerobjectyesThe credential offer object, as the issuer returned it — opaque, one-shot; redeem it at once.
expiresAtstring (date-time)yesWhen the code and the credential it yields expire — the verification's own expiry at the latest. No grace.
credentialCredentialStateyes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
agentOperatorRefpathyesstringThe agent operator's opaque reference (aop_…).

Request body

application/json — required

Schema: AgentOperatorCredentialReport

PropertyTypeRequiredDescriptionValues
statestringyesheld, or self_revoked (revoked is the older spelling of the same report)."held", "self_revoked", "revoked"
credentialRefstringnoAn 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

PropertyTypeRequiredDescriptionValues
idstringyesThe operator's opaque reference (aop_…).
walletall of PayToyesThe wallet the operator's agent pays from.
operatorTypestringyes"person", "organisation"
statestringyespending_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"
billingstringyesThe 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"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
agentsAgentCountsno
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

PropertyTypeRequiredDescriptionValues
contentSha256stringyesThe sha256 of the vocabulary content it was read under (hex).
versionstringno
scopesarray of stringyes
custodyClassesarray of stringyes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
agentOperatorRefpathyesstringThe agent operator's opaque reference (aop_…).

Responses

200 — The operator's agents.

Content type application/json.

Schema: Agents

PropertyTypeRequiredDescriptionValues
itemsarray of Agentyes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
agentOperatorRefpathyesstringThe agent operator's opaque reference (aop_…).

Request body

application/json — required

Schema: AgentDelegation

PropertyTypeRequiredDescriptionValues
scopearray of stringyesScope values of the vocabulary (GET /v0/agent-scopes), each once.
limitAgentLimityes
custodyClassstringyesThe key-custody class of the agent's key, from the vocabulary.
leaseDaysintegeryesThe 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

PropertyTypeRequiredDescriptionValues
offerobjectyesThe agent offer as the issuer returned it — opaque, one-shot; the operator's agent client redeems it at once.
expiresAtstring (date-time)yesWhen the agent's credential expires — its lease's end or the principal's verification, whichever is first.
agentAgentyes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
agentOperatorRefpathyesstringThe agent operator's opaque reference (aop_…).
agentIdpathyesstringThe agent's pseudonymous handle (agt_…).

Responses

200 — The agent.

Content type application/json.

Schema: Agent

PropertyTypeRequiredDescriptionValues
idstringyesThe agent's pseudonymous handle (agt_…), assigned by ZadQ.
statestringyes"offered", "held", "revoked", "expired"
reasonstringno"operator_revoked", "principal_revoked", "self_revoked", "lease_expired"
delegationAgentDelegationyes
leaseUntilstring (date-time)yesThe lease's end as the issuer set it.
generationintegeryes1 at the mint, one more per renewal (replacement).
credentialRefstringnoThe operator's own opaque reference for the held credential. Never a key.
feeDeclaredbooleanyesWhether ZadQ's fee declaration for the current generation is recorded.
offeredAtstring (date-time)yes
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
agentOperatorRefpathyesstringThe agent operator's opaque reference (aop_…).
agentIdpathyesstringThe agent's pseudonymous handle (agt_…).

Request body

application/json — required

Schema: AgentCredentialReport

PropertyTypeRequiredDescriptionValues
statestringyes"held", "self_revoked"
credentialRefstringnoAn 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

PropertyTypeRequiredDescriptionValues
idstringyesThe agent's pseudonymous handle (agt_…), assigned by ZadQ.
statestringyes"offered", "held", "revoked", "expired"
reasonstringno"operator_revoked", "principal_revoked", "self_revoked", "lease_expired"
delegationAgentDelegationyes
leaseUntilstring (date-time)yesThe lease's end as the issuer set it.
generationintegeryes1 at the mint, one more per renewal (replacement).
credentialRefstringnoThe operator's own opaque reference for the held credential. Never a key.
feeDeclaredbooleanyesWhether ZadQ's fee declaration for the current generation is recorded.
offeredAtstring (date-time)yes
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
agentOperatorRefpathyesstringThe agent operator's opaque reference (aop_…).
agentIdpathyesstringThe agent's pseudonymous handle (agt_…).

Responses

201 — The renewal offer, handed out once, and the agent's record.

Content type application/json.

Schema: AgentOfferHandoff

PropertyTypeRequiredDescriptionValues
offerobjectyesThe agent offer as the issuer returned it — opaque, one-shot; the operator's agent client redeems it at once.
expiresAtstring (date-time)yesWhen the agent's credential expires — its lease's end or the principal's verification, whichever is first.
agentAgentyes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

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

NameInRequiredTypeDescription
agentOperatorRefpathyesstringThe agent operator's opaque reference (aop_…).
agentIdpathyesstringThe agent's pseudonymous handle (agt_…).

Responses

200 — The revoked agent.

Content type application/json.

Schema: Agent

PropertyTypeRequiredDescriptionValues
idstringyesThe agent's pseudonymous handle (agt_…), assigned by ZadQ.
statestringyes"offered", "held", "revoked", "expired"
reasonstringno"operator_revoked", "principal_revoked", "self_revoked", "lease_expired"
delegationAgentDelegationyes
leaseUntilstring (date-time)yesThe lease's end as the issuer set it.
generationintegeryes1 at the mint, one more per renewal (replacement).
credentialRefstringnoThe operator's own opaque reference for the held credential. Never a key.
feeDeclaredbooleanyesWhether ZadQ's fee declaration for the current generation is recorded.
offeredAtstring (date-time)yes
createdAtstring (date-time)yes

default — RFC 9457 problem details.

Content type application/problem+json.

Schema: Problem

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno

Example

curl -sS -X POST "<base-url>/v0/agent-operators/<agentOperatorRef>/agents/<agentId>/revoke" \
  -H "Authorization: Bearer <session-credential>"

Schemas

TermsDocument

PropertyTypeRequiredDescriptionValues
versionstringyesVersion name of the acknowledgement document.
sha256stringyesHex SHA-256 of text.
urlstring (uri)yesWhere the full Terms of Service are published.
textstringyesThe acknowledgement text the wallet signs for.

SellerTerms

PropertyTypeRequiredDescriptionValues
currentTermsDocumentyes
acknowledgedbooleanyesWhether the current version is acknowledged by this seller.
acknowledgedVersionstringno
signedAtstring (date-time)noWhen the current version was signed, if it was.
messagestringyesThe exact text to sign for the current version.
messageSignedAtstring (date-time)yesThe signing time embedded in message; submit it back as signedAt.

TermsAcknowledgement

PropertyTypeRequiredDescriptionValues
versionstringyes
sha256stringyes
signedAtstring (date-time)yes
signaturestringyesWallet signature over the message (hex for EVM, base58 or hex for Solana).

SellerWebhooks

PropertyTypeRequiredDescriptionValues
webhooksarray of Webhookyes

SellerEvent

PropertyTypeRequiredDescriptionValues
typestringyesEvent name of the seller stream (e.g. intake, kyc_approved, attested, status_suspended, consent_withdrawn).
occurredAtstring (date-time)yes

SellerEvents

PropertyTypeRequiredDescriptionValues
eventsarray of SellerEventyes

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

PropertyTypeRequiredDescriptionValues
chainChainyes
addressstringyes

AuthChallenge

PropertyTypeRequiredDescriptionValues
noncestringyes
messagestringyesExact text to sign, byte for byte.
expiresAtstring (date-time)yes

AuthSessionRequest

PropertyTypeRequiredDescriptionValues
chainChainyes
addressstringyes
noncestringyes
signaturestringyesEVM — 0x-hex r||s||v from personal_sign; Solana — base58 (or hex) 64-byte signature.

AuthSession

PropertyTypeRequiredDescriptionValues
bearerstringyesOpaque session credential for the Authorization header.
expiresAtstring (date-time)yes
chainChainyes
addressstringyes

Health

PropertyTypeRequiredDescriptionValues
statusstringyes"ok"
versionstringno

CredentialOfferHandoff

PropertyTypeRequiredDescriptionValues
offerobjectyesThe credential offer object, as the issuer returned it — opaque, one-shot; redeem it at once.
expiresAtstring (date-time)yesWhen the code and the credential it yields expire — the verification's own expiry at the latest. No grace.
credentialCredentialStateyes

CredentialReport

PropertyTypeRequiredDescriptionValues
statestringyesheld, or self_revoked (revoked is the older spelling of the same report)."held", "self_revoked", "revoked"
credentialRefstringnoAn opaque reference of the seller's choosing for the credential it holds. Never a key.
presentationUrlstring (uri)noWhere 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.

PropertyTypeRequiredDescriptionValues
statestringyes"none", "offered", "held", "expired", "revoked", "self_revoked"
offeredAtstring (date-time)no
expiresAtstring (date-time)noThe verification's expiry, which is the credential's.
credentialRefstringno
presentationUrlstring (uri)noThe holder's presentation endpoint registered with held (ZADQ-84); absent on the v0 path.
lastPresentationPresentationOutcomeno

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.

PropertyTypeRequiredDescriptionValues
stagestringyes"challenge", "transport", "decision"
acceptedbooleanyes
reasonstringyes
sessionIdstringnoThe verifier's session id of the decision record.
decidedAtstring (date-time)yes

SubstrateStatus

PropertyTypeRequiredDescriptionValues
substratestringyes"ok", "degraded", "unknown"
sourcestringyesWhere the state comes from — the verifier's status read, or this service's cadence accounting."verifier", "cadence"
reasonstringnoWhy the substrate is not ok, in the verifier's standing vocabulary (for example height_stale, root_expired, state_unavailable). Absent when ok.
checkedAtstring (date-time)yes
lastSuccessfulStandingReadstring (date-time)noThe last time a standing read answered with a known status. Absent before the first.
cadenceobjectyes
cadence.recheckEveryHoursintegeryesThe published cadence promise.
cadence.lastCycleAtstring (date-time)no
cadence.checkedintegernoAttestations the last cycle asked the verifier about.
cadence.withheldintegernoOf those, how many came back with an unknown standing.

BillingCheckout

PropertyTypeRequiredDescriptionValues
tierstringyesThe schedule tier, by name. Amounts are on the checkout page and in the operator agreement."attested_vendor", "regulated_compliance"

BillingHandoff

PropertyTypeRequiredDescriptionValues
urlstring (uri)yesThe hosted checkout page — open it as-is.
billingstringyes"none", "pending", "active"

BillingEventReceipt

PropertyTypeRequiredDescriptionValues
appliedbooleanyes

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.

PropertyTypeRequiredDescriptionValues
noncestringyes
signaturestringyes

EndpointAddition

PropertyTypeRequiredDescriptionValues
resourceUrlstring (uri)yes
payToPayTono
proofAddressProofno

WalletRotation

PropertyTypeRequiredDescriptionValues
payToPayToyes
proofAddressProofyes

OperatorEndpoints

PropertyTypeRequiredDescriptionValues
endpointsarray of EndpointSummaryyes

EndpointSummary

PropertyTypeRequiredDescriptionValues
sellerIdstringyes
payToPayToyes
resourceUrlstringyes
statestringyes"pending_kyc", "kyc_expired", "admitted", "rejected", "withdrawn"
attestationStatusstringno"active", "suspended", "revoked"

InterestForm

The register-interest form's fields; contact data is not among them.

PropertyTypeRequiredDescriptionValues
sidestringyesWhich side of the trust perimeter the person stands on."vendor", "agent_operator"
resource_urlstring (uri)noThe x402 resource URL a vendor sells at (optional, vendors).
agent_count_bandstringnoThe size band of an operator's fleet (optional, agent operators)."up_to_10", "up_to_100", "more_than_100"
websitestringnoTrap field for automated submitters; a person leaves it empty.

SellerIntake

PropertyTypeRequiredDescriptionValues
payToPayToyes
resourceUrlstring (uri)yes
operatorTypestringyes"person", "organisation"
segmentSegmentDeclarationyes

PayTo

Chain-qualified x402 payment address.

PropertyTypeRequiredDescriptionValues
chainChainyes
addressstringyesThe address that receives the payment; on base stored and compared lowercase.

SegmentDeclaration

PARAM-29 declaration. Recorded, not enforced in v0.

PropertyTypeRequiredDescriptionValues
annualVolumeUsdAtLeast300booleanyes
regulatedB2bCounterpartiesbooleanyes

Seller

PropertyTypeRequiredDescriptionValues
idstringyes
payToPayToyes
resourceUrlstring (uri)yes
operatorTypestringyes"person", "organisation"
statestringyeskyc_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"
billingstringyesBilling 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"
billingTierstringnoThe schedule tier chosen at checkout, by name."attested_vendor", "regulated_compliance"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
renewalall of KycHandoffnoThe 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.
attestationAttestationno
createdAtstring (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.

PropertyTypeRequiredDescriptionValues
statestringyes"pending", "approved", "rejected", "expired", "withdrawn"
handoffUrlstring (uri)no
expiresAtstring (date-time)noProofing deadline of the current session.
reusableUntilstring (date-time)noEnd 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).
freshnessBandstringnoAge 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"
reverificationDueAtstring (date-time)noWhen "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.
reverificationDuebooleannoTrue 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.

PropertyTypeRequiredDescriptionValues
sessionIdstringyes
outcomestringyes"approved", "rejected"
assuranceLevelstringnoAssurance grade met by the proofing process (e.g. "G").

Attestation

PropertyTypeRequiredDescriptionValues
idstringyes
sellerIdstringyes
assuranceLevelstringyes
statusstringyes"active", "suspended", "revoked"
statusReasonstringnoWhy 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"
freshnessBandstringnoFreshness band of the backing session, as the issuer reports it."F4", "F3", "F2", "F1"
issuedAtstring (date-time)yes
updatedAtstring (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.

PropertyTypeRequiredDescriptionValues
attestation_refstringyes32-byte opaque reference, hex.
issuer_idstringyes
chainstringyes
pay_tostringyes
resource_urlstringyes
assurance_gradeintegeryes
issued_at_unixintegeryes
signaturestringyesEd25519 by the issuer's registered status signing key, hex (64 bytes).

AttestationStatusUpdate

PropertyTypeRequiredDescriptionValues
statusstringyes"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.

PropertyTypeRequiredDescriptionValues
outcomestringyes"human_backed", "revoked", "stale", "no_signal"
subjectobjectyes
subject.chainChainyes
subject.payTostringyes
subject.resourceUrlstringyes
assuranceLevelstringno
verifiedAtstring (date-time)no
recheckEveryHoursintegeryes
signaturestringnoEd25519 over the canonical verdict payload, base64.
keyIdstringno
reasonstringnoWhy 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

PropertyTypeRequiredDescriptionValues
urlstring (uri)yes

Webhook

PropertyTypeRequiredDescriptionValues
idstringyes
urlstring (uri)yes
secretstringnoHex signing secret for X-Signature. Returned once at registration.
createdAtstring (date-time)yes

VerificationKeys

PropertyTypeRequiredDescriptionValues
keysarray of VerificationKeyyes

VerificationKey

PropertyTypeRequiredDescriptionValues
keyIdstringyesFirst 8 bytes of the public key's SHA-256, hex.
algorithmstringyes"Ed25519"
publicKeystringyesRaw 32-byte Ed25519 public key, base64.
statusstringnocurrent 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

PropertyTypeRequiredDescriptionValues
walletall of PayToyesThe wallet the operator's agent pays from — the one the wallet session proves.
operatorTypestringyesperson 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.

PropertyTypeRequiredDescriptionValues
idstringyesThe operator's opaque reference (aop_…).
walletall of PayToyesThe wallet the operator's agent pays from.
operatorTypestringyes"person", "organisation"
statestringyespending_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"
billingstringyesThe 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"
kycSessionIdstringnoOpaque session handle at the KYC boundary. Not identity data.
kycKycHandoffno
credentialCredentialStateno
agentsAgentCountsno
createdAtstring (date-time)yes

AgentOperatorCredentialReport

PropertyTypeRequiredDescriptionValues
statestringyesheld, or self_revoked (revoked is the older spelling of the same report)."held", "self_revoked", "revoked"
credentialRefstringnoAn 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.

PropertyTypeRequiredDescriptionValues
contentSha256stringyesThe sha256 of the vocabulary content it was read under (hex).
versionstringno
scopesarray of stringyes
custodyClassesarray of stringyes

AgentLimit

The per-transaction limit the issuer signs into the agent's delegation.

PropertyTypeRequiredDescriptionValues
amountstringyesA positive decimal amount (up to 8 decimals), as a string.
unitstringyesThe 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.

PropertyTypeRequiredDescriptionValues
scopearray of stringyesScope values of the vocabulary (GET /v0/agent-scopes), each once.
limitAgentLimityes
custodyClassstringyesThe key-custody class of the agent's key, from the vocabulary.
leaseDaysintegeryesThe 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).

PropertyTypeRequiredDescriptionValues
idstringyesThe agent's pseudonymous handle (agt_…), assigned by ZadQ.
statestringyes"offered", "held", "revoked", "expired"
reasonstringno"operator_revoked", "principal_revoked", "self_revoked", "lease_expired"
delegationAgentDelegationyes
leaseUntilstring (date-time)yesThe lease's end as the issuer set it.
generationintegeryes1 at the mint, one more per renewal (replacement).
credentialRefstringnoThe operator's own opaque reference for the held credential. Never a key.
feeDeclaredbooleanyesWhether ZadQ's fee declaration for the current generation is recorded.
offeredAtstring (date-time)yes
createdAtstring (date-time)yes

Agents

PropertyTypeRequiredDescriptionValues
itemsarray of Agentyes

AgentOfferHandoff

PropertyTypeRequiredDescriptionValues
offerobjectyesThe agent offer as the issuer returned it — opaque, one-shot; the operator's agent client redeems it at once.
expiresAtstring (date-time)yesWhen the agent's credential expires — its lease's end or the principal's verification, whichever is first.
agentAgentyes

AgentCredentialReport

PropertyTypeRequiredDescriptionValues
statestringyes"held", "self_revoked"
credentialRefstringnoAn 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.

PropertyTypeRequiredDescriptionValues
activeintegeryes

MetricsReport

PropertyTypeRequiredDescriptionValues
schemastringyesThe wire shape's name (published-metrics/v1).
computedAtstring (date-time)yes
asOfstring (date)yesThe cut-off — the last day of the last complete month (UTC). Partial months are never published.
periodMetricsPeriodno
cadenceobjectyes
cadence.publicationstringyesmonthly.
cadence.snapshotstringyesquarterly, aligned to the two-consecutive-quarters clock.
relatedPartybooleanyesAlways true — the operator's position is a property of the dataset.
operatorMetricsOperatoryes
seriesarray of MetricsSeriesyes
contextNotestringyes

MetricsPeriod

Inclusive month range, YYYY-MM at both ends. Absent while no complete month holds a record.

PropertyTypeRequiredDescriptionValues
fromstringyes
tostringyes

MetricsOperator

PropertyTypeRequiredDescriptionValues
servicestringyes
operatorstringyes
relatedPartybooleanyes
positionstringyes
disclosurestringyesThe canonical metrics disclosure, embedded in every report.

MetricsSeries

PropertyTypeRequiredDescriptionValues
idstringyes"sellers_attested", "retention_r90", "verifications_free_path", "independent_verifiers", "anchor_service_revenue"
titlestringyes
disciplinestringyes"registry-based", "aggregate", "context"
definitionstringyes
relatedPartybooleanyesAlways true, on every series.
periodMetricsPeriodno
availablebooleanyesFalse when the records hold no source for the series; unavailableReason says why and points is empty.
unavailableReasonstringno
pointsarray of MetricsPointyes

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).

PropertyTypeRequiredDescriptionValues
periodstringyesYYYY-MM.
cumulativeintegerno
activeintegerno
independentintegerno
cohortintegerno
retainedintegerno
rnumbernoRatio, two decimals.
countintegerno
registrationsintegerno

Problem

RFC 9457 problem details.

PropertyTypeRequiredDescriptionValues
typestringno
titlestringno
statusintegerno
detailstringno
instancestringno