{"openapi":"3.0.0","info":{"title":"IXO Credentials","version":"0.1.0","description":"Offer inbox, presentation broker, status-list registry and public catalog for IXO Credentials (SD-JWT VC, OpenID4VCI, OpenID4VP, Token Status List). Errors are `{ \"error\": \"<code>\", \"message\": \"<plain sentence>\", \"reference\": \"<request id>\" }` (quote `reference` to support; it is also the `X-Request-Id` header); 429 and 503 carry `Retry-After`. Authenticated routes are rate limited per principal. The per-IP limit (an IPv6 address by its /64) never stands in front of a valid call: only FAILED work counts, after the work (a token that fails authentication, a public read that fails with a 4xx, an unknown route), and over the budget the answer to that failure is 429. A successful public read, a valid token and a DID document lookup are never refused because of other callers on the same IP. A did:web must be written in its canonical form; every DID is its own sender, exactly as written (`sender_key` is the DID of the sender; a did:web on a subdomain or with a path is its own sender). `HEAD` on an authenticated route is 405."},"components":{"securitySchemes":{"Bearer":{"type":"http","scheme":"bearer","description":"UCAN invocation (base64 CAR, ≤ 24 KB, at most 12 blocks, delegation chain at most 4 deep, at most 4 proofs per UCAN, each proof once) addressed to this worker's did:web (/.well-known/did.json), lifetime ≤ 300 s, single use, with a fact `{ method, path }` naming this request (method upper-case, full path without the query; 401 without it). Every signer in the chain, and so the principal, is a did:ixo or did:web (a did:key is refused). Resource `ixo:credentials` (act as yourself; an entity signs with a key on its own DID document) or `ixo:credentials/<did:ixo:entity:…>` (a controller or a DAO member with voting weight > 0 acting as the entity: catalog writes only). Delegations in the chain are checked against the UCAN store's revocation registry."}},"schemas":{"Health":{"type":"object","properties":{"status":{"type":"string","enum":["ok","degraded"]},"version":{"type":"string"},"timestamp":{"type":"string"},"environment":{"type":"string"}},"required":["status","version","timestamp","environment"]},"OfferDepositResponse":{"type":"object","properties":{"id":{"type":"string","example":"ofr_6f1c…"},"status":{"type":"string","enum":["pending","accepted","declined","retracted","expired"]},"consent":{"type":"string","enum":["valid","none"],"description":"'valid' while the verified consent has not expired; 'none' without one or once it expired."},"display_dropped":{"type":"array","items":{"type":"string"},"description":"Members that were NOT stored. Catalog entries: card-look members that are not valid (a colour that is not `#rrggbb`, an image address that is not https on the default port, has a query string or fragment, is not on a public host or is over 512 characters) and unknown `display` members, as paths (`logo_uri`, `color`, `types.0.display.text_color`). Offers: every `display` member other than `name`, `vct` and `description` (the card look belongs in the catalog entry). At most 20 names. Empty when everything was kept.","example":["text_color"]}},"required":["id","status","consent","display_dropped"]},"Error":{"type":"object","properties":{"error":{"type":"string","example":"not_found"},"message":{"type":"string","example":"We couldn't find that. Check the link; it may have been removed."},"reference":{"type":"string","example":"0b6f3c2e-2c1d-4f5e-9a51-6f1f3f2a9d10","description":"The request id (also the `X-Request-Id` header). Quote it to support: our logs carry the same id."}},"required":["error","message"]},"OfferDeposit":{"type":"object","properties":{"subject":{"type":"string","pattern":"^did:ixo:ixo1[02-9ac-hj-np-z]{38}$","example":"did:ixo:ixo1qqqsyqcyq5rqwzqfpg9scrgwpugpzysn4uq0hv","description":"The holder (a did:ixo person)."},"ref":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:~-]+$","example":"vid_01JC9X","description":"Your reference. Same (issuer, ref) and same body answers the same offer (200)."},"credential_offer_uri":{"type":"string","minLength":1,"maxLength":2048,"description":"Preferred (OpenID4VCI §4.1.3): an https URL at the issuer that returns the credential offer JSON with a fresh pre-authorized code. Send exactly one of credential_offer_uri and credential_offer. The worker never fetches it."},"credential_offer":{"$ref":"#/components/schemas/CredentialOffer"},"display":{"$ref":"#/components/schemas/OfferDisplayInput"},"expires_at":{"type":"integer","minimum":0,"exclusiveMinimum":true,"example":1793966400,"description":"When the offer lapses, unix seconds (future, at most 30 days ahead)."},"consent":{"type":"string","minLength":1,"maxLength":8192,"description":"Base64 CAR UCAN delegation subject -> issuer, `credentials/offer` on `ixo:credentials`, `nb.vct` containing display.vct, expiring within 366 days. Verified; invalid = 400."}},"required":["subject","ref","display","expires_at"],"additionalProperties":false},"CredentialOffer":{"type":"object","properties":{"credential_issuer":{"type":"string","minLength":1,"maxLength":2048},"credential_configuration_ids":{"type":"array","items":{"type":"string","minLength":1,"maxLength":256},"minItems":1,"maxItems":20},"grants":{"type":"object","properties":{"urn:ietf:params:oauth:grant-type:pre-authorized_code":{"type":"object","properties":{"pre-authorized_code":{"type":"string","minLength":1,"maxLength":2048},"tx_code":{"type":"object","properties":{"input_mode":{"type":"string","enum":["numeric","text"]},"length":{"type":"integer","minimum":1,"maximum":64},"description":{"type":"string","maxLength":300}},"additionalProperties":false}},"required":["pre-authorized_code"],"additionalProperties":false}},"required":["urn:ietf:params:oauth:grant-type:pre-authorized_code"],"additionalProperties":false}},"required":["credential_issuer","credential_configuration_ids","grants"],"additionalProperties":false,"description":"The offer by value (pre-authorized code grant). Send exactly one of credential_offer_uri and credential_offer."},"OfferDisplayInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"vct":{"type":"string","minLength":1,"maxLength":512},"description":{"type":"string","maxLength":500}},"required":["name","vct"],"additionalProperties":{"nullable":true},"description":"Shown to the person. Text: control, bidi and zero-width characters are removed and whitespace is collapsed before storing (an empty or too long name is a 400). Nothing else is accepted here: the card look, the issuer's name and its logo come only from the issuer's catalog entry (`PUT /v1/catalog/issuers/{did}`), so an offer cannot change them. Any other member (`background_color`, `text_color`, `logo_uri`, `background_image_uri` included) is dropped, never a 400, and listed in the answer's `display_dropped`."},"OfferList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/OfferSummary"}},"next_cursor":{"type":"string","nullable":true}},"required":["items","next_cursor"]},"OfferSummary":{"type":"object","properties":{"id":{"type":"string"},"issuer":{"type":"string"},"sender_key":{"type":"string","description":"The sender key: the issuer's DID exactly as written (every DID is its own sender; equal to `issuer`). Look the sender's name, mark and card look up by it (`GET /v1/catalog/issuers/{sender_key}`), and block it with `POST /v1/blocks`.","example":"did:web:example.com"},"subject":{"type":"string"},"ref":{"type":"string"},"credential_issuer":{"type":"string","nullable":true,"description":"From an offer sent by value; null for an offer sent by reference (the worker does not fetch it)."},"vct":{"type":"string"},"display":{"$ref":"#/components/schemas/OfferDisplay"},"status":{"type":"string","enum":["pending","accepted","declined","retracted","expired"]},"consent":{"type":"string","enum":["valid","none"],"description":"'valid' while the verified consent has not expired (re-checked on every read); else 'none'."},"created_at":{"type":"integer"},"expires_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","issuer","sender_key","subject","ref","credential_issuer","vct","display","status","consent","created_at","expires_at","updated_at"]},"OfferDisplay":{"type":"object","properties":{"name":{"type":"string"},"vct":{"type":"string"},"description":{"type":"string"}},"required":["name","vct"],"description":"Exactly as stored: the cleaned text."},"OfferView":{"allOf":[{"$ref":"#/components/schemas/OfferSummary"},{"type":"object","properties":{"credential_offer_uri":{"type":"string","description":"Subject only, while pending, when the offer was sent by reference."},"credential_offer":{"allOf":[{"$ref":"#/components/schemas/CredentialOffer"},{"description":"Subject only, while pending, when the offer was sent by value."}]},"consent_token":{"type":"string","description":"Subject only, while pending and while the consent has not expired: the verified consent delegation. Before storing a credential without asking, the wallet must check that the issued credential's `iss` and `vct` match the consent (audience and `nb.vct`)."}}}]},"OfferStatusResponse":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending","accepted","declined","retracted","expired"]}},"required":["id","status"]},"OfferStatusUpdate":{"type":"object","properties":{"status":{"type":"string","enum":["accepted","declined","retracted"],"description":"Subject: 'accepted' | 'declined'. Issuer: 'retracted' (a no-op once the offer is no longer pending)."}},"required":["status"],"additionalProperties":false},"RequestDepositResponse":{"type":"object","properties":{"id":{"type":"string","example":"req_9a1e…"},"request_uri":{"type":"string","description":"OpenID4VP request_uri: GET it for the request object."},"wallet_url":{"type":"string","description":"Where the person answers in the Auth Hub (when HUB_URL is configured)."},"openid4vp_url":{"type":"string","description":"The standard OpenID4VP Authorization Request for any wallet: `openid4vp://?client_id=<client_id>&request_uri=<request_uri>` (show it as a QR code or link).","example":"openid4vp://?client_id=decentralized_identifier%3Adid%3Aweb%3Averifier.example&request_uri=https%3A%2F%2Fcredentials.example%2Fv1%2Frequests%2Freq_9a1e%2Fobject"},"status":{"type":"string","enum":["pending","answered","declined","cancelled","expired"]}},"required":["id","request_uri","openid4vp_url","status"]},"RequestDeposit":{"type":"object","properties":{"subject":{"type":"string","pattern":"^did:ixo:ixo1[02-9ac-hj-np-z]{38}$","example":"did:ixo:ixo1qqqsyqcyq5rqwzqfpg9scrgwpugpzysn4uq0hv","description":"The holder (a did:ixo person)."},"ref":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[A-Za-z0-9._:~-]+$","example":"vid_01JC9X","description":"Your reference. Same (verifier, ref) and same body answers the same request (200)."},"request":{"type":"string","minLength":1,"maxLength":16384,"description":"The signed OpenID4VP request object (typ oauth-authz-req+jwt), client_id = decentralized_identifier:<your DID>. It must carry `exp` (an IXO profile rule for deposits; the stored expiry never outlives it)."},"purpose":{"type":"string","minLength":1,"maxLength":140,"description":"Why you ask, shown to the person (at most 140 characters after control, bidi and zero-width characters are removed and whitespace is collapsed)."},"expires_at":{"type":"integer","minimum":0,"exclusiveMinimum":true,"example":1793966400,"description":"Unix seconds. Default 15 minutes from now, at most 24 hours, never later than the request object's exp."}},"required":["subject","ref","request","purpose"],"additionalProperties":false},"RequestList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/RequestSummary"}},"next_cursor":{"type":"string","nullable":true}},"required":["items","next_cursor"]},"RequestSummary":{"type":"object","properties":{"id":{"type":"string"},"verifier":{"type":"string"},"sender_key":{"type":"string","description":"The sender key: the verifier's DID exactly as written (every DID is its own sender; equal to `verifier`). Look the sender's name, mark and card look up by it (`GET /v1/catalog/verifiers/{sender_key}`), and block it with `POST /v1/blocks`.","example":"did:web:example.com"},"subject":{"type":"string"},"ref":{"type":"string"},"client_id":{"type":"string"},"purpose":{"type":"string"},"summary":{"type":"array","items":{"$ref":"#/components/schemas/DcqlQuerySummary"}},"response_host":{"type":"string"},"status":{"type":"string","enum":["pending","answered","declined","cancelled","expired"]},"created_at":{"type":"integer"},"expires_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","verifier","sender_key","subject","ref","client_id","purpose","summary","response_host","status","created_at","expires_at","updated_at"]},"DcqlQuerySummary":{"type":"object","properties":{"id":{"type":"string"},"vctValues":{"type":"array","items":{"type":"string"}},"claims":{"type":"array","items":{"type":"string"}},"issuers":{"type":"array","items":{"type":"string"}}},"required":["id","vctValues","claims"]},"RequestHolderView":{"allOf":[{"$ref":"#/components/schemas/RequestSummary"},{"type":"object","properties":{"request":{"type":"string","description":"The signed request object."}},"required":["request"]}]},"RequestVerifierView":{"type":"object","properties":{"id":{"type":"string"},"ref":{"type":"string"},"status":{"type":"string","enum":["pending","answered","declined","cancelled","expired"]},"created_at":{"type":"integer"},"expires_at":{"type":"integer"},"updated_at":{"type":"integer"}},"required":["id","ref","status","created_at","expires_at","updated_at"]},"RequestStatusResponse":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending","answered","declined","cancelled","expired"]}},"required":["id","status"]},"RequestStatusUpdate":{"type":"object","properties":{"status":{"type":"string","enum":["answered","declined","cancelled"],"description":"Subject: 'answered' | 'declined'. Verifier: 'cancelled'."}},"required":["status"],"additionalProperties":false},"BlockCreateResponse":{"allOf":[{"$ref":"#/components/schemas/Block"},{"type":"object","properties":{"closed":{"type":"integer","description":"How many of this sender's waiting offers and requests for you were closed as declined by this call (0 when it was already blocked)."}},"required":["closed"]}]},"Block":{"type":"object","properties":{"sender":{"type":"string","description":"The blocked DID, exactly as written (a did:ixo, or a did:web in canonical form). Read the catalog entry of this DID to show the party.","example":"did:web:issuer.example.com"},"created_at":{"type":"integer","description":"When it was blocked, unix seconds."}},"required":["sender","created_at"]},"BlockCreate":{"type":"object","properties":{"sender":{"type":"string","maxLength":256,"pattern":"^did:(ixo|web):[A-Za-z0-9._:%-]+$","example":"did:web:issuer.example.com","description":"The issuer or verifier to block (a did:ixo, or a did:web in canonical form). Exactly this DID is blocked: `did:web:example.com`, `did:web:a.example.com` and `did:web:example.com:b` are three senders."}},"required":["sender"],"additionalProperties":false},"BlockRemoveResponse":{"type":"object","properties":{"sender":{"type":"string","description":"The DID (as in `Block.sender`)."},"removed":{"type":"boolean","description":"False when the sender was not blocked (nothing changed)."}},"required":["sender","removed"]},"BlockRemove":{"type":"object","properties":{"sender":{"type":"string","maxLength":256,"pattern":"^did:(ixo|web):[A-Za-z0-9._:%-]+$","example":"did:web:issuer.example.com","description":"The issuer or verifier to block (a did:ixo, or a did:web in canonical form). Exactly this DID is blocked: `did:web:example.com`, `did:web:a.example.com` and `did:web:example.com:b` are three senders."}},"required":["sender"],"additionalProperties":false},"BlockList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Block"}},"next_cursor":{"type":"string","nullable":true}},"required":["items","next_cursor"]},"StatusListPublished":{"type":"object","properties":{"uri":{"type":"string"},"iat":{"type":"integer"},"exp":{"type":"integer"}},"required":["uri","iat","exp"]},"CatalogIssuerList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/CatalogIssuerRecord"}},"next_cursor":{"type":"string","nullable":true}},"required":["items","next_cursor"]},"CatalogIssuerRecord":{"type":"object","properties":{"sender_key":{"type":"string","description":"The DID this entry belongs to (the same as `entry.did`; every DID is its own sender, so a subdomain or path did:web has its own entry). One entry per DID and kind.","example":"did:web:example.com"},"entry":{"$ref":"#/components/schemas/CatalogIssuerEntry"},"updated_at":{"type":"integer"}},"required":["sender_key","entry","updated_at"]},"CatalogIssuerEntry":{"type":"object","properties":{"did":{"type":"string","maxLength":256,"pattern":"^did:(ixo|web):[A-Za-z0-9._:%-]+$","example":"did:ixo:ixo1qqqsyqcyq5rqwzqfpg9scrgwpugpzysn4uq0hv"},"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":1000},"url":{"type":"string","minLength":1,"maxLength":2048},"credential_issuer":{"type":"string","minLength":1,"maxLength":2048},"types":{"type":"array","items":{"$ref":"#/components/schemas/CatalogIssuerType"},"maxItems":50},"logo_uri":{"type":"string","format":"uri","maxLength":512,"description":"The issuer's mark (a small image). https on the default port with no query string and no fragment (not even an empty `?` or `#`), on a public host, no user name or password, at most 512 characters (http and any port on a loopback host only in local development). png, jpeg or webp. Dropped (and listed in `display_dropped`) when not valid. Never fetched by this worker.","example":"https://issuer.example/mark.png"},"color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","description":"The issuer's mark colour, `#rrggbb` (stored lower case). Dropped (and listed in `display_dropped`) when not valid.","example":"#0b6b7e"}},"required":["did","name","credential_issuer","types"],"additionalProperties":false,"description":"What this DID says it issues. A claim by the DID, not a trust list: the worker does not vouch for names or URLs. Verifiers pin issuer DIDs; display text is cleaned of control, bidi and zero-width characters."},"CatalogIssuerType":{"type":"object","properties":{"vct":{"type":"string","minLength":1,"maxLength":512},"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":1000},"type_metadata_uri":{"type":"string","minLength":1,"maxLength":2048},"claims":{"type":"array","items":{"$ref":"#/components/schemas/CatalogClaim"},"maxItems":100},"display":{"$ref":"#/components/schemas/CardDisplay"}},"required":["vct","name","claims"],"additionalProperties":false},"CatalogClaim":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"label":{"type":"string","minLength":1,"maxLength":100},"sd":{"type":"string","enum":["always","allowed","never"]}},"required":["name","label","sd"],"additionalProperties":false},"CardDisplay":{"type":"object","properties":{"background_color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","description":"Card background, `#rrggbb` only (OpenID4VCI `background_color`). Stored lower case.","example":"#0b6b7e"},"text_color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","description":"Card text, `#rrggbb` only (OpenID4VCI `text_color`). Wallets ignore it when its contrast with the background is below 4.5:1.","example":"#ffffff"},"logo_uri":{"type":"string","format":"uri","maxLength":512,"description":"The issuer's mark, shown small (OpenID4VCI `logo.uri`). https on the default port with no query string and no fragment (not even an empty `?` or `#`), on a public host, no user name or password, at most 512 characters (http and any port on a loopback host only in local development). png, jpeg or webp (wallets refuse anything else, SVG included). Never fetched by this worker.","example":"https://issuer.example/mark.png"},"background_image_uri":{"type":"string","format":"uri","maxLength":512,"description":"An image that covers the card (OpenID4VCI `background_image.uri`). Same rules as `logo_uri`.","example":"https://issuer.example/card.jpg"}},"additionalProperties":{"nullable":true},"description":"The card look for credentials of this type, used by wallets when the offer carried none. Every member optional; a member that is not valid, and any unknown member, is dropped (never a 400) and listed in the answer's `display_dropped`. Stored and returned with valid members only; left out when none is valid."},"CatalogIssuerPutResponse":{"allOf":[{"$ref":"#/components/schemas/CatalogIssuerRecord"},{"type":"object","properties":{"display_dropped":{"type":"array","items":{"type":"string"},"description":"Members that were NOT stored. Catalog entries: card-look members that are not valid (a colour that is not `#rrggbb`, an image address that is not https on the default port, has a query string or fragment, is not on a public host or is over 512 characters) and unknown `display` members, as paths (`logo_uri`, `color`, `types.0.display.text_color`). Offers: every `display` member other than `name`, `vct` and `description` (the card look belongs in the catalog entry). At most 20 names. Empty when everything was kept.","example":["text_color"]}},"required":["display_dropped"]}]},"CatalogVerifierList":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/CatalogVerifierRecord"}},"next_cursor":{"type":"string","nullable":true}},"required":["items","next_cursor"]},"CatalogVerifierRecord":{"type":"object","properties":{"sender_key":{"type":"string","description":"The DID this entry belongs to (the same as `entry.did`; every DID is its own sender, so a subdomain or path did:web has its own entry). One entry per DID and kind.","example":"did:web:example.com"},"entry":{"$ref":"#/components/schemas/CatalogVerifierEntry"},"updated_at":{"type":"integer"}},"required":["sender_key","entry","updated_at"]},"CatalogVerifierEntry":{"type":"object","properties":{"did":{"type":"string","maxLength":256,"pattern":"^did:(ixo|web):[A-Za-z0-9._:%-]+$","example":"did:ixo:ixo1qqqsyqcyq5rqwzqfpg9scrgwpugpzysn4uq0hv"},"name":{"type":"string","minLength":1,"maxLength":100},"description":{"type":"string","maxLength":1000},"url":{"type":"string","minLength":1,"maxLength":2048},"accepts":{"type":"array","items":{"$ref":"#/components/schemas/CatalogVerifierAccept"},"maxItems":50},"logo_uri":{"type":"string","format":"uri","maxLength":512,"description":"The verifier's mark (a small image). https on the default port with no query string and no fragment (not even an empty `?` or `#`), on a public host, no user name or password, at most 512 characters (http and any port on a loopback host only in local development). png, jpeg or webp. Dropped (and listed in `display_dropped`) when not valid. Never fetched by this worker.","example":"https://issuer.example/mark.png"},"color":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$","description":"The verifier's mark colour, `#rrggbb` (stored lower case). Dropped (and listed in `display_dropped`) when not valid.","example":"#0b6b7e"}},"required":["did","name","accepts"],"additionalProperties":false,"description":"What this DID says it accepts. A claim by the DID, not a trust list: the worker does not vouch for names. Display text is cleaned of control, bidi and zero-width characters."},"CatalogVerifierAccept":{"type":"object","properties":{"purpose":{"type":"string","minLength":1,"maxLength":140},"vct":{"type":"string","minLength":1,"maxLength":512},"claims":{"type":"array","items":{"type":"string","minLength":1,"maxLength":100},"maxItems":100},"issuers":{"type":"array","items":{"type":"string","maxLength":256,"pattern":"^did:(ixo|web):[A-Za-z0-9._:%-]+$","example":"did:ixo:ixo1qqqsyqcyq5rqwzqfpg9scrgwpugpzysn4uq0hv"},"maxItems":50}},"required":["purpose","vct","claims","issuers"],"additionalProperties":false},"CatalogVerifierPutResponse":{"allOf":[{"$ref":"#/components/schemas/CatalogVerifierRecord"},{"type":"object","properties":{"display_dropped":{"type":"array","items":{"type":"string"},"description":"Members that were NOT stored. Catalog entries: card-look members that are not valid (a colour that is not `#rrggbb`, an image address that is not https on the default port, has a query string or fragment, is not on a public host or is over 512 characters) and unknown `display` members, as paths (`logo_uri`, `color`, `types.0.display.text_color`). Offers: every `display` member other than `name`, `vct` and `description` (the card look belongs in the catalog entry). At most 20 names. Empty when everything was kept.","example":["text_color"]}},"required":["display_dropped"]}]}},"parameters":{}},"paths":{"/health":{"get":{"tags":["Service"],"summary":"Health check","description":"Answers 200 when D1 answers, 503 otherwise. `environment` is the configured ENVIRONMENT (`local`, `devnet`, `testnet`, `mainnet`), or `unknown` (production rules apply). Only a failed read counts against the IP (SPEC §17.1).","responses":{"200":{"description":"Healthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"503":{"description":"D1 is unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}}}}},"/v1/offers":{"post":{"tags":["Offers"],"summary":"Deposit a credential offer (issuer)","description":"Ability `credentials/issue`, resource `ixo:credentials` (an entity issuer signs with a key on its own DID document; a controller's `ixo:credentials/<entity>` token is refused here). `subject` must be a did:ixo person that exists on chain (else 400 `unknown_subject`). Exactly one of `credential_offer_uri` (preferred; never fetched by the worker) or `credential_offer` (by value). Idempotent on (issuer, ref): the same body answers 200 with the same offer; a different body with a used ref is 409. URLs must be https on a public host. `expires_at` is unix seconds. A `consent` delegation, when present, is verified, including against the UCAN store's revocation registry (invalid or revoked = 400). The same rule for every issuer (429 `too_many_pending`): at most 2 waiting offers per person from one issuer (SENDER_PERSON_PENDING_CAP), and at most 2000 waiting offers in total (SENDER_PENDING_CAP); requests are counted separately. Every issuer DID is its own sender, exactly as written (a did:web on a subdomain or with a path is its own sender, with its own caps). A person who blocked your DID refuses the offer: 403 `blocked`, nothing stored, no webhook. A repeat of an offer you already deposited (same ref, same body) is answered 200 even when a cap is full. `display` is `{ name, vct, description? }`; text is cleaned (control, bidi and zero-width characters removed, whitespace collapsed). The card look, your name and your logo come only from your catalog entry: any other `display` member (the look members included) is dropped, never a 400, and listed in `display_dropped`. Emits webhook `offer.created`. Body ≤ 16 KB.","security":[{"Bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferDeposit"}}}},"responses":{"200":{"description":"Same offer already stored (idempotent)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferDepositResponse"}}}},"201":{"description":"Offer stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferDepositResponse"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicts with what is stored: `ref_in_use` (same ref, different body), `stale` (a status list whose `iat` is not newer than the stored one), `too_many_lists` (an issuer already has 20 status lists) or `too_many_blocks` (a person already blocks 500 senders)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body over the size cap","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Offers"],"summary":"List the holder's offers (holder)","description":"Ability `credentials/hold`; the principal is the subject. Newest first, keyset pagination (`limit` ≤ 100, `cursor`). Summaries never carry the pre-authorized code.","security":[{"Bearer":[]}],"parameters":[{"schema":{"type":"string","enum":["pending","accepted","declined","retracted","expired"],"default":"pending"},"required":false,"name":"status","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"example":50,"description":"Items per page (1–100)."},"required":false,"description":"Items per page (1–100).","name":"limit","in":"query"},{"schema":{"type":"string","maxLength":512,"description":"Opaque `next_cursor` from the previous page."},"required":false,"description":"Opaque `next_cursor` from the previous page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"One page of offers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferList"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"`HEAD` on an authenticated route (`method_not_allowed`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/offers/{id}":{"get":{"tags":["Offers"],"summary":"Read one offer (issuer or holder)","description":"Issuer (`credentials/issue`, principal = issuer): the summary, never the code. Holder (`credentials/hold`, principal = subject): the summary plus `credential_offer_uri` or `credential_offer` (whichever was deposited, while pending) and `consent_token` (while pending and while the consent has not expired). Anyone else: 404.","security":[{"Bearer":[]}],"parameters":[{"schema":{"type":"string","maxLength":64},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The offer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferView"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"`HEAD` on an authenticated route (`method_not_allowed`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/offers/{id}/status":{"post":{"tags":["Offers"],"summary":"Answer or retract an offer","description":"Holder (`credentials/hold`): 'accepted' or 'declined' a pending offer (410 once it is no longer pending). Issuer (`credentials/issue`): 'retracted' (emits webhook `offer.retracted`; a no-op answering the current status once the offer is no longer pending). Setting the status it already has is a no-op. Terminal states wipe the stored credential_offer.","security":[{"Bearer":[]}],"parameters":[{"schema":{"type":"string","maxLength":64},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferStatusUpdate"}}}},"responses":{"200":{"description":"The status now","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferStatusResponse"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"No longer pending","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body over the size cap","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/requests":{"post":{"tags":["Requests"],"summary":"Deposit a presentation request (verifier)","description":"Ability `credentials/verify`, resource `ixo:credentials` (an entity verifier signs with a key on its own DID document). The request object is verified: typ oauth-authz-req+jwt, signature by the key `kid` names on the client DID's document, `client_id` = `decentralized_identifier:<your DID>`, `exp` present and not passed (IXO deposit rule), response_type vp_token, response_mode direct_post.jwt, https response_uri on a public host, a nonce, and DCQL inside the profile's subset. `subject` must be a did:ixo person that exists on chain (else 400 `unknown_subject`). Idempotent on (verifier, ref). The same rule for every verifier (429 `too_many_pending`): at most 2 waiting requests per person from one verifier (SENDER_PERSON_PENDING_CAP), and at most 2000 waiting requests in total (SENDER_PENDING_CAP); offers are counted separately. Every verifier DID is its own sender, exactly as written (a did:web on a subdomain or with a path is its own sender, with its own caps). A person who blocked your DID refuses the request: 403 `blocked`, nothing stored, no webhook. A repeat of a request you already deposited (same ref, same body) is answered 200 even when a cap is full. `purpose` (your own words for why you ask) is cleaned (control, bidi and zero-width characters removed, whitespace collapsed). Answers `request_uri`, the standard `openid4vp_url` (`openid4vp://?client_id=…&request_uri=…`) for any wallet, and `wallet_url` for the Auth Hub when configured. Emits webhook `request.created`. Body ≤ 18 KB (JWT ≤ 16 KB).","security":[{"Bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestDeposit"}}}},"responses":{"200":{"description":"Same request already stored (idempotent)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestDepositResponse"}}}},"201":{"description":"Request stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestDepositResponse"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicts with what is stored: `ref_in_use` (same ref, different body), `stale` (a status list whose `iat` is not newer than the stored one), `too_many_lists` (an issuer already has 20 status lists) or `too_many_blocks` (a person already blocks 500 senders)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body over the size cap","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Requests"],"summary":"List the holder's requests (holder)","description":"Ability `credentials/hold`; the principal is the subject. Newest first, keyset pagination (`limit` ≤ 100, `cursor`).","security":[{"Bearer":[]}],"parameters":[{"schema":{"type":"string","enum":["pending","answered","declined","cancelled","expired"],"default":"pending"},"required":false,"name":"status","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"example":50,"description":"Items per page (1–100)."},"required":false,"description":"Items per page (1–100).","name":"limit","in":"query"},{"schema":{"type":"string","maxLength":512,"description":"Opaque `next_cursor` from the previous page."},"required":false,"description":"Opaque `next_cursor` from the previous page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"One page of requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestList"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"`HEAD` on an authenticated route (`method_not_allowed`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/requests/{id}/object":{"get":{"tags":["Requests"],"summary":"The request object (OpenID4VP request_uri, public)","description":"The signed request object as `application/oauth-authz-req+jwt` (`Cache-Control: no-store`), while the request is pending. 404 once it is answered, declined, cancelled or expired. Only a failed read counts against the IP (SPEC §17.1).","parameters":[{"schema":{"type":"string","maxLength":64},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The request object","content":{"application/oauth-authz-req+jwt":{"schema":{"type":"string"}}}},"404":{"description":"Not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/requests/{id}":{"get":{"tags":["Requests"],"summary":"Read one request (verifier or holder)","description":"Verifier (`credentials/verify`, principal = verifier): status only. Holder (`credentials/hold`, principal = subject): the summary plus the signed request object. Anyone else: 404.","security":[{"Bearer":[]}],"parameters":[{"schema":{"type":"string","maxLength":64},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"The request (holder view or verifier view)","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RequestHolderView"},{"$ref":"#/components/schemas/RequestVerifierView"}]}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"`HEAD` on an authenticated route (`method_not_allowed`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/requests/{id}/status":{"post":{"tags":["Requests"],"summary":"Answer, decline or cancel a request","description":"Holder (`credentials/hold`): 'answered' or 'declined'. Verifier (`credentials/verify`): 'cancelled' (emits webhook `request.cancelled`). Only a pending request changes (410 otherwise); setting the status it already has is a no-op.","security":[{"Bearer":[]}],"parameters":[{"schema":{"type":"string","maxLength":64},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestStatusUpdate"}}}},"responses":{"200":{"description":"The status now","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestStatusResponse"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"No longer pending","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body over the size cap","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/blocks":{"post":{"tags":["Blocks"],"summary":"Block a sender (holder)","description":"Ability `credentials/hold`; the principal is the person (a did:ixo person, else 403). From now on every offer and request this sender deposits for you is refused (the sender gets 403 `blocked`: \"This person is not accepting offers or requests from you.\"; nothing is stored and no webhook is sent). One list for issuers and verifiers. Every DID is its own sender, blocked exactly as written: blocking `did:web:a.example.com` does not block `did:web:b.example.com`, `did:web:example.com` or `did:web:example.com:x` (block each one you want to refuse); `sender` in the answer is the DID you named. A did:web must be in canonical form (lower-case host, no trailing dot, no percent-encoding in the host except `%3A` before a port, an IDN in punycode), else 400. Idempotent: blocking a sender that is already blocked answers the stored block. In the same transaction, every waiting offer and request from this sender for you is closed as declined (`closed` says how many) and the Hub is told (webhook `offer.retracted` / `request.cancelled`: the item is gone from the inbox; its status is `declined`). At most 500 blocks per person (409 `too_many_blocks`). Body ≤ 1 KB.","security":[{"Bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlockCreate"}}}},"responses":{"200":{"description":"The sender is blocked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlockCreateResponse"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicts with what is stored: `ref_in_use` (same ref, different body), `stale` (a status list whose `iat` is not newer than the stored one), `too_many_lists` (an issuer already has 20 status lists) or `too_many_blocks` (a person already blocks 500 senders)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body over the size cap","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"tags":["Blocks"],"summary":"List the blocked senders (holder)","description":"Ability `credentials/hold`; the principal is the person. Newest first, keyset pagination (`limit` ≤ 100, `cursor`). `sender` is the blocked DID exactly as it was blocked (show the party by reading its catalog entry with this DID).","security":[{"Bearer":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"example":50,"description":"Items per page (1–100)."},"required":false,"description":"Items per page (1–100).","name":"limit","in":"query"},{"schema":{"type":"string","maxLength":512,"description":"Opaque `next_cursor` from the previous page."},"required":false,"description":"Opaque `next_cursor` from the previous page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"One page of blocks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlockList"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"`HEAD` on an authenticated route (`method_not_allowed`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/blocks/remove":{"post":{"tags":["Blocks"],"summary":"Unblock a sender (holder)","description":"Ability `credentials/hold`; the principal is the person. The sender may deposit offers and requests for you again (items closed by the block stay closed). Idempotent: `removed` is false when the sender was not blocked. It removes the block of exactly this DID; `sender` in the answer is that DID. Body ≤ 1 KB.","security":[{"Bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlockRemove"}}}},"responses":{"200":{"description":"The sender is not blocked (any more)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlockRemoveResponse"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body over the size cap","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/status/{issuerDid}/{listId}":{"get":{"tags":["Status lists"],"summary":"Read a status list (public)","description":"The Token Status List token exactly as the issuer signed it, as `application/statuslist+jwt` (`Access-Control-Allow-Origin: *`, cacheable for at most min(ttl, 60) s). Verifiers check it themselves (signature, sub, exp) and fail closed. Only a failed read counts against the IP (SPEC §17.1).","parameters":[{"schema":{"type":"string","maxLength":256,"pattern":"^did:(ixo|web):[A-Za-z0-9._:%-]+$","example":"did:ixo:ixo1qqqsyqcyq5rqwzqfpg9scrgwpugpzysn4uq0hv","description":"The issuer DID exactly as written (not percent-encoded)."},"required":true,"description":"The issuer DID exactly as written (not percent-encoded).","name":"issuerDid","in":"path"},{"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$","example":"1"},"required":true,"name":"listId","in":"path"}],"responses":{"200":{"description":"The status list token","content":{"application/statuslist+jwt":{"schema":{"type":"string"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"tags":["Status lists"],"summary":"Publish a status list (issuer)","description":"Ability `credentials/issue`, principal = `issuerDid` acting as itself (resource `ixo:credentials`; an entity issuer signs with a key on its own DID document). Body: the raw token (`Content-Type: application/statuslist+jwt`, ≤ 64 KB). Verified: typ, `iss` = issuer, `sub` = this exact URL, signature (EdDSA / Ed25519) by the assertionMethod key named by `kid`; `iat`, `exp` (and `ttl`) whole unix seconds; `iat` at most 60 s in the future; `exp` after `iat`, in the future and at most 400 days ahead; and `iat` STRICTLY newer than the stored token's: equal or older is 409 `stale` (sign again with a fresh `iat`). At most 20 lists per issuer: a new list id beyond that is 409 `too_many_lists`. While the service is close to its storage limit a NEW list is 507 `storage_full` (an existing list can still be replaced). A list whose `exp` passed more than 30 days ago is deleted. 201 on first publish, 200 after.","security":[{"Bearer":[]}],"parameters":[{"schema":{"type":"string","maxLength":256,"pattern":"^did:(ixo|web):[A-Za-z0-9._:%-]+$","example":"did:ixo:ixo1qqqsyqcyq5rqwzqfpg9scrgwpugpzysn4uq0hv","description":"The issuer DID exactly as written (not percent-encoded)."},"required":true,"description":"The issuer DID exactly as written (not percent-encoded).","name":"issuerDid","in":"path"},{"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$","example":"1"},"required":true,"name":"listId","in":"path"}],"requestBody":{"required":true,"content":{"application/statuslist+jwt":{"schema":{"type":"string"}}}},"responses":{"200":{"description":"Newer version published","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusListPublished"}}}},"201":{"description":"First version published","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusListPublished"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicts with what is stored: `ref_in_use` (same ref, different body), `stale` (a status list whose `iat` is not newer than the stored one), `too_many_lists` (an issuer already has 20 status lists) or `too_many_blocks` (a person already blocks 500 senders)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body over the size cap","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"Unsupported content type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"507":{"description":"`storage_full`: the service is close to its storage limit (`D1_WRITE_GUARD_BYTES`), so it takes no NEW status list and no new or larger catalog entry for now. Updates of an existing status list, catalog replacements that are not larger, offers, requests and blocks still work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/catalog/issuers":{"get":{"tags":["Catalog: issuers"],"summary":"List issuers (public; optional token)","description":"One record per DID (every DID is its own sender), ordered by DID, keyset pagination (`limit` ≤ 100). `vct` filters to the issuers that SAY they issue that type: entries are claims by their DIDs, not a trust list (the worker does not vouch for names). Without a token: public and cacheable; only a failed read counts against the IP. With a token (optional; ability `credentials/hold` or `*` on `ixo:credentials`, request-binding fact, same checks as every authenticated route): rate limited per principal instead, answered `Cache-Control: private`; an invalid token is 401, never a silent fall back. `HEAD` with a token is 405.","security":[{},{"Bearer":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Only entries that issue / accept this vct."},"required":false,"description":"Only entries that issue / accept this vct.","name":"vct","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"example":50,"description":"Items per page (1–100)."},"required":false,"description":"Items per page (1–100).","name":"limit","in":"query"},{"schema":{"type":"string","maxLength":512,"description":"Opaque `next_cursor` from the previous page."},"required":false,"description":"Opaque `next_cursor` from the previous page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"One page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogIssuerList"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"`HEAD` on an authenticated route (`method_not_allowed`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/catalog/issuers/{did}":{"get":{"tags":["Catalog: issuers"],"summary":"Read one issuer entry (public; optional token)","description":"Put the DID in the path exactly as written (not percent-encoded; a did:web in canonical form). Answers the entry of exactly this DID: `did:web:example.com`, `did:web:a.example.com` and `did:web:example.com:p1` are three senders with three entries. Wallets look a sender up by the `sender_key` (its DID) of its offer or request. The entry is what that sender says about itself. Without a token: public and cacheable; only a failed read counts against the IP. With a token (optional; ability `credentials/hold` or `*` on `ixo:credentials`, request-binding fact, same checks as every authenticated route): rate limited per principal instead, answered `Cache-Control: private`; an invalid token is 401, never a silent fall back. `HEAD` with a token is 405.","security":[{},{"Bearer":[]}],"parameters":[{"schema":{"type":"string","maxLength":256,"description":"The DID exactly as written (a did:ixo, or a did:web in canonical form: `did:web:host`, `did:web:host%3A8443`, `did:web:host:user:alice`).","example":"did:web:issuer.example.com"},"required":true,"description":"The DID exactly as written (a did:ixo, or a did:web in canonical form: `did:web:host`, `did:web:host%3A8443`, `did:web:host:user:alice`).","name":"did","in":"path"}],"responses":{"200":{"description":"The entry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogIssuerRecord"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"`HEAD` on an authenticated route (`method_not_allowed`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"tags":["Catalog: issuers"],"summary":"Write your issuer entry","description":"Ability `credentials/issue`, principal = {did} (to write an entity's entry, invoke with resource `ixo:credentials/<entity did>` as a controller or a DAO member with voting weight > 0, or sign with a key on the entity's DID document). Body ≤ 32 KB; `did` must equal the path; the DID is a did:ixo or any did:web in canonical form (a subdomain or a path DID writes its own entry). The entry is stored under exactly this DID; https URLs on public hosts; display text is cleaned. Card look (SPEC §15.1): `types[].display` (`background_color`, `text_color`, `logo_uri`, `background_image_uri`) and the entry-level `logo_uri` and `color` (the mark); image addresses must be https on the default port with no query string and no fragment; a member that is not valid (or, inside `display`, unknown) is dropped, never a 400, and listed in `display_dropped`. The worker never fetches these URLs. Replaces the whole entry. 201 when new, 200 when replaced. While the service is close to its storage limit, a new entry or a larger replacement is 507 `storage_full` (a replacement that is not larger still works).","security":[{"Bearer":[]}],"parameters":[{"schema":{"type":"string","maxLength":256,"description":"The DID exactly as written (a did:ixo, or a did:web in canonical form: `did:web:host`, `did:web:host%3A8443`, `did:web:host:user:alice`).","example":"did:web:issuer.example.com"},"required":true,"description":"The DID exactly as written (a did:ixo, or a did:web in canonical form: `did:web:host`, `did:web:host%3A8443`, `did:web:host:user:alice`).","name":"did","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogIssuerEntry"}}}},"responses":{"200":{"description":"Replaced (the entry as stored)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogIssuerPutResponse"}}}},"201":{"description":"Created (the entry as stored)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogIssuerPutResponse"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body over the size cap","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"507":{"description":"`storage_full`: the service is close to its storage limit (`D1_WRITE_GUARD_BYTES`), so it takes no NEW status list and no new or larger catalog entry for now. Updates of an existing status list, catalog replacements that are not larger, offers, requests and blocks still work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Catalog: issuers"],"summary":"Remove your issuer entry","description":"Ability `credentials/issue`, principal = {did} (an entity's controller may act through `ixo:credentials/<entity did>`). Removes the entry of exactly this DID.","security":[{"Bearer":[]}],"parameters":[{"schema":{"type":"string","maxLength":256,"description":"The DID exactly as written (a did:ixo, or a did:web in canonical form: `did:web:host`, `did:web:host%3A8443`, `did:web:host:user:alice`).","example":"did:web:issuer.example.com"},"required":true,"description":"The DID exactly as written (a did:ixo, or a did:web in canonical form: `did:web:host`, `did:web:host%3A8443`, `did:web:host:user:alice`).","name":"did","in":"path"}],"responses":{"204":{"description":"Removed"},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/catalog/verifiers":{"get":{"tags":["Catalog: verifiers"],"summary":"List verifiers (public; optional token)","description":"One record per DID (every DID is its own sender), ordered by DID, keyset pagination (`limit` ≤ 100). `vct` filters to the verifiers that SAY they accept that type: entries are claims by their DIDs, not a trust list (the worker does not vouch for names). Without a token: public and cacheable; only a failed read counts against the IP. With a token (optional; ability `credentials/hold` or `*` on `ixo:credentials`, request-binding fact, same checks as every authenticated route): rate limited per principal instead, answered `Cache-Control: private`; an invalid token is 401, never a silent fall back. `HEAD` with a token is 405.","security":[{},{"Bearer":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":512,"description":"Only entries that issue / accept this vct."},"required":false,"description":"Only entries that issue / accept this vct.","name":"vct","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"example":50,"description":"Items per page (1–100)."},"required":false,"description":"Items per page (1–100).","name":"limit","in":"query"},{"schema":{"type":"string","maxLength":512,"description":"Opaque `next_cursor` from the previous page."},"required":false,"description":"Opaque `next_cursor` from the previous page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"One page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogVerifierList"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"`HEAD` on an authenticated route (`method_not_allowed`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/catalog/verifiers/{did}":{"get":{"tags":["Catalog: verifiers"],"summary":"Read one verifier entry (public; optional token)","description":"Put the DID in the path exactly as written (not percent-encoded; a did:web in canonical form). Answers the entry of exactly this DID: `did:web:example.com`, `did:web:a.example.com` and `did:web:example.com:p1` are three senders with three entries. Wallets look a sender up by the `sender_key` (its DID) of its offer or request. The entry is what that sender says about itself. Without a token: public and cacheable; only a failed read counts against the IP. With a token (optional; ability `credentials/hold` or `*` on `ixo:credentials`, request-binding fact, same checks as every authenticated route): rate limited per principal instead, answered `Cache-Control: private`; an invalid token is 401, never a silent fall back. `HEAD` with a token is 405.","security":[{},{"Bearer":[]}],"parameters":[{"schema":{"type":"string","maxLength":256,"description":"The DID exactly as written (a did:ixo, or a did:web in canonical form: `did:web:host`, `did:web:host%3A8443`, `did:web:host:user:alice`).","example":"did:web:issuer.example.com"},"required":true,"description":"The DID exactly as written (a did:ixo, or a did:web in canonical form: `did:web:host`, `did:web:host%3A8443`, `did:web:host:user:alice`).","name":"did","in":"path"}],"responses":{"200":{"description":"The entry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogVerifierRecord"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"405":{"description":"`HEAD` on an authenticated route (`method_not_allowed`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"tags":["Catalog: verifiers"],"summary":"Write your verifier entry","description":"Ability `credentials/verify`, principal = {did} (to write an entity's entry, invoke with resource `ixo:credentials/<entity did>` as a controller or a DAO member with voting weight > 0, or sign with a key on the entity's DID document). Body ≤ 32 KB; `did` must equal the path; the DID is a did:ixo or any did:web in canonical form (a subdomain or a path DID writes its own entry). The entry is stored under exactly this DID; https URLs on public hosts; display text is cleaned. Card look (SPEC §15.1): the entry-level `logo_uri` and `color` (the mark); image addresses must be https on the default port with no query string and no fragment; a member that is not valid (or, inside `display`, unknown) is dropped, never a 400, and listed in `display_dropped`. The worker never fetches these URLs. Replaces the whole entry. 201 when new, 200 when replaced. While the service is close to its storage limit, a new entry or a larger replacement is 507 `storage_full` (a replacement that is not larger still works).","security":[{"Bearer":[]}],"parameters":[{"schema":{"type":"string","maxLength":256,"description":"The DID exactly as written (a did:ixo, or a did:web in canonical form: `did:web:host`, `did:web:host%3A8443`, `did:web:host:user:alice`).","example":"did:web:issuer.example.com"},"required":true,"description":"The DID exactly as written (a did:ixo, or a did:web in canonical form: `did:web:host`, `did:web:host%3A8443`, `did:web:host:user:alice`).","name":"did","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogVerifierEntry"}}}},"responses":{"200":{"description":"Replaced (the entry as stored)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogVerifierPutResponse"}}}},"201":{"description":"Created (the entry as stored)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogVerifierPutResponse"}}}},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body over the size cap","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"507":{"description":"`storage_full`: the service is close to its storage limit (`D1_WRITE_GUARD_BYTES`), so it takes no NEW status list and no new or larger catalog entry for now. Updates of an existing status list, catalog replacements that are not larger, offers, requests and blocks still work.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Catalog: verifiers"],"summary":"Remove your verifier entry","description":"Ability `credentials/verify`, principal = {did} (an entity's controller may act through `ixo:credentials/<entity did>`). Removes the entry of exactly this DID.","security":[{"Bearer":[]}],"parameters":[{"schema":{"type":"string","maxLength":256,"description":"The DID exactly as written (a did:ixo, or a did:web in canonical form: `did:web:host`, `did:web:host%3A8443`, `did:web:host:user:alice`).","example":"did:web:issuer.example.com"},"required":true,"description":"The DID exactly as written (a did:ixo, or a did:web in canonical form: `did:web:host`, `did:web:host%3A8443`, `did:web:host:user:alice`).","name":"did","in":"path"}],"responses":{"204":{"description":"Removed"},"400":{"description":"The request is not valid (`invalid_request`), or the person it is for does not exist (`unknown_subject`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Cannot authenticate (missing, invalid, oversized, expired, replayed or unbound invocation, or a signer that is not a did:ixo / did:web)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Authenticated but not permitted (ability, resource, revoked delegation, not your item), or `blocked`: the person has blocked you, so your offer or request was not stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`rate_limited` (with `Retry-After`) or `too_many_pending` (you already have as many waiting items as allowed for this person, or in total)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A dependency (Blocksync, a DID host, the UCAN store, D1) is unavailable; `Retry-After` says when to try again","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}