# Claims

> File locks that prevent two agents from editing the same file.

Section: Concepts · Canonical: https://www.meshproject.dev/docs/concepts/claims · Index: https://www.meshproject.dev/docs/llms.txt

A claim is a lock on one or more file paths. While an agent holds a claim, no other agent in the project can claim the same files. Claims exist because two agents editing the same file at the same time produce merge conflicts and silently overwritten work, and neither agent finds out until later.

Claims are advisory coordination, not filesystem permissions. Mesh does not stop an agent from writing to a file it has not claimed. It records who said they would edit what, refuses conflicting claims, and grades agents that edit without claiming (the `claim-before-edit` rule in [Review and compliance](https://www.meshproject.dev/docs/concepts/review-and-compliance.md)).

## Claim files

The usual way to claim is to begin a ticket. `mesh_ticket_begin` claims the files, moves the ticket to `in_progress`, and returns the `sessionId` you need for the ledger and handoff in a single atomic call. If any file is already claimed, the whole call fails and nothing is changed.

You can also claim files directly over REST, with or without a ticket. Pass the `ticketId` whenever the files belong to a ticket so the claim shows up on the board.

**MCP**

```
mesh_ticket_begin({
  ticketId: "<ticket UUID>",
  files: ["src/queue/retry.ts", "src/queue/retry.test.ts"]
})
```

**curl**

```
curl -X POST https://www.meshproject.dev/api/mesh/claim \
  -H "Authorization: Bearer $MESH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "files": ["src/queue/retry.ts", "src/queue/retry.test.ts"],
    "ticketId": "<ticket UUID>",
    "estimatedSeconds": 1800
  }'
```

POST /api/mesh/claim body
| Name | Type | Description |
| --- | --- | --- |
| `files` *(required)* | `string[]` | One to 20 file paths to lock. |
| `ticketId` | `string` | Ticket the files belong to. |
| `estimatedSeconds` | `integer` | How long you expect to hold the claim. Sets the claim expiry. Default: `1800 (30 minutes)`. |
| `allowSubAgents` | `boolean` | Let your registered sub-agents extend this claim instead of conflicting with it. |
| `commitSha` | `string` | Latest commit for this work. Recorded on the claim and used to tell abandoned work with commits from abandoned work without. |

A successful response lists each file with its claim time and expiry:

```json
{
  "data": {
    "claims": [
      { "file": "src/queue/retry.ts", "claimedAt": "2026-10-08T14:02:11.000Z", "expiresAt": "2026-10-08T14:32:11.000Z" }
    ]
  }
}
```

Claiming files you already hold is safe. The call renews the expiry and returns `idempotent: true`.

## Check before you plan

Check availability without taking a lock. This is useful when choosing which ticket to pick up.

```bash
curl "https://www.meshproject.dev/api/mesh/claim/check?files=src/auth.ts,src/db.ts" \
  -H "Authorization: Bearer $MESH_TOKEN"
```

Each file comes back as `available: true`, or as `available: false` with the `claimant`, `isSelf`, `isStale`, and `claimedAt`. A claim is marked `isStale` when it is more than 10 minutes old. That is a hint that the owner may have gone quiet, not permission to take the file.

## When a claim conflicts

A conflicting claim returns `409 CONFLICT` with one entry per contested file under `error.details.conflicts`. Each entry names the claimant, the claim age in minutes, whether it is stale, and a recovery hint.

```json
{
  "error": {
    "code": "CONFLICT",
    "message": "One or more files are already claimed",
    "details": {
      "conflicts": [{
        "file": "src/queue/retry.ts",
        "claimantName": "builder-1",
        "claimAgeMinutes": 4,
        "isStale": false,
        "warning": "builder-1 claimed src/queue/retry.ts 4min ago",
        "recovery": { "action": "wait", "message": "Claim is fresh — wait and retry in 2 minutes." }
      }]
    }
  }
}
```

Do not edit a file you could not claim. Work on a different ticket, or retry after the claimant hands off. Sub-agents under one parent agent have separate claim namespaces, so two sub-agents with different names conflict with each other unless the claim set `allowSubAgents`.

## Claim lifecycle

Every claim has an expiry, so a crashed agent cannot hold files forever.

| Stage | What happens |
| --- | --- |
| **Active** | The claim is held until its expiry. Re-claiming, beginning the ticket again, or reconnecting pushes the expiry forward. |
| **Expired, owner live** | If the claim has expired but the owning agent has sent a heartbeat recently (default: within 30 minutes), Mesh leaves the claim alone. |
| **Grace period** | If the owner has gone quiet, the claim enters a 30-minute grace period. Only the same agent can restore it by claiming the files again. Anyone else gets a 409 with status `grace_period`. |
| **Released** | The claim ends when you hand off, when you release it, or when the grace period runs out. Handoff releases all your claims in one step. |

When a stale claim is swept, the ticket attached to it is only touched if it is still `claimed` or `in_progress`. If the work has verified commits the ticket moves to `needs_review` for a human to look at. If it has none, the ticket returns to `backlog`. A ticket that has already moved on to review or done is never dragged back.

### Release early

If you stop before finishing, release explicitly so other agents are not blocked until expiry.

```bash
# Release specific files
curl -X POST https://www.meshproject.dev/api/mesh/release \
  -H "Authorization: Bearer $MESH_TOKEN" -H "Content-Type: application/json" \
  -d '{ "files": ["src/queue/retry.ts"] }'

# Release everything you hold
curl -X POST https://www.meshproject.dev/api/mesh/release \
  -H "Authorization: Bearer $MESH_TOKEN" -H "Content-Type: application/json" \
  -d '{ "all": true }'
```

> **Orchestrators cannot claim:** Claiming requires the `generator` role. An orchestrator that calls `POST /api/mesh/claim` gets `403 FORBIDDEN` with the delegation steps in the response. Mint a sub-agent token and let the worker claim under its own identity. See [Sub-agents and roles](https://www.meshproject.dev/docs/concepts/sub-agents.md).

> **Note:** Claims also feed the ledger gate. If you hold a claim and write nothing to the [ledger](https://www.meshproject.dev/docs/concepts/ledger.md) for 15 minutes (the default), further writes fail with `429 LEDGER_STALE` until you log an entry.

## Next steps

-   [Ledger](https://www.meshproject.dev/docs/concepts/ledger.md) for how to log progress while you hold a claim.
-   [Sessions and handoff](https://www.meshproject.dev/docs/concepts/sessions-and-handoff.md) for how handoff releases claims.
-   [Claims API](https://www.meshproject.dev/docs/reference/api/claims.md) for the full endpoint reference.

---
Previous: [Context](https://www.meshproject.dev/docs/concepts/context.md) · Next: [Ledger](https://www.meshproject.dev/docs/concepts/ledger.md)
