> ## Documentation Index
> Fetch the complete documentation index at: https://docs.my-aichatbot.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API key

> Send messages to any of your bot's conversations, at any time, with an API key.

When there is no event to reply to — or the 45-minute window has closed — use an API key. Cron follow-ups, overnight batch results, and messages you start yourself all live here.

```text theme={null}
POST https://app.my-aichatbot.com/api/v1/conversations/{conversation_id}/messages
Authorization: Bearer mac_live_…
```

## Get a key

Create one in your bot's settings. It is shown **once** — we only keep a hashed copy. Each key belongs to exactly one chatbot and can only reach that bot's conversations.

## Send a message

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST \
    "https://app.my-aichatbot.com/api/v1/conversations/$CONVERSATION_ID/messages" \
    -H "Authorization: Bearer $MAC_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "mid": "nightly-summary",
      "blocks": [
        { "type": "text", "text": "Overnight sync finished — **412 records** updated." }
      ]
    }'
  ```

  ```javascript Node.js theme={null}
  const res = await fetch(
    `https://app.my-aichatbot.com/api/v1/conversations/${conversationId}/messages`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.MAC_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        mid: 'nightly-summary',
        blocks: [
          { type: 'text', text: 'Overnight sync finished — **412 records** updated.' }
        ]
      })
    }
  )

  const { messages } = await res.json()
  ```

  ```python Python theme={null}
  import os, requests

  res = requests.post(
      f"https://app.my-aichatbot.com/api/v1/conversations/{conversation_id}/messages",
      headers={"Authorization": f"Bearer {os.environ['MAC_KEY']}"},
      json={
          "mid": "nightly-summary",
          "blocks": [
              {"type": "text", "text": "Overnight sync finished — **412 records** updated."}
          ],
      },
  )

  messages = res.json()["messages"]
  ```

  ```php PHP theme={null}
  $ch = curl_init("https://app.my-aichatbot.com/api/v1/conversations/{$conversationId}/messages");
  curl_setopt_array($ch, [
      CURLOPT_POST => true,
      CURLOPT_HTTPHEADER => [
          'Authorization: Bearer ' . getenv('MAC_KEY'),
          'Content-Type: application/json',
      ],
      CURLOPT_POSTFIELDS => json_encode([
          'mid' => 'nightly-summary',
          'blocks' => [
              ['type' => 'text', 'text' => 'Overnight sync finished — **412 records** updated.'],
          ],
      ]),
      CURLOPT_RETURNTRANSFER => true,
  ]);
  $messages = json_decode(curl_exec($ch), true)['messages'];
  ```

  ```go Go theme={null}
  body, _ := json.Marshal(map[string]any{
      "mid": "nightly-summary",
      "blocks": []map[string]string{
          {"type": "text", "text": "Overnight sync finished — **412 records** updated."},
      },
  })

  req, _ := http.NewRequest("POST",
      "https://app.my-aichatbot.com/api/v1/conversations/"+conversationID+"/messages",
      bytes.NewReader(body))
  req.Header.Set("Authorization", "Bearer "+os.Getenv("MAC_KEY"))
  req.Header.Set("Content-Type", "application/json")

  resp, err := http.DefaultClient.Do(req)
  ```
</CodeGroup>

```json Response theme={null}
{
  "ok": true,
  "messages": [
    { "message_id": "0f8c…", "mid": "nightly-summary", "updated": false }
  ]
}
```

## Read history

Page through history with `after_seq`. Every message has a `seq` number that only goes up, which makes it a safe bookmark.

```bash theme={null}
curl "https://app.my-aichatbot.com/api/v1/conversations/$CONVERSATION_ID/messages?after_seq=0&limit=50" \
  -H "Authorization: Bearer $MAC_KEY"
```

<ParamField query="after_seq" default="0" type="number">
  Return messages with a `seq` higher than this.
</ParamField>

<ParamField query="limit" default="50" type="number">
  Between 1 and 100. Numbers outside that range are pulled back in.
</ParamField>

```json Response theme={null}
{
  "messages": [
    {
      "id": "0f8c…",
      "seq": 14,
      "sender_type": "visitor",
      "blocks": [{ "type": "text", "text": "Where is my order?" }],
      "block_kit_version": "1",
      "mid": null,
      "text_preview": "Where is my order?",
      "created_at": "2026-07-17T03:15:12.398Z",
      "edited_at": null
    }
  ],
  "last_seq": 14
}
```

To check for new messages, remember the highest `seq` you have seen and send it back as `after_seq`. When `last_seq` matches your bookmark, you are up to date.

## Limits

| Operation                | Limit                |
| ------------------------ | -------------------- |
| Writes (`POST`, `PATCH`) | 120 / minute per key |
| Reads (`GET`)            | 300 / minute per key |
| Body size                | 256 KB               |
| Messages per call        | 10                   |

## Errors

| Status | Code                      | Meaning                                                      |
| ------ | ------------------------- | ------------------------------------------------------------ |
| `401`  | `invalid_api_key`         | Missing, broken, or revoked. Must match `Bearer mac_live_…`. |
| `404`  | `conversation_not_found`  | Unknown conversation, **or** it belongs to a different bot.  |
| `409`  | `conversation_taken_over` | A human has taken over.                                      |
| `413`  | `PAYLOAD_TOO_LARGE`       | Body over 256 KB.                                            |
| `422`  | `BLOCK_VALIDATION_FAILED` | Bad Block Kit.                                               |
| `429`  | `rate_limited`            | Over the limit. Wait for `retry_after` seconds.              |

<Note>
  A conversation that belongs to another bot returns `404`, not `403`. It looks the same as an id that does not exist — on purpose, so a key cannot be used to hunt for other bots' conversations.
</Note>

## Next

<CardGroup cols={2}>
  <Card title="Edit a sent message" icon="pencil" href="/sending-messages/updating-messages">
    `PATCH` any bot message. No time limit.
  </Card>

  <Card title="Attach a file" icon="paperclip" href="/sending-messages/files">
    Upload it, then use the `file_id`.
  </Card>
</CardGroup>
