Documents
A document is what people write in the embedded editor: its text, its suggestions and comments, and the record of how it was written. Every document has one or more owners, and other people reach it as members or through its link access. See Documents and access.
The document object
Section titled “The document object”| Attribute | Type | Description |
|---|---|---|
id |
string | Proof’s id for the document, starting doc_. |
name |
string or null | The name an editor gave it, or null when it has none. |
title |
string | What to call it in your lists: its name, or its first line when it has no name. |
owners |
array | The Proof ids of its owners, in the order they became owners. |
link_access |
string | What people get by link without being members: restricted, view, suggest or edit. |
created_at |
timestamp | When it was created. |
edited_at |
timestamp | When its text last changed. |
trashed_at |
timestamp or null | When it was moved to the trash, or null. |
{ "id": "doc_8Hq2Lm4R", "name": "On the Analytical Engine", "title": "On the Analytical Engine", "owners": ["usr_5Kq2Lm8R"], "link_access": "restricted", "created_at": "2026-10-13T21:12:40Z", "edited_at": "2026-10-13T23:30:52Z", "trashed_at": null}Create a document
Section titled “Create a document”POST /v1/documents
Creates an empty document owned by owner, who becomes its first member, and sends document.created.
| Parameter | Type | Description |
|---|---|---|
owner |
string, required | The Proof id of the user who owns it. The user must exist. |
name |
string | Up to 200 characters. Without one, the document is called by its first line. |
reuse_untouched |
boolean | true to answer with one of the owner’s documents that nobody has touched yet, if there is one, instead of making another: none written in, named, shared or sealed. It comes back to the top of their list, with 200. For a “New document” button people may press again and again. |
except |
array | With reuse_untouched, document ids not to reuse, such as the one already open. |
Starting text isn’t supported yet: markdown is refused with 400 invalid_parameter. People and agents write the text in the editor or through the MCP tools.
curl https://api.proof.exalto.ai/v1/documents \ -H "Authorization: Bearer $PROOF_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"owner": "usr_5Kq2Lm8R", "name": "On the Analytical Engine"}'const doc = await proof.documents.create({ owner: "usr_5Kq2Lm8R", name: "On the Analytical Engine",});Returns the document with 201, or a reused one with 200.
{ "id": "doc_8Hq2Lm4R", "name": "On the Analytical Engine", "title": "On the Analytical Engine", "owners": ["usr_5Kq2Lm8R"], "link_access": "restricted", "created_at": "2026-10-13T21:12:40Z", "edited_at": "2026-10-13T21:12:40Z", "trashed_at": null}| Error | When |
|---|---|
400 invalid_parameter on markdown |
Starting text isn’t supported yet. |
404 user_not_found |
owner doesn’t exist. Create the user first. |
Retrieve a document
Section titled “Retrieve a document”GET /v1/documents/{document_id}
| Parameter | Type | Description |
|---|---|---|
document_id |
string, path, required | The document’s id. |
curl https://api.proof.exalto.ai/v1/documents/doc_8Hq2Lm4R \ -H "Authorization: Bearer $PROOF_SECRET_KEY"const doc = await proof.documents.retrieve("doc_8Hq2Lm4R");Returns the document, or 404 document_not_found. Documents in the trash are returned with trashed_at set.
Update a document
Section titled “Update a document”PATCH /v1/documents/{document_id}
Renames a document or changes its link access. Only the fields you send change. People in the editor see a new name at once.
| Parameter | Type | Description |
|---|---|---|
document_id |
string, path, required | The document’s id. |
name |
string or null | Up to 200 characters. null removes the name, so the document is called by its first line again. |
link_access |
string | restricted, view, suggest or edit. See Share documents. |
curl https://api.proof.exalto.ai/v1/documents/doc_8Hq2Lm4R \ -X PATCH \ -H "Authorization: Bearer $PROOF_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"link_access": "suggest"}'const doc = await proof.documents.update("doc_8Hq2Lm4R", { link_access: "suggest",});Returns the updated document.
Lowering link access takes effect at once: people in the editor who came by link get the new role, or lose the document if it’s now restricted.
Move a document to the trash
Section titled “Move a document to the trash”POST /v1/documents/{document_id}/trash
A document goes to the trash for everyone when an owner trashes it. While it’s there, its owners can still open it in the editor, only to read; nobody else can, agents can’t reach it, and its seals are down. It’s listed with trashed=true on its owners’ documents. Proof deletes it 30 days after it was trashed, as if you had called Delete a document.
Any other member who trashes it puts it in their own trash only: it leaves their list and nobody else’s, and they can still open it to read. After 30 days there, they stop being a member.
| Parameter | Type | Description |
|---|---|---|
document_id |
string, path, required | The document’s id. |
user |
string | The Proof id of the member trashing it. Leave it out to trash it as its first owner, for everyone. |
curl https://api.proof.exalto.ai/v1/documents/doc_8Hq2Lm4R/trash \ -X POST \ -H "Authorization: Bearer $PROOF_SECRET_KEY"const doc = await proof.documents.trash("doc_8Hq2Lm4R", { user: "usr_5Kq2Lm8R" });Returns the document. With user, it has one more field, trash: "everyone" when it went to the trash for everyone, "own" when it went to the member’s own, or null when it was there already.
{ "id": "doc_8Hq2Lm4R", "name": "On the Analytical Engine", "title": "On the Analytical Engine", "owners": ["usr_5Kq2Lm8R"], "link_access": "restricted", "created_at": "2026-10-13T21:12:40Z", "edited_at": "2026-10-13T23:30:52Z", "trashed_at": "2026-10-30T18:20:00Z", "trash": "everyone"}Trashing a document that’s already in the trash returns it unchanged.
| Error | When |
|---|---|
403 not_member |
user isn’t a member of the document. |
Restore a document
Section titled “Restore a document”POST /v1/documents/{document_id}/restore
Takes a document out of the trash, with its members, link access and agents’ roles as they were. With user, it takes it out of that member’s trash: an owner’s restores it for everyone; anyone else’s, for them.
| Parameter | Type | Description |
|---|---|---|
document_id |
string, path, required | The document’s id. |
user |
string | The Proof id of the member restoring it. Leave it out to restore it as its first owner. |
curl https://api.proof.exalto.ai/v1/documents/doc_8Hq2Lm4R/restore \ -X POST \ -H "Authorization: Bearer $PROOF_SECRET_KEY"const doc = await proof.documents.restore("doc_8Hq2Lm4R");Returns the document, with trash when you passed user, as for trashing.
Delete a document
Section titled “Delete a document”DELETE /v1/documents/{document_id}
Deletes a document now, for everyone. Open editors close, agents lose it, its images are deleted, and every seal made from it is withdrawn: its pages at proof.exalto.ai say it was withdrawn. This can’t be undone; to give people a way back, move it to the trash instead.
| Parameter | Type | Description |
|---|---|---|
document_id |
string, path, required | The document’s id. |
curl https://api.proof.exalto.ai/v1/documents/doc_8Hq2Lm4R \ -X DELETE \ -H "Authorization: Bearer $PROOF_SECRET_KEY"await proof.documents.delete("doc_8Hq2Lm4R");{ "id": "doc_8Hq2Lm4R", "deleted": true}Export a document
Section titled “Export a document”GET /v1/documents/{document_id}/export
The document’s text as it stands, as Markdown (text/markdown; charset=utf-8). Pending suggestions and comments are left out. The response body is the file itself, not JSON. HTML and Word come from the editor’s export method for now.
| Parameter | Type | Description |
|---|---|---|
document_id |
string, path, required | The document’s id. |
format |
string, required | markdown. Anything else is refused with 400 invalid_parameter. |
curl "https://api.proof.exalto.ai/v1/documents/doc_8Hq2Lm4R/export?format=markdown" \ -H "Authorization: Bearer $PROOF_SECRET_KEY"const markdown = await proof.documents.export("doc_8Hq2Lm4R", { format: "markdown" });# On the Analytical Engine
The Analytical Engine weaves algebraical patterns just as the Jacquard loom weaves flowers and leaves.Read a document’s provenance
Section titled “Read a document’s provenance”GET /v1/documents/{document_id}/provenance
Where a document’s words came from and how it was written, read live under the current method. These are the numbers its seal would show if it were sealed now; a seal keeps the numbers it was sealed with. See Provenance for what each part means.
| Parameter | Type | Description |
|---|---|---|
document_id |
string, path, required | The document’s id. |
curl https://api.proof.exalto.ai/v1/documents/doc_8Hq2Lm4R/provenance \ -H "Authorization: Bearer $PROOF_SECRET_KEY"const reading = await proof.documents.provenance("doc_8Hq2Lm4R");{ "document": "doc_8Hq2Lm4R", "method": "v0.2", "composition": { "typed": 71.4, "shaped": 15.9, "ai": 7.8, "external": 4.9 }, "presence": { "share": 97, "verified_share": 0, "verified": false }, "prompts": 4, "sittings": 2, "duration": "1hr48min"}| Attribute | Type | Description |
|---|---|---|
method |
string | The version of the method that produced these numbers. |
composition |
object | Percentages of the document by where its words came from, summing to 100: typed by people, shaped (agent text people revised), ai (agent text kept as written) and external (pasted or imported). Unrounded; a seal rounds them to whole numbers. |
presence |
object | share: percent of session time with a person present, or null before anyone has worked on it. verified_share: the part of it vouched for by check-ins. verified: true when every sitting people wrote in was verified. |
prompts |
integer | Rounds of back-and-forth with agents. |
sittings |
integer | Separate stretches of work on the document. |
duration |
string | Session time across those sittings, as the seal line writes it, such as 1hr48min; empty when nothing was measured. |
Who wrote what, person by person and agent by agent, is in the editor’s breakdown (the openBreakdown method) and on each seal’s page.