docs
API

Spec versions

How do you snapshot a spec body, list and read saved versions, and restore one over the API?

A version is a saved copy of a document's body. Nothing snapshots on your behalf except a restore, so a client that rewrites a spec and wants the old one kept has to ask for the snapshot first. A version stores the body and nothing else: not the title, not the metadata, not the ticket link.

On for every workspace. Any member's API key can hold versions:read and versions:write. There is no MCP tool for versions.

Only the 10 most recent versions of a document survive. Every snapshot deletes the ones past that, permanently.

Spec version operations

MethodPathScopeMCP tool
POST/documents/{id}/snapshotversions:writenone
GET/documents/{id}/versionsversions:readnone
GET/documents/{id}/versions/{versionId}versions:readnone
POST/documents/{id}/restore/{versionId}versions:writenone

Paths are relative to https://api.getnima.io, and every call carries Authorization: Bearer <your key>. These paths carry no version segment: it is /documents, not /v1/documents. Both scopes are open to any member, unlike documents:write, which takes the delete permission with it. See Scopes and permissions.

All four operations check only that you own the document or belong to the workspace holding it. Unlike the list and the reads on Specs, they do not bind themselves to the workspace your credential belongs to, so a credential whose owner is a member of two workspaces can snapshot and restore in both.

Snapshot a spec's current body

POST/documents/{id}/snapshot
scopeversions:write

Saves the body as it stands right now and answers 201 with an empty body. Call it before writing the markdown back, which does not snapshot on its own.

If the body has not changed since the newest existing version, nothing is written and you still get 201.

triggerstring
Default checkpoint. Any string of up to 20 characters is stored and comes back on the version row. nima's own values are idle, navigate_away, checkpoint and restore. A longer string is not rejected at validation: it fails at the database and answers 500, so keep it to 20 characters.

Errors from the snapshot route

StatuserrorWhen
400Bad Requesttrigger is not a string
403ForbiddenNot a member of the workspace holding the document
404Not FoundNo such document
500INTERNAL_SERVER_ERRORtrigger is longer than 20 characters

List a spec's saved versions

GET/documents/{id}/versions
scopeversions:read

The saved copies of one document, newest first, metadata only and without the bodies. Paginated with page and pageSize like the document list; sort is also accepted and never changes the newest-first order. Capped the same way at 3000 rows.

pageinteger
Minimum 1, default 1.
pageSizeinteger
1 to 100, default 20. A larger value is rejected, not clamped.

At most 10 versions exist per document, so one default page always holds all of them.

Errors from the list route

StatuserrorWhen
400Bad Requestpage or pageSize not an integer in range (page ≥ 1, pageSize 1 to 100), or a malformed sort
403ForbiddenNot a member of the workspace holding the document
404Not FoundNo such document

Read one saved spec version

GET/documents/{id}/versions/{versionId}
scopeversions:read

The same fields as a list row plus blocks, the saved body. There is no markdown representation of a version: to compare it with the live body, read the current markdown and these blocks.

A version id that belongs to another document is a 404, not someone else's version.

Errors from the read route

StatuserrorWhen
403ForbiddenNot a member of the workspace holding the document
404Not FoundNo such document, or no such version on it. The message reads Version not found for the second.

Restore a spec to a saved version

POST/documents/{id}/restore/{versionId}
scopeversions:write

Snapshots the current body first, with the trigger restore, then replaces the body with the saved one and increments version. Answers 201 with the whole document, the same object the read returns.

Title and metadata are left alone, because a version does not store them: restoring an old body onto a spec that has since been promoted keeps the ticket link, the status and the priority exactly as they are.

The snapshot it takes counts against the 10, so restoring repeatedly is what ages the oldest versions out fastest. If the current body already matches the newest version, no extra copy is taken. When 10 versions exist and you restore the oldest, the snapshot the restore takes ages that version out: the body is restored, but the version no longer appears in the list.

Errors from the restore route

StatuserrorWhen
403ForbiddenNot a member of the workspace holding the document
404Not FoundNo such document, or no such version on it

Fields of the spec version object

iduuid
The version. This is what a restore takes.
versionNumberinteger
The document's version at the moment the copy was taken, so it is the number the body had, not a count of versions.
triggerstring
Whatever was sent to the snapshot, or restore when nima took it on your behalf.
createdAtISO 8601
When the copy was taken, UTC. The list is ordered by this, newest first.
blocksobject[]
The saved body as editor blocks. Only on the single version read, never in a list row.

Plan feature gate on spec version routes

These four operations are declared under the plan feature documents, the same as the rest of the spec routes. A workspace whose plan lacks documents can get a 403 with error set to INSUFFICIENT_PERMISSIONS and details.feature set to documents. Handle that 403. Nobody is billed today.

Errors from spec version routes

Errors use nima's standard envelope, described in Errors. As on Errors, the error field is not dependable: branch on statusCode, and on details when it is there.

StatuserrorWhen
400Bad RequestA field failed validation. message reads Bad Request Exception and names nothing.
400VALIDATION_ERRORPaging past 3000 rows. details carries maxOffset.
403ForbiddenThe document belongs to a workspace you are not a member of
403INSUFFICIENT_PERMISSIONSWrong scope, or a role that no longer carries the permission
404Not FoundNo such document, or no such version on it
429RATE_LIMITEDOver the limit. See Rate limits.
500INTERNAL_SERVER_ERRORAn id that is not a UUID reaches the database as it is, so a malformed document id or version id answers 500 rather than 404. Validate before you send. A trigger longer than 20 characters also answers 500.