Skip to content
Fredrin
Documentation

Build on Fredrin

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
  • 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:

{ "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:

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

ResourceRoutes
WorkspacesGET /api/v1/workspaces
ProjectsGET /api/v1/workspaces/{id}/projects
StatusesGET /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 from a terminal or an agent session. It wraps this API.
  • 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 - the same surface, ergonomically.
  • MCP server - for agents rather than scripts.