Skip to main content
Draft — ahead of the backend; nothing here is live yet. This surface is written against the design, not against a running server. There is no api.contracts.io to call, no key to issue, and every response below is an illustration rather than a recording. It is published so the shape can be argued with before it is built.It also predates CONTRACT-SCHEMA v0 and still uses the older nouns — ask for Proposal, receipt for Version, relationship for Room. The glossary maps them.
contracts.io is infrastructure for contracts. You wire contract flows into your ATS, your CRM, or your own product, and an outside AI negotiates through the same verbs the UI has. That sentence is the whole design. It is also a constraint we accept in both directions, and everything on this page follows from it.

The parity principle

One verb set, two hands. Every button in the app is an endpoint here, and every endpoint here is a button somewhere in the app.
The rule runs both ways, and both directions cost us something:
  • If a verb has no button anywhere in the UI, it is not public. There are no integration-only conveniences: no bulk-decide, no “auto-accept anything above 0.9 confidence”, no filter DSL for search. An API-only verb is a second product with the same logo on it.
  • If a button exists and the API cannot do it, parity is broken and an endpoint is missing. That is a bug report, not a feature request.
Parity binds the verbs that act (refined 2026-08-24). Anything that stages, changes state, moves a word, sends or signs must exist on both surfaces, or one of them is broken.A reading is not an acting verb. When the in-app agent explains a clause, summarizes a letter, or scans for risk, it is composing prose over public reads it already holds: GET /contracts/{id}, GET /contracts/{id}/state, GET /contracts/{id}/letters/{id}. There is no POST /contracts/{id}/explain, and its absence is not a gap: freezing one phrasing of an explanation into an API contract would give the product a second place to disagree with itself. You get equal access to the data, not equal access to the sentences. Every reading the agent performs, you can compose from the same reads.
The practical consequence: this API has no integration-specific vocabulary. POST /contracts/{id}/decisions writes to the same private reply draft the Accept button writes to. POST /ask is the same call the in-app agent makes. The integration and the person are two hands on one store.

Three laws the API inherits from the product

1

The contract is the atom

There is no create-a-container call. No envelope, no recipients list to configure, no routing order. You draft a contract and a person sends it. A room is a derived relationship record, born on that first Send. POST /contracts then POST /contracts/{id}/send is the whole origination story.
2

Words move on unanimity, never on Send

No caller can write clause text. Not a human, not a key, not an agent. The API stages decisions and commits letters; it never applies a diff. You propose; the words change when every required negotiator has accepted, and not before. PUT /contracts/{id}/blocks/{id} does not exist and will not.
3

No key sends or signs on its own authority

Send and sign carry a human-confirmation requirement. An API key is an agent with better latency, and the standing lock applies to both.

The never-sends 409

Send-class calls do not fail open and they do not fail soft. Omit confirmed_by on a send, a reply, or a signature and you get one status code back:
409 confirmation_required
That is the whole agent story in one status code. There is no flag, key, scope, rung, or org setting that removes it. send and sign are grantable scopes, and a key holding both still gets the 409 without confirmed_by.
A harness that retries a confirmation_required result is a bug, not a fallback. It is not a rate limit and not a transient failure. It is the end of the road. The next actor is a person.
A guest is a person with no org: the counterparty’s freelancer, the founder on the other side of the table. Guests are first-class negotiators and signers, and they never hold keys. A guest acts through the signed link they were sent, and they act in both directions. Reading uses the link, so the address somebody was emailed just works:
Writing uses a header, because web addresses end up in server logs and support tickets:
That is a guest countering a clause, and the same credential signs. The link grants exactly what the contract’s required-party list grants that person, and nothing else: no other contract, no corpus question, no webhooks. The token names one contract and one person, so a guest call sends no Contracts-Acting-Person and no confirmed_by. The token already supplies both. See Guests. There is no guest key issuance endpoint, and there will not be one. That would be an ACL for a person who must never see one. Guests also never get MCP, for the same reason.

What you can do

The public surface is everything a negotiator or a signer can do to a contract, plus the reads that make a contract legible to somebody who was not in the room.

What is not here

Stated plainly so you do not go looking:

No envelope API

No create envelope → add recipients → set routing order → send. Capacity does that work.

No rooms-first API

No POST /rooms, no membership call, no name you set. Relationships are derived.

No direct text writes

Words move on unanimity or they do not move.

No live collaboration

No presence, cursors, or realtime document stream. Webhooks and polling are the whole story.
Also out of scope at v1: workflows and connectors, internal comments as an API surface, playbook authoring, approval routing, seats, billing, SSO, branding, and per-clause or per-asset permissions.

Start here

Quickstart

Draft, send, run a reply cycle, and sign. All as curl.

Core concepts

Contracts, letters, asks, facts, capacity, receipts.

Authentication

Org keys, per-person tokens, acting persons, scopes.

MCP

The same verbs for an outside agent, one seat down.
Status: draft. This documentation is written against the API design drafts, ahead of the endpoints existing. Shapes here are the contract we intend to ship; see the Changelog for what is stable and what is still moving.