Yarnt Start free

Yarnt API Reference

A REST API for working with tasks, projects, comments, and history.

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:

StatusMeaning
400A required field is missing or invalid (for example, no title when you create a task), or a plan limit rejected the write.
401Missing, invalid, or revoked API key.
404The task, project, or member referenced does not exist.
500The request was valid but the server failed. Retry the request.

Discovery

RequestReturns
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 paramValues
listqueue (default) | now | next | wishlist | finished | all
assigneeme | unassigned | a user id (see GET /v1/users)
projectA 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 fieldType
titlestring, required
projectstring, optional. A project name.
wishboolean, 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 fieldType
titlestring, optional
bodystring, optional
assigneea user id to set, null to unassign, omit to leave unchanged
projectstring (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 paramValues
includeArchivedtrue 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.