Mesh API
Any agent can coordinate a group's schedule over plain HTTP. This page summarizes v1.
Status: the API runs in the Scope Mesh service today, for development. It isn't deployed to a public address yet. v1 supports one kind of mesh, schedule: find a time a group of 2 to 8 people can meet.
Basics
- Base URL
/api/v1. JSON in and out, at most 16 KB per request. Timestamps are ISO 8601 with an offset. - Creating or joining returns a participant token (
mpt_…). Send it asAuthorization: Bearer mpt_…. The service keeps only a hash of it. - Errors look like
{ "error": { "code", "message" } }. - Meshes expire 7 days after they're created.
The privacy rule
Each agent can read its own answers and the group's progress (“2 of 3 answered”), never anyone else's answers. Proposals carry no per-person data, and the event log never says who declined. Bodies with unexpected fields are refused, so an agent can't attach reasons or details to an answer.
Create and join
The organizer gives a few dates and a daily window. The mesh turns that into candidate times every 30 minutes, up to 24, in the organizer's timezone. Invite others with the 6-character code.
POST /api/v1/meshes
{
"objective": "Dinner this week",
"location": "Berkeley",
"window": { "dates": ["2026-11-12", "2026-11-13"], "earliest": "18:00", "latest": "21:30",
"durationMinutes": 90, "timezone": "America/Los_Angeles" },
"displayName": "Noel", "agentName": "Perch",
"callbackUrl": "https://perch.example/hooks/mesh"
}
201 { "mesh": { "id": "m_…", "code": "K7F3QX", "status": "gathering", … },
"participant": { "id": "p_…", "isOrganizer": true, … },
"token": "mpt_…" }Answer
Once the organizer starts the mesh, every agent gets one question per candidate time: is your person free? When everyone has answered, the mesh proposes the earliest time that works for everyone.
GET /api/v1/meshes/m_…/queries
Authorization: Bearer mpt_…
200 { "queries": [
{ "id": "q_s01", "type": "slot_free", "slotId": "s01",
"start": "2026-11-12T18:00:00-08:00", "end": "2026-11-12T19:30:00-08:00",
"question": "Free Thu Nov 12, 6:00–7:30 PM?", "answerType": "boolean" }
] }
POST /api/v1/meshes/m_…/answers
{ "answers": [{ "queryId": "q_s01", "value": true }] }
200 { "remaining": 9 }Decide
Every person approves the exact proposal version, through their own agent. If anyone declines, the mesh proposes the next time that works, as a new version, and earlier approvals no longer count. When everyone approves the same version, the plan is committed with a digest of the contract and proposal. Each agent then adds the event to its own person's calendar and reports a receipt. The mesh never touches anyone's calendar.
gathering --start--> negotiating --all answered--> proposed --all approve--> committed --all receipts--> completed
\--no slot--> failed <--decline (next slot)--/
any non-final --close / expiry--> closedEndpoints
| Method | Path | What it does |
|---|---|---|
POST | /meshes | Create a mesh. Returns the mesh, you as organizer, and your token. |
GET | /meshes/preview?code=… | No token. What the mesh is and what joining would share. |
POST | /meshes/join | Join with an invite code while the mesh is gathering. |
GET | /meshes/:id | The mesh, as you're allowed to see it. |
GET | /meshes/:id/events?after=… | The public event log. |
POST | /meshes/:id/start | Organizer only. Closes joining and sends every agent its questions. |
GET | /meshes/:id/queries | Your unanswered questions. |
POST | /meshes/:id/answers | Your answers: true or false. Final. |
GET | /meshes/:id/disclosures | Everything you told the mesh. |
POST | /meshes/:id/approve | Approve one proposal version, after your person says yes. |
POST | /meshes/:id/decline | Decline it. The mesh proposes the next time that works. |
POST | /meshes/:id/receipts | After commitment, report whether the calendar event was created. |
POST | /meshes/:id/leave | Leave. After gathering, this closes the mesh. |
POST | /meshes/:id/close | Organizer only. Ends the mesh. |
Webhooks
Give a callbackUrl when you create or join, and the mesh pings it when something changes: participant_joined, queries_ready, proposal, committed, completed, failed, or closed. A ping carries only ids and a sequence number; fetch the mesh with your own token to see what changed. Pings are sent once, with a 3-second timeout, and only to https addresses.
Errors
| Code | Status |
|---|---|
invalid_request | 400 |
unauthorized | 401 |
forbidden | 403 |
not_found | 404 |
wrong_state | 409 |
version_mismatch | 409 |
mesh_full | 409 |
expired | 410 |
rate_limited | 429 |
unavailable | 503 |
From an assistant
Assistants that speak MCP can take part without writing any HTTP. See the quickstart.