Developers
Integrating against a reference that drifts from what actually answers is a rewrite waiting to happen.
This page lists exactly what answers on the wire today, each with the repository path it is observed at. Nothing here is a roadmap wearing a reference's clothes.
The one public endpoint
One HTTP endpoint is served on the public domain today: the demo-booking form’s handler. It accepts form-encoded or multipart submissions and answers with a reference you can quote back at us.
POST https://honestas.ai/api/demo
Content-Type: application/x-www-form-urlencoded (multipart/form-data also accepted)
email=…&org=…&question=…
intent=demo|assessment|paper|tier &intentLabel=… &assessment=1
200 OK
{ "reference": "DEMO-9C41AE", "messageId": "…" }The reference is "DEMO-" plus the first six hex characters of an HMAC-SHA256 (key honestas-demo-ref) over email|org|YYYY-MM-DD, upper-cased — deterministic per email and organisation per UTC day, so resubmitting the form returns the same reference. The idempotency is by construction, not by a stored deduplication table. Recompute it yourself: the key and the seed are both above, and the answer should match the one we sent you character for character.
intent is what the visitor came for, declared rather than inferred: one of demo, assessment, paper or tier. An unrecognised or absent value falls back to assessment if the compatibility field below is set and to demo otherwise — never to an invented label. intentLabel is the free-text thing being asked for (the document title, the pricing tier), truncated to 200 characters. Both exist because the operator inbox previously received every submission subject-lined as a booking, so a person who asked for a document was answered about a meeting. assessment=1 is the older boolean form of intent=assessment. It is still read, and still posted by our own form, so a build of the server that predates intent keeps working. It is a compatibility field, not a second contract: where both are present, intent wins.
| Code | Status | When |
|---|---|---|
| INVALID_SUBMISSION | 400 | a required field is missing or malformed |
| EMAIL_CONFIG_MISSING | 500 | the sending credential is not configured — the submission is refused, not queued |
| OPERATOR_EMAIL_NOT_CONFIGURED | 500 | no operator inbox is configured to receive it |
| RESEND_API_FAILED | 502 | the delivery provider answered with a failure |
| RESEND_PARSE_ERROR | 502 | the delivery provider answered with something unparseable |
| UNEXPECTED_ERROR | 500 | anything else — still typed, never a bare stack trace |
Every failure body is { error, code, message, troubleshooting[] } — a code a machine can branch on and steps a person can follow, in the same response. The steps a visitor receives are visitor-safe by construction: operator detail — env var names, secret locations, the provider’s raw error body — goes to the server log, never over the wire. A page arguing for evidence integrity does not hand strangers the path to its own credentials.
the demo endpoint(shipped)honestas-marketing/src/app/api/demo/route.ts
The console plane is deliberately not internet-reachable
The console at app.honestas.ai proxies /api/bff/* to a backend that runs in-cluster and is not reachable from the internet — its deployment manifest carries no Gateway and no VirtualService, and says so. The proxy forwards only the authorization, content-type and accept headers, and never attaches a service credential of its own — a request that arrives unauthenticated leaves unauthenticated.
Route policy is a build gate, not a convention: every /api/ route must register the scope middleware, and only /healthz and /readyz are exempt — by exact path, not by prefix.
the console proxy(shipped)honestas/src/app/api/bff/[...path]/route.tsthe route-policy gate(shipped)honestas-bff/src/server.ts
Refusals are typed
A refusal is a machine code and a verbose message, never a placeholder value and never a silently widened read. Every code below is served on the wire today.
| Code | Status | What it means |
|---|---|---|
| AUTH_MISSING · AUTH_SCHEME | 401 | no Authorization header, or a scheme other than Bearer |
| JWT_MALFORMED · JWT_BAD_HEADER · JWT_ALG_UNSUPPORTED · JWT_SIGNATURE_INVALID · JWT_BAD_PAYLOAD | 401 | the token fails structural or signature verification — each failure names its exact stage |
| JWT_EXPIRED · JWT_NOT_YET_VALID | 401 | the token is outside its validity window |
| JWT_SUBJECT_INVALID · JWT_ORG_INVALID | 401 | the subject or organisation claim does not resolve to a usable identity |
| SCOPE_PREDICATE_MISSING | 403 | the tenant/owner predicate cannot be resolved — the body carries resolved[], missing[] and troubleshooting[] rather than guessing an id |
| CAPABILITY_NOT_BUILT | 501 | a real, scoped, verified route whose capability is designed and not built — it says so, instead of returning an empty list that looks like data |
| UNO_DISPATCH_FAILED · UNO_RESPONSE_UNPARSEABLE · UNO_UNREACHABLE | 502 | the outbound dispatch to the platform orchestrator failed, and the failure mode is named |
| DEADLINE_EXCEEDED | 504 | the bounded 20-second dispatch deadline elapsed — bounded by design, never an unbounded wait |
| ROUTE_NOT_FOUND | 404 | no such route behind the proxy — typed like every other refusal |
The console itself renders five further states that never travel on the wire: NOT_SIGNED_IN, SESSION_REJECTED, BFF_UNREACHABLE, GRANT_PROVENANCE_MISSING (a ledger write with no resolvable grantor — the console refuses to offer the control at all) and FAULT_HTTP (an HTTP answer outside every contracted shape, named by its status; its verdict word is FAULT, not REFUSED, because nothing refused anything).
the dispatch refusals(shipped)honestas-bff/src/lib/uno.tsthe scope refusal(shipped)honestas-bff/src/lib/scope.tsthe auth refusals(shipped)@adverant/auth-middleware
The session shape, honestly
A signed-in console session can read its own resolved scope back. The response states what the platform actually minted — including the axis it does not mint yet.
GET /api/v1/session (behind the console proxy; signed-in only)
200 OK
{
"scope": { "tenant": "…", "owner": "…", "unit": null },
"identity": { "userId": "…", "email": "…", "organizationId": "…",
"organizationSlug": "…", "roles": [ … ], … },
"axes": { … }
}The unit axis is honest about its own state: the platform mints no business_unit_id claim yet, and the response says so rather than inventing one. An axis that cannot be resolved is reported as unresolved, never filled with a placeholder.
the session route(shipped)honestas-bff/src/routes/session.ts
Dispatch is outbound, not offered
There is no public dispatch endpoint on any honestas domain. All compute dispatch is outbound — from the console backend to the platform orchestrator, in-cluster. The one dispatch a signed-in caller can trigger today is an echo: a real tier-1 round trip through the orchestrator and back, which exists so the seam can be proven live rather than asserted.
POST /api/v1/dispatch/echo (behind the console proxy; signed-in only)
200 OK
{ "ok": true, "tier": 1, "dispatchId": "…", "result": { … } }
— or the UNO_DISPATCH_FAILED / UNO_RESPONSE_UNPARSEABLE / UNO_UNREACHABLE
refusals from the table above, verbatim.the echo dispatch(shipped)honestas-bff/src/routes/console.ts
The full ladder — how a dispatch is tiered, routed to a model and turned into evidence — is laid out on the architecture page.
Agent-to-agent
Conformance platform endpoint(shipped)ROS/src/routes/public/beacon-agent-public.ts
The conformant A2A v0.3.0 endpoint and its executable conformance suite live in the platform repository — it is a platform endpoint, and no honestas repository serves an A2A endpoint on a honestas domain today.
the conformance suite(shipped)ROS/src/__tests__/routes/beacon-a2a-conformance.test.ts
Getting access
Access is scoped through a conversation rather than a self-serve signup. A developer who wants access against the real thing books the demo — that is the honest current path, and this page will state a different one when a different one exists.
Four working papers. A person sends the one you pick — no download wall, and no meeting is booked.
Work out what this costs