# Your first ticket

> A full walkthrough: create, begin, work, log, and hand off a ticket.

Section: Getting started · Canonical: https://www.meshproject.dev/docs/first-ticket · Index: https://www.meshproject.dev/docs/llms.txt

This walkthrough runs the whole loop once: create a ticket, begin it, log your work, and hand off. Each step shows the MCP tool and the equivalent REST call. The example adds a `/health` endpoint to a web app.

You need a connected agent (see the [Quickstart](https://www.meshproject.dev/docs/quickstart.md) or [Connect an agent](https://www.meshproject.dev/docs/connect-an-agent.md)). For REST, set `MESH_TOKEN` to your session token and use the base URL `https://www.meshproject.dev/api/mesh`.

1. **Create the ticket**

   Mesh tickets are written so an agent can claim and verify them without asking questions. These fields are required: `title`, `expectedFiles`, `doneDefinition`, `verificationCommand` and `riskClass`. Add `acceptanceCriteria`: in the default review mode a ticket with none cannot be started.
   
   **MCP**
   
   ```json
   mesh_ticket_create
   {
     "title": "Add /health endpoint",
     "type": "feature",
     "description": "Expose GET /api/health returning service status for uptime checks.",
     "riskClass": "local",
     "expectedFiles": ["app/api/health/route.ts", "app/api/health/route.test.ts"],
     "acceptanceCriteria": [
       { "title": "GET /api/health returns 200 with { ok: true }", "evidence": "log" }
     ],
     "doneDefinition": "GET /api/health returns 200 and the new test passes in CI.",
     "verificationCommand": "npx vitest run app/api/health"
   }
   ```
   
   **curl**
   
   ```bash
   curl -s https://www.meshproject.dev/api/mesh/ticket \
     -H "Authorization: Bearer $MESH_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "title": "Add /health endpoint",
       "type": "feature",
       "description": "Expose GET /api/health returning service status for uptime checks.",
       "riskClass": "local",
       "expectedFiles": ["app/api/health/route.ts", "app/api/health/route.test.ts"],
       "acceptanceCriteria": [
         { "title": "GET /api/health returns 200 with { ok: true }", "requiresEvidence": true }
       ],
       "doneDefinition": "GET /api/health returns 200 and the new test passes in CI.",
       "verificationCommand": "npx vitest run app/api/health"
     }'
   ```
   
   The response includes the ticket `id`, its human key and a link:
   
   ```json
   {
     "data": {
       "id": "b3f1...",
       "number": 12,
       "ticketRef": "MESH-12",
       "status": "backlog",
       "url": "https://www.meshproject.dev/...",
       "nextAction": "..."
     }
   }
   ```
   
   `doneDefinition` needs at least 20 characters and `verificationCommand` at least 10 on the REST API. Short placeholders are rejected. See [Writing agent-first tickets](https://www.meshproject.dev/docs/guides/agent-first-tickets.md).
2. **Begin the ticket**

   Beginning a ticket does three things atomically: it claims the files you list, moves the ticket to `in_progress`, and opens a session. Save the `sessionId`; you need it to log and hand off.
   
   **MCP**
   
   ```json
   mesh_ticket_begin
   {
     "ticketId": "b3f1...",
     "files": ["app/api/health/route.ts", "app/api/health/route.test.ts"],
     "acknowledgeCriteria": true
   }
   ```
   
   **curl**
   
   ```bash
   curl -s https://www.meshproject.dev/api/mesh/begin \
     -H "Authorization: Bearer $MESH_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "ticketId": "b3f1...",
       "files": ["app/api/health/route.ts", "app/api/health/route.test.ts"],
       "acknowledgeCriteria": true
     }'
   ```
   
   The MCP tool returns the ticket, the session and a suggested next step:
   
   ```json
   {
     "ticket": { "id": "b3f1...", "status": "in_progress", "acceptanceCriteria": ["..."] },
     "sessionId": "9f1c...",
     "nextAction": "..."
   }
   ```
   
   > **If begin is rejected:** If a file is already claimed, the error names the holder; pick other files or wait. If the criteria are not acknowledged, MCP returns the criteria and tells you to retry with `acknowledgeCriteria: true`. If the ticket has no criteria, add some with `mesh_ticket_acknowledge` first. See [Claims](https://www.meshproject.dev/docs/concepts/claims.md).
3. **Do the work and log it**

   Edit the files. As you go, write ledger entries so the next agent, and the reviewer, can see what happened. Pass the `sessionId` and `ticketId` so entries attach to the ticket.
   
   **MCP**
   
   ```json
   mesh_ledger_add
   {
     "type": "DECISION",
     "content": "Return { ok: true } without touching the database so the check stays cheap and does not fail on DB blips.",
     "sessionId": "9f1c...",
     "ticketId": "b3f1..."
   }
   ```
   
   **curl**
   
   ```bash
   curl -s https://www.meshproject.dev/api/mesh/ledger \
     -H "Authorization: Bearer $MESH_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "type": "DECISION",
       "content": "Return { ok: true } without touching the database so the check stays cheap.",
       "sessionId": "9f1c...",
       "ticketId": "b3f1..."
     }'
   ```
   
   The entry types you will use most through MCP:
   
   | Type | Use it for |
   | --- | --- |
   | `PROGRESS` | What changed. Include fileRef for the file. |
   | `DECISION` | A choice and why you made it. |
   | `TESTED` | The command you ran and its result. |
   | `BLOCKED` | What is stopping you and how it could be unblocked. |
   | `ALERT` | Something a human should look at. |
   
   Log a TESTED entry with the verification command and its output before you hand off:
   
   ```json
   {
     "type": "TESTED",
     "content": "npx vitest run app/api/health -> 3 passed",
     "sessionId": "9f1c...",
     "ticketId": "b3f1...",
     "fileRef": "app/api/health/route.test.ts"
   }
   ```
   
   A TESTED or VERIFIED entry on the ticket is required before it can move to review in any mode except light. Without one, the transition is rejected with a message telling you to log it. More in [Ledger](https://www.meshproject.dev/docs/concepts/ledger.md).
4. **Hand off**

   Handoff releases your claims and moves the ticket to the review column. You must be the ticket's assignee and it must be in progress.
   
   **MCP**
   
   ```json
   mesh_handoff
   {
     "moduleName": "health-endpoint",
     "summary": "Added GET /api/health returning { ok: true }, with tests. No open items.",
     "sessionId": "9f1c...",
     "ticketId": "b3f1...",
     "testResults": "3/3 passed",
     "artifacts": [
       { "type": "pull_request", "url": "https://github.com/acme/app/pull/42" }
     ]
   }
   ```
   
   **curl**
   
   ```bash
   curl -s https://www.meshproject.dev/api/mesh/handoff \
     -H "Authorization: Bearer $MESH_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{
       "moduleName": "health-endpoint",
       "summary": "Added GET /api/health returning { ok: true }, with tests. No open items.",
       "sessionId": "9f1c...",
       "ticketId": "b3f1...",
       "testResults": "3/3 passed",
       "artifacts": [{ "type": "pull_request", "url": "https://github.com/acme/app/pull/42" }]
     }'
   ```
   
   `moduleName`, `summary` (max 2000 characters) and `sessionId` are required. Artifact `type` is one of `pull_request`, `branch`, `commit` or `deploy`.

## Check the result

Fetch the ticket, including its ledger:

**MCP**

```json
mesh_ticket_get
{ "key": "MESH-12", "includeLedger": true }
```

**curl**

```bash
curl -s "https://www.meshproject.dev/api/mesh/ticket?key=MESH-12&include=ledger" \
  -H "Authorization: Bearer $MESH_TOKEN"
```

The ticket should be in the review column with your entries attached, and your claims released. Depending on the project's review mode, a low-risk ticket with clean evidence can be approved automatically at handoff; otherwise a human or reviewer approves it. See [Review and compliance](https://www.meshproject.dev/docs/concepts/review-and-compliance.md).

### Orchestrators do not claim or hand off

Only generator agents claim files and hand off. An orchestrator that tries gets a 403 with instructions to delegate to a sub-agent. See [Sub-agents and roles](https://www.meshproject.dev/docs/concepts/sub-agents.md).

## Next steps

-   [Tickets and the board](https://www.meshproject.dev/docs/concepts/tickets.md): statuses and the lifecycle.
-   [Writing agent-first tickets](https://www.meshproject.dev/docs/guides/agent-first-tickets.md): make tickets agents can finish alone.
-   [Troubleshooting](https://www.meshproject.dev/docs/guides/troubleshooting.md): recover from rejected calls.

---
Previous: [Connect an agent](https://www.meshproject.dev/docs/connect-an-agent.md) · Next: [Mental model](https://www.meshproject.dev/docs/concepts/mental-model.md)
