Versioned. Cited.
On the record.
The d8m.io API answers questions about vendor data. Every response is grounded in source documents, carries an access-controlled projection, and includes provenance. If we can't cite, we decline.
§ 01 · OverviewWhat the API actually does.
The d8m.io API exposes a structured, governed view over your vendor portfolio. Every endpoint operates on the same underlying graph: contracts, amendments, invoices, performance reports, and the relationships between them.
The API is read-mostly. The handful of write endpoints generate artifacts — claim packages, notice letters, evidence appendices — from data that's already on file. Source documents are immutable; the API never mutates them.
Three properties hold across every response:
- Cited. Every dollar amount, every date, every clause-level claim carries a source pointer.
- Scoped. Document- and field-level access enforced at runtime. The
_accessblock is honest about filtering. - Confident. Every extracted field carries a confidence score. Human-validated fields are marked authoritative.
§ 02 · AuthenticationBearer tokens. OAuth on top.
The API supports three auth modes. Most production deployments use OAuth 2.0 with role assertions; the API key mode is intended for server-to-server scripts inside trusted networks.
# Bearer token (most common) curl https://api.d8m.io/v1/entitlements \ -H "Authorization: Bearer $D8M_TOKEN" \ -H "X-User-Role: cfo" # API key curl https://api.d8m.io/v1/vendors \ -H "X-D8M-Key: d8m_live_2c4f_…" # OAuth 2.0 — token exchange curl https://auth.d8m.io/oauth/token \ -d "grant_type=client_credentials" \ -d "client_id=…" \ -d "client_secret=…" \ -d "scope=read:contracts read:entitlements"
Role assertion
Every authenticated request carries a role. Roles are configured at tenant level and map to access policies. Pass the role in X-User-Role for bearer/API-key requests, or as an OAuth scope claim. App-namespaced calls also pass X-App-Context so write-backs land in the right app's annotation namespace.
§ 03 · ConventionsWhat every response looks like.
Responses are JSON. Timestamps are ISO 8601 in UTC. Money fields use whole units of the contract's currency — amount_usd, amount_eur, amount_gbp. Currency is always declared on the parent entity.
{ "data": { // the requested payload "…": "…", "citations": [ // every claim with a source { "document_id": "doc_orbital_sla_addendum", "canonical_section_id": "sec-3-4", "clause": "Section 3.4 — Service Credit Calculation", "page": 7, "excerpt": "Service Credits shall equal the difference between the committed uptime…" } ] }, "_access": { // honest about filtering "acting_as": "cfo", "documents_visible": 7, "documents_filtered": 0, "fields_redacted": [], "audit_id": "audit_001003" }, "_meta": { "request_id": "req_2c4f…", "timestamp": "2026-05-13T14:23:11Z", "latency_ms": 1240, "api_version": "v1" } }
§ 04 · EndpointsThe full surface, by section.
The API is organized into ten endpoint families. Every endpoint operates on the same underlying graph and returns the same response envelope (data, _access, _meta); citations live inside data.citations. Path parameters are wrapped in {braces}; query parameters are documented per-endpoint in the OpenAPI spec.
04.1 · Vendors
The vendor is the root entity. Every other endpoint can be filtered or scoped by vendor. The vendor sub-resources are shortcuts to the most common queries.
/v1/entitlements).04.2 · Documents
Every ingested artifact has three forms: source (original binary), canonical (structured machine-readable), and rendered (HTML for display). The canonical form is what the AI grounds against — inspectable, audit-grade, the substrate every other answer cites.
04.3 · Contracts
A contract is a logical relationship composed of one or more documents (MSA + SOWs + amendments + side letters). The active view consolidates clauses in force, after supersession.
04.4 · Entitlements
An entitlement is an actionable right or obligation — a defined trigger, action window, and status. SLA credits, rebates, audit rights, MFC rights, termination rights, renewal notice windows, change-of-control consent rights. Recovery-shaped entitlements are one profile; lifecycle and risk profiles are equally first-class.
04.5 · Invoices & POs
04.6 · Query & search
04.7 · Events & webhooks
Every material change in the system emits an event — from contract execution to a single field flipping to human-validated. Subscribe via webhooks, or pull the change feed directly.
*.field_changed change feed.04.8 · Administration
04.9 · Platform
The platform endpoints make d8m usable as a data layer for any vendor-aware application, not just the recovery profile. Schema introspection, bulk export, and app-namespaced write-back annotations.
scope=tenant.notice_window_open, claim_submitted). Carries a note + evidence refs and emits entitlement.status_changed.04.10 · Custom for enterprise
Where an enterprise tenant needs logic beyond schema extension — a specialized claim package format, a non-standard reconciliation, an industry-specific aggregation — custom endpoints live under:
GET /v1/custom/{tenant_key}/freight-lane-pricing POST /v1/custom/{tenant_key}/baa-review-package GET /v1/custom/{tenant_key}/regulatory-attestation-pack
Custom endpoints are not part of the public surface. They're documented in the tenant's private reference, built on top of the core primitives (read core fields, read tenant extension fields, write annotations), and respect the same access control and audit model as every other call.
The same pattern extends to schema: tenants can add custom fields, industry profiles, enum values, entitlement types, and event types under extensions.{tenant_key}.*. Discoverable via GET /v1/schema. First-class with respect to access control, provenance, audit, and the NL query endpoint.
Example: POST /v1/query
vendor, date_range, or other structured selectors. Improves latency.
true. When false, omits the citations array.
POST /v1/query Authorization: Bearer $D8M_TOKEN X-User-Role: cfo Content-Type: application/json { "question": "What SLA credits are we owed from Orbital Cloud this quarter?", "filters": { "vendor": "orbital", "date_range": "2026-Q1" }, "include_citations": true, "confidence_threshold": 0.80 }
{ "data": { "answer": "Orbital Cloud owes $84,200 in SLA credits for Q1 2026…", "total_owed_usd": 84200, "confidence": 0.96, "citations": [ { "document_id": "doc_orbital_sla_addendum", "canonical_section_id": "sec-3-4", "clause": "Section 3.4 — Service Credit Calculation", "page": 7 } ] }, "_access": { "acting_as": "cfo", "documents_visible": 7, "documents_filtered": 0, "audit_id": "audit_001003" } }
§ 05 · Access controlsFiltering is a first-class response.
Document- and field-level access are enforced at the API edge — not in the client. Every response carries an _access block describing what was filtered for the current role. Critically, the system does not silently omit; it tells you.
Roles are defined per tenant. The defaults look like:
- CFO. Full dollar amounts. Full entitlement detail. Internal strategy memos, target pricing models, and board updates visible.
- Procurement Manager. Operational detail and commercial terms on contracts they own. Internal strategy memos and target pricing models filtered out.
- Compliance Officer. Audit trail, data residency, breach notification, signatory chain, controls. Commercial pricing and operational metrics redacted.
- AP Clerk. Invoices, POs, and the specific contract sections referenced by them. No full contract bodies.
- AI Engineer (service account). Metadata-only by default. Elevates only when bound to an app context with explicit document permissions — see
X-App-Context.
Filtering is composable. A field can be visible in one scope, redacted in another, and surface a derived projection in a third — all from the same underlying data, applied at runtime. See Security · Access.
§ 06 · ErrorsHonest. Useful. Stable.
Errors are JSON with a stable code, a human-readable message, and a request_id you can quote to support. Status codes follow HTTP semantics; we do not return 200 for failed answers.
A 409 declined is the API's way of saying "I don't know." It is the correct answer when the alternative would be a guess. The response body includes the missing inputs.
§ 07 · Rate limitsGenerous. Honest. In headers.
Default limits are 1,000 requests per minute per tenant token, 600 rpm for the ai_engineer service account, and 200 rpm for human user roles. POST /v1/query is gated separately by token throughput on the LLM side.
Every response includes:
X-RateLimit-Limit· current ceiling for the bucket.X-RateLimit-Remaining· requests left in the window.X-RateLimit-Reset· seconds until the bucket refills.
Enterprise tenants can request elevated limits. Contact us with a representative load profile.
§ 08 · MCP & GraphQLSame data. Different shapes.
The same governed graph is exposed under three surfaces. Pick the one that fits your client:
- REST — the canonical surface, documented above.
- MCP (Model Context Protocol) — for AI agents that speak it natively. Tool names map 1:1 to REST endpoints.
- GraphQL — for clients that need selective fields and a single round-trip per page. Schema mirrors the REST graph.
# Claim the d8m MCP server { "mcpServers": { "d8m": { "url": "https://mcp.d8m.io", "headers": { "Authorization": "Bearer $D8M_TOKEN" } } } }
§ 09 · WebhooksPush, when the data tells you to.
Webhooks fire on material events: an SLA breach crosses an entitlement threshold, a renewal window opens, a vendor's change-of-control news appears in monitored feeds, or a previously-extracted field gains human validation. Subscribe per topic.
entitlement.threshold_crossed— a new claim is recoverable.renewal.window_opened— non-renewal notice deadline is approaching.vendor.change_of_control— M&A event detected for a vendor.document.validated— a field flipped from extracted to authoritative.
Signed with HMAC-SHA256. Retried with exponential backoff up to 72 hours. Delivery receipts available in the audit log.
§ 10 · SDKsTyped bindings. Three languages.
Generated from the same OpenAPI spec the server validates against. Typed responses; data.citations and the _access block are first-class on every result.
import { D8M } from "@d8m/sdk"; const client = new D8M({ token: process.env.D8M_TOKEN, role: "cfo" }); const result = await client.query({ question: "What SLA credits are we owed from Orbital this quarter?", filters: { vendor: "orbital", date_range: "2026-Q1" } }); console.log(result.data.answer); // "Orbital Cloud owes $84,200…" console.log(result.data.citations); // every claim with a source console.log(result._access.documents_visible); // 7 for CFO, fewer for procurement
from d8m import D8M client = D8M(token=os.environ["D8M_TOKEN"], role="cfo") result = client.query( question="What SLA credits are we owed from Orbital this quarter?", filters={"vendor": "orbital", "date_range": "2026-Q1"}, ) for cite in result.data.citations: print(cite.document_id, cite.canonical_section_id, cite.excerpt)
Also available: @d8m/sdk-go, @d8m/sdk-rust, and a thin @d8m/cli for shell automation. All on GitHub, MIT-licensed.
Run the surface.
Every endpoint, live in the Playground, against a curated portfolio. Switch roles. Expand citations. No call required.
Open the Playground →File a discovery call.
Thirty minutes. Bring a question your installed stack can't answer; we'll show you the endpoint that does.
Book the call →