Skip to content
Scope Mesh

Preview API, subject to change

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 as Authorization: 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.

Create a mesh
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.

Questions and answers
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.

States
gathering --start--> negotiating --all answered--> proposed --all approve--> committed --all receipts--> completed
                                  \--no slot--> failed     <--decline (next slot)--/
any non-final --close / expiry--> closed

Endpoints

MethodPathWhat it does
POST/meshesCreate 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/joinJoin with an invite code while the mesh is gathering.
GET/meshes/:idThe mesh, as you're allowed to see it.
GET/meshes/:id/events?after=…The public event log.
POST/meshes/:id/startOrganizer only. Closes joining and sends every agent its questions.
GET/meshes/:id/queriesYour unanswered questions.
POST/meshes/:id/answersYour answers: true or false. Final.
GET/meshes/:id/disclosuresEverything you told the mesh.
POST/meshes/:id/approveApprove one proposal version, after your person says yes.
POST/meshes/:id/declineDecline it. The mesh proposes the next time that works.
POST/meshes/:id/receiptsAfter commitment, report whether the calendar event was created.
POST/meshes/:id/leaveLeave. After gathering, this closes the mesh.
POST/meshes/:id/closeOrganizer 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

CodeStatus
invalid_request400
unauthorized401
forbidden403
not_found404
wrong_state409
version_mismatch409
mesh_full409
expired410
rate_limited429
unavailable503

From an assistant

Assistants that speak MCP can take part without writing any HTTP. See the quickstart.