Programmatic read and write access to your AppStruct Docs documents.
Authentication
Every request carries an API key, created from your AppStruct dashboard. Send it as either header:
Authorization: Bearer ak_live_xxxxxxxxxxxxxxxxxxxxxxxx
# or
x-api-key: ak_live_xxxxxxxxxxxxxxxxxxxxxxxx
Keys are shown once, at creation. A key reaches the documents its own account owns and nothing else — documents shared with that account, and anything opened by a share link, are not visible through the API.
Scopes
AI assistants (MCP)
The same key also opens a Model Context Protocol server at POST /v1/docs/mcp, so Claude, Claude Code, Cursor or any other MCP client can read and write these documents directly. It speaks Streamable HTTP and takes the same Authorization: Bearer header as the endpoints below.
The tools an assistant is offered are derived from the key's scopes — a docs:read key never sees the write tools at all — and every tool runs through the same service and the same limits as the REST calls documented here. Copy-paste configuration for each client is in the dashboard: https://appstruct.ai/dashboard/docs/api/connect
Browser clients are refused: the endpoint rejects any request carrying an Origin header, which is what the MCP specification requires against DNS rebinding, and real MCP clients are not browsers and send none.
You read HTML, and you write text
A document stores its body as the sanitized rich-text HTML the editor renders, and html gives it to you exactly as stored — formatting, tables, links and all. Every response also carries body.text, the same body with the markup removed, one line per paragraph.
Writes go the other way: every field that adds content takes plain text, and there is no way to send markup at all. The sanitizer that protects the editor runs in the browser, so a server that accepted HTML here would be a way to put script into a document somebody else opens — arriving pre-trusted, because the client renders whatever the server hands back. Formatting an edit already carries is preserved around it; formatting it introduces is not something this API can do.
An edit never re-serialises the markup it did not touch. Attributes, inline styles and the editor's own classes come back byte-identical, so a document stays exactly as the person who wrote it left it.
Editing
:batchUpdate applies a list of edits in order, in one save. Indexes are 1-based positions in body.text, as Google's are: index 1 is before the first character, and one past the last character is where an append lands. Each edit sees the document as the edits before it in the same batch left it, so read body.text once, work out your indexes, and send them together.
Google's API wraps an index in a location object and a range in a range object, each carrying a segmentId that names a header or a footer. This API edits the body only, so the flat form is documented — but the nested one is accepted too, and so is Google's containsText: {text, matchCase}, because a script ported from their API should not have to discover the difference from a 400.
A batch either applies whole or changes nothing: an edit the server cannot understand is refused with the position of the request that was wrong, and nothing before it is written. It is one save, so the document takes a single version-history snapshot and collaborators get a single notification — the same as if the edit had been typed. Compare revisionId against the one you read to know whether anybody changed the text underneath you first.
Limits
- 120 requests per minute per key, plus a coarser 600 per minute per IP. Both answer
429 with Retry-After; every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (seconds), and the per-IP tier reports the same three suffixed -ip. - 100,000 requests per rolling 24 hours per account, shared across every key. The Sheets API keeps its own
separate allowance, so traffic there does not spend this one. Reported in X-AppStruct-RateLimit-Daily and X-AppStruct-RateLimit-Daily-Remaining.
- 100 edits per
:batchUpdate call, each adding at most 100,000 characters. - A title of at most 200 characters.
- List endpoints take
?limit= (1–200, default 50) and ?offset=, and return { data, meta }.
Errors
Standard HTTP status codes with a JSON body. Switch on statusCode, not on the human-readable message.
Documents
Finding documents, reading their contents, and creating new ones
GET
/v1/docs/documents
List documents
Every document this account owns, newest edit first. Bodies are left out — fetch one document to read its HTML.
Requires the docs:read scope.
Example requestcurl -X GET "https://api.appstruct.cloud/v1/docs/documents" \
-H "Authorization: Bearer ak_live_your_key_here"
Query parameters
Response200
Success
Errors
401Missing, invalid, expired or revoked API key403API key lacks the required scope404Not found on this account429Rate limit or daily budget exceeded
POST
/v1/docs/documents
Create a document
Make a new document. Send text to give it a body straight away, or leave it out for an empty one you fill in with :batchUpdate.
Requires the docs:write scope.
Example requestcurl -X POST "https://api.appstruct.cloud/v1/docs/documents" \
-H "Authorization: Bearer ak_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"title":"Q3 launch plan","text":"Launch plan\nStatus: DRAFT"}'
Request bodyapplication/json, required
Example body{
"title": "Q3 launch plan",
"text": "Launch plan\nStatus: DRAFT"
}
Response200
The document as stored, including the id you will need for every later call.
Errors
400Request validation failed401Missing, invalid, expired or revoked API key403API key lacks the required scope404Not found on this account429Rate limit or daily budget exceeded
GET
/v1/docs/documents/{documentId}
Retrieve a document
One document with its full body, as both HTML and plain text.
Requires the docs:read scope.
Example requestcurl -X GET "https://api.appstruct.cloud/v1/docs/documents/<documentId>" \
-H "Authorization: Bearer ak_live_your_key_here"
Path parameters
Response200
Success
Errors
401Missing, invalid, expired or revoked API key403API key lacks the required scope404Not found on this account429Rate limit or daily budget exceeded
Editing
Changing the text of a document that already exists
POST
/v1/docs/documents/{documentId}:batchUpdate
Edit a document
Apply a list of edits to the body — insert text, delete a span, replace every occurrence of a phrase, or rename the document. Edits run in the order you give them.
Requires the docs:write scope.
Example requestcurl -X POST "https://api.appstruct.cloud/v1/docs/documents/<documentId>:batchUpdate" \
-H "Authorization: Bearer ak_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"requests":[{"replaceAllText":{"containsText":"DRAFT","replaceText":"Final"}},{"insertText":{"text":"\nApproved by the design review."}}]}'
Path parameters
Request bodyapplication/json, required
Example body{
"requests": [
{
"replaceAllText": {
"containsText": "DRAFT",
"replaceText": "Final"
}
},
{
"insertText": {
"text": "\nApproved by the design review."
}
}
]
}
Response200
Applied as ONE save, so the document takes a single version snapshot and collaborators get a single notification. replies lines up with requests, one entry each.
Errors
400Request validation failed401Missing, invalid, expired or revoked API key403API key lacks the required scope404Not found on this account429Rate limit or daily budget exceeded
Ready to build?
Keys are created in the AppStruct dashboard and shown once, at creation.
Get an API key