> ## Documentation Index
> Fetch the complete documentation index at: https://opentrain.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Instructions API

> Complete HTTP reference for creating, editing, reviewing, publishing, searching, and rendering rich instruction manuals.

The Instructions API lets agents and integrations maintain the same rich, nested manuals that people edit in OpenTrain. The canonical format is versioned TipTap JSON; page-content and tree mutations travel through atomic changesets, and every publication is an immutable snapshot.

Base path:

```text theme={null}
https://app.opentrain.ai/api/public/v1/instructions
```

Send `Authorization: Bearer ot_pat_…` on every request. Start with `GET /capabilities` so your client uses the current schema vocabulary and limits. The complete machine-readable contract is also available in the [OpenAPI document](https://app.opentrain.ai/api/public/v1/openapi.json).

## Scopes and roles

Token scopes and workspace roles are both enforced. A token never grants access to a workspace the user cannot access.

| Scope                  | Purpose                                                                                                       |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `instructions:read`    | Read workspaces, manuals, trees, pages, revisions, publications, assets, search results, and renders.         |
| `instructions:write`   | Create workspaces/manuals and apply guarded changesets.                                                       |
| `instructions:propose` | Validate, diff, and submit a changeset without applying it when the user is a workspace `EDITOR` or `OWNER`.  |
| `instructions:review`  | Approve or reject submitted changesets when the user has reviewer access.                                     |
| `instructions:publish` | Manage workspace grants and publish immutable versions; publication also requires the workspace `OWNER` role. |

Workspace creation persists an `OWNER` permission record for the creator. Owners can grant `EDITOR`, `REVIEWER`, or `VIEWER` access to other users. See [Scopes and capabilities](/docs/developers/concepts/scopes-and-capabilities) for token creation and least-privilege guidance.

## Endpoint map

All paths below are relative to the base path.

### Discovery, workspaces, and permissions

| Method  | Path                                                         | Scope                                                | Purpose                                                                                                    |
| ------- | ------------------------------------------------------------ | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `GET`   | `/capabilities`                                              | `instructions:read`                                  | Return schema version, nodes, marks, attributes, patch operations, audiences, formats, and limits.         |
| `GET`   | `/workspaces`                                                | `instructions:read`                                  | List accessible workspaces with cursor pagination.                                                         |
| `POST`  | `/workspaces`                                                | `instructions:write`                                 | Create a workspace for a verified user who belongs to an employer organization. Accepts `Idempotency-Key`. |
| `GET`   | `/workspaces/{workspaceId}`                                  | `instructions:read`                                  | Read workspace metadata.                                                                                   |
| `PATCH` | `/workspaces/{workspaceId}`                                  | `instructions:write`                                 | Rename a workspace.                                                                                        |
| `POST`  | `/workspaces/{workspaceId}/archive`                          | `instructions:write` + owner                         | Retain but archive a workspace; it is no longer accessible through this API.                               |
| `GET`   | `/workspaces/{workspaceId}/permissions`                      | `instructions:read` + `instructions:publish` + owner | List persisted user roles, including the owner's record.                                                   |
| `POST`  | `/workspaces/{workspaceId}/permissions`                      | `instructions:publish` + owner                       | Grant or update `EDITOR`, `REVIEWER`, or `VIEWER`.                                                         |
| `POST`  | `/workspaces/{workspaceId}/permissions/{principalId}/revoke` | `instructions:publish` + owner                       | Revoke a user grant; the owner's role cannot be revoked.                                                   |

Create a workspace with `{"name":"Operations"}`. Grant access with `{"principalId":"<userId>","role":"REVIEWER"}`.

### Manuals, trees, and pages

| Method  | Path                                | Scope                        | Purpose                                                                                     |
| ------- | ----------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------- |
| `GET`   | `/workspaces/{workspaceId}/manuals` | `instructions:read`          | List manuals in a workspace.                                                                |
| `POST`  | `/workspaces/{workspaceId}/manuals` | `instructions:write`         | Create a manual with an optional audience and bindings. Accepts `Idempotency-Key`.          |
| `GET`   | `/manuals/{manualId}`               | `instructions:read`          | Read manual metadata, bindings, and current head.                                           |
| `PATCH` | `/manuals/{manualId}`               | `instructions:write`         | Update title, default audience, or the complete binding set.                                |
| `POST`  | `/manuals/{manualId}/archive`       | `instructions:write` + owner | Retain but archive a manual; it is no longer accessible through this API.                   |
| `GET`   | `/manuals/{manualId}/tree`          | `instructions:read`          | Return the bounded nested tree with page revisions and checksums.                           |
| `GET`   | `/pages/{pageId}`                   | `instructions:read`          | Read canonical JSON; optionally request an immutable revision and derived HTML or Markdown. |

Manual bindings are `{kind,targetId,precedence}` objects. `kind` is `JOB`, `FOLDER`, or `WORK_PROJECT`; lower precedence wins among bindings of the same specificity. A manual body is not edited through `PATCH /manuals` or `PATCH /pages`—content and tree mutations always use changesets.

`GET /pages/{pageId}` accepts `revision` and `format=json|html|markdown`. Markdown responses include a loss report; canonical JSON is always returned so clients never depend on a lossy representation.

### Atomic changesets and review

| Method | Path                                | Scope                                                                       | Purpose                                                                   |
| ------ | ----------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `POST` | `/changesets/validate`              | `instructions:write` or `instructions:propose` + workspace `EDITOR`/`OWNER` | Dry-run schema, revision, tree, audience, and asset validation.           |
| `POST` | `/changesets/diff`                  | `instructions:write` or `instructions:propose` + workspace `EDITOR`/`OWNER` | Return a bounded page/node summary without writing.                       |
| `POST` | `/changesets/apply`                 | `instructions:write`                                                        | Apply every operation atomically or none. Accepts `Idempotency-Key`.      |
| `POST` | `/changesets/submit`                | `instructions:propose` + workspace `EDITOR`/`OWNER`                         | Store the same changeset for human review. Accepts `Idempotency-Key`.     |
| `GET`  | `/changesets?workspaceId=…`         | `instructions:read`                                                         | List changesets, optionally filtered by status and cursor.                |
| `GET`  | `/changesets/{changesetId}`         | `instructions:read`                                                         | Read a stored changeset and its operations.                               |
| `POST` | `/changesets/{changesetId}/approve` | `instructions:review`                                                       | Approve a submitted proposal, with an optional `decisionNote`.            |
| `POST` | `/changesets/{changesetId}/reject`  | `instructions:review`                                                       | Reject a submitted or approved proposal, with an optional `decisionNote`. |
| `POST` | `/changesets/{changesetId}/apply`   | `instructions:write`                                                        | Revalidate and apply an approved proposal; drift records `CONFLICTED`.    |

Validate, diff, apply, and submit share one body:

```json theme={null}
{
  "workspaceId": "<workspaceId>",
  "description": "Add the reviewer edge-case section",
  "operations": [
    {
      "kind": "PATCH_CONTENT",
      "pageId": "<pageId>",
      "baseRevisionNumber": 3,
      "baseChecksum": "<pageChecksum>",
      "ops": [
        {
          "op": "append_children",
          "nodeId": null,
          "nodes": [
            {
              "type": "paragraph",
              "attrs": { "id": "edge-case-intro" },
              "content": [{ "type": "text", "text": "Escalate ambiguous evidence." }]
            }
          ]
        }
      ]
    }
  ]
}
```

Changesets support `PATCH_CONTENT`, explicitly confirmed `REPLACE_CONTENT`, `CREATE_PAGE`, `RENAME_PAGE`, `MOVE_PAGE`, `SET_AUDIENCE`, `ARCHIVE_PAGE`, `RESTORE_PAGE`, and `ATTACH_ASSET`. Node patches support `replace_node`, `insert_before`, `insert_after`, `append_children`, `set_attrs`, and `delete_node`.

Use a `clientPageKey` on `CREATE_PAGE` and refer to the new parent as `new:<clientPageKey>` to create a nested section in one request. Operation order does not matter.

<Warning>
  Whole-document replacement requires `confirmReplace: true`. Prefer stable-node patches so concurrent edits to unrelated blocks can be rebased safely.
</Warning>

## Conflicts and idempotency

Revision, checksum, node, tree, schema, and asset conflicts are structured:

```json theme={null}
{
  "operationIndex": 0,
  "code": "REVISION_MISMATCH",
  "message": "The page changed after this edit was prepared.",
  "pageId": "<pageId>",
  "nodeId": null,
  "currentRevisionNumber": 4,
  "currentChecksum": "<currentChecksum>"
}
```

Possible conflict codes include `REVISION_MISMATCH`, `CHECKSUM_MISMATCH`, `NODE_NOT_FOUND`, `PAGE_NOT_FOUND`, `MANUAL_NOT_FOUND`, `PARENT_NOT_FOUND`, `PAGE_ARCHIVED`, `DEPTH_EXCEEDED`, `TREE_CYCLE`, `DUPLICATE_CLIENT_KEY`, `AUDIENCE_INVALID`, `ASSET_NOT_READY`, `SCHEMA_INVALID`, and `REPLACE_NOT_CONFIRMED`.

Send a stable `Idempotency-Key` for creates, applies, submits, restores, and publications. Retrying the same request returns the original result; reusing the key with a different body returns `409`.

### Revisions and restoration

| Method | Path                                                     | Scope                | Purpose                                                               |
| ------ | -------------------------------------------------------- | -------------------- | --------------------------------------------------------------------- |
| `GET`  | `/manuals/{manualId}/revisions`                          | `instructions:read`  | List immutable manual revisions.                                      |
| `GET`  | `/manuals/{manualId}/revisions/diff?a=N&b=N`             | `instructions:read`  | Diff two revisions.                                                   |
| `POST` | `/manuals/{manualId}/revisions/{revisionNumber}/restore` | `instructions:write` | Restore a snapshot as a new audited head; history is never rewritten. |

### Publications

| Method | Path                                                            | Scope                          | Purpose                                                                               |
| ------ | --------------------------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------- |
| `GET`  | `/manuals/{manualId}/publications`                              | `instructions:read`            | List immutable publications.                                                          |
| `POST` | `/manuals/{manualId}/publications`                              | `instructions:publish` + owner | Freeze an exact manual revision for one or more audiences. Accepts `Idempotency-Key`. |
| `GET`  | `/manuals/{manualId}/publications/{publicationNumber}/manifest` | `instructions:read`            | Read the immutable page manifest, optionally filtered by audience.                    |

Publish with `{"baseRevisionNumber":7,"audiences":["WORKER","REVIEWER"],"note":"First release"}`. Audience values are `ADMIN`, `CONTRIBUTOR`, `REVIEWER`, and `WORKER`.

### Private assets

| Method | Path                         | Scope                | Purpose                                                         |
| ------ | ---------------------------- | -------------------- | --------------------------------------------------------------- |
| `GET`  | `/assets?workspaceId=…`      | `instructions:read`  | List asset metadata and short-lived download URLs.              |
| `GET`  | `/assets/{assetId}`          | `instructions:read`  | Read one asset record and short-lived download URL.             |
| `POST` | `/assets/prepare`            | `instructions:write` | Validate metadata and obtain a short-lived signed `PUT` target. |
| `POST` | `/assets/{assetId}/finalize` | `instructions:write` | Verify the uploaded object and mark it ready.                   |

Prepare accepts `workspaceId`, `filename`, `mimeType`, `sizeBytes`, `sha256`, and optional `kind` (`IMAGE`, `FILE`, `AUDIO`, or `VIDEO`). Upload the bytes to the returned URL with the returned headers, then finalize. Canonical documents reference asset IDs, never storage URLs.

### Search and rendering

| Method | Path              | Scope               | Purpose                                                                       |
| ------ | ----------------- | ------------------- | ----------------------------------------------------------------------------- |
| `GET`  | `/search`         | `instructions:read` | Search accessible pages by query, workspace, manual, audience, and cursor.    |
| `POST` | `/render-preview` | `instructions:read` | Render saved or ad-hoc canonical content as HTML or Markdown without writing. |

Render preview accepts either `{pageId,revisionNumber?,format}` or `{content,format}`. `format` is `html` or `markdown`; Markdown always includes a machine-readable loss report.

## Error envelope

Transport and permission failures use the standard API error shape:

```json theme={null}
{
  "error": "Human-readable summary",
  "code": "FORBIDDEN",
  "requestId": "<requestId>",
  "details": {}
}
```

Expect `400` for malformed requests or canonical documents, `403` for missing token scopes or workspace permission, `404` for inaccessible resources, and `409` for revision, checksum, tree, node, or idempotency conflicts.

## Next steps

* [Instruction manuals](/docs/developers/concepts/instruction-manuals) explains the data model and safety guarantees.
* [Author instruction manuals](/docs/developers/guides/author-instruction-manuals) walks through an end-to-end HTTP flow.
* [CLI: Instructions](/docs/developers/cli/instructions) covers terminal and docs-as-code workflows.
* [MCP: Instructions tools](/docs/developers/mcp/instructions) lists the grouped agent tools and resources.
