> ## 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.

# Author Instruction Manuals

> Create a workspace and manual, author nested pages with atomic changesets, attach private media, review proposals, and publish for workers.

This guide walks the full agent authoring loop with the HTTP API. The [CLI](/docs/developers/cli/instructions) and [MCP tools](/docs/developers/mcp/instructions) wrap the same endpoints one-to-one, so every step translates directly.

**Requirements:** a verified user who belongs to an employer organization, plus a personal API token with `instructions:read` and either `instructions:write` (direct authoring) or `instructions:propose` (reviewed proposals). Proposal authors need workspace `EDITOR` or `OWNER`; reviewer decisions need `instructions:review` plus `REVIEWER` or `OWNER`. Publishing needs `instructions:publish` plus `OWNER`. See [scopes](/docs/developers/concepts/scopes-and-capabilities).

## 1. Discover the vocabulary

Fetch the schema contract once and cache it — it tells you every valid node type, mark, attribute, operation, audience, and asset limit:

```bash theme={null}
curl -s https://app.opentrain.ai/api/public/v1/instructions/capabilities \
  -H "Authorization: Bearer $OPENTRAIN_API_TOKEN"
```

Author only node/mark types the response lists. Unknown types are rejected at write time because the human editor cannot round-trip them.

## 2. Create a workspace and manual

```bash theme={null}
curl -s -X POST https://app.opentrain.ai/api/public/v1/instructions/workspaces \
  -H "Authorization: Bearer $OPENTRAIN_API_TOKEN" \
  -H "Idempotency-Key: ws-setup-1" \
  -H "Content-Type: application/json" \
  -d '{"name": "Operations"}'

curl -s -X POST https://app.opentrain.ai/api/public/v1/instructions/workspaces/{workspaceId}/manuals \
  -H "Authorization: Bearer $OPENTRAIN_API_TOKEN" \
  -H "Idempotency-Key: manual-setup-1" \
  -H "Content-Type: application/json" \
  -d '{"title": "Reviewer handbook", "defaultAudience": "REVIEWER", "bindings": [{"kind": "WORK_PROJECT", "targetId": "<projectId>", "precedence": 0}]}'
```

Bindings decide where the manual appears: on a job, a folder, or a work project. More specific bindings win (job over folder over work project).

## 3. Author nested pages in one atomic changeset

Give each `CREATE_PAGE` a `clientPageKey`, then reference it from children as `new:<key>` — order doesn't matter, and the whole section lands atomically or not at all:

```bash theme={null}
curl -s -X POST https://app.opentrain.ai/api/public/v1/instructions/changesets/apply \
  -H "Authorization: Bearer $OPENTRAIN_API_TOKEN" \
  -H "Idempotency-Key: handbook-v1" \
  -H "Content-Type: application/json" \
  -d '{
    "workspaceId": "<workspaceId>",
    "description": "Initial handbook structure",
    "operations": [
      {"kind": "CREATE_PAGE", "manualId": "<manualId>", "parentPageId": null, "title": "Review workflow", "clientPageKey": "workflow"},
      {"kind": "CREATE_PAGE", "manualId": "<manualId>", "parentPageId": "new:workflow", "title": "Edge cases", "clientPageKey": "edge-cases"}
    ]
  }'
```

The response returns the new page IDs, each page's revision and checksum, and the manual's new head revision. Keep those — subsequent edits must present them as base guards.

## 4. Edit content with stable-node patches

Read a page ([`GET /pages/{pageId}`](/docs/developers/api-reference/instructions/get-page)) to get its canonical JSON, revision, and per-block `attrs.id` values, then patch specific blocks:

```json theme={null}
{
  "workspaceId": "<workspaceId>",
  "operations": [{
    "kind": "PATCH_CONTENT",
    "pageId": "<pageId>",
    "baseRevisionNumber": 3,
    "ops": [
      {"op": "append_children", "nodeId": null, "nodes": [
        {"type": "paragraph", "content": [{"type": "text", "text": "Always cite the rubric row you applied."}]}
      ]}
    ]
  }]
}
```

If another writer changed the page first, you get a structured `REVISION_MISMATCH` conflict with the current revision — re-read, rebase your patch, retry. Run the same body through [`/changesets/validate`](/docs/developers/api-reference/instructions/validate-changeset) first when you want a dry run.

## 5. Attach private media

```bash theme={null}
# 1. Register the bytes you are about to upload
curl -s -X POST https://app.opentrain.ai/api/public/v1/instructions/assets/prepare \
  -H "Authorization: Bearer $OPENTRAIN_API_TOKEN" -H "Content-Type: application/json" \
  -d '{"workspaceId": "<workspaceId>", "filename": "walkthrough.mp4", "mimeType": "video/mp4", "sizeBytes": 104857600, "sha256": "<hex>", "kind": "VIDEO"}'
# 2. PUT the file bytes to the returned upload.url with the returned headers
# 3. Finalize so the asset becomes READY
curl -s -X POST https://app.opentrain.ai/api/public/v1/instructions/assets/{assetId}/finalize \
  -H "Authorization: Bearer $OPENTRAIN_API_TOKEN"
```

Then attach it with an `ATTACH_ASSET` operation. Documents reference assets by ID only; readers get short-lived signed URLs. Finalization checks the prepared object and trusted upload metadata, but it does not independently download and re-hash the stored bytes; keep your local SHA-256 as the source integrity check.

## 6. Propose, review, publish

On a shared manual, an `EDITOR` or `OWNER` can submit instead of applying: [`POST /changesets/submit`](/docs/developers/api-reference/instructions/submit-changeset) stores the proposal and returns a review URL. A `REVIEWER` or `OWNER` approves or rejects it (optionally with a `decisionNote`), and the approved proposal is applied by ID — still revision-guarded, so stale proposals conflict instead of clobbering newer edits.

When the manual is ready for workers:

```bash theme={null}
curl -s -X POST https://app.opentrain.ai/api/public/v1/instructions/manuals/{manualId}/publications \
  -H "Authorization: Bearer $OPENTRAIN_API_TOKEN" -H "Content-Type: application/json" \
  -d '{"baseRevisionNumber": 7, "audiences": ["WORKER", "REVIEWER"], "note": "First worker release"}'
```

Workers read only publications, filtered to their audience — never your drafts.

## Prefer files? Use the CLI's docs-as-code loop

`opentrain instructions checkout` materializes a manual as local JSON files with a manifest of revisions and checksums; edit, `status`/`diff`, then `sync` pushes your changes back as one guarded changeset. See the [CLI reference](/docs/developers/cli/instructions).
