# REST API

Publish and manage pages from scripts, CI and your own apps.

- **Base URL:** `https://api.docpin.app`
- **Auth:** `Authorization: Bearer dp_…`. API keys belong to one workspace and act with the rights of the owner who created them; they stop working if that person leaves the workspace. Workspace owners create keys in the DocPin app under **Connections → API keys**. The full key is shown once.
- **Format:** JSON in and out, except uploads, which can be `multipart/form-data`.
- **Errors:** a non-2xx status with `{ "error": "Plain-English message" }`.
- **Rate limits:** about 30 publishes a minute per workspace, and separately about 30 data writes a minute. Over that: `429`.

Pages are identified by **id** (a UUID) in paths. `GET /v1/pages` lists them, and publishing returns `doc.id`.

## Publish a page

`POST /v1/pages`

Upload one HTML file, several files, or a `.zip` (up to 25 MB) as `multipart/form-data`, or send HTML as JSON.

| Field | Type | Notes |
| --- | --- | --- |
| `file` | file(s) | `index.html`, other files, or one `.zip`. Multipart only. |
| `html` | string | The whole HTML document. JSON only. |
| `title` | string | Defaults to the document's `<title>`. |
| `slug` | string | The page's address, e.g. `sales` → `/sales`. **Reusing a slug publishes a new version of that page**: same link, earlier versions kept. |
| `board` | string | Board slug to publish into. |
| `unlisted` | boolean | Only the short link opens it, and it stays off listings. |
| `access` | object | Who can open it: `{"kind":"public"}`, `{"kind":"password","password":"…"}`, or `{"kind":"people","people":["sam@acme.com","@acme.com"]}`. Private kinds need Pro. In multipart, send it as a JSON string. |

```bash
curl https://api.docpin.app/v1/pages -H "Authorization: Bearer $DOCPIN_API_KEY" \
  -F file=@index.html -F slug=q4-report

curl https://api.docpin.app/v1/pages -H "Authorization: Bearer $DOCPIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<!doctype html><title>Hello</title><h1>Hello</h1>", "slug": "hello"}'
```

Response (`201` for a new page, `200` for a new version):

```json
{
  "created": true,
  "url": "https://acme.docpin.it/hello",
  "prettyUrl": "https://acme.docpin.it/hello",
  "shortUrl": "https://docpin.it/Ab3dE7",
  "doc": { "id": "61fb0253-…", "title": "Hello", "slug": "hello", "board": null, "unlisted": false, "access": { "kind": "public" } },
  "version": { "id": "…", "files": 1, "bytes": 52 }
}
```

`url` is the link to share.

## Pages

| Method | Path | Body | Description |
| --- | --- | --- | --- |
| `GET` | `/v1/pages` | | Lists pages, newest first: `id`, `title`, `slug`, `board`, `prettyUrl`, `shortUrl`, `access`, `views`, `updated`, `archived`. |
| `PATCH` | `/v1/pages/{id}` | `title`, `slug`, `board`, `unlisted`, `listed`, `archived` | Changes a page. A new slug changes the link; the old link redirects. |
| `DELETE` | `/v1/pages/{id}` | | Deletes the page, its files, versions and data. |
| `PUT` | `/v1/pages/{id}/access` | an `access` object (above), or `{"kind":"inherit"}` | Changes who can open it, without changing the link. |
| `GET` | `/v1/pages/{id}/views` | | View counts by day, and named viewers for people-only pages. |

## Page data

A JSON object per page that the published page reads with `docpin.data()`. See [Page data](/docs/page-data.md).

| Method | Path | Body | Description |
| --- | --- | --- | --- |
| `GET` | `/v1/pages/{id}/data` | | `{ "data": {…}, "updatedAt": "…" \| null }` |
| `PATCH` | `/v1/pages/{id}/data` | JSON object | Merges top-level keys; `null` removes a key. Returns the full data. |
| `PUT` | `/v1/pages/{id}/data` | JSON object | Replaces everything. Returns the full data. |

Up to 1 MB per page, measured as JSON text.

## Versions

Every publish to the same slug is kept.

| Method | Path | Description |
| --- | --- | --- |
| `GET` | `/v1/pages/{id}/versions` | Newest first, with `number`, `publishedAt`, `via`, `by`, and `current`. |
| `POST` | `/v1/pages/{id}/versions/{versionId}/preview` | `{ "url": "…" }`: a private preview link that works for an hour. |
| `POST` | `/v1/pages/{id}/versions/{versionId}/restore` | Makes that version live again behind the same links. Newer versions are kept. |

## Boards

Boards group pages under one landing page, like `/team-updates`.

| Method | Path | Body | Description |
| --- | --- | --- | --- |
| `GET` | `/v1/boards` | | Lists boards. |
| `POST` | `/v1/boards` | `title`, optional `slug`, `description` | Creates a board. |
| `PATCH` | `/v1/boards/{slug}` | `title`, `slug`, `description` | Changes a board. |
| `PUT` | `/v1/boards/{slug}/access` | an `access` object | Who can open the board and its pages. |
| `DELETE` | `/v1/boards/{slug}` | | Deletes the board. Its pages are kept unless you add `?pages=delete`. |

## Status codes

| Status | Meaning |
| --- | --- |
| `200` / `201` | OK / created. |
| `400` | Something in the request is wrong; the message says what. |
| `401` | Missing or invalid key, or the key's creator left the workspace. |
| `403` | Not allowed: a Pro feature on Free, an owner-only action, or a taken-down page. |
| `404` | Not found in this workspace. |
| `409` | Conflict, e.g. a slug already in use. |
| `413` | Too large. |
| `415` | Unsupported content type for publishing. |
| `429` | Rate limited. Wait a minute. |
