# One contract. Every protocol

> Developers. Records, documents, signatures, workflows, retention, and the audit trail sit behind one API. You build the experience your users see; Tabellix keeps the record, the rules, and the proof.

## The contract: Build against one contract.

One contract is served over every protocol, so a call means the same thing from a backend, a browser, or an agent. Resources and methods follow the resource-oriented style of the Google API Improvement Proposals, and every promised method exists: a test fails if the contract lacks one.

| Protocol | Use it for |
| --- | --- |
| gRPC | Service-to-service integration over the Protobuf contract. |
| gRPC-Web and JSON over HTTP | Browsers and any HTTP client, with the same messages and the same errors. |
| Resource-style REST | Plain HTTP resources, described by a generated OpenAPI document. |
| MCP | AI agents, under the authority of the person they act for. Every staff capability is reachable. |

- **A catalog to render from.** Types, lifecycles, signature meanings, roles, and labels are readable through the API, so your application can build its screens from configuration.
- **Codes, not sentences.** States, events, fields, and audit events carry labels in each language the deployment uses. Your interface shows them in the reader's language.
- **Your brand on every surface.** One setting puts your product name on every customer-visible surface Tabellix provides, from the agent server and confirmation text to evidence titles and webhooks. Wire identifiers never change.

## What it feels like: Retry anything. Duplicate nothing.

Illustrative shapes, simplified, not the reference. Every write carries a request id you choose, so a retry after a timeout returns the first result instead of a second record, and reusing an id for different content is refused.

1. **capture** (POST /v1/records; request_id 7c1e…)
2. **same id, same body** (timeout, retry; first result returned)
3. **refused** (same id, new body; reason: request reused)
4. **committed** (ledger entry; event to your cursor)

1. **started** (POST …; wait up to a timeout)
2. **handle** (not done yet; operation id)
3. **done** (poll or notify; webhook, NATS, MCP)
4. **returned** (result; as a direct call would)

same contract on every interface

## Writes: Writes you can trust under load.

High-rate sources, flaky networks, and two people editing at once are the normal case. The API is built for each of them.

- **Idempotent writes.** Every mutation honors a request id. A repeated request returns its first result; the same id with different content is refused.
- **Optimistic concurrency.** An edit names the version it was based on. If that version is no longer the latest, the save is refused before any content is stored, even when it went stale in a queue.
- **Batch capture.** Integration sources sign in as service accounts and capture up to 100 records per call. Records are deduplicated by your source reference.
- **Resumable uploads.** Large files upload in resumable chunks with checksums and locking, and stream to the object store. Partial uploads and duplicate requests are handled.
- **Uploads that can be released.** An upload larger than the scanner can scan, or than the deployment allows, is refused at creation, so nothing is accepted that could never be served.
- **Locked capture.** Captured fields lock at capture and late captures are flagged. A correction is an amendment with a reason, and the original value stays visible.
- **Exact bytes for imports.** CSV, JSON, and other formats you list can be kept exactly as uploaded, checked, scanned, and signed over their content digest.
- **Read back before commit.** A finished upload can be read back by its creator or a few named readers before it is committed, verified and scanned first, with every read audited.
- **Your own references.** An optional reference per document, unique per type, with exact and prefix filters. Changing it is audited and creates no version.

## Reads and search: Ask for anything. Get only what you may see.

Permissions are applied inside the same database query as the read, so lists, search results, and counts reveal nothing a caller may not see, and every page is complete.

- **Ranked full-text search.** Field-weighted, language-aware ranking, with exact and prefix filters on your document references. The same search is available to agents over MCP.
- **Stable page tokens.** Page tokens are bound to the caller and the filters, so pages stay stable while new content is indexed.
- **Read your own writes.** Search, lists, and the audit list can read from the standby database and still see the caller's own latest writes.
- **History in one query.** A document's and its versions' events in ledger order, with descriptions in the reader's language. Any two versions can be compared by metadata and digest.
- **Names when you need them.** The ledger names people by stable ids. A directory resolves up to 200 ids to display names per call, under its own permission, without login names or roles.
- **Hidden looks like missing.** An object the caller may not see answers exactly as one that does not exist, in responses, errors, and the security stream.

[Hidden existence and access control](https://tabellix.dev/security/)

## Events and triggers: Events come from the ledger.

The audit ledger is the outbox. Consumers read committed events only, each with its own cursor, so an event you receive always matches a change that happened.

1. **committed** (change; with its audit entry)
2. **event** (ledger; your cursor advances)
3. **delivered** (webhook; signed, no content)
4. **fetches** (your service; under its own permissions)

- **Signed webhooks.** Signatures follow Standard Webhooks and envelopes follow CloudEvents. Retries, pause, redelivery, and a test delivery are built in.
- **Nothing sensitive leaves.** Webhooks carry a few fixed fields, never titles, content, or sealed values. Agent and access events are never sent out.
- **Key rotation without gaps.** While a webhook key rotates, both keys sign for 24 hours, so receivers can switch on their own schedule.
- **Safe egress.** Each destination is resolved and checked when the connection is made, so a webhook cannot be pointed at internal addresses.
- **A NATS bridge.** Optionally, events also flow to a NATS JetStream you provide, with message ids taken from the audit event names.
- **Triggers that pin versions.** An event record, such as an exceedance, links to the procedure version in force when it happened. That link never moves.

## Long-running operations: Wait, or take a handle.

Work that takes time runs as a long-running operation with the same contract on every interface.

- **Wait up to a timeout.** Ask for the result and wait up to the time you choose. If it finishes in time, you get the result in the same call.
- **Or take a handle.** Otherwise you get an operation handle. Poll it, or be notified by webhook, NATS, or an MCP notification.
- **Durable underneath.** Jobs run on the database with leases, heartbeats, fencing, and retries with backoff. Terminal failures are audited.
- **Pinned configuration.** Every request and job runs under exactly one configuration release, and its digest is stamped on the job and on every audit entry it writes.

## Identity: Your identity provider, your directory.

People sign in through any OIDC provider and are provisioned through SCIM 2.0. Roles are defined in Tabellix and assigned from your directory groups.

- **Any OIDC provider.** Separate issuers for company staff, invited outside accounts, and optional vendor support. The server refuses to start if the support issuer equals the company issuer.
- **SCIM 2.0.** Pre-provision people, bind their first sign-in, map directory groups to roles, and disable accounts. Accounts are disabled, never deleted, so attribution stays intact.
- **Service principals.** Client-credentials tokens of the integration clients you list act as service principals. A token without a subject is refused.
- **Audiences that cannot cross.** Accepted token audiences are configured lists per surface. A token issued for another surface is refused, and an audience can be renamed without downtime.
- **Fresh sign-in to sign.** A signature needs an interactive sign-in within the deployment's window, through the authorization code flow with PKCE. ACR step-up values are supported.
- **MCP with OAuth 2.1.** The MCP server is an OAuth 2.1 resource server with protected resource metadata (RFC 9728), read and write scopes, and step-up when a scope is missing.

## Errors and failure modes: Errors a program can act on.

Every failure has a stable reason your code can branch on, and the behavior behind it is written down. Tabellix specifies 448 failure modes, from dependency outages to clock skew, each with defined behavior.

| What happened | What your program gets | What it does next |
| --- | --- | --- |
| A dependency is down | Unavailable, with a reason and Retry-After | Retry after the stated time. Readiness reports which dependency a role is missing. |
| The base version went stale | A refusal before any content is stored | Read the latest version and save again on top of it. |
| A request id was reused for other content | A refusal | Use a new request id for a new intent. |
| An upload exceeds what can be scanned | A refusal when the upload is created | Ask the deployer for its limit before sending. |
| The caller may not see the object | The same answer as for a missing object | Nothing to infer: hidden and missing are identical. |
| A policy cannot be evaluated | A denial: policy errors fail closed | Nothing was granted. The deployer's policy needs attention. |

- **A catalog of failure modes.** Each failure mode states what triggers it and how the system behaves, and almost every one has a fault test written to induce it.
- **Generated references.** Reference documents for the API, operations, errors, configuration, and the deployment profile are generated from the same sources as the code.

[Catalog figures and validation evidence](https://tabellix.dev/trust/)

## Stability: Upgrades that do not break you.

A release that would break an integration does not ship. A compatibility gate compares each release with the previous one and refuses any removal or change; additive changes pass.

- **A compatibility gate.** A release that removes or changes a field, a method, an HTTP binding, a catalog contract, or an error mapping is refused.
- **What integrators rely on, recorded.** The operation families integrators depend on are recorded with their scope. A change that removes or retypes one is rejected.
- **Upgrades rehearsed.** An upgrade test starts from the previous release with seeded data, upgrades it, compares reads and stored data, and verifies the sealed history offline across versions.
- **A skill for coding agents.** An agent skill with progressive disclosure, agent instructions, and recipes driven by an end-to-end test, so a coding agent with no prior context can build on Tabellix.
- **Observable by default.** Metrics on every role, structured JSON logs, request ids, and a security event stream over OpenTelemetry (OTLP).
- **Request ids end to end.** The request id in a response is the one the ledger records. Behind your own gateway, its id and client address can be recorded over mutual TLS.

## FAQ: Developer questions.

### Which protocols does the API support?

One contract is served as gRPC, gRPC-Web, JSON over HTTP, and resource-style REST with a generated OpenAPI document, and as MCP for AI agents. The same methods, messages, and errors apply on every interface.

### Is it safe to retry a write?

Yes. Every mutation honors a request id. A repeated request returns its first result, and reusing the id with different content is refused.

### How do we get notified of changes?

Read committed events from the ledger with your own cursor, receive signed webhooks that follow Standard Webhooks and CloudEvents, or use an optional NATS JetStream bridge. Webhooks carry no content; your service fetches what it needs under its own permissions.

### Do we need to build our own sign-in?

No. People sign in through your OIDC provider and are provisioned through SCIM 2.0. Integration clients authenticate as service principals.

### Do we have to use a Tabellix user interface?

No. You build the interface. A thin reference interface exists to exercise the public API; it is not a product and does not ship.

### How does Tabellix keep upgrades from breaking our integration?

A compatibility gate refuses any release that removes or changes a field, a method, an HTTP binding, or an error mapping, and an upgrade test starts from the previous release and checks reads, stored data, and the sealed history.

## Contact

Tell us what your product captures, signs, and has to prove. We will walk through how it maps to the contract.

Contact form: https://tabellix.dev/contact/

---

This page as HTML: https://tabellix.dev/developers/ · [Site guide for language models](https://tabellix.dev/llms.txt)
