Yarnt API Reference
The Yarnt REST API does the same operations as the MCP server, with the same permission model. It uses plain JSON over HTTP. Use it from a script, a webhook, or a CI job when you cannot use an MCP client.
Base URL: https://api.getyarnt.com. Every endpoint below
starts with this URL. Example: https://api.getyarnt.com/v1/tasks.
Authentication
Every /v1/* request needs an Authorization header with an
API key:
Authorization: Bearer yk_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Create a key in Yarnt under Settings → Robots, on a robot you made. A robot is a workspace member that stands for the script or service that uses the key. The key can see and do only what that robot's membership permits. Revoke a key on the same settings page. The API rejects revoked and unknown keys immediately.
A missing, malformed, invalid, or revoked key returns 401 with
{ "error": "..." }.
Errors
Every non-2xx response has the body { "error": "<message>" }. The
status code shows the kind of error:
| Status | Meaning |
|---|---|
400 | A required field is missing or invalid (for example, no title when you create a task), or a plan limit rejected the write. |
401 | Missing, invalid, or revoked API key. |
404 | The task, project, or member referenced does not exist. |
500 | The request was valid but the server failed. Retry the request. |
Discovery
| Request | Returns |
|---|---|
GET / (no key needed) | A short description of the service and a link to these docs. |
GET /v1 (with a key) | A machine-readable index of every route below, one line each. |
GET /health (no key needed) | { "status": "ok" }, or 503 with "degraded" when the database is unreachable. |
Endpoints
GET /v1/tasks POST /v1/tasks GET /v1/tasks/:id PATCH /v1/tasks/:id POST /v1/tasks/:id/start|finish|drop|unstart GET/POST /v1/tasks/:id/comments GET /v1/tasks/:id/history GET/POST /v1/tasks/:id/attachments GET/DELETE /v1/attachments/:id POST /v1/tasks/:id/uploads GET /v1/users GET /v1/whoami GET /v1/projects
GET/v1/tasks
List tasks in the workspace, filtered by list, assignee, or project.
| Query param | Values |
|---|---|
list | queue (default) | now | next | wishlist | finished | all |
assignee | me | unassigned | a user id (see GET /v1/users) |
project | A project name. |
now and next are the two sublists of queue.
now holds tasks in progress. next holds tasks that wait.
all returns every list.
Each task in the response has the list it is on. assignee is a user id (or null). To get a name, look the id up with GET /v1/users.
{ "tasks": [ { "id": "...", "title": "...", "list": "next", "assignee": "dcba4321-..." } ] }
POST/v1/tasks
Create a task. title is required.
| Body field | Type |
|---|---|
title | string, required |
project | string, optional. A project name. |
wish | boolean, optional. Puts the task on the Wishlist instead of the Queue. |
201: { "id": "...", "wish": false }
GET/v1/tasks/:id
Full detail for one task:
{
"id": "...", "title": "...", "list": "now", "assignee": "dcba4321-...",
"project": "...", "labels": ["..."], "blocked": false, "blockedReason": null,
"createdAt": "...", "updatedAt": "...", "body": "..."
}
PATCH/v1/tasks/:id
Edit one or more fields of a task.
| Body field | Type |
|---|---|
title | string, optional |
body | string, optional |
assignee | a user id to set, null to unassign, omit to leave unchanged |
project | string (name) to set, null to clear, omit to leave unchanged |
Returns { "id": "..." }.
POST/v1/tasks/:id/start
POST/v1/tasks/:id/finish
POST/v1/tasks/:id/drop
POST/v1/tasks/:id/unstart
Move a task through its lifecycle. No body. Each returns { "id": "..." }.
unstart reverses start. The task goes back to state
todo at the top of Next. Its blocked flag and reason are
cleared. If the task is not started, unstart does nothing.
GET/v1/tasks/:id/comments
Returns { "comments": [ { "createdAt": "...", "authorId": "...", "body": "..." } ] }.
POST/v1/tasks/:id/comments
Body: { "body": "..." }, required. 201 on success.
GET/v1/tasks/:id/history
Returns { "history": [ { "createdAt": "...", "kind": "...", "fromVal": "...", "toVal": "..." } ] }.
GET/v1/tasks/:id/attachments
Returns { "attachments": [ { "id": "...", "name": "...", "size": 1234,
"contentType": "...", "createdAt": "...", "createdBy": "...", "url": "..." } ] },
newest first.
The bucket is private, so url is a signed download link. Fetch the bytes from
it directly. The link expires one hour after the response. Every read returns a new link.
Store the attachment id, not the URL.
POST/v1/tasks/:id/attachments
Body: { "name": "build.log", "data": "<base64>", "contentType": "text/plain" }.
name and data are required. contentType defaults to
application/octet-stream. Returns 201 with the created attachment.
Your plan's caps apply as they do in the app: file size, files per task, and total workspace storage. A rejected upload leaves nothing behind.
This route sends the file in the JSON body, so use it for small files. For larger files, and for files an agent produced, use the two-step upload below. It sends the bytes straight to storage.
POST/v1/tasks/:id/uploads
Body: { "name": "render.png", "contentType": "image/png" }. name is
required. Returns 201 with { "upload": "<handle>", "url": "...",
"expiresIn": 7200, "contentType": "image/png" }. PUT the file's bytes to
url. Nothing is recorded until you finish the upload. An upload you do not
finish attaches nothing.
curl -X PUT -H "Content-Type: image/png" --upload-file render.png "$url"
The returned contentType is the type the attachment is stored as. It is the
type you asked for, or a type derived from the filename if you left it out. The API records
this type, not the type your PUT declares. curl --upload-file sends
application/octet-stream by default, and an image with that type downloads
instead of showing inline. Send the header anyway, so storage and the record agree.
POST/v1/attachments
Body: { "upload": "<handle>" }. Records the file you uploaded. Returns
201 with the attachment. The API reads the size from storage and does not take
it from you. Your plan's caps apply to the bytes that arrived. If a cap rejects the upload,
the API removes the uploaded object.
GET/v1/attachments/:id
One attachment's metadata and a new signed url. The API does not serve the
bytes. The URL serves them.
DELETE/v1/attachments/:id
Removes the attachment and its stored bytes. Returns { "id": "...", "name": "..." }.
An id that is not yours, or does not exist, returns 404. You cannot undo this.
To get the file back, upload it again.
GET/v1/users
List the workspace members you can assign tasks to. Use a member's id as the
assignee value in other requests. kind is human for people and
integration for bots and service accounts.
{ "users": [ { "id": "dcba4321-...", "name": "Ada", "kind": "human" },
{ "id": "abcd1234-...", "name": "Yarntbot", "kind": "integration" } ] }
GET/v1/whoami
Your own membership row and the person who granted you access. Use the returned
id as the assignee value in other requests. It shows which
GET /v1/users row is you.
{ "id": "abcd1234-...", "name": "Yarntbot", "kind": "integration",
"human": { "id": "dcba4321-...", "name": "Ada" } }
GET/v1/projects
List projects in the workspace.
| Query param | Values |
|---|---|
includeArchived | true to include archived projects (default: excluded) |
Returns { "projects": [ { "name": "...", "archived": false } ] }.
Examples
Create a task
curl -s https://api.getyarnt.com/v1/tasks \
-H "Authorization: Bearer yk_xxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"title": "Fix the flaky deploy step", "project": "infra"}'
{ "id": "1234abcd...", "wish": false }
List your tasks in Now
curl -s "https://api.getyarnt.com/v1/tasks?list=now&assignee=me" \
-H "Authorization: Bearer yk_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
{ "tasks": [ { "id": "1234abcd...", "title": "Fix the flaky deploy step", "list": "now", "assignee": "abcd1234-..." } ] }
Questions about the API: hello@getyarnt.com.