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
| Method | Path | Scope | MCP tool |
|---|---|---|---|
POST | /documents/{id}/snapshot | versions:write | none |
GET | /documents/{id}/versions | versions:read | none |
GET | /documents/{id}/versions/{versionId} | versions:read | none |
POST | /documents/{id}/restore/{versionId} | versions:write | none |
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
/documents/{id}/snapshotversions:writeSaves 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.
triggerstringcheckpoint. 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
| Status | error | When |
|---|---|---|
400 | Bad Request | trigger is not a string |
403 | Forbidden | Not a member of the workspace holding the document |
404 | Not Found | No such document |
500 | INTERNAL_SERVER_ERROR | trigger is longer than 20 characters |
List a spec's saved versions
/documents/{id}/versionsversions:readThe 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.
pageintegerpageSizeintegerAt most 10 versions exist per document, so one default page always holds all of them.
Errors from the list route
| Status | error | When |
|---|---|---|
400 | Bad Request | page or pageSize not an integer in range (page ≥ 1, pageSize 1 to 100), or a malformed sort |
403 | Forbidden | Not a member of the workspace holding the document |
404 | Not Found | No such document |
Read one saved spec version
/documents/{id}/versions/{versionId}versions:readThe 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
| Status | error | When |
|---|---|---|
403 | Forbidden | Not a member of the workspace holding the document |
404 | Not Found | No such document, or no such version on it. The message reads Version not found for the second. |
Restore a spec to a saved version
/documents/{id}/restore/{versionId}versions:writeSnapshots 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
| Status | error | When |
|---|---|---|
403 | Forbidden | Not a member of the workspace holding the document |
404 | Not Found | No such document, or no such version on it |
Fields of the spec version object
iduuidversionNumberintegerversion at the moment the copy was taken, so it is the number the body had, not a count of versions.triggerstringrestore when nima took it on your behalf.createdAtISO 8601blocksobject[]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.
| Status | error | When |
|---|---|---|
400 | Bad Request | A field failed validation. message reads Bad Request Exception and names nothing. |
400 | VALIDATION_ERROR | Paging past 3000 rows. details carries maxOffset. |
403 | Forbidden | The document belongs to a workspace you are not a member of |
403 | INSUFFICIENT_PERMISSIONS | Wrong scope, or a role that no longer carries the permission |
404 | Not Found | No such document, or no such version on it |
429 | RATE_LIMITED | Over the limit. See Rate limits. |
500 | INTERNAL_SERVER_ERROR | An 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. |