Skip to content

Design — API surface

  • Date: 2026-04-16
  • Depends on: ADR-0001..0005
  • Base: https://puccha.hxlab.io/api (same-origin for all surfaces; no CNAME alias needed).
  • Auth: cookie session (app) or Authorization: Bearer puccha_live_... (API) or widget JWT (Authorization: Bearer wjwt_...).
  • Tenant resolution (ADR-0002): subdomain OR /c/{tenant}/... path OR API key owner. Body/query tenant params rejected.
  • Encoding: JSON request/response, UTF-8. Streams use SSE (text/event-stream).
  • IDs: nanoid (doc_..., usr_..., tnt_...).
  • Errors: RFC 9457 Problem Details + stable code enum.
  • Idempotency: Idempotency-Key header on POST; server replays first response within 24h.
  • Rate limits: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset on every response.
Method Path Purpose
POST /api/auth/signup Self-serve tenant+user creation (Free tier only)
POST /api/auth/signin Email + password / magic link
GET /api/auth/callback/{provider} OAuth callback
GET /api/public/tenant/{slug} Tenant presence check (no details leaked)
Method Path Purpose
POST /api/widget/session Exchange tenant slug + origin + optional public key for short-TTL JWT
GET /api/widget/config Tenant branding (accent, fonts, theme) + enabled features
POST /api/widget/events Analytics events (no query text)
POST /api/answer One-shot answer (JSON) — shared with app
POST /api/chat Streaming chat (SSE) — shared with app
Method Path Role Purpose
GET /api/docs Viewer+ List docs (paginated, filter by tag/collection/status)
GET /api/docs/{id} Viewer+ (ACL) Get doc + chunks
POST /api/docs Editor+ Create doc (MD body or multipart upload)
PATCH /api/docs/{id} Editor+ Update doc body/metadata/ACL
DELETE /api/docs/{id} Editor+ Soft-delete (7d grace)
POST /api/docs/{id}/reindex Editor+ Force re-ingest
POST /api/docs/batch Editor+ Bulk import (MD zip, up to 100 files)
GET /api/docs/{id}/status Viewer+ Ingest status + progress
Method Path Purpose
POST /api/search Lexical+semantic hybrid search (no LLM); returns chunks
POST /api/answer Retrieve → rerank → single answer with citations
POST /api/chat Retrieve → rerank → streaming chat (multi-turn)
GET /api/answer/{id} Fetch a past answer (admin plane only; no conversation scrollback in v1)
Method Path Role Purpose
GET /api/tenant Viewer+ Current tenant details
PATCH /api/tenant Admin+ Name, brand accent, allowed origins, settings
GET /api/tenant/members Admin+ List members
POST /api/tenant/invitations Admin+ Create invitation
POST /api/tenant/invitations/{id}/resend Admin+ Resend
DELETE /api/tenant/invitations/{id} Admin+ Revoke
POST /api/tenant/invitations/accept public (token) Accept invite
PATCH /api/tenant/members/{id} Admin+ Change role
DELETE /api/tenant/members/{id} Admin+ Remove
POST /api/tenant/owner-transfer Owner Transfer ownership
Method Path Role Purpose
GET /api/tenant/api-keys Admin+ List (prefix + metadata; never the key)
POST /api/tenant/api-keys Admin+ Create (returns full key once)
DELETE /api/tenant/api-keys/{id} Admin+ Revoke
Method Path Purpose
GET /api/billing/subscription Current plan + usage
POST /api/billing/checkout Start Stripe Checkout session for plan upgrade
POST /api/billing/portal Open Stripe billing portal
POST /api/billing/webhook Stripe webhook (signed)
Method Path Role Purpose
GET /api/insights/gaps Admin+ Top queries with low confidence or IDK
GET /api/insights/usage Admin+ Daily/weekly/monthly counters
GET /api/insights/audit Admin+ Audit log export (CSV or JSON)

Internal admin plane (C2G staff; Zero Trust gated)

Section titled “Internal admin plane (C2G staff; Zero Trust gated)”
Method Path Purpose
GET /admin/tenants Cross-tenant list
POST /admin/tenants Provision tenant
PATCH /admin/tenants/{id} Suspend / unsuspend / change plan
DELETE /admin/tenants/{id} Hard delete
POST /admin/tenants/{id}/impersonate Issue a tenant-scoped staff session (audit-logged)
GET /admin/health System health, queue depth, error rates

Request:

{
"query": "How long does ISO 27001 certification usually take?",
"locale": "en",
"mode": "answer",
"conversationId": null
}

Response (200):

{
"id": "ans_7f...",
"answer": "A typical ISO 27001 certification takes 3–6 months...",
"sources": [
{
"id": 1,
"url": "https://puccha.hxlab.io/c/acme/docs/iso-27001#timeline",
"title": "ISO 27001 · Timeline",
"updatedAt": "2026-04-10T08:12:00Z",
"snippet": "Stage 1 audit typically occurs..."
}
],
"confidence": 0.84,
"idk": false,
"latencyMs": 1284
}

IDK example (200):

{
"id": "ans_7g...",
"answer": "I don't have documentation covering that in this knowledge base. Please contact your admin or the ISMS lead.",
"sources": [],
"confidence": 0.12,
"idk": true,
"latencyMs": 640
}

Request:

{
"conversationId": "conv_abc",
"messages": [
{ "role": "user", "content": "Follow-up on that..." }
],
"locale": "en"
}

Response: SSE stream, events per AI SDK UI v5 data-stream protocol (toUIMessageStreamResponse()). Includes text deltas + a final structured data-citations event.

Request:

{ "query": "ISO 27001 timeline", "locale": "en", "topK": 10, "filters": { "collection": ["policies"], "tags": ["iso-27001"] } }

Response: ranked array of chunk objects (title, url, snippet, score, sourceDoc). No LLM call.

{
"type": "https://puccha.hxlab.io/problems/acl-denied",
"title": "Access denied",
"status": 403,
"code": "acl_denied",
"detail": "This document is restricted to admins in your organization.",
"instance": "req_abc123",
"retryable": false
}

Stable code values include: auth_required, auth_invalid, tenant_suspended, acl_denied, not_found, validation_failed, rate_limited, quota_exceeded, ingest_failed, llm_unavailable, idempotency_conflict, origin_not_allowed.

  • Implicit v1 for launch (no /v1/ prefix). Breaking changes ship under /v2/ later.
  • Additive changes (new fields, new endpoints) are not versioned.
  • Deprecated fields flagged in Deprecation + Sunset headers for ≥ 6 months before removal.
  • Generated from Zod schemas in packages/types via zod-to-openapi.
  • Published at https://puccha.hxlab.io/docs/openapi.json.
  • Swagger UI at https://puccha.hxlab.io/docs/api.