---
title: "REST API"
description: "A Bearer-token HTTP surface over the logic the app uses: tickets, projects, notes, attachments and connectors. Conventions, auth, and the main endpoints."
url: "https://www.fredrin.com/docs/api"
updated: "2026-08-13"
---

# REST API

An HTTP surface that mirrors what the app itself does, so skills, CLIs and external automations can drive a board without a browser session.

> **v0.** The shape below is stable enough to build on and is what the CLI and the MCP server both use. It is not frozen: breaking changes bump the major version in the path. Tell us what you build so we know what we are constraining.

## Conventions

- **Base path:** `/api/v1`
- **Machine-readable spec:** `GET /api/v1/openapi.json` serves an OpenAPI 3.1 document generated from the schemas every endpoint validates with. No key needed.
- **Content type:** `application/json`, both directions.
- **IDs:** cuid strings.
- **Timestamps:** ISO 8601, UTC.
- **Pagination:** cursor-based, `?cursor=<id>&limit=<n>`. Default 50, maximum 100.

Every response uses the same envelope, and the HTTP status follows it:

```json
{ "ok": true,  "data": { } }
{ "ok": false, "error": "ticket_not_found" }
```

Errors are stable string slugs, never free-form prose: `unauthorized`, `forbidden`, `validation_error`, `ticket_not_found`, `invalid_status`, and so on. Match on the slug.

## Authentication

```
Authorization: Bearer fredrin_live_…
```

Generate a key under **Settings → API keys**. Keys are:

- **Scoped.** A key is bound to one workspace, and can be narrowed further to a single project. It never reaches outside that boundary.
- **Bound to the issuing member's role**, so a key cannot do more than the person who made it.
- **Stored hashed.** SHA-256, compared in constant time. You see the value once, at creation.

Fredrin also mints short-lived keys automatically for its own Workers, which is how an agent inside a ticket can talk to the board it belongs to.

## Tickets

```
GET    /api/v1/tickets?projectId={projectId}
GET    /api/v1/tickets/{ticketId}
POST   /api/v1/tickets
PATCH  /api/v1/tickets/{ticketId}
POST   /api/v1/tickets/{ticketId}/move
POST   /api/v1/tickets/{ticketId}/start
DELETE /api/v1/tickets/{ticketId}
```

The list takes `projectId` as a query parameter, and filters on `statusId`, `assigneeId` and `includeArchived`.

Creating one:

```json
POST /api/v1/tickets
{
  "projectId": "ckxx…",
  "statusId": "ckxx…",
  "title": "Fix the retry backoff on 429",
  "description": "…",
  "plan": "## Outcome\n…",
  "dependsOn": ["FRED-ABC123"],
  "attachments": [{ "attachmentId": "ckxx…", "label": "Repro" }]
}
```

`dependsOn` accepts ids or human identifiers and creates the new ticket blocked by each. `attachments` ports existing uploads onto the ticket in the same call, and validates every id up front: one bad reference fails the whole create rather than producing a half-attached ticket.

`POST /start` is the one that matters most. It dispatches a Worker, which is the API equivalent of pressing Run.

## Other resources

| Resource | Routes |
|---|---|
| Workspaces | `GET /api/v1/workspaces` |
| Projects | `GET /api/v1/workspaces/{id}/projects` |
| Statuses | `GET /api/v1/workspaces/{id}/statuses` |
| Labels, members, invitations | `/api/v1/workspaces/{id}/labels`, `/members`, `/invitations` |
| Dependencies | `/api/v1/tickets/{id}/dependencies` |
| Approvals | `/api/v1/tickets/{id}/approval` |
| Teams | `/api/v1/tickets/{id}/teams` |
| Comments | `/api/v1/tickets/{id}/comments` |
| Artifacts | `/api/v1/tickets/{id}/previews` |
| Attachments | `/api/v1/attachments` |
| Goals | `/api/v1/projects/{id}/goals` |
| Notes | `/api/v1/projects/{id}/notes` |
| Automations | `/api/v1/projects/{id}/automations` |
| Connectors | `/api/v1/projects/{id}/connectors` |

## Which surface should you use

- **The [CLI](https://www.fredrin.com/docs/cli)** from a terminal or an agent session. It wraps this API.
- **[MCP](https://www.fredrin.com/docs/mcp)** to let another AI agent manage the board.
- **This API** for everything else: your own automation, a bot, a CI step.

## Next steps

- [The CLI](https://www.fredrin.com/docs/cli) - the same surface, ergonomically.
- [MCP server](https://www.fredrin.com/docs/mcp) - for agents rather than scripts.
