API reference

KYE Protocol™ App API

132 operations · app.yaml

Servers
https://app.kyeprotocol.com/api/v1
Version
1.0.0
Source
app.yaml

Clerk-gated customer app surface for app.kyeprotocol.com. All endpoints require a Clerk JWT. Responses are automatically scoped to the caller's tenant derived from the JWT org_id. All entity endpoints are read-only for tenant principals.

Tenants

GET/tenantsGet caller's own tenant

Auth: Session (Clerk JWT)

Responses

  • 200 OK
  • 404 Tenant not yet provisioned
GET/tenants/{id}Get tenant by id (only own tenant allowed)

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 OK
  • 403 Cross-tenant access attempt
  • 404 Resource not found
GET/reportsList signed report envelopes for the caller's tenant (KYE™ Reporting Engine™ tenant view)

Auth: Session (Clerk JWT)

Responses

  • 200 OK
  • 401 Missing or invalid bearer token
GET/settingsLoad merged tenant settings

Auth: Session (Clerk JWT)

Responses

  • 200 OK
  • 401 Missing or invalid bearer token
PATCH/settingsUpdate one or more tenant settings fields

Auth: Session (Clerk JWT)

Responses

  • 200 OK
  • 400 Invalid field
  • 401 Missing or invalid bearer token

LegalEntities

BillingAccounts

GET/billing-accounts

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
limitqueryinteger

Responses

  • 200 OK
GET/billing-accounts/{id}

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 OK
  • 403 Cross-tenant access attempt

Domains

GET/domains

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
limitqueryinteger

Responses

  • 200 OK
GET/domains/{id}

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 OK
  • 403 Cross-tenant access attempt

Policies

GET/policies

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
limitqueryinteger

Responses

  • 200 OK
GET/policies/{id}

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 OK
  • 403 Cross-tenant access attempt

Workspaces

GET/workspaces

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
limitqueryinteger

Responses

  • 200 OK
GET/workspaces/{id}

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 OK
  • 403 Cross-tenant access attempt

Projects

GET/projects

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
limitqueryinteger

Responses

  • 200 OK
GET/projects/{id}

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 OK
  • 403 Cross-tenant access attempt

Teams

GET/teams

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
limitqueryinteger

Responses

  • 200 OK
GET/teams/{id}

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 OK
  • 403 Cross-tenant access attempt

Principals

GET/principals

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
limitqueryinteger

Responses

  • 200 OK
GET/principals/{id}

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 OK
  • 403 Cross-tenant access attempt

Resources

GET/resources

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
limitqueryinteger

Responses

  • 200 OK
GET/resources/{id}

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 OK
  • 403 Cross-tenant access attempt

Models

GET/models

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
limitqueryinteger

Responses

  • 200 OK
GET/models/{id}

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 OK
  • 403 Cross-tenant access attempt

Tools

GET/tools

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
limitqueryinteger

Responses

  • 200 OK
GET/tools/{id}

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 OK
  • 403 Cross-tenant access attempt

ExternalApps

GET/external-apps

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
limitqueryinteger

Responses

  • 200 OK
GET/external-apps/{id}

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 OK
  • 403 Cross-tenant access attempt

AuditStreams

GET/audit-streams

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
limitqueryinteger

Responses

  • 200 OK
GET/audit-streams/{id}

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 OK
  • 403 Cross-tenant access attempt

StateRegistry

GET/state-eventsList state events for an entity (tenant-scoped)

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
entity_id requiredquerystring
limitqueryinteger

Responses

  • 200 OK
  • 400 entity_id required
  • 403 Cross-tenant access attempt
GET/state-events/{id}

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 OK
  • 403 Cross-tenant access attempt
  • 404 Resource not found

StateLibrary

GET/state-libraryBrowse published KYE™ State Library entries

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
categoryquerystring
includequerystring

Responses

  • 200 OK
GET/state-machines/from-libraryList state machine derivations for caller's tenant

Auth: Session (Clerk JWT)

Responses

  • 200 OK
POST/state-machines/from-libraryAdopt a State Library entry into caller's tenant

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
library_id requiredstring
library_version requiredstring
tenant_entity_class requiredstring
overridesobject

Responses

  • 200 OK
  • 400 Validation error
  • 404 Library entry not found
  • 409 Seal mismatch

Analytics

GET/analytics-decisions-per-hourHourly decision rollup for the caller's tenant

Aggregates the tenant's decisions ledger into hourly buckets with per-verdict counts (allow / deny / review / quarantine). The window is capped at 90 days; longer windows are served by the warehouse path on the gateway-worker. Powers the usage sparkline and dashboard time-series drilldowns.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
sincequerystringWindow start (defaults to until minus 7 days)
untilquerystringWindow end (defaults to now)

Responses

  • 200 Hourly rows with decision_count, allow_count, deny_count, review_count, quarantine_count
  • 400 Invalid since/until, since not before until, or window over 90 days
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/analytics-widget-callsPer-widget call counts for the caller's tenant

Groups the tenant's widget_calls telemetry by widget_slug over the requested window (default trailing 7 days), reporting total and successful call counts per widget.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
sincequerystringWindow start (defaults to until minus 7 days)
untilquerystringWindow end (defaults to now)

Responses

  • 200 Per-widget summary rows with widget_slug, total_calls, ok_calls
  • 400 Invalid since or until timestamp
  • 503 Required D1 / service binding not attached (db_binding_missing)

Dashboard

GET/dashboard-statsSeven top-level dashboard KPIs for the caller's tenant

Honest D1 aggregates scoped to the caller's tenant — decisions today, pending reconfirmations (Purpose Permissions expiring within 30 days), open drift events, evidence packs awaiting sign-off, 24h decision breakdown, authority finality (in-force / attenuated / revoked delegations + p99 lookup), and the most active agent today. Cold tenants get flat zeros, never fabricated values.

Auth: Session (Clerk JWT)

Responses

  • 200 KPI snapshot for the tenant
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/dashboard-snapshotCombined dashboard snapshot (SSE poll-fallback)

Poll fallback for clients whose EventSource connection to /stream/dashboard fails. Returns the last 50 decisions, the authority graph, the 24x7 density heatmap, the edge infra topology, and the §0.3 attestation envelope for the read. A client-supplied tenant_id that diverges from the session-resolved tenant is refused per §0.11.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
tenant_idquerystringOptional echo of the caller's tenant; refused with 403 if it diverges from the session

Responses

  • 200 Snapshot with decisions, density_heatmap, authority_graph, infra_topology, attestation
  • 401 Missing or invalid bearer token
  • 403 cross_tenant_smuggle_refused — client tenant_id diverges from session (refusal attestation included)
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/stream/dashboardServer-Sent Events stream for the realtime dashboard

Long-lived text/event-stream emitting typed events — subscribe envelope on open, then decision, authority_change, metric_tick and 25-second ping heartbeats, with a closing kye.compliance.attestation.v1 envelope. Connections are capped at 5 minutes; EventSource auto-reconnect re-verifies the rotating Clerk JWT. Client-supplied tenant_id diverging from the session is refused before the stream opens (§0.11).

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
tenant_idquerystringOptional echo of the caller's tenant; refused with 403 if it diverges from the session

Responses

  • 200 SSE stream of kye.dashboard.event.v1 frames
  • 401 Missing or invalid bearer token
  • 403 cross_tenant_smuggle_refused — client tenant_id diverges from session
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/density-heatmap24x7 decision-density grid for the caller's tenant

Buckets the tenant's decisions by UTC day-of-week and hour-of-day into a kye.density_heatmap.v1 snapshot with per-cell allow / deny / review counts. Default window is the trailing 7 days.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
daysqueryintegerTrailing window in days
tenant_idquerystringOptional echo of the caller's tenant; refused with 403 if it diverges from the session

Responses

  • 200 kye.density_heatmap.v1 snapshot with cells, max_cell_count, total_count, attestation
  • 401 Missing or invalid bearer token
  • 403 cross_tenant_smuggle_refused — client tenant_id diverges from session
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/infra-topologyEdge-infrastructure topology snapshot

Returns the kye.infra_topology.v1 graph (Worker, D1, Queue, R2, KV nodes plus read/write/produce/consume edges) for the kye-infra-topology web component. Each node carries the §51 No-SPOF posture; D1 status reflects whether the KYE_DB binding is reachable. Per-tenant throughput values are honest zeros until the per-binding counter export ships.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
tenant_idquerystringOptional echo of the caller's tenant; refused with 403 if it diverges from the session

Responses

  • 200 kye.infra_topology.v1 snapshot with nodes, edges, attestation
  • 401 Missing or invalid bearer token
  • 403 cross_tenant_smuggle_refused — client tenant_id diverges from session

Authority

GET/authority-graphTenant-wide authority graph of in-force delegations

Derives a kye.authority_graph.v1 node/edge graph from the tenant's most recent 200 delegations — nodes classified by URN segment (agent / capability / scope / policy / delegate / principal), edges typed delegates_to with an in_force flag and audit_ref. Includes a §0.3 attestation envelope.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
tenant_idquerystringOptional echo of the caller's tenant; refused with 403 if it diverges from the session

Responses

  • 200 kye.authority_graph.v1 snapshot with nodes, edges, attestation
  • 401 Missing or invalid bearer token
  • 403 cross_tenant_smuggle_refused — client tenant_id diverges from session
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/authority-walletList the tenant's wallet credentials with KPI roll-up

A wallet credential is a presentable, revocable, downstream-delegatable authority grant held by the tenant principal. Read-only — credentials enter the wallet when grants are accepted or Operating Model seals are published. KPI counts active credentials, active delegations issued, credentials presentable for at most 30 more days, and revocations in the last 30 days; includes distinct holders for the filter bar.

Auth: Session (Clerk JWT)

Responses

  • 200 Credential rows (credential_id, credential_class, issuer, holder, presentable_until, status, issued_at, revoked_at) plus kpi and holders
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/authoritiesAuthorities held by the calling tenant, with issuer rollup

Auth: Session (Clerk JWT)

Responses

  • 200 Authorities, their issuers and a KPI rollup
  • 503 Required D1 / service binding not attached (db_binding_missing)

Entities

GET/entitiesList the tenant's entity inventory with KPI roll-up

Every actor bound to the tenant — principals, agents, model endpoints, tools, datasets, connectors, partners — each identified by a stable kye URN. Read-only; entities are registered through the Operating Model authoring flow, not created here. KPI counts agents, people and tools by entity class.

Auth: Session (Clerk JWT)

Responses

  • 200 Entity list with id, entity_class, display_name, trust_domain, status, last_seen, plus kpi
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/entitiesAdmit an entity into the governed graph

The admission half of the one governed register. Validates the request against kye.entity_registration.v1, checks whether the registrant may register into this trust domain, then runs the decision engine for the action entity.register. entity.register is a consequential action, so a request carrying no recorded approval is HELD (202) and nothing is written; the row and the decision that admitted it are created together or not at all. trust_domain_id is taken from the verified session and any value supplied in the body is ignored — a caller-asserted scope is not a scope.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
entity_id requiredstringCanonical entity URN
entity_type requiredstring
display_namestring,null
registered_by_entity_id requiredstringThe registering entity's URN
basis requiredstringOne of self_registration, delegated_registration, bulk_import, agent_nomination_admitted, bootstrap_genesis
delegation_idstring,nullRequired for delegated_registration and bulk_import
nomination_refstring,nullRequired for agent_nomination_admitted
consequential_action_classesarray
labelsarray
approval_recordedbooleanWhether an approval for this admission is on record

Responses

  • 201 Admitted — entity_id plus the decision that admitted it
  • 202 Held for approval — reason_code and decision reference; nothing written
  • 400 Contract violation — the offending field is named
  • 403 Refused — registrant may not register into this trust domain
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/hierarchyEntity-relation tree for the caller's tenant

Builds the full adjacency-list tree from the tenant's hierarchy nodes (delegation / containment / binding edges) with a KPI roll-up of node_count, depth, agent_count and principal_count. Read-only — the tree is derived from the operating model, not authored here.

Auth: Session (Clerk JWT)

Responses

  • 200 Nested tree of nodes with node_class, label, lifecycle, edge_kind, children, plus kpi
  • 503 Required D1 / service binding not attached (db_binding_missing)

Events

Runtime

GET/live-runtimeRecent PDP decisions with a 5-minute KPI roll-up

Newest-first decision events for the caller's tenant with a KPI block covering the last 5 minutes (decisions/min rate plus allow / review / deny percentages) and distinct capability and actor lists for filter dropdowns. Updates the tenant's last-polled cursor. Read-only — decisions are emitted by the PDP, never created here.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
limitqueryinteger
sincequerystringOnly decisions decided at or after this instant
capabilityquerystringFilter by capability_id
actorquerystringMatches actor_entity_id or agent_entity_id
decisionquerystringFilter by decision verdict

Responses

  • 200 Decision rows, kpi (rate, allow_pct, review_pct, deny_pct), capabilities, actors
  • 400 invalid_since — unparseable timestamp
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/runtime/evaluateEvaluate an agent action and return an Authority Finality decision + receipt

Runs the deterministic runtime authority decision for one proposed agent action and returns the three-outcome verdict (allow | require_approval | deny), a replay-stable decision_id, a patent-safe evidence reference, a receipt verify_url, and an Ed25519-sealed, offline-verifiable Evidence Pack. Emits the full evidence-event family server-side. Auth is a minted API key (runtime:write or runtime:* scope) or a Clerk session.

Auth: API key (kye_live_ / kye_test_) / Session (Clerk JWT)

Request body (required)

fieldtypedescription
subjectstringKYE™ URN of the acting agent/principal (e.g. kye:agent:acme:kyc-triage). Alias: agent.
agentstringAlias for subject (quickstart-friendly).
action requiredstringDotted capability id (e.g. payments.transfer, kyc.screening.run).
purpose requiredstringDeclared purpose class the action is bound to.
contextobjectOptional decision context (amount, jurisdiction, approval_recorded, irreversible, …).

Responses

  • 200 Governed decision: decision (allow | require_approval | deny), reason_code (canonical vocabulary), decision_id (kye:decision:<hex>, replay-stable), evidence (patent-safe audit_reference), verify_url (receipt deep-link on evidence.html), replay_seed, latency_m…
  • 400 invalid_json | missing_subject | subject_not_kye_urn | missing_action | action_malformed | missing_purpose
  • 401 Missing or invalid bearer token
  • 403 permission_scope_exceeded — key lacks runtime:write / runtime:*
  • 429 quota_exceeded — the tenant's monthly agent_action quota is exhausted (governed refusal with a patent-safe evidence reference and quota {used, limit, remaining, resets_at}; configure via KYE_AGENT_ACTION_MONTHLY_QUOTA, 0 = unlimited).

Memory

GET/memoryList signed agent-memory records for the caller's tenant

Reads the agent_memory table, excluding forgotten records. Exposes the observable contract only — id, agent_entity_id, memory_class, purpose, created_at, signed_by_kid — plus total, distinct-agent and distinct-class counts.

Auth: Session (Clerk JWT)

Responses

  • 200 Memory rows (newest 200) with total, agents_count, classes_count
  • 503 Required D1 / service binding not attached (db_binding_missing)

Onboarding

GET/onboarding/workflowsRead-only onboarding workflow projection

The workflow is a projection of the six per-step rows in the onboarding_steps table — there is no separate workflow store (§0 one source of truth). Returns a single-element array when the tenant has started onboarding, otherwise an empty array.

Auth: Session (Clerk JWT)

Responses

  • 200 Array of zero or one kye.onboarding.workflow.v1 objects
  • 401 Missing or invalid bearer token
  • 503 Required D1 / service binding not attached (db_binding_missing)

Purposes

GET/purposesList the tenant's declared purposes with KPI roll-up

A purpose is the bounded reason any agent, connector or partner may act — every Purpose Permission grant cites one. Grants themselves live under /purpose-permissions; this registry holds the definitions.

Auth: Session (Clerk JWT)

Responses

  • 200 Purpose rows (purpose_id, class, display_name, lawful_basis, active_grants, status) plus kpi
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/purposesDeclare a new purpose

Mints a kye:purpose URN for the tenant. The class must be a lowercase slug and the lawful basis one of the six GDPR Article 6 bases.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
class requiredstring
display_name requiredstring
lawful_basis requiredstringOne of consent, contract, legal_obligation, vital_interests, public_task, legitimate_interests

Responses

  • 201 Created purpose with its minted purpose_id and active status
  • 400 invalid_json, invalid_class, display_name_required or invalid_lawful_basis
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/purpose-least-privilegeDeclared-vs-exercised purpose drift for the caller's tenant

Answers the least-privilege question no single existing surface answers: of the purposes this tenant declared and granted, how many were ever actually exercised? Joins the purpose registry and the grant ledger to the per-decision purpose attribution on the evidence-pack index, and classifies every declared purpose as exercised, unexercised, or not_observable. Purpose attribution only exists on decisions sealed after the decision writer began recording it, so the response reports attributed and unattributed decision counts separately and a purpose whose whole life predates the attributed window is reported as not_observable — never as unused, which would be a fabricated finding. Tenant scoping is applied in SQL on every statement.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
window_daysqueryintegerObservation window in days; out-of-range values are clamped, non-numeric falls back to the default

Responses

  • 200 Per-purpose findings (exercised / unexercised / not_observable), finding counts, the honest attributed and unattributed decision denominators, the observation window, unexercised live grants, and a compliance attestation
  • 401 No session, or the session resolves to no tenant
  • 403 A client-supplied tenant_id did not match the session-resolved tenant (§0.11 cross-tenant refusal)
  • 503 Required D1 / service binding not attached (db_binding_missing)

DataGovernance

GET/data-flow-graphList signed data-flow seals for the caller's tenant

Exposes the observable contract of the Data Mapping Agent — seal_id, asset_count, flow_count, pii_assets, sealed_at, signed_by_kid — newest first (up to 200), with the latest seal's totals lifted to top-level asset_total / flow_total / pii_assets.

Auth: Session (Clerk JWT)

Responses

  • 200 Seal rows plus latest-seal totals
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/classificationList the tenant's data-asset classifications with KPI roll-up

Every classification is tenant-scoped and Ed25519-signed (the signing kid is recorded). KPI counts special-category and restricted assets plus rows still pending a signature.

Auth: Session (Clerk JWT)

Responses

  • 200 Classification rows (classification_id, asset_id, classification, detection, confidence, signature_kid, effective_at) plus kpi
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/classificationRegister a new asset classification

Records the classification exactly as submitted (effective immediately); signature_kid is stored as supplied — empty until the record is signed.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
asset_id requiredstring
classification requiredstringOne of public, internal, confidential, restricted, top_secret, special_category
detectionstringOne of human_review, regex_scan, llm_inference, gdpr_art9_match, sector_template, schema_inference
confidencenumber
signature_kidstring

Responses

  • 201 Created classification with its minted classification_id
  • 400 invalid_json, asset_id_required, invalid_classification, invalid_detection or confidence_must_be_0_to_1
  • 503 Required D1 / service binding not attached (db_binding_missing)

Evidence

GET/publisher-access-ledgerList the tenant's Publisher Access Ledger snapshots with KPI roll-up

Backs the KYE™ Publisher Access Ledger™ dashboard — the tenant's licensed-vs-breach split, the advisory GBP 500-per-Product accrual per operator, and the possible-spoof surfacing. Every snapshot is tenant-scoped (newest first, up to 200) and the KPI totals sum snapshots / breach_events / possible_spoof_events / accrued_total. The schedule is ADVISORY (§70): the publisher-claimable accrual from attribution signals, never a KYE-metered charge; unknown/unattributed access is never invoiced and possible-spoof is surfaced, not invoiced as certain.

Auth: Session (Clerk JWT)

Responses

  • 200 ok, contract_url, honesty{advisory,basis}, kpi roll-up and snapshots[] (each with summary + operators + effective_at)
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/publisher-access-ledgerDerive and record a Publisher Access Ledger snapshot from a classified-access report

Accepts a kye.crawler_classification.v1 report (records[]) from the collector (Tier A edge or Tier C log ingest), derives the advisory breach schedule via the ONE canonical breach-schedule engine (never re-implemented, §0), and records the tenant-scoped snapshot. The customer-visible response carries a patent-safe evidence reference built only by the canonical publicEvidence() helper (§0.35).

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
report requiredobjectA kye.crawler_classification.v1 instance
access_fee_per_productintegerPer-website Clause 14 override of the advisory GBP 500-per-Product accrual
currencystring

Responses

  • 201 ok, snapshot{snapshot_id, summary, operators, honesty, effective_at} and a patent-safe evidence reference
  • 400 invalid_json, report_records_required or invalid_report
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/evidence-importList the tenant's recent evidence imports

Tenant-visible history of GRC-export imports — source tool, target kind, format and row / mapped / error counts for the latest 100 batches.

Auth: Session (Clerk JWT)

Responses

  • 200 Import history rows
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/evidence-importMap a GRC-tool export into a canonical evidence batch

Forwards the upload to the single canonical mapping engine (kye-evidence-import-worker) over a service binding with the worker bearer, then records the import for tenant-visible history. Fails closed — with no service binding or bearer configured the request is refused with 503 rather than silently passing content through unmapped. Content is capped at 5 MiB of decoded text.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
source_tool requiredstring
format requiredstringOne of csv, json
target_kindstringOne of control, obligation, evidence_item, ai_system
content requiredstringRaw export text (max 5 MiB)
column_mapobjectOptional source-column to canonical-field overrides
defaultsobjectOptional default field values applied to every mapped row

Responses

  • 200 Import summary (rows, mapped, errors) plus the mapped kye.connector.evidence_import.v1 batch
  • 400 invalid_json, source_tool_required, invalid_format, invalid_target_kind or content_required
  • 413 content_too_large — over the 5 MiB cap
  • 502 mapping_failed or import_service_unreachable — the mapping engine errored
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/evidence-coverageDecision-to-Evidence-Pack coverage for the caller's tenant

Joins the tenant's decision ledger to its sealed-pack index on decision_id and reports coverage as covered / decisions, plus the full list of decisions that resolve to no pack. Breakdowns are returned per verdict, per capability and per day. dimensions reports how many rows actually carry each decision axis, so an axis the ledger does not record reads as unrecorded rather than as an empty finding. Tenant scoping is applied in SQL on both sides of the join.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
sincequerystringWindow start (omit for the whole ledger)
untilquerystringWindow end (omit for the whole ledger)
limitqueryintegerMaximum uncovered decisions returned

Responses

  • 200 Coverage totals, per-verdict / per-capability / per-day breakdowns, axis-recording health, and the uncovered-decision list
  • 400 Invalid since/until, or since not before until
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/evidence-packsList evidence packs for the caller's tenant

Evidence Packs are signed audit-ready envelopes for each governed action window. This is the console-level registry view; sealing and signing mechanics live in the runtime.

Auth: Session (Clerk JWT)

Responses

  • 200 Pack rows (window, actions, sealed_at, status, sha256, compilation_seal, size_bytes, signers)
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/evidence-packsCreate a new draft evidence pack

Opens a draft pack for the chosen window (default daily) with zero actions and no signers.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
windowstringOne of daily, weekly, quarterly

Responses

  • 201 Created draft pack
  • 400 invalid_json
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/evidence-packs/{id}Fetch a single evidence pack

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 The pack row with parsed signers array
  • 404 Resource not found
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/evidence-packs/{id}Attest a pack (append signer)

Appends the caller as a signer; the pack advances from awaiting_signoff to attested once three signers have signed. Each caller may sign only once.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Request body

fieldtypedescription
actionstringOne of attest

Responses

  • 200 Updated signers list and status
  • 400 unsupported_action
  • 404 Resource not found
  • 409 already_signed_by_caller
  • 503 Required D1 / service binding not attached (db_binding_missing)

Approvals

GET/action-approvalsList the tenant's action proposals awaiting or decided review

Backs the GovernedUI approval queue (envelope kye.governedui.action_proposal.v1). Optional risk-level and approval-mode filters; KPI counts pending / approved / rejected / escalated proposals.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
risk_levelquerystring
approval_modequerystring
limitqueryinteger

Responses

  • 200 Proposal rows (proposal_id, actor_id, action_type, target_system, risk_level, approval_mode, state, proposed_at, decided_at, decided_by) plus kpi
  • 503 Required D1 / service binding not attached (db_binding_missing)

Actions

GET/actionsPDP-mediated action history for the caller's tenant

Console-level slice of the shared decisions ledger — each row maps a decision to its action_id, actor, capability, verdict, reason_code and decided_at, newest first.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
decisionquerystringFilter by decision verdict; "all" or absent returns every verdict
limitqueryinteger

Responses

  • 200 Action rows derived from the decisions ledger
  • 503 Required D1 / service binding not attached (db_binding_missing)

Agents

GET/agentsList agents with activity roll-up

Merges the explicit agents registry with per-agent activity derived from the decisions ledger (decision_count, last_seen_at, allow / review / deny counts). Registry rows are authoritative for metadata; agents observed only in decisions appear with null metadata. Sorted by last activity.

Auth: Session (Clerk JWT)

Responses

  • 200 Merged agent list with registry metadata and activity counts
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/agentsRegister a new agent

Mints a kye:agent URN. New agents start in the pilot lifecycle state with zero activity — no fabricated counts.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
label requiredstring
kindstringOne of agent, service, human, model
capability_idstring

Responses

  • 201 Registered agent in lifecycle_state pilot
  • 400 invalid_json or missing_label
  • 503 Required D1 / service binding not attached (db_binding_missing)

AICalls

GET/ai-callsPer-call ledger of model invocations

Cost, latency, token counts, purpose binding and evidence link for every model invocation; reconciles to provider invoices. Includes tenant-level KPI aggregates and distinct purpose / model lists for filter dropdowns.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
datequerystringRestrict to a single UTC day (YYYY-MM-DD)
purposequerystring
modelquerystring
min_costquerynumberOnly calls with cost at or above this value
limitqueryinteger

Responses

  • 200 Call rows plus kpis (calls, cost_total, tokens, avg_latency) and distinct purposes/models
  • 503 Required D1 / service binding not attached (db_binding_missing)

Webhooks

GET/webhooksList the caller's tenant's webhook endpoints

Returns endpoint metadata only. The signing secret is stored (KYE™ must sign each outbound delivery with it) but is NEVER returned here — only a short non-usable hint. Includes a 30-day delivery summary per endpoint.

Auth: Session (Clerk JWT)

Responses

  • 200 Endpoint list for the caller's tenant
  • 401 Missing or invalid bearer token
  • 503 Database binding unavailable
POST/webhooksRegister a webhook endpoint (signing secret returned once)

Registers an HTTPS endpoint for governance-event delivery. The signing secret is generated server-side and returned EXACTLY once — no endpoint will hand it back. Plain http is refused rather than downgraded. One active subscription per endpoint URL per tenant.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
endpoint_url requiredstringAbsolute HTTPS URL
signal_typesarrayDefaults to every canonical signal type when omitted

Responses

  • 201 Endpoint registered; signing secret returned once
  • 400 Invalid JSON, non-HTTPS or malformed URL, or unknown signal type
  • 401 Missing or invalid bearer token
  • 409 An active subscription already exists for this endpoint URL
  • 503 Database binding unavailable
GET/webhooks/{id}Read one webhook endpoint

Tenant-scoped read. An id belonging to another tenant resolves to 404, never to another tenant's record (§0.11).

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 Endpoint detail
  • 401 Missing or invalid bearer token
  • 404 Unknown endpoint for this tenant
DELETE/webhooks/{id}Disable a webhook endpoint

Disables rather than row-deletes: webhook_deliveries rows reference subscriber_id, so a hard delete would orphan the delivery history the §30 audit trail depends on. Deliveries stop immediately. Idempotent — disabling an already-disabled endpoint returns 200 with idempotent true.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 Endpoint disabled (or already disabled)
  • 401 Missing or invalid bearer token
  • 404 Unknown endpoint for this tenant

ApiKeys

GET/api-keysList API keys for the caller's tenant

Returns key metadata only — id, label, display prefix, scope, env, created_by/at, last_used_at, revoked_at. The secret is never stored; only its SHA-256 hash is persisted.

Auth: Session (Clerk JWT)

Responses

  • 200 Key metadata rows (no secrets)
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/api-keysCreate a new API key (secret returned once)

Mints a kye_<live|test>_ key with 192 bits of entropy. The plaintext secret is returned exactly once in this response; only its SHA-256 hash and 12-character display prefix are persisted. Unrecognised scopes are dropped; an empty scope defaults to runtime:read.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
label requiredstring
scopestringComma-separated scopes from the canonical kye:dictionary:api-key-scopes: runtime:read, runtime:write, runtime:*, evidence:read, evidence:write, attestation:write, directory:read, directory:write, admin:*
envstringOne of live, test
ttl_daysintegerOptional TTL in days. When set, the key carries expires_at = created_at + ttl_days and stops verifying after it. Omit for a long-lived key.
ip_allowlistarrayOptional CIDR/IP allowlist. When set, the verifier rejects a presented key from any source IP outside the list.

Responses

  • 201 Key metadata (incl. expires_at) plus the one-time plaintext secret, an Authority Finality receipt (patent-safe evidence), and a copy-now warning
  • 400 invalid_json, missing_label or invalid_ttl
  • 503 Required D1 / service binding not attached (db_binding_missing)
DELETE/api-keys/{id}Revoke an API key (governed hard-kill)

Sets status=revoked + revoked_at on the tenant's key and emits kye.admin.api_key.revoked.v1 to the WORM chain. Idempotent revocations of an already-revoked or unknown key return 404.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 Revoked — returns id, revoked_at and an Authority Finality receipt
  • 404 not_found_or_already_revoked
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/api-keys/{id}/rotateRotate an API key (mint successor + grace-window the predecessor)

Mints a successor key inheriting the predecessor's scope/env, marks the predecessor status=rotated with a bounded grace window (default 3600s, during which it still verifies before it expires), and emits kye.admin.api_key.rotated.v1 to the WORM chain. The plaintext successor secret is returned exactly once. The executable form of continuously re-earned authority (§0.33).

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Request body

fieldtypedescription
grace_secondsinteger
reasonstring

Responses

  • 201 Successor key metadata + one-time secret + Authority Finality receipt
  • 404 not_found_or_not_active
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/api-keys/agent/issueAgent-callable M2M issuance of a short-lived heartbeat-bound key

The doctrine-core path (§0.30 agent principal, §0.33 Authority Finality™): an agent obtains a SHORT-LIVED, HEARTBEAT-BOUND bearer key whose authority must be continuously re-earned. The key carries a near-term expiry and a heartbeat binding; missing the check-in loses the mandate (#160). Governed identically to human issuance (§0.3 evidence family + metering). Kill- switchable via KYE_KEY_AUTHORITY_AGENT_ISSUE_DISABLED.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
principal_id requiredstringThe agent principal (kye:agent:* / kye:principal:*) the key is bound to.
labelstring
scopestringComma-separated canonical scopes (defaults runtime:read).
ttl_secondsintegerShort TTL. Capped low — this is not a long-lived credential.
heartbeat_interval_secondsinteger

Responses

  • 201 Short-lived key metadata (incl. expires_at + heartbeat) + one-time secret + Authority Finality receipt
  • 400 invalid_json or missing_principal_id
  • 403 agent_issuance_disabled (kill-switch)
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/api-keys/agent/{id}/renewAgent-callable heartbeat renewal (re-earn the mandate)

Re-earns a heartbeat-bound key's authority: a renewal MUST pass a fresh admissibility check (§12 PDP) within the heartbeat window, extending expires_at by one interval and stamping last_heartbeat_at. A renewal outside the window is denied — the mandate is already lost and the key must be re-issued. This is authority as a STATE you continuously re-earn, not a grant you keep. Governed (§0.3) + metered (§23).

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 Renewed — new expires_at + Authority Finality receipt
  • 403 heartbeat_window_missed or not_a_heartbeat_key
  • 404 not_found_or_not_active
  • 503 Required D1 / service binding not attached (db_binding_missing)

Apps

GET/appsList the tenant's installed apps with KPI roll-up

Apps are tenant-installed AI agents, copilots, runbooks and partner-supplied integrations operating under the tenant's Operating Model. KPI counts active (calls in last 24h), shadow-mode and suspended apps.

Auth: Session (Clerk JWT)

Responses

  • 200 App rows (app_id, category, display_name, mode, status, calls_24h) plus kpi
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/appsInstall an app

Mints a kye:app URN. A freshly installed app starts active with zero call counts; shadow mode logs decisions without enforcing them.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
category requiredstringOne of ai_agent, copilot, runbook, integration, connector_app
display_name requiredstring
modestringOne of shadow, advisory, guarded, strict

Responses

  • 201 Installed app with its minted app_id
  • 400 invalid_json, invalid_category, display_name_required or invalid_mode
  • 503 Required D1 / service binding not attached (db_binding_missing)

AssuranceCards

GET/assurance-cardsList the tenant's assurance cards with KPI roll-up

Assurance cards are time-bounded signed attestations mapping real evidence to regulatory framework controls, on a 90-day rotation per §0.3. KPI counts active, expiring (within 30 days), expired cards and distinct frameworks covered.

Auth: Session (Clerk JWT)

Responses

  • 200 Card rows (card_id, framework, controls, scope, status, attested_at, expires_at, verifier_url) plus kpi
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/assurance-cardsGenerate a new assurance card

Mints a kye:assurance-card URN with honest timestamps — attested now, expiring 90 days later — and a public verifier URL. Controls must be a non-empty array of control identifiers.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
framework requiredstringOne of SOC2, ISO27001, ISO42001, EU_AI_ACT, DORA, FCA_OPRES, NIST_AI_RMF, OSCAL
controls requiredarray
scope requiredstring

Responses

  • 201 Created card with verifier_url and a 90-day expires_at
  • 400 invalid_json, invalid_framework, scope_required or controls_required_array
  • 503 Required D1 / service binding not attached (db_binding_missing)

AuditEvents

GET/audit-eventsQuery the tenant's append-only hash-chained audit trail

Each row stores prev_hash and a SHA-256 hash over the canonical JSON of (seq, event, actor, summary, at, prev_hash) so the chain is client-verifiable by recomputing from seq=1. Rows are returned newest first.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
familyquerystringEvent-name prefix filter
fromquerystring
toquerystring
qquerystringSubstring match across actor, summary and id
limitqueryinteger

Responses

  • 200 Event rows with seq, event, actor, summary, at, prev_hash, hash
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/audit-eventsAppend an event to the tenant's audit chain

Appends the next sequence entry, linking prev_hash to the prior row (or a 64-zero genesis) and hashing the canonical JSON. Normally driven by the runtime; exposed so admins can record compliance markers such as a manually attested evidence pack.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
event requiredstring
actorstringDefaults to the caller's email or user id
summarystring

Responses

  • 201 Appended event with its seq, prev_hash and hash
  • 400 invalid_json or missing_event
  • 503 Required D1 / service binding not attached (db_binding_missing)

BehaviourModel

GET/behaviour-modelLoad the tenant's latest behaviour-model revision

Returns the highest revision of the tenant's behaviour model (allowed actions, obligations, stop-conditions, escalation paths). A tenant with no model yet gets revision 0 with empty rows.

Auth: Session (Clerk JWT)

Responses

  • 200 Latest revision with rows, signed flag, updated_at, updated_by
  • 503 Required D1 / service binding not attached (db_binding_missing)
PUT/behaviour-modelReplace the tenant's behaviour model (new immutable revision)

Inserts a new unsigned revision; prior revisions are kept immutable for sign-off and replay. Rows are shape-validated and capped at 500; string fields are length-clamped.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
rows requiredarray

Responses

  • 200 New revision number, accepted row count and updated_at
  • 400 invalid_json or rows_array_required
  • 503 Required D1 / service binding not attached (db_binding_missing)

Billing

GET/billingAll-in-one billing view (subscription + invoices + seats)

Reads the canonical kye_grants store (binding KYE_GRANTS_DB, populated by the commercial-lifecycle worker's Stripe projection): latest subscription, up to 50 invoices with hosted Stripe URLs, and attestation seats. Returns 503 db_binding_missing until the KYE_GRANTS_DB binding is attached to the Pages project.

Auth: Session (Clerk JWT)

Responses

  • 200 subscription (or null), invoices, seats
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/billingRun the BPS fee simulator

Pure function per §23 §4 — 3 basis points on the transaction value with a £5 floor and £50 cap. The only supported action is bps_simulate.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
action requiredstringOne of bps_simulate
value_penceintegerTransaction value in pence
currencystring
action_classstring

Responses

  • 200 Fee breakdown: rate_bps, computed_fee_pence, final_fee_pence, was_floored, was_capped
  • 400 invalid_json or unsupported_action
  • 503 Required D1 / service binding not attached (db_binding_missing)

Connectors

GET/connectorsList the tenant's installed connectors with KPI roll-up

Connectors bring outside-system events into the tenant's KYE™ evidence stream; the canonical kind vocabulary is kye:dictionary:connectors. KPI counts healthy, degraded and disconnected connectors.

Auth: Session (Clerk JWT)

Responses

  • 200 Connector rows (connector_id, kind, profile_family, display_name, status, last_harvest_at, events_24h) plus kpi
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/connectorsInstall a connector

Mints a kye:connector URN. A freshly installed connector is disconnected until its first authenticated harvest — honest initial state, no fabricated health.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
kind requiredstring
display_name requiredstring
profile_family requiredstringOne of payments, open_finance, identity, agent_runtime, commerce, compliance_evidence, security_siem, health, insurance, pension, utilities, legal, open_data

Responses

  • 201 Installed connector in disconnected status
  • 400 invalid_json, invalid_kind, display_name_required or invalid_profile_family
  • 503 Required D1 / service binding not attached (db_binding_missing)

Delegations

GET/delegationsList active and revoked delegations

Authority flows actor to principal to subject under a scope with an expiry; attenuations are separate rows referencing a parent_id. State is derived lazily — revoked when revoked_at is set, expired when past expires_at, otherwise active.

Auth: Session (Clerk JWT)

Responses

  • 200 Delegation rows with derived state (active / expired / revoked)
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/delegationsCreate a new delegation

Issues a delegation with a TTL (default 365 days, max 730). When attenuating via parent_id, the parent must exist and belong to the caller's tenant.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
actor requiredstring
principal requiredstring
subject requiredstring
scope requiredstring
parent_idstringParent delegation to attenuate
ttl_daysinteger

Responses

  • 201 Created delegation with expires_at
  • 400 invalid_json or missing_fields
  • 404 parent_not_found — attenuation parent missing or not owned by this tenant
  • 503 Required D1 / service binding not attached (db_binding_missing)
DELETE/delegations/{id}Revoke a delegation (soft-delete)

Sets revoked_at / revoked_by and flips state to revoked; an optional reason is recorded.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring
reasonquerystringOptional revocation reason

Responses

  • 200 Revoked — returns id and revoked_at
  • 404 not_found_or_already_revoked
  • 503 Required D1 / service binding not attached (db_binding_missing)

DriftEvents

GET/drift-eventsList drift events with open/closed counts

The runtime opens drift events automatically; humans close them. Optional state filter; counts power the tab badges.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
statequerystringAbsent returns both

Responses

  • 200 Event rows (type, actor, severity, opened, closed, resolution) plus open/closed counts
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/drift-eventsOpen a new drift event

Records a meaning-continuity drift marker. Unrecognised types default to meaning_broken and unrecognised severities to P2.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
typestringOne of meaning_broken, obligation_unmet, scope_overflow
severitystringOne of P0, P1, P2
actorstring
detailstring

Responses

  • 201 Opened event with its id and opened timestamp
  • 400 invalid_json
  • 503 Required D1 / service binding not attached (db_binding_missing)
PATCH/drift-events/{id}Close (resolve) a drift event

Marks the event closed with a resolution; unrecognised or absent resolutions default to remediated. The resolver identity is recorded.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Request body

fieldtypedescription
resolutionstringOne of reconfirmed, revoked, remediated

Responses

  • 200 Closed — returns id, closed timestamp, resolution, resolved_by
  • 404 not_found_or_already_closed
  • 503 Required D1 / service binding not attached (db_binding_missing)

Decisions

GET/refusal-mapRefusal clusters for the caller's tenant

Returns only the non-allow decisions (deny, reject, revoke, quarantine, require_approval, require_human_review) for the caller's tenant, clustered by reason_code x capability x actor_entity_id and ranked on each axis separately. Each cluster reports how many of its refusals resolve to an Evidence Pack. dimensions reports the real recording coverage of each axis over the refused set — reason_code is null on historical rows and populates from newer decisions forward, and a null is never inferred or defaulted. Tenant scoping is applied in SQL.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
sincequerystringWindow start (omit for the whole ledger)
untilquerystringWindow end (omit for the whole ledger)
limitqueryintegerMaximum clusters returned

Responses

  • 200 Refusal totals, per-axis rankings, axis-recording health, and the reason_code x capability x actor clusters
  • 400 Invalid since/until, or since not before until
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/decisionsDecision records for the calling tenant

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
sincequerystringLower bound on decision time
decisionquerystringFilter by outcome
actorquerystring
limitqueryinteger

Responses

  • 200 Matching decision records
  • 400 `invalid_since`
  • 503 Required D1 / service binding not attached (db_binding_missing)

Listings

GET/my-listingsList the tenant's directory listings with KPI roll-up

Listings are the tenant's public-facing entries in the KYE™ Directory — rule packs, agents, connectors, assurance cards — each requiring moderation before becoming visible. KPI counts published, in-review and draft listings plus removals in the last 90 days.

Auth: Session (Clerk JWT)

Responses

  • 200 Listing rows (listing_id, listing_type, title, status, views_30d) plus kpi
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/my-listingsCreate a new directory listing (starts as draft)

Mints a kye:listing URN. A new listing starts as draft — it must be submitted and pass moderation before becoming visible; view counts start at zero.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
listing_type requiredstringOne of rule_pack, agent, connector, assurance_card
title requiredstring

Responses

  • 201 Created draft listing
  • 400 invalid_json, invalid_listing_type, title_required or title_too_long
  • 503 Required D1 / service binding not attached (db_binding_missing)

Partners

GET/partnersList partners under tenant authority

Auth: Session (Clerk JWT)

Responses

  • 200 Partner rows (name, class, assurance tier, status, since, onboarded_by)
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/partnersOnboard a new partner

Registers a third-party partner under the tenant's authority. Unrecognised assurance values default to Tier 3 and unrecognised statuses to active.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
name requiredstring
class requiredstring
assurancestringOne of Tier 1, Tier 2, Tier 3
statusstringOne of active, suspended, off-boarded

Responses

  • 201 Onboarded partner with its minted id
  • 400 invalid_json or missing_fields
  • 503 Required D1 / service binding not attached (db_binding_missing)

Plugins

GET/pluginsList the tenant's installed plugins with KPI roll-up

Plugins are tenant-side extensions of the protocol — PDP rule packs, conformance probes, custom obligations, SDK shims. KPI counts enabled, failing and update-available plugins.

Auth: Session (Clerk JWT)

Responses

  • 200 Plugin rows (plugin_id, kind, display_name, version, state, health, invocations_24h, update_available) plus kpi
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/pluginsInstall a plugin

Mints a kye:plugin URN. A freshly installed plugin is disabled until the tenant enables it in the decision flow — honest initial state. Version must be semver-shaped.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
kind requiredstringOne of rule_pack, conformance_probe, obligation, sdk_shim
display_name requiredstring
versionstring

Responses

  • 201 Installed plugin in disabled state
  • 400 invalid_json, invalid_kind, display_name_required or invalid_version
  • 503 Required D1 / service binding not attached (db_binding_missing)

PurposePermissions

GET/purpose-permissionsList Purpose Permission grants for the caller's tenant

Every action under a KYE-governed agent must cite an issued Purpose Permission. Status is derived per row — revoked, pending_reconfirm (past expiry), expiring (within 30 days) or active.

Auth: Session (Clerk JWT)

Responses

  • 200 Grant rows (grantee, purpose, scope, expires, derived status, issued/revoked/reconfirmed audit fields)
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/purpose-permissionsIssue a new Purpose Permission

Issues an active grant with a TTL (default 90 days, max 365). The issuer identity is recorded from the session.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
grantee requiredstring
purpose requiredstring
scope requiredstring
ttl_daysinteger

Responses

  • 201 Issued grant with expires timestamp
  • 400 invalid_json or missing_fields
  • 503 Required D1 / service binding not attached (db_binding_missing)
PATCH/purpose-permissions/{id}Reconfirm a Purpose Permission (extend expiry)

Extends the grant by ttl_days (default 90, max 365) from now, records last_reconfirmed_at and resets status to active. Revoked grants cannot be reconfirmed.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Request body

fieldtypedescription
ttl_daysinteger

Responses

  • 200 New expires, last_reconfirmed_at and reconfirmed_by
  • 404 not_found_or_revoked
  • 503 Required D1 / service binding not attached (db_binding_missing)
DELETE/purpose-permissions/{id}Revoke a Purpose Permission (soft-delete)

Sets revoked_at / revoked_by and flips status to revoked; an optional reason is recorded.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring
reasonquerystringOptional revocation reason

Responses

  • 200 Revoked — returns id and revoked_at
  • 404 not_found_or_already_revoked
  • 503 Required D1 / service binding not attached (db_binding_missing)

Replay

GET/replayList the tenant's replay runs with KPI roll-up

Replay runs reference an evidence_pack_id and are signed via kye.replay.proof.v1; divergence opens a Resilience Loop ticket automatically. KPI reports replays in the last 24h, overall match rate, divergences in the last 30 days and signature failures.

Auth: Session (Clerk JWT)

Responses

  • 200 Run rows (replay_id, evidence_pack_id, decision_id, verdict, signature_ok, requested/completed timestamps) plus kpi
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/replayQueue a new replay of an evidence pack

Queues a run in the pending verdict — only the Replay Engine may resolve it to verified, diverged or signature_failed. Signature status is unknown (false) until the engine runs.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
evidence_pack_id requiredstring
decision_idstringOptional single decision to replay

Responses

  • 201 Queued run in pending verdict
  • 400 invalid_json or evidence_pack_id_required
  • 503 Required D1 / service binding not attached (db_binding_missing)

Risk

GET/riskList the tenant's risk assessments with KPI roll-up

Assessments are tenant-scoped, audit-chained and framework-floor-governed. KPI counts prohibited verdicts in the last 24h plus high, limited and minimal tiers across the latest 500 assessments.

Auth: Session (Clerk JWT)

Responses

  • 200 Assessment rows (assessment_id, subject_id, subject_class, tier, score, reason_codes, framework_floor, effective_at) plus kpi
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/riskRegister a new risk assessment for a subject

Records the assessment exactly as submitted — no fabricated scores or tier upgrades. Score is an integer 0-100.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
subject_id requiredstring
subject_class requiredstringOne of decision, agent, capability, scenario, operating_model, deal, rule_pack, connector
tierstringOne of minimal, limited, high, unacceptable, prohibited
scoreinteger
reason_codesarray
framework_floorstringOne of eu_ai_act, dora, gdpr, nist_ai_rmf, iso_42001, fca_opres, pci_dss, sox, none

Responses

  • 201 Recorded assessment with its minted assessment_id
  • 400 invalid_json, subject_id_required, invalid_subject_class, invalid_tier, score_must_be_0_to_100 or invalid_framework_floor
  • 503 Required D1 / service binding not attached (db_binding_missing)

Scopes

GET/scopesList the tenant's declared scopes with KPI roll-up

Scopes are capability + dataset + jurisdiction triples bounding what an agent may do within a granted purpose. KPI counts active scopes, scopes bound to at least one purpose, total datasets and distinct jurisdictions.

Auth: Session (Clerk JWT)

Responses

  • 200 Scope rows (scope_id, capability, datasets, jurisdiction, bound_purposes, status) plus kpi
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/scopesDeclare a new scope

Mints a kye:scope URN. A freshly declared scope starts active with zero purpose bindings — bindings are created through the Purpose Permission rail. Datasets accept an array or comma-separated string.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
capability requiredstringOne of read, write, delete, verify, attest, audit, delegate
jurisdictionstringOne of GB, EU, US, SG, AU
datasetsobject

Responses

  • 201 Declared scope with zero purpose bindings
  • 400 invalid_json, invalid_capability or invalid_jurisdiction
  • 503 Required D1 / service binding not attached (db_binding_missing)

StateMachines

GET/state-machine-assignmentList the tenant's state machine assignments with KPI roll-up

An assignment binds a tenant state machine to a specific entity and tracks its current state. KPI counts assignments, distinct bound entities, transitions in the last 24h and assignments with no transition in 30 days (stuck); includes distinct entity classes for the filter bar.

Auth: Session (Clerk JWT)

Responses

  • 200 Assignment rows (assignment_id, state_machine_id, entity_id, entity_class, current_state, state_since, last_transition_at, transitions_24h) plus kpi and entity_classes
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/state-machine-assignmentAssign a state machine to an entity

Verifies the referenced state machine exists and belongs to the caller's tenant, then creates the assignment in the given initial state (default pending) with zero transitions.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
state_machine_id requiredstring
entity_id requiredstring
entity_class requiredstringLowercased; non-alphanumerics become underscores
initial_statestring

Responses

  • 201 Created assignment in its initial state
  • 400 invalid_json, state_machine_id_required, entity_id_required or entity_class_required
  • 404 state_machine_not_found_or_not_owned
  • 503 Required D1 / service binding not attached (db_binding_missing)

Stripe

POST/stripe/checkout-sessionCreate a Stripe Checkout Session for the tenant's subscription

Creates a subscription-mode Checkout Session via the Stripe REST API, stamping the tenant id into client_reference_id and metadata, and persists a checkout intent so the webhook can correlate checkout.session.completed back to the tenant. The client redirects to the returned url.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
price_id requiredstringStripe Price id
success_pathstringReturn path on success (default /billing.html?checkout=ok)
cancel_pathstringReturn path on cancel (default /billing.html?checkout=cancel)

Responses

  • 200 session_id, hosted checkout url and expires_at
  • 400 invalid_json or bad_price_id
  • 502 stripe_error — Stripe API rejected the request
  • 503 Required D1 / service binding not attached (db_binding_missing)
GET/stripe/invoicesList invoices for the caller's tenant

Reads the canonical invoices table in the kye_grants store (binding KYE_GRANTS_DB), populated by the commercial-lifecycle worker's Stripe projection. Hosted Stripe invoice URLs are passed straight through.

Auth: Session (Clerk JWT)

Responses

  • 200 Invoice rows with totals, status, hosted URL and billing period (latest 100)
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/stripe/portal-sessionCreate a Stripe Customer Portal session

Looks up the tenant's stripe_customer_id from its most recent non-cancelled subscription in the kye_grants store, then creates a Billing Portal session returning to /billing.html.

Auth: Session (Clerk JWT)

Responses

  • 200 Portal url for the caller's Stripe customer
  • 404 no_active_subscription — create one via /stripe/checkout-session first
  • 502 stripe_error — Stripe API rejected the request
  • 503 Required D1 / service binding not attached (db_binding_missing)

Usage

GET/usageUsage and billing aggregates for the caller's tenant

Month-to-date decision count, evidence packs created, AI cost and token totals, a 4-week weekly decision sparkline, and a 30-day meter summary grouped by meter class. Honest zeros when tables are empty.

Auth: Session (Clerk JWT)

Responses

  • 200 decisions_month, evidence_packs_month, ai_cost_month, ai_tokens_month, weekly_decisions, meters
  • 503 Required D1 / service binding not attached (db_binding_missing)

WhiteLabel

GET/white-label-configRead the active white-label config for the caller's tenant

Resolves the brand config through the tenant's active consultant link, preferring a tenant-specific row over the consultant-wide default. Read-only.

Auth: Session (Clerk JWT)

Responses

  • 200 Brand config (brand_name, colours, logo_url, domain, support_email, legal_footer)
  • 401 Missing or invalid bearer token
  • 404 No active white-label config applies to this tenant
  • 503 db_binding_missing or white-label tables not provisioned

Widgets

GET/widgetsList deployed widgets for the caller's tenant

Each widget carries its own licence and revocation path; the embed CDN returns 410 once a widget is revoked.

Auth: Session (Clerk JWT)

Responses

  • 200 Widget rows (kind, host, pages, licence, status, installed, views_30d, revoked_at)
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/widgetsGenerate a new widget embed

Mints a kye:widget URN with a fresh 24-character embed key and returns the ready-to-paste embed snippet pointing at the widget CDN.

Auth: Session (Clerk JWT)

Request body (required)

fieldtypedescription
kind requiredstring
hoststringOptional host the embed is installed on
licencestringOne of commercial, evaluation, open

Responses

  • 201 Created widget with embed_key and embed_snippet
  • 400 invalid_json or missing_kind
  • 503 Required D1 / service binding not attached (db_binding_missing)
POST/widgets/{id}Rotate a widget's embed key

Issues a fresh 24-character embed key for an unrevoked widget. The only supported action is rotate.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Request body (required)

fieldtypedescription
action requiredstringOne of rotate

Responses

  • 200 New embed_key for the widget
  • 400 unsupported_action
  • 404 not_found_or_revoked
  • 503 Required D1 / service binding not attached (db_binding_missing)
DELETE/widgets/{id}Revoke a widget (soft-delete)

Flips status to revoked and sets revoked_at; the embed CDN then serves 410 for this widget.

Auth: Session (Clerk JWT)

Parameters

nameintypedescription
id requiredpathstring

Responses

  • 200 Revoked — returns id and revoked_at
  • 404 not_found_or_already_revoked
  • 503 Required D1 / service binding not attached (db_binding_missing)