relai has three ways for your own tools to work with pins: the API, webhooks and AI agents over MCP. All three act for a person in one workspace and see only what that person can see.
The API
Create a personal token on the page for connecting an agent, at staging.relai.team/dashboard/settings/connections/ai-agent. Send it with every request as Authorization: Bearer relai_pat_…. The API’s address is https://www.relai.team.
| Request | What it does |
|---|---|
GET /api/v1/me | Checks a token: who it belongs to and the workspace. |
GET /api/v1/pins | Lists pins (open ones unless you ask for another status), with the same filters as the agent’s list_pins. |
GET /api/v1/pins/:ref | One pin in full, by its number or id. |
POST /api/v1/pins/:ref/status | Changes the status. Body: { status, note?, link? }. Needs a role that can change status. |
POST /api/v1/pins/:ref/comments | Comments on a pin. Body: { body }. Any role. |
Statuses
status keeps its three values, so nothing you built breaks. The resolved status means "marked fixed": where the workspace checks fixes, the pin then waits for its check before it is done.
| status | What it means |
|---|---|
open | Nobody is working on it yet, or it was reopened. |
in_progress | Someone is working on it. |
resolved | Marked fixed. Read stage to know whether it waits for its check, is done or was closed. |
Stages and the new fields
Every pin also carries these fields. All four are optional, so treat a missing one as unknown.
stage: Where the pin is (the table below).resolution: Why a resolved pin is resolved: fixed or closed; null while it is not.checkBy: Who checks a fix that waits: a person or the Admins; null when nobody is named.previousStage: Only in the answer to a status change: the stage before it.
| stage | What it means | What people see in relai |
|---|---|---|
unowned | Nobody assigned yet. | Open |
team | In a team's queue, waiting for someone on the team to take it. | Open |
handed | Handed to a person who has not accepted it yet. | Open |
working | A person is working on it. | In progress |
fix_check | Marked fixed, waiting for its check (see checkBy). | Waiting for check |
done | Fixed, and the fix is confirmed (or the workspace doesn't check fixes). | Done |
closed | Closed without a fix. | Closed |
The answer to a status change:
{
"ok": true,
"pin": {
"number": 12,
"id": "…",
"status": "resolved",
"previousStatus": "in_progress",
"stage": "fix_check",
"previousStage": "working",
"resolution": "fixed",
"checkBy": { "kind": "person", "email": "dana@example.com", "name": "Dana Mizrahi" }
}
}checkBy names a person (with their address, in the API only) or { "kind": "admins" } when the Admins check it.
Webhooks
relai sends a POST with a JSON body to each address your workspace adds, for the events ticked for it. Every request has the header X-Pod-Event with the event’s name. When the address has a secret, it also has X-Pod-Signature: sha256=…, the HMAC-SHA256 of the raw body with that secret.
| X-Pod-Event | When |
|---|---|
pin_created | A pin was dropped. |
comment | Someone commented on a pin. |
mention | Someone was mentioned on a pin. |
status_change | A pin's status changed. detail has from and to (status codes). |
handoff | A pin changed hands, or its fix was confirmed. Off until the endpoint's Hand-offs box is ticked. |
test | A sample sent by the endpoint's test button (the body has sample: true). |
Every body has event, timestamp, actor (name and address), pin and, for some events, detail. The pin keeps its status and adds stage, resolution and checkBy, where checkBy gives a name, never an address. Security pins and private pins are never sent.
To check that a message came from relai (Node.js):
import { createHmac, timingSafeEqual } from "node:crypto";
function fromRelai(rawBody, header, secret) {
const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
return header?.length === expected.length && timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}The handoff message
handoff is sent when a pin changes hands or its fix is confirmed, only to addresses with the Hand-offs box ticked. It names people and teams by name only: actor.email is always null and there is never a note. detail.from and detail.to are who had the pin before and after (a person, a team, “the Admins”, or null for nobody).
{
"event": "handoff",
"timestamp": "2026-10-15T09:30:00.000Z",
"actor": { "name": "Chaim Rosen", "email": null },
"pin": {
"id": "…", "title": "Checkout button overlaps the total", "status": "open",
"stage": "handed", "resolution": null, "checkBy": null, "…": "…"
},
"detail": { "move": "handed", "from": "Checkout", "to": "Ari Cohen", "via": "person" }
}| detail.move | What happened |
|---|---|
handed | Someone handed the pin to a person. |
sent_to_team | Someone sent the pin to a team's queue. |
taken | Someone took the pin from a team's queue. |
accepted | The person it was handed to accepted it. |
not_mine | The person it was handed to said it isn't theirs, so it bounced back. |
gave_back | The person working on it gave it back. |
unassigned | Someone took the pin off its person. |
released | relai moved the pin on because its person left, was removed, became Comment only or can no longer see it. |
reopened | Someone reopened the pin and it went back to its last person. |
fix_confirmed | The fix was confirmed, so the pin is done. |
detail.via says who made the move: person is a person in relai; api is the API or an agent; rule is an automation rule; relai is relai itself.
AI agents (MCP)
Your agent sees only what its account can see. It acts as the person whose token it uses, with that person’s rights: a Comment only person’s agent can only comment. Private pins are never returned to an agent, and a security pin only when that person may see it. Every tool tells the agent: “You see what your account can see.”
Connect an agent with the hosted address (always up to date):
claude mcp add --transport http relai https://www.relai.team/api/mcpor run the local server from packages/mcp-server in relai’s repository (its README has the steps). Either way the agent has five tools: list_pins, get_pin, update_pin_status, resolve_pin, comment_on_pin. resolve_pin marks a pin fixed; where the workspace checks fixes, the pin then waits for its check, and the answer says who checks it.
- Getting started: From nothing to your first pin.
- How relai works: What relai captures, and who sees what.
- Make relai pin the same spot every time: Test hooks for the elements you pin most.