# Shared Thread Compose State

Read and edit an email **compose card**'s state — and manage its outgoing attachments — on a **live-shared** thread you **joined**. These are the slug-scoped twins of the owner compose-state endpoints, for a joiner co-working on someone else's thread.

A compose card is the interactive email-draft surface an assistant produces (tone options, editable body, subject, From-identity, recipients, attachments). A joiner co-working live can rehydrate the card after a reload, edit it, and manage attachments before it is sent.

Every operation is authorized by the joiner's **live**-share grant on the slug. A `view` or `fork` slug returns `403` (before any card lookup); an unknown or unauthorized link, or a missing card, returns `404`. Edits are **version-guarded** (optimistic concurrency): pass the `version` you last saw; a stale version returns `409` unless the edit is already applied.

## Authentication

Requires **user** authentication (Bearer token).

## Path Parameters

**`slug`** string required

The share-link slug, from the share URL. The link must be a `live` link.

**`tool_call_id`** string required

The compose card's tool-call id (the join key to the card).

## Returns

The compose card's current edit/approval state: `tool_call_id`, `selected_option_id`, `edited_body`, `subject_override`, `body_is_html`, `attachments`, `to`/`cc`/`bcc`, `from_identity_id`, `status` (`draft` | `pending_send` | `sent` | `failed` | `denied` | `expired`), `version`, `sent_message_id`, `error`, and `updated_at`.

## Other Operations

### Update the compose card

Persist the joiner's inline edits. Version-guarded — send `version` (the value you last saw) plus only the changed fields (`selected_option_id`, `edited_option_id` + `edited_body`, `subject_override`, `body_is_html`, `from_identity_id`, `to`/`cc`/`bcc`). A terminal card (`sent`/`denied`/`expired`) is immutable and returns `409`. Returns the updated compose-state object.

### Add an attachment

Upload a file (multipart `file`) and attach it to the card. Pass the current `version` as a query parameter. Limits: 10 attachments per email, 25 MB per file and per email total. Returns the added attachment, the card's full attachment list, and the new `version`.

### Remove an attachment

Remove one pending attachment by its `file_id`. Pass the current `version` as a query parameter. Returns the card's full attachment list and the new `version`.

## Code Examples

### cURL


```bash
# Read
curl "https://api.aitronos.com/v1/shared-threads/abc123slug/compose-state/tc_1" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Patch (select a tone option)
curl -X PATCH "https://api.aitronos.com/v1/shared-threads/abc123slug/compose-state/tc_1" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"version": 3, "selected_option_id": "opt_friendly"}'

# Attach a file
curl -X POST "https://api.aitronos.com/v1/shared-threads/abc123slug/compose-state/tc_1/attachments?version=4" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "file=@report.pdf"
```

### Python SDK


```python
from aitronos import Aitronos

client = Aitronos(api_key="", token="YOUR_TOKEN")
card = client.threads.shared.compose_state.get(slug="abc123slug", tool_call_id="tc_1")
card = client.threads.shared.compose_state.patch(
    slug="abc123slug",
    tool_call_id="tc_1",
    version=card.version,
    selected_option_id="opt_friendly",
)
print(card.status, card.version)
```

### Python (requests)


```python
import requests

response = requests.patch(
    "https://api.aitronos.com/v1/shared-threads/abc123slug/compose-state/tc_1",
    headers={"Authorization": "Bearer YOUR_TOKEN"},
    json={"version": 3, "selected_option_id": "opt_friendly"},
)
print(response.json()["version"])
```

### JavaScript


```javascript
const response = await fetch(
  "https://api.aitronos.com/v1/shared-threads/abc123slug/compose-state/tc_1",
  {
    method: "PATCH",
    headers: {
      Authorization: "Bearer YOUR_TOKEN",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ version: 3, selected_option_id: "opt_friendly" }),
  },
);
const card = await response.json();
console.log(card.status, card.version);
```

## Response Examples

### Success (200)


```json
{
  "tool_call_id": "tc_1",
  "selected_option_id": "opt_friendly",
  "edited_body": { "opt_friendly": null },
  "subject_override": null,
  "body_is_html": null,
  "attachments": [],
  "to": null,
  "cc": null,
  "bcc": null,
  "from_identity_id": null,
  "status": "draft",
  "version": 4,
  "sent_message_id": null,
  "error": null,
  "updated_at": "2026-07-06T11:26:38Z"
}
```

### Error — Stale version (409)


```json
{
  "success": false,
  "error": {
    "code": "VERSION_CONFLICT",
    "message": "There was a conflict with the current state of the resource.",
    "system_message": "compose-state version is stale; refetch and retry",
    "type": "client_error",
    "status": 409,
    "details": { "tool_call_id": "tc_1", "expected_version": 3, "current_version": 4 },
    "trace_id": "...",
    "timestamp": "..."
  }
}
```

### Error — Not a live link (403)


```json
{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_PERMISSIONS",
    "message": "You do not have permission to perform this action.",
    "system_message": "This share link does not grant live co-working access",
    "type": "authorization_error",
    "status": 403,
    "details": { "link_id": "tslnk_...", "access_level": "view" },
    "trace_id": "...",
    "timestamp": "..."
  }
}
```

## Related Resources

- [Join Shared Thread](/api-reference/shared-threads/join-shared-thread) — Join the live-shared thread first
- [Shared Thread Tool Approval](/api-reference/shared-threads/shared-thread-tool-approval) — Approve/deny the compose card's Send
- [Shared Thread Reactions](/api-reference/shared-threads/shared-thread-reactions) — React to messages as a joiner
- [Live Shared Thread](/api-reference/shared-threads/live-shared-thread) — The real-time co-working stream