Neither Context Layer API (MVP 1.3)
Customer REST surface: BC-scoped context reads (GET /api/context/*) and push ingest (POST /api/ingest*, batch, jobs, delete).
Workspace Context API keys use Authorization: Bearer sk_ctx_*. Dashboard key management and rate-limit overrides use the member JWT from the Neither app (same bearer style, not a Context API key).
Authentication
- Context API key (
ContextApiKey): sendAuthorization: Bearer sk_ctx_…on all/api/context/*and/api/ingest*routes unless noted. - Member JWT (
MemberJWT): required for/api/workspaces/{workspaceId}/context-api-keys*,/context-api-keys/{keyId}/rotate,/context-api-runtime,/context-api-webhooks*, rate limits, dashboard, and revoke. - Public demo (
POST /api/public/context-demo): no API key; rate-limited; not for production integrations.
BC scope and reads
Context API keys may be bound to a business context (BC). BC-scoped keys only see data for that BC (403 bc_scope_violation when crossing BCs). BC-scoped keys must not call ingest, batch, job poll, or delete (403 with scoped_key_ingest_forbidden on ingest family).
Capabilities
Each Context API key carries capabilities (read, ingest, delete, …). Missing capability returns 403 with { "error": "forbidden_capability", "required_capability": "…" }.
Errors
Common error string values include: invalid_or_missing_bearer, invalid_api_key, schema_validation_failed, bc_scope_violation, scoped_key_ingest_forbidden, forbidden_capability, workspace_mismatch, rate_limited, key_lookup_failed.
Rate limits
Throttled responses return 429 with JSON body { "error": "rate_limited", "class": "read"|"ingest"|"batch", "limit": <int>, "retry_after_seconds": <int> } and may include Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset headers when enabled for the route.
Request IDs and correlation
All bearer MVP13 responses include X-Request-ID (UUID v4). Clients may send X-Request-Id; when it matches UUID v4 it is echoed, otherwise the API generates one.
X-Correlation-ID is echoed when supplied (max 256 chars); otherwise it mirrors the request id. JSON bodies may duplicate request_id / correlation_id for support tooling.
Idempotency
Optional Idempotency-Key header on POST /api/ingest and POST /api/ingest/batch (max 256 chars). Replay hashes use canonical JSON (object keys sorted recursively; arrays preserve order). Same workspace + API key + route + key + equivalent JSON body replays the stored 200 response within ~72h TTL. Same key with a different body returns 409 with error: idempotency_conflict. Concurrent in-flight duplicates return 409 with error: idempotency_in_progress. Expired pending rows are reclaimed so stalled clients can retry safely.
Webhooks (completion)
Workspace admins register HTTPS callbacks via member JWT routes (/context-api-webhooks). URLs must pass TLS/host guards and DNS-aware SSRF checks (resolved targets cannot be private loopback, link-local, ULA, IPv4-mapped-private, or cloud-metadata). Signing secrets are encrypted at rest; create/rotate returns plaintext once. When an MVP13 push-ingest job is completed, failed, or cancelled, the API enqueues one signed POST per matching enabled endpoint (mvp13.ingest.job.terminal; filter via environment_filter). Dedupe is scoped per (ingest_job_id, endpoint_id) pair. Verify X-Neither-Signature = v1=<hex> for HMAC_SHA256(secret, ${unix_ts}.${raw_utf8_body}) alongside X-Neither-Timestamp. GET /api/ingest/jobs/{id} remains supported.
Idempotency completion failures
Rare 503 with error: idempotency_completion_failed means ingest side-effects likely succeeded but the durable idempotency row could not be finalized — retain job_id / source_document_id / support IDs; coordinate before blindly reusing the same Idempotency-Key.
Runtime sandbox mode
Workspaces expose context_api_environment (production | sandbox) for JWT admins (GET/PATCH …/context-api-runtime). Ingest metadata carries api_environment for webhook filtering and dashboards.
Public API changelog
Changes below are documentation and contract-shape updates shipped with info.version; runtime behavior evolves independently—use 503/429 payloads and error strings above as compatibility hints.
Changelog
- 2026-05-04 (ship-readiness) — Canonical idempotency hashing + pending recovery,
503 idempotency_completion_failed, webhook DNS SSRF validation, encrypted webhook secrets, per-endpoint completion deliveries, and richer webhook/OpenAPI docs. - 2026-05-04 — Phase 3 runtime primitives:
X-Request-ID/X-Correlation-ID,Idempotency-Key, ingest completion webhooks (signed HTTPS callbacks), workspace sandbox/production runtime flag, key rotation grace overlap fields. - 2026-05-03 (b) — Hardened Part 1 parity: contextual
429on every authenticated route family, aligned workspace-admin throttling docs, restored read-path503coverage gaps, ingest request fields aligned with AJV schemas, fullerRateLimitedand BC examples. - 2026-05-03 — Expanded OpenAPI coverage for all Part 1 customer routes, public demo, and admin key/revoke surfaces; aligned narrative with rate limits and BC scoping.
- 1.0.0 — Initial
v1Context + ingest track.
Ingest payloads and JSON Schemas
POST /api/ingest and POST /api/ingest/batch request bodies validate with AJV against the published drafts:
docs/api/ingest-schemas/v1/ingest-body.v1.jsondocs/api/ingest-schemas/v1/ingest-batch-body.v1.json(array wrapper → same per-item constraints)
Versioning and deprecation
Prefix /api routes in this document are v1 semantics (current). Breaking changes will surface as new major doc versions or explicit sunset headers; non-breaking additions (optional fields, new read fields) are backward compatible.