API reference

KYE Protocol™ Site API

23 operations · site.yaml

Servers
https://kyeprotocol.com
Version
1.0.0
Source
site.yaml

Public-site Pages Functions surface for kyeprotocol.com — the lead-capture forms (Audit Pilot™, PoC, GovernedUI access, contact), DSAR intake, expert-review wall, the Consultant Marketplace™ public listing, the public stats ticker, the quiz evidence sink, the §38 Comms Engine™ unsubscribe surface, the Clerk webhook receiver and the public self-audit trust surface.

Operations are unauthenticated public form endpoints unless noted. Form endpoints are rate-limited per salted IP hash (the raw IP is never stored) and carry a hidden website honeypot field — a non-empty honeypot is silently discarded with a 200 so bots learn nothing. Outbound email always routes through the KYE™ Comms Engine™ (constitution §38); privileged actions emit the §0.3 evidence-event family.

Internal

GET/api/_internal/healthcheck-workersFan-out healthcheck of every deployed KYE™ Worker (operator-only)

Probes each deployed KYE™ Worker's /healthz via the Service bindings configured on the kye-protocol Pages project (the only reachable path because every Worker runs with workers_dev=false and no public route). Returns one JSON payload with a per-worker status, latency and version, plus a summary block; the healthcheck-workers.yml workflow archives it to _diagnostics/healthcheck/<ts>.json. Gated by the HEALTHCHECK_INTERNAL_TOKEN shared secret when set.

Auth: HealthcheckBearer

Responses

  • 200 Every probed Worker reported healthy
  • 207 Multi-status — at least one Worker reported unhealthy (same body shape as the 200).
  • 401 Bearer token missing or mismatched

LeadCapture

GET/api/audit-pilotEndpoint self-description for the Audit Pilot™ application form

Returns a small JSON descriptor (expected method + the pilot-apply page URL) so a GET probe of the form endpoint is self-documenting rather than a 405.

Responses

  • 200 Endpoint descriptor
POST/api/audit-pilotSubmit an Audit Pilot™ application (public)

Receives the pilot-apply form (JSON or form-data), validates the enum fields (role, company size, industry, urgency, SKU), blocks free-email-provider addresses, requires all four consent clauses (tos, privacy, aup, authority), emits a signed kye.consent.acceptance.v1 record, persists application + consent to D1, then in the background dispatches the §38 admin alert + applicant confirmation (suppressed for detected test traffic) and enqueues the §22 onboarding and §27 commercial-lifecycle workflows. Rate limit: 3 submissions per 24 h per IP hash. Honeypot field website.

Request body (required)

fieldtypedescription
full_name requiredstring
email requiredstringWork email — free providers are rejected
role requiredstringOne of CISO, CDO, Head of AI, Head of Compliance, Procurement Lead, Engineering Lead, Other
company requiredstring
company_size requiredstringOne of <100, 100-1k, 1k-10k, 10k-50k, 50k+
industry requiredstringOne of Financial Services - Banking, Financial Services - Insurance, Financial Services - Payments/PSP, Healthcare, Pharma, Public Sector, Other Regulated, Other
regulatory_regimeobjectOne or more applicable regulators (unknown values are dropped)
ai_workflow requiredstring
urgency requiredstringOne of This quarter, Next quarter, This half, Within 12 months, Exploring
sku_id requiredstringOne of KYE-DISCOVERY-001, KYE-AUDIT-PILOT-001, KYE-REG-PILOT-001, KYE-GOVOPS-PILOT-001, KYE-EDGE-PILOT-001, KYE-SANDBOX-PILOT-001, KYE-CKAN-AUDIT-PILOT-001, KYE-CKAN-GOVOPS-PILOT-001, UNDECIDED
heard_fromstring
consent_tosobjectMust be accepted
consent_privacyobjectMust be accepted
consent_aupobjectMust be accepted
consent_authorityobjectMust be accepted
consent_dpaobjectOptional DPA clause
websitestringHoneypot — leave empty

Responses

  • 200 Application accepted (or honeypot silently discarded). The signed consent record is echoed back so the client can store the receipt.
  • 400 Validation failure. Error codes include `invalid_body`, `missing_field:<name>`, `invalid_sku_id`, `invalid_email_shape`, `free_email_provider_blocked`, `invalid_role`, `invalid_company_size`, `invalid_industry`, `invalid_urgency`, `ai_workflow_too_short`, `co…
  • 429 Per-IP-hash rate limit exceeded. The `retry-after` header carries the wait in seconds.
GET/api/contactEndpoint self-description for the contact form

Responses

  • 200 Hint that the endpoint accepts POSTed contact forms
POST/api/contactSubmit the contact-modal form (public)

Receives the contact-modal submission from any KYE Protocol™ page (JSON or form-urlencoded), validates + spam-filters it, dispatches the §38 contact.inbound.v1 admin notification (with one-click Approve/Reject buttons for partner/trainer/auditor topics, §27 §4 dual-channel admin) and a contact.applicant-ack.v1 receipt to the submitter, then persists an audit row to D1. Rate limit: 5 submissions per IP per 5 minutes. Honeypot field website. When the mail binding is unattached the endpoint answers 503 with fallback: "mailto" so the client degrades to a mailto: link — a submission is never silently dropped.

Request body (required)

fieldtypedescription
name requiredstring
email requiredstring
organisation requiredstring
phone requiredstring
position requiredstring
topicstringRouting topic (default `general`; partner/trainer/auditor get admin action buttons)
message requiredstring
accept requiredobjectTerms + privacy acceptance — required truthy
websitestringHoneypot — leave empty

Responses

  • 200 Mail dispatched (or honeypot silently discarded)
  • 400 Validation failure — `invalid_body`, `missing_field:<name>`, `invalid_email`, `terms_not_accepted`, `message_too_long`.
  • 429 Per-IP-hash rate limit exceeded. The `retry-after` header carries the wait in seconds.
  • 502 The mail-sender dispatch failed; client should fall back to mailto
  • 503 Mail binding unattached on this deployment; client should fall back to mailto
POST/api/engage-accessRequest access to a GovernedUI™ SKU tier (public)

Single canonical entrypoint for every GovernedUI™ access request from the public site (constitution §27 §4 dual-channel admin). Maps the tier field to one of the 5 canonical GovernedUI SKUs (pilot → KYE-GUI-PILOT-001, department → KYE-GUI-DEPT-001, enterprise → KYE-GUI-ENT-001, regulated → KYE-GUI-REG-001, national → KYE-GUI-NAT-001), persists the request to D1, emits the engagement-access evidence event to the audit chain, then dispatches the §38 admin alert with Approve / Request-changes buttons plus an applicant confirmation. The Stripe payment link is returned after admin approval, never inline. Rate limit: 3 per 24 h per IP hash. Honeypot field website. Work email required.

Request body (required)

fieldtypedescription
tier requiredstringOne of pilot, department, enterprise, regulated, national
name requiredstring
email requiredstringWork email — free providers are rejected
organisation requiredstring
phonestring
role requiredstring
agent_classesstringScope — agent classes to govern
protected_systemsstringScope — systems the agents touch
business_unitsstring
regulatory_exposurestring
deployment_topologystring
messagestring
consent_accepted requiredbooleanMust be truthy
websitestringHoneypot — leave empty

Responses

  • 200 Request recorded (or honeypot discarded with `request_id: "discarded"`)
  • 400 Validation failure — `json_parse_failed`, `unknown_tier` (body lists the valid tiers), `missing_required_field`, `invalid_email`, `work_email_required`, `consent_required`.
  • 429 Per-IP-hash rate limit exceeded. The `retry-after` header carries the wait in seconds.
  • 500 Unhandled handler exception (message echoed)
OPTIONS/api/consultant-leadCORS preflight for the consultant-lead capture form

Answers 204 allowing GET/POST/OPTIONS with the content-type header from the https://kyeprotocol.com origin, cacheable for 24 h. The GET/POST operations themselves are declared in the consultant-programme OpenAPI document (§0 — one declaration per operation).

Responses

  • 204 Preflight accepted (empty body)
POST/api/v1/poc/applySubmit a Proof-of-Concept application (public)

Backs /poc.html. Maps the tier field to the canonical PoC SKU (discovery/undecided → KYE-POC-DISCOVERY-001, audit → KYE-POC-AUDIT-001, reg → KYE-POC-REG-001, govops → KYE-POC-GOVOPS-001, edge → KYE-POC-EDGE-001, sandbox → KYE-POC-SANDBOX-001), persists the kye.poc.application.v1 shape to D1, then dispatches the §38 admin alert with Approve / Request-changes buttons and the applicant confirmation (§27 §4 dual-channel admin). Rate limit: 3 per 24 h per IP hash. Honeypot field website. Work email required.

Request body (required)

fieldtypedescription
tier requiredstringOne of discovery, audit, reg, govops, edge, sandbox, undecided
name requiredstring
email requiredstringWork email — free providers are rejected
company requiredstring
rolestring
workflowstringThe AI workflow to govern in the PoC
frameworksstringRegulatory frameworks in scope
websitestringHoneypot — leave empty

Responses

  • 200 Application recorded (or honeypot discarded with `application_id: "discarded"`)
  • 400 Validation failure — `json_parse_failed`, `unknown_tier` (body lists the valid tiers), `missing_required_field`, `invalid_email`, `work_email_required`.
  • 429 Per-IP-hash rate limit exceeded. The `retry-after` header carries the wait in seconds.
  • 500 Unhandled handler exception (message echoed)

Dsar

GET/api/dsarEndpoint self-description for the DSAR intake

Responses

  • 200 Hint describing the expected POST body
POST/api/dsarFile a Data Subject Access Request (public)

Self-service DSAR intake under GDPR Art. 15–21 and the equivalent rights in UK GDPR, CCPA/CPRA, PIPEDA, LGPD, PIPL and POPIA (backs /dsar.html). Mints a stable kye:dsar-request:<date>.<hex> reference, persists a WORM row to D1 (status transitions are append-only via supersession), emits a §0.3 kye.evidence.decision_map.v1 event (dsar_request.filed), then sends the §38 admin alert with one-click Acknowledge/Reject buttons and a subject acknowledgement carrying the statutory deadline. Rate limit: 5 requests per 24 h per IP hash.

Request body (required)

fieldtypedescription
controller requiredstringThe data controller the request targets
right requiredstringOne of access, rectification, erasure, restriction, portability, objection, withdraw_consent, ccpa_know, ccpa_delete, ccpa_opt_out
regimestringOne of gdpr, uk_gdpr, ccpa, pipeda, lgpd, pipl, popia, other
email requiredstring
namestring
detailstringSpecific items / time window
consent requiredbooleanMust be `true`

Responses

  • 201 Request filed; statutory window computed per regime
  • 400 Validation failure — body carries `reason` ∈ {invalid_json, controller_required, invalid_right, invalid_regime, invalid_email, consent_required}.
  • 429 More than 5 requests in 24 h from the same IP hash
OPTIONS/api/dsarCORS preflight for the DSAR intake

Answers 204 with the allowed methods (POST, GET, OPTIONS) and allowed request header (content-type). No allow-origin header is set — the form is same-origin on kyeprotocol.com.

Responses

  • 204 Preflight accepted (empty body)

ExpertReviews

GET/api/expert-reviewList published expert reviews (public)

Lists reviews for the expert wall. Only status=published is publicly readable; requesting pending or rejected returns 401 (moderation listing is owners-only via the Admin Console). Without the D1 binding the endpoint degrades to an honest empty list (not_provisioned: true) so the page still renders.

Parameters

nameintypedescription
statusquerystringOnly `published` is allowed unauthenticated
limitqueryinteger

Responses

  • 200 Published reviews, newest first
  • 401 Non-published status requested without owner auth
POST/api/expert-reviewSubmit an expert review for moderation (public)

Accepts a review of a named KYE™ artefact (JSON or form-data), validates the verdict enum and the optional LinkedIn profile URL (https linkedin.com /in or /pub paths only), emits a kye.evidence.audit_event.v1 to the AI Call Ledger queue, inserts the row as status=pending, then notifies the moderation inbox via the §38 expert-review.brief.v1 template with one-click Approve-&-publish / Reject buttons and sends the submitter the expert-review.applicant-ack.v1 receipt (both fail-soft — the persisted row is canonical). Rate limit: 3 submissions per 24 h per day-salted IP hash. Honeypot field website.

Request body (required)

fieldtypedescription
name requiredstring
role requiredstring
affiliation requiredstring
email requiredstringUsed for the receipt + embed code; never published
artefact requiredstringThe KYE™ artefact reviewed
review_text requiredstring
verdict requiredstringOne of approved, approved_with_suggestions, changes_requested, under_discussion
linkedinstringOptional public LinkedIn profile URL (https only)
websitestringHoneypot — leave empty

Responses

  • 200 Review queued for moderation (or honeypot discarded)
  • 400 Validation failure — `invalid_body`, `missing_field:<name>`, `invalid_email`, `invalid_verdict`, `review_too_long`, `invalid_linkedin` (each with a human `hint`).
  • 429 Per-IP-hash rate limit exceeded. The `retry-after` header carries the wait in seconds.
  • 500 D1 insert failed (truncated cause in `hint`, review id echoed)
  • 503 D1 binding not yet attached on this deployment (`not_provisioned`)
OPTIONS/api/expert-reviewCORS preflight for the expert-review endpoint

Answers 204 allowing GET/POST/OPTIONS with the content-type header from the https://kyeprotocol.com origin, cacheable for 24 h.

Responses

  • 204 Preflight accepted (empty body)

Marketplace

GET/api/marketplace/consultantsList visible consultants for the public marketplace

Returns every consultant whose status is onboarding or certified (master → professional → associate, then newest first) for the Consultant Marketplace™ page, with facet counts for the filter UI. Email addresses are never returned and applicant lead rows are never exposed. Edge-cached for 5 minutes. Without the D1 binding (or before the table exists) the endpoint degrades to an honest empty list (not_provisioned: true).

Parameters

nameintypedescription
sectorquerystringKeep only consultants whose sectors include this tag
jurisdictionquerystringISO-3166 alpha-2 country filter
levelquerystringCertification-level filter
qquerystringCase-insensitive name / bio match
limitqueryinteger

Responses

  • 200 Visible consultants + facet counts
  • 500 Query failed (truncated message echoed)
OPTIONS/api/marketplace/consultantsCORS preflight for the marketplace listing

Answers 204 allowing GET/OPTIONS with the content-type header from the https://kyeprotocol.com origin, cacheable for 24 h.

Responses

  • 204 Preflight accepted (empty body)

PublicData

GET/api/v1/public/statsPublic counters for the hero live ticker

Returns the small public counter set consumed by assets/live-ticker.js (evidence packs verified, decisions governed, frameworks mapped). Tier 1 reads bounded COUNT queries from D1 when bound; otherwise it serves the repo-derived snapshot (source pins which tier answered). Edge-cached 60 s to match the client poll. Emits a fire-and-forget kye.compliance.attestation.v1 per response (§0.3).

Responses

  • 200 Current public counters
POST/api/v1/quiz/respondRecord a completed quiz response (public, best-effort)

Evidence sink for the public quiz widget (/quiz.html → assets/quiz.js): the widget POSTs a kye.quiz.response.v1 envelope on completion so even the lead-capture funnel lands in the WORM audit chain (§0.3). The client treats this as fire-and-forget, so the handler is resilient — it persists when D1 is bound, emits the audit event when the audit-chain base URL is set, and always answers 200 for a well-formed body so a transient backend wobble never breaks the share/score UX.

Request body (required)

fieldtypedescription
quiz_id requiredstring
score requirednumber
band requiredstringScoring band the result fell into
answersarrayRaw answer list (persisted as JSON, capped at 8000 chars)
submitted_atstring

Responses

  • 200 Response accepted; persistence + evidence flags reported honestly
  • 400 `invalid_body` (unparseable JSON) or `missing_required_field` (quiz_id, score, band)
  • 500 Unhandled handler exception (truncated message echoed)

Comms

GET/comms/unsubscribeHuman one-click unsubscribe (renders a confirmation page)

Destination of the canonical link.unsubscribe footer link in every §38 / §62 outbound email (PECR reg. 22, CAN-SPAM §5, GDPR Art. 21 — one click, no login, honoured immediately). Verifies the signed token in ?u=, records the suppression through the ONE canonical comms-engine suppress path (the recipient travels as a one-way hash, never the raw email), then renders an HTML confirmation page. A backend hiccup still confirms intent with a 202 — the suppress call is idempotent on the next click.

Parameters

nameintypedescription
u requiredquerystringSigned unsubscribe token minted into the email footer

Responses

  • 200 Suppression recorded; confirmation page rendered
  • 202 Token verified but the suppress backend errored — intent acknowledged, will be actioned
  • 400 Token missing, altered or expired (error page rendered)
POST/comms/unsubscribeRFC 8058 one-click unsubscribe (mail-client POST)

Mail clients POST List-Unsubscribe=One-Click here; the signed token rides in ?u= and/or the form/JSON body (u or token). No HTML is returned — a 2xx text/plain is the RFC 8058 contract. Suppression goes through the same canonical comms-engine path as the GET flow.

Request body

fieldtypedescription
ustring
tokenstring

Responses

  • 200 Suppression recorded ("unsubscribed")
  • 202 Token verified, suppress backend errored — accepted for action
  • 400 Missing or invalid token

Trust

GET/trust/self-audit/live/{[path}]Fetch a production-signed live self-audit bundle (public)

Public read side of §0.3 self-governance-on-production: serves the Ed25519-signed live self-audit bundles the kye-self-audit-daemon Worker publishes to the shared R2 bucket. The catch-all segment addresses one partition — latest, latest/bundle.json, <YYYY-MM-DD>/ or <YYYY-MM-DD>/bundle.json; an empty segment resolves to latest. Any other shape is rejected (no arbitrary R2 traversal). Anyone can verify the signature against /trust/self-audit-jwks.json with node scripts/verify-self-audit.mjs. Edge-cached 5 minutes.

Parameters

nameintypedescription
[path requiredpathstringCatch-all partition selector — `latest` or a `YYYY-MM-DD` date, optionally followed by `/bundle.json`.

Responses

  • 200 The signed bundle bytes, verbatim
  • 404 Non-addressable path shape (`not_found`) or no bundle published for the partition yet (`no_live_bundle`)
  • 503 R2 binding not attached on this deployment (`r2_binding_unavailable`) — honest machine-readable fail, never a synthesised bundle

Webhooks

GET/webhooks/clerkWebhook receiver self-description

Returns the receiver hint plus the list of the 18 routed Clerk event types, so a GET probe of the configured webhook URL is self-documenting.

Responses

  • 200 Receiver hint + routed event list
POST/webhooks/clerkReceive a Clerk webhook delivery (Svix-signed)

Verifies the Svix v1 signature (svix-id / svix-timestamp / svix-signature headers) against the configured signing secret, then projects each of the 18 routed Clerk event types (user.*, session.*, organization.*, organizationMembership.*, organizationInvitation.*, invitation.*) to a kye.evidence.<bucket>.<verb>.v1 envelope persisted to R2 and fanned out on the webhook queue when those bindings are wired. Recognised events are always ACKed 200 even if a downstream projection fails (the audit chain is canonical; the D1 projection is rebuildable) — Clerk's retry budget is small and a half-applied projection is worse than a missed one. With the secret unset the endpoint answers 503 so Clerk retries with backoff.

Parameters

nameintypedescription
svix-id requiredheaderstring
svix-timestamp requiredheaderstring
svix-signature requiredheaderstringSpace-separated `v1,<base64>` signature tokens

Request body (required)

fieldtypedescription
typestring
dataobject

Responses

  • 200 Delivery verified and acknowledged
  • 400 `missing_svix_headers` or `invalid_json`
  • 401 `invalid_signature` or `signature_verification_failed`
  • 503 `webhook_secret_unconfigured` — signing secret unset; Clerk will retry