---
audience: developers
summary: Authoritative REST endpoint list and MCP tool catalogue.
title: API
path: reference/api
status: published
---


# API

Every operation ScaiScribe exposes, across both transports. The SDKs wrap these; you can also call them directly.

## REST

Base URL: `https://scaiscribe.scailabs.ai/v1`

All endpoints require `Authorization: Bearer <token>` unless marked PUBLIC.

### Documents

| Method | Path | Body / params | Returns |
|---|---|---|---|
| `POST` | `/v1/documents` | `{format, theme?, title?, metadata?}` | `DocumentEnvelope` |
| `GET` | `/v1/documents/{doc_id}` | — | `DocumentEnvelope` (with spec) |
| `DELETE` | `/v1/documents/{doc_id}` | — | `{ok}` |
| `POST` | `/v1/documents/{doc_id}/close` | — | `{ok}` |
| `PATCH` | `/v1/documents/{doc_id}` | `{operations: PatchOp[]}` | `{doc_id, version, applied}` |
| `POST` | `/v1/documents/{doc_id}/finalize` | `{formats?: string[], sync?: bool}` | `{doc_id, version, renders: RenderArtefact[]}` |
| `POST` | `/v1/documents/{doc_id}/preview` | `{mode}` | preview payload |
| `POST` | `/v1/documents/from_template` | `{template_id, data, format?, theme?}` | `DocumentEnvelope` |
| `POST` | `/v1/documents/ingest` | multipart: `file`, `format`, `sync` | `IngestResponse` |

### PatchOp shape

```json
{
  "op": "add_element" | "edit_element" | "delete_element" | "move_element",
  "anchor": "<element_id, optional>",
  "element": { /* element body — only on add/edit */ },
  "element_id": "<id, only on edit/delete>",
  "target_id": "<id, only on move>",
  "position": "before" | "after" | "first_child" | "last_child"
}
```

### Jobs

| Method | Path | Returns |
|---|---|---|
| `GET` | `/v1/jobs` | List the caller's OWN jobs. Strict per-user isolation. Supports `status`, `limit`, `offset` query params. |
| `GET` | `/v1/jobs/{job_id}` | Poll one of your own jobs; returns `JobEnvelope`. 404 if the job exists in your tenant but was submitted by a colleague. |
| `DELETE` | `/v1/templates/{template_id}` | Soft-delete a template in your tenant (any authenticated user). |

Both endpoints scope strictly to `tenant_id == caller.tenant_id AND submitted_by == caller.user_id`. A user can't see colleagues' jobs even by guessing IDs — colleagues' jobs return 404, not 403, so existence isn't leaked. Operators wanting tenant-wide visibility (across all submitters) should use `/v1/admin/jobs` (requires `tenant_admin` or higher).

Sync-wrapped operations (`finalize`, `ingest_document`, `preview`) block until the job completes and return the final result inline. Pass `sync=false` to those endpoints to get a `JobEnvelope` immediately and poll yourself.

### Assets / fonts / templates

| Method | Path | Notes |
|---|---|---|
| `POST` | `/v1/assets` | multipart upload; `kind=image|binary` |
| `GET` | `/v1/assets` | paginated list |
| `POST` | `/v1/fonts` | multipart upload; `family`, `weight?`, `style?` |
| `GET` | `/v1/fonts` | paginated list |
| `DELETE` | `/v1/fonts/{font_id}` | — |
| `POST` | `/v1/templates` | multipart upload; `name`, `format` |
| `GET` | `/v1/templates` | paginated list |

### Health

| Method | Path | Auth | Returns |
|---|---|---|---|
| `GET` | `/v1/health` | PUBLIC | `{status: "ok"}` |
| `GET` | `/v1/health/ready` | PUBLIC | `{status, checks}` |

### Admin

18 endpoints under `/v1/admin/*`. Require admin-scoped tokens. See the [SDK reference](/docs/scaiscribe/reference/sdks#admin-sub-client) for usage; the [admin UI](https://scaiscribe.scailabs.ai/admin/) is the friendliest entry point for browser-based operators.

| Method | Path | Purpose |
|---|---|---|
| `GET` | `/v1/admin/config` (PUBLIC) | SPA bootstrap signal |
| `GET` | `/v1/admin/me` | Current identity |
| `GET` | `/v1/admin/health` | Dashboard counts |
| `GET` | `/v1/admin/jobs` | Paginated job list |
| `GET` | `/v1/admin/jobs/{id}` | Job + full `result_json` |
| `GET` | `/v1/admin/users` | Paginated user list (from ScaiKey mirror) |
| `GET` | `/v1/admin/users/{u}/roles` | List of role assignments |
| `POST` | `/v1/admin/users/{u}/roles` | Grant a role |
| `DELETE` | `/v1/admin/users/{u}/roles/{id}` | Revoke a role |
| `GET` | `/v1/admin/users/{u}/groups` | Groups the user belongs to (direct + transitive) |
| `GET` | `/v1/admin/groups` | List ScaiKey groups across visible tenants |
| `GET` | `/v1/admin/groups/{id}` | One group + members + parent/child group context |
| `GET` | `/v1/admin/tenants` | List of tenants (with quota usage) |
| `GET` | `/v1/admin/partners` | List of partners |
| `GET` | `/v1/admin/quotas/partner/{id}` | Aggregate quota usage across a partner's tenants |
| `GET` | `/v1/admin/quotas/tenant/{id}/users` | Break a tenant's quota usage down by acting user |
| `POST` | `/v1/admin/scaikey/rotate-webhook-secret` | Mint a fresh ScaiKey webhook signing secret (superadmin) |
| `GET` | `/v1/admin/templates` | List templates across visible tenants |
| `POST` | `/v1/admin/templates` | Upload a template on behalf of a tenant (multipart) |
| `GET` | `/v1/admin/templates/{id}/download` | Presigned download URL for the source binary |
| `GET` | `/v1/admin/templates/{id}/file` | Stream the source binary directly (fallback when presigning isn't supported) |
| `DELETE` | `/v1/admin/templates/{id}` | Soft-delete a template |

## MCP

The MCP server is reachable at `https://scaiscribe.scailabs.ai/mcp/` over Streamable HTTP. AI agents (Claude, GPT, custom) can connect and call any of the **46 `scribe_*` tools**.

### Tool catalogue

The full tool list is mechanically derived from the running MCP server. Categories at a glance:

| Category | Tools |
|---|---|
| Document lifecycle | `scribe_create_document`, `scribe_get_document`, `scribe_close_document`, `scribe_delete_document`, `scribe_finalize` |
| Body elements | `scribe_add_heading`, `scribe_add_paragraph`, `scribe_add_list`, `scribe_add_table`, `scribe_add_image`, `scribe_add_callout`, `scribe_add_code_block`, `scribe_add_quote`, `scribe_add_chart`, `scribe_add_toc`, `scribe_add_page_break` |
| Slides (PPTX) | `scribe_add_slide`, `scribe_add_text_box`, `scribe_add_slide_table`, `scribe_add_slide_chart`, `scribe_add_slide_image`, `scribe_add_shape` |
| Sheets (XLSX) | `scribe_add_sheet`, `scribe_add_cells`, `scribe_add_sheet_chart`, `scribe_add_sheet_image`, `scribe_set_column_widths`, `scribe_set_row_heights`, `scribe_add_merged_cells`, `scribe_add_conditional_format` |
| Templates | `scribe_register_template`, `scribe_instantiate_template`, `scribe_list_templates` |
| Themes | `scribe_set_theme`, `scribe_list_themes` |
| Ingestion | `scribe_ingest_document` |
| Edit operations | `scribe_edit_element`, `scribe_delete_element`, `scribe_move_element`, `scribe_patch_document` |
| Assets | `scribe_upload_asset`, `scribe_list_assets` |
| Fonts | `scribe_upload_font`, `scribe_list_fonts` |
| Preview | `scribe_preview` |

Every tool's `input_schema` is exposed by the MCP server itself — most agent runtimes auto-discover them on connect.

### Connecting an agent

For Claude Desktop, GPT-customs, or any MCP-aware agent, add the transport:

```jsonc
// Example MCP server config
{
  "scaiscribe": {
    "transport": "streamable_http",
    "url": "https://scaiscribe.scailabs.ai/mcp/",
    "headers": {
      "Authorization": "Bearer <your scaikey-issued token>"
    }
  }
}
```

The token is the same one the REST SDKs use — get it via `client_credentials` against ScaiKey.

## Spec format

The document specification is the canonical payload — what gets PATCHed into a document and what comes back out of `ingest_document`. It's documented in detail in the [Concepts → Spec format](/docs/scaiscribe/concepts) page; the formal JSON Schema lives at [`schema/scaiscribe.schema.json`](https://github.com/scailabs/scaiscribe/blob/main/schema/scaiscribe.schema.json) and is the source of truth for SDK-generated types.

## OpenAPI

The full OpenAPI spec is served at `https://scaiscribe.scailabs.ai/openapi.json`. The interactive Swagger UI at `/docs` is gated behind auth for the alpha; we'll move it to a public docs page once content is stable.
