{
  "openapi": "3.1.0",
  "info": {
    "title": "Chirp API",
    "version": "1.0.0",
    "description": "The Chirp API reads and changes the conversations in your Chirp workspace and sends mail from your inboxes.\n\n## Authentication\n\nCreate a key in Chirp under **Settings → API keys**. Chirp shows the secret once; it starts with `chirp_`. Send it in the\n`Authorization` header of every request:\n\n    Authorization: Bearer chirp_…\n\nA key acts as the user who created it: it has that user's role and sees only the inboxes that user can see. A conversation\nthe user cannot see answers 404, as if it did not exist. A key stops working when it is revoked or expires, or when its\nuser is disabled or leaves the workspace.\n\n## Scopes\n\nA key holds one or more scopes. Each operation names the scope it needs in `x-required-scope`.\n\n- `read`: list inboxes and conversations, and read a conversation.\n- `write`: change the status or assignee, add and remove tags, and add notes.\n- `send`: send replies and start conversations.\n\nA call without the scope answers `403` with the code `insufficient_scope`. A Viewer's key keeps only `read`, whatever\nscopes it was made with.\n\n## Rate limits\n\nEach key may make a fixed number of requests per minute. Every authenticated response carries `X-RateLimit-Limit` and\n`X-RateLimit-Remaining`. When the limit is used up, the API answers `429` with a `Retry-After` header in seconds.\n\n## Pagination\n\n`GET /conversations` returns one page and a `nextCursor`. Pass it as `cursor` to get the next page; it is null on the\nlast page.\n\n## Idempotency\n\nThe send operations and adding a note accept an `Idempotency-Key` header holding a UUID. A retry with the same key\nreturns the first result instead of sending or adding again.\n\n## Conflicts\n\nEach conversation has a `version` that goes up with each change. Send it back as `expectedVersion` to have a change\nrefused with `409` if someone changed the conversation first.\n\n## Errors\n\nEvery error has the same body: `{\"error\": {\"code\": \"not_found\", \"message\": \"Conversation not found\"}}`. Branch on\n`code`; the `message` is for people. The codes are listed in the `Error` schema.",
    "contact": {
      "name": "Chirp",
      "url": "https://teamchirp.io"
    },
    "termsOfService": "https://teamchirp.io/terms"
  },
  "servers": [
    {
      "url": "https://app.teamchirp.io/v1",
      "description": "Production"
    },
    {
      "url": "http://localhost:3100/v1",
      "description": "A local Chirp server"
    }
  ],
  "tags": [
    {
      "name": "Inboxes",
      "description": "The inboxes the key's user can see."
    },
    {
      "name": "Conversations",
      "description": "List, read and triage conversations."
    },
    {
      "name": "Tags",
      "description": "Add and remove tags on a conversation."
    },
    {
      "name": "Notes",
      "description": "Internal notes that customers never see."
    },
    {
      "name": "Sending",
      "description": "Send mail from an inbox, at once."
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "paths": {
    "/inboxes": {
      "get": {
        "operationId": "listInboxes",
        "summary": "List inboxes",
        "tags": [
          "Inboxes"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "read",
        "description": "The inboxes the key's user can see, in the order they were added.\n\nRequires an API key with the `read` scope.",
        "responses": {
          "200": {
            "description": "Inboxes",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "description": "The inboxes.",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Inbox"
                      },
                      "description": "The items."
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": 1,
                      "address": "support@example.com",
                      "name": "Acme Support",
                      "color": "#e9a74a",
                      "openCount": 12
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized",
            "description": "The API key is missing, invalid, revoked or expired."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden",
            "description": "The key lacks the scope this operation needs."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "The key used up its requests for this minute."
          },
          "500": {
            "$ref": "#/components/responses/InternalError",
            "description": "The server failed to complete the request."
          }
        }
      }
    },
    "/conversations": {
      "get": {
        "operationId": "listConversations",
        "summary": "List conversations",
        "tags": [
          "Conversations"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "read",
        "description": "Conversations in the inboxes the key's user can see, newest activity first, in every status unless you filter.\n\nFilters use the app's search syntax: pass tokens in `q` (such as `status:open tag:\"billing issue\"`) or as parameters. Different filters must all match; a repeated `status`, `assignee`, `inbox` or `linked` matches any of its values, and a repeated `tag` needs every tag. `q` and the filters together may hold at most 200 characters.\n\nThe result is one page. Pass `nextCursor` as `cursor` to get the next one. A conversation that gets new activity during a walk moves to the front, so a walk that started earlier does not return it again.\n\nRequires an API key with the `read` scope.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search text, matched in addresses, subjects, bodies and notes; may hold filter tokens.",
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "example": "refund"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "open, pending, snoozed or closed.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "open",
                  "pending",
                  "snoozed",
                  "closed"
                ]
              }
            },
            "style": "form",
            "explode": true,
            "example": [
              "open",
              "pending"
            ]
          },
          {
            "name": "assignee",
            "in": "query",
            "required": false,
            "description": "me, none or a teammate's display name.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true,
            "example": [
              "me"
            ]
          },
          {
            "name": "tag",
            "in": "query",
            "required": false,
            "description": "A tag name; every tag given must be on the conversation.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true,
            "example": [
              "billing"
            ]
          },
          {
            "name": "inbox",
            "in": "query",
            "required": false,
            "description": "An inbox id, name, address or local part.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true,
            "example": [
              "support"
            ]
          },
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "Latest activity on or after this day, in the user's time zone: YYYY-MM-DD, today, a weekday such as mon, lastweek, 1d–365d days back, or a month day such as sep15.",
            "schema": {
              "type": "string"
            },
            "example": "7d"
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "description": "Latest activity before this day, in the same forms as after.",
            "schema": {
              "type": "string"
            },
            "example": "2026-09-01"
          },
          {
            "name": "linked",
            "in": "query",
            "required": false,
            "description": "open, done, any, none or a Linear issue identifier.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "style": "form",
            "explode": true,
            "example": [
              "open"
            ]
          },
          {
            "name": "is",
            "in": "query",
            "required": false,
            "description": "attention: the Needs attention list (open and waiting on you, or with a delivery problem).",
            "schema": {
              "type": "string",
              "enum": [
                "attention"
              ]
            },
            "example": "attention"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1–100. The default is 50.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "example": 25
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The nextCursor of the previous page.",
            "schema": {
              "type": "string"
            },
            "example": "1790694245000000.1042"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of conversations",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "description": "A page of conversations.",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ConversationSummary"
                      },
                      "description": "The items."
                    },
                    "nextCursor": {
                      "oneOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "Pass as cursor to get the next page; null on the last page."
                    }
                  }
                },
                "example": {
                  "data": [
                    {
                      "id": "1042",
                      "inboxId": 1,
                      "subject": "Refund for order 5521",
                      "sender": "dana@example.com",
                      "preview": "Hi, I was charged twice for order 5521.",
                      "latestAt": "2026-09-29T15:04:05.000Z",
                      "messageCount": 2,
                      "status": "open",
                      "assignee": {
                        "id": "7",
                        "displayName": "Sam Rivera"
                      },
                      "tags": [
                        {
                          "id": "12",
                          "name": "billing"
                        }
                      ],
                      "version": 4
                    }
                  ],
                  "nextCursor": "1790694245000000.1042"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "A filter, limit or cursor is not valid, or q and the filters exceed 200 characters."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized",
            "description": "The API key is missing, invalid, revoked or expired."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden",
            "description": "The key lacks the scope this operation needs."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "The key used up its requests for this minute."
          },
          "500": {
            "$ref": "#/components/responses/InternalError",
            "description": "The server failed to complete the request."
          }
        }
      },
      "post": {
        "operationId": "createConversation",
        "summary": "Start a conversation",
        "tags": [
          "Sending"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "send",
        "description": "Start a new conversation with one message, sent now from the inbox with its From name and signature. The app's undo window does not apply. Send an `Idempotency-Key` so a retry cannot send twice.\n\nA 201 means Chirp recorded the message, not that it was delivered. Check `state`: only `sent` means the mail provider accepted it. On `failed` the provider refused it; on `unknown`, read the conversation before you send again.\n\nRequires an API key with the `send` scope.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "A UUID. A retry with the same key returns the first result instead of doing the work again.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "3b0c4f1e-8a52-4d0b-9c7e-2f6a1d9e5b21"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "inboxId",
                  "to",
                  "subject",
                  "body"
                ],
                "additionalProperties": false,
                "description": "The first message.",
                "properties": {
                  "inboxId": {
                    "type": "integer",
                    "description": "The inbox to send from. The key's user must be able to see it."
                  },
                  "to": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254,
                    "description": "One recipient address."
                  },
                  "subject": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "The subject, 1–200 characters."
                  },
                  "body": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 10000,
                    "description": "The plain text, 1–10000 characters. The inbox signature is added."
                  }
                }
              },
              "example": {
                "inboxId": 1,
                "to": "dana@example.com",
                "subject": "Your refund for order 5521",
                "body": "Hi Dana, we have refunded the second charge."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The sent message",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                },
                "example": {
                  "conversationId": "1043",
                  "messageId": "88214",
                  "state": "sent"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "A field is missing, unknown or not valid, or Idempotency-Key is not a UUID."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized",
            "description": "The API key is missing, invalid, revoked or expired."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden",
            "description": "The key lacks the scope this operation needs."
          },
          "404": {
            "$ref": "#/components/responses/NotFound",
            "description": "No such inbox, or the key's user cannot see it."
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "The inbox cannot send mail right now; its connection needs attention in Chirp."
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge",
            "description": "The JSON body exceeds 64 KB."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "The key used up its requests for this minute."
          },
          "500": {
            "$ref": "#/components/responses/InternalError",
            "description": "The server failed to complete the request."
          }
        }
      }
    },
    "/conversations/{id}": {
      "get": {
        "operationId": "getConversation",
        "summary": "Get a conversation",
        "tags": [
          "Conversations"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "read",
        "description": "A conversation with its messages, its timeline of notes and changes, and its linked Linear issues.\n\nRequires an API key with the `read` scope.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation's id.",
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "example": "1042"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Conversation"
                },
                "example": {
                  "id": "1042",
                  "inbox": {
                    "id": 1,
                    "address": "support@example.com"
                  },
                  "status": "open",
                  "assignee": {
                    "id": "7",
                    "displayName": "Sam Rivera"
                  },
                  "tags": [
                    {
                      "id": "12",
                      "name": "billing"
                    }
                  ],
                  "version": 4,
                  "subject": "Refund for order 5521",
                  "messages": [
                    {
                      "id": "88210",
                      "direction": "incoming",
                      "from": "dana@example.com",
                      "to": "support@example.com",
                      "subject": "Refund for order 5521",
                      "body": "Hi, I was charged twice for order 5521.",
                      "createdAt": "2026-09-29T14:58:12.000Z",
                      "author": null,
                      "attachments": [],
                      "state": null
                    },
                    {
                      "id": "88211",
                      "direction": "sent",
                      "from": "support@example.com",
                      "to": "dana@example.com",
                      "subject": "Re: Refund for order 5521",
                      "body": "Sorry about that, Dana. We have refunded the second charge.",
                      "createdAt": "2026-09-29T15:04:05.000Z",
                      "author": "7",
                      "attachments": [],
                      "state": "sent"
                    }
                  ],
                  "activity": [
                    {
                      "id": "9000",
                      "kind": "assignment",
                      "body": null,
                      "detail": {
                        "from": null,
                        "to": "7"
                      },
                      "actor": {
                        "id": "7",
                        "displayName": "Sam Rivera"
                      },
                      "createdAt": "2026-09-29T15:00:30.000Z",
                      "parentNoteId": null
                    },
                    {
                      "id": "9001",
                      "kind": "note",
                      "body": "Refund issued in Stripe; waiting for the bank.",
                      "detail": {},
                      "actor": {
                        "id": "7",
                        "displayName": "Sam Rivera"
                      },
                      "createdAt": "2026-09-29T15:10:00.000Z",
                      "parentNoteId": null
                    }
                  ],
                  "issueLinks": [
                    {
                      "identifier": "ENG-42",
                      "title": "Duplicate charge on retry",
                      "url": "https://linear.app/example/issue/ENG-42",
                      "state": "In Progress",
                      "stateType": "started"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized",
            "description": "The API key is missing, invalid, revoked or expired."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden",
            "description": "The key lacks the scope this operation needs."
          },
          "404": {
            "$ref": "#/components/responses/NotFound",
            "description": "No such conversation, or the key's user cannot see its inbox."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "The key used up its requests for this minute."
          },
          "500": {
            "$ref": "#/components/responses/InternalError",
            "description": "The server failed to complete the request."
          }
        }
      },
      "patch": {
        "operationId": "updateConversation",
        "summary": "Change status or assignee",
        "tags": [
          "Conversations"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "write",
        "description": "Change the status or the assignee: one of the two per call. To snooze, send status `snoozed` with `snoozedUntil`: a future time, or null to wait for the customer's reply. The change shows in the timeline as the key's user.\n\nRequires an API key with the `write` scope.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation's id.",
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "example": "1042"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "description": "Exactly one of status and assigneeId.",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "open",
                      "pending",
                      "snoozed",
                      "closed"
                    ],
                    "description": "The new status."
                  },
                  "assigneeId": {
                    "oneOf": [
                      {
                        "type": "string",
                        "pattern": "^\\d+$"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "The teammate to assign, or null to unassign. The teammate must be able to see the inbox."
                  },
                  "snoozedUntil": {
                    "oneOf": [
                      {
                        "type": "string",
                        "format": "date-time"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "Required with status snoozed: when to wake it, or null to wait for the customer's reply."
                  },
                  "expectedVersion": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Refuse the change with 409 if the conversation's version is no longer this."
                  }
                }
              },
              "example": {
                "status": "pending",
                "expectedVersion": 4
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The conversation's workflow state",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workflow"
                },
                "example": {
                  "id": "1042",
                  "inbox": {
                    "id": 1,
                    "address": "support@example.com"
                  },
                  "status": "pending",
                  "assignee": {
                    "id": "7",
                    "displayName": "Sam Rivera"
                  },
                  "tags": [
                    {
                      "id": "12",
                      "name": "billing"
                    }
                  ],
                  "version": 5
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "Not exactly one change, an unknown field, a bad status or time, or an assignee who cannot see the inbox."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized",
            "description": "The API key is missing, invalid, revoked or expired."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden",
            "description": "The key lacks the scope this operation needs."
          },
          "404": {
            "$ref": "#/components/responses/NotFound",
            "description": "No such conversation, or the key's user cannot see its inbox."
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "expectedVersion no longer matches: someone changed the conversation first. Read it again and retry."
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge",
            "description": "The JSON body exceeds 64 KB."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "The key used up its requests for this minute."
          },
          "500": {
            "$ref": "#/components/responses/InternalError",
            "description": "The server failed to complete the request."
          }
        }
      }
    },
    "/conversations/{id}/tags": {
      "post": {
        "operationId": "addTag",
        "summary": "Add a tag",
        "tags": [
          "Tags"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "write",
        "description": "Add a tag by name. A new name creates the tag and adds it to the inbox's tag list. Adding a tag the conversation already has changes nothing.\n\nRequires an API key with the `write` scope.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation's id.",
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "example": "1042"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "additionalProperties": false,
                "description": "The tag to add.",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 32,
                    "description": "The tag name. It is stored in lower case, with hyphens for spaces."
                  },
                  "expectedVersion": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "Refuse the change with 409 if the conversation's version is no longer this."
                  }
                }
              },
              "example": {
                "name": "refund"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The conversation's workflow state",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workflow"
                },
                "example": {
                  "id": "1042",
                  "inbox": {
                    "id": 1,
                    "address": "support@example.com"
                  },
                  "status": "open",
                  "assignee": {
                    "id": "7",
                    "displayName": "Sam Rivera"
                  },
                  "tags": [
                    {
                      "id": "12",
                      "name": "billing"
                    },
                    {
                      "id": "15",
                      "name": "refund"
                    }
                  ],
                  "version": 5
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "The name is empty or longer than 32 characters, or a field is not valid."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized",
            "description": "The API key is missing, invalid, revoked or expired."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden",
            "description": "The key lacks the scope this operation needs."
          },
          "404": {
            "$ref": "#/components/responses/NotFound",
            "description": "No such conversation, or the key's user cannot see its inbox."
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "expectedVersion no longer matches: someone changed the conversation first. Read it again and retry."
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge",
            "description": "The JSON body exceeds 64 KB."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "The key used up its requests for this minute."
          },
          "500": {
            "$ref": "#/components/responses/InternalError",
            "description": "The server failed to complete the request."
          }
        }
      }
    },
    "/conversations/{id}/tags/{tagId}": {
      "delete": {
        "operationId": "removeTag",
        "summary": "Remove a tag",
        "tags": [
          "Tags"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "write",
        "description": "Remove a tag by id. Removing a tag the conversation does not have changes nothing.\n\nRequires an API key with the `write` scope.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation's id.",
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "example": "1042"
          },
          {
            "name": "tagId",
            "in": "path",
            "required": true,
            "description": "The tag's id.",
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "example": "12"
          },
          {
            "name": "expectedVersion",
            "in": "query",
            "required": false,
            "description": "Refuse with 409 if the conversation's version is no longer this.",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "example": 4
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation's workflow state",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Workflow"
                },
                "example": {
                  "id": "1042",
                  "inbox": {
                    "id": 1,
                    "address": "support@example.com"
                  },
                  "status": "open",
                  "assignee": {
                    "id": "7",
                    "displayName": "Sam Rivera"
                  },
                  "tags": [],
                  "version": 5
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "expectedVersion is not a whole number."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized",
            "description": "The API key is missing, invalid, revoked or expired."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden",
            "description": "The key lacks the scope this operation needs."
          },
          "404": {
            "$ref": "#/components/responses/NotFound",
            "description": "No such conversation, or the key's user cannot see its inbox."
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "expectedVersion no longer matches: someone changed the conversation first. Read it again and retry."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "The key used up its requests for this minute."
          },
          "500": {
            "$ref": "#/components/responses/InternalError",
            "description": "The server failed to complete the request."
          }
        }
      }
    },
    "/conversations/{id}/notes": {
      "post": {
        "operationId": "addNote",
        "summary": "Add a note",
        "tags": [
          "Notes"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "write",
        "description": "Add an internal note, or a reply to a note. Customers never see notes. An @mention of a teammate notifies them as in the app.\n\nRequires an API key with the `write` scope.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation's id.",
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "example": "1042"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "A UUID. A retry with the same key returns the first result instead of doing the work again.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "3b0c4f1e-8a52-4d0b-9c7e-2f6a1d9e5b21"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "body"
                ],
                "additionalProperties": false,
                "description": "The note.",
                "properties": {
                  "body": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 5000,
                    "description": "The note's text, 1–5000 characters."
                  },
                  "parentNoteId": {
                    "oneOf": [
                      {
                        "type": "string",
                        "pattern": "^\\d+$"
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "To reply to a note, that note's id. Replies go one level deep."
                  }
                }
              },
              "example": {
                "body": "Refund issued in Stripe; waiting for the bank."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The note",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Activity"
                },
                "example": {
                  "id": "9001",
                  "kind": "note",
                  "body": "Refund issued in Stripe; waiting for the bank.",
                  "detail": {},
                  "actor": {
                    "id": "7",
                    "displayName": "Sam Rivera"
                  },
                  "createdAt": "2026-09-29T15:10:00.000Z",
                  "parentNoteId": null
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "The body is empty or too long, parentNoteId is not an id, or Idempotency-Key is not a UUID."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized",
            "description": "The API key is missing, invalid, revoked or expired."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden",
            "description": "The key lacks the scope this operation needs."
          },
          "404": {
            "$ref": "#/components/responses/NotFound",
            "description": "No such conversation, or the key's user cannot see its inbox. Also sent when parentNoteId is not a top-level note on this conversation."
          },
          "409": {
            "$ref": "#/components/responses/Conflict",
            "description": "The Idempotency-Key was already used for a different note on this conversation."
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge",
            "description": "The JSON body exceeds 64 KB."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "The key used up its requests for this minute."
          },
          "500": {
            "$ref": "#/components/responses/InternalError",
            "description": "The server failed to complete the request."
          }
        }
      }
    },
    "/conversations/{id}/replies": {
      "post": {
        "operationId": "sendReply",
        "summary": "Send a reply",
        "tags": [
          "Sending"
        ],
        "security": [
          {
            "apiKey": []
          }
        ],
        "x-required-scope": "send",
        "description": "Send a reply to the customer now, from the conversation's inbox with its From name and signature. The app's undo window does not apply. Send an `Idempotency-Key` so a retry cannot send twice.\n\nA 201 means Chirp recorded the message, not that it was delivered. Check `state`: only `sent` means the mail provider accepted it. On `failed` the provider refused it; on `unknown`, read the conversation before you send again.\n\nRequires an API key with the `send` scope.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation's id.",
            "schema": {
              "type": "string",
              "pattern": "^\\d+$"
            },
            "example": "1042"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "A UUID. A retry with the same key returns the first result instead of doing the work again.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "3b0c4f1e-8a52-4d0b-9c7e-2f6a1d9e5b21"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "body"
                ],
                "additionalProperties": false,
                "description": "The reply.",
                "properties": {
                  "body": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 10000,
                    "description": "The reply's plain text, 1–10000 characters. The inbox signature is added."
                  }
                }
              },
              "example": {
                "body": "Sorry about that, Dana. We have refunded the second charge."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The sent message",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendResult"
                },
                "example": {
                  "conversationId": "1042",
                  "messageId": "88213",
                  "state": "sent"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest",
            "description": "The body is empty or too long, a field is unknown, or Idempotency-Key is not a UUID."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized",
            "description": "The API key is missing, invalid, revoked or expired."
          },
          "403": {
            "$ref": "#/components/responses/Forbidden",
            "description": "The key lacks the scope this operation needs."
          },
          "404": {
            "$ref": "#/components/responses/NotFound",
            "description": "No such conversation, or the key's user cannot see its inbox."
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge",
            "description": "The JSON body exceeds 64 KB. Also sent when the finished message exceeds 10 MB."
          },
          "429": {
            "$ref": "#/components/responses/RateLimited",
            "description": "The key used up its requests for this minute."
          },
          "500": {
            "$ref": "#/components/responses/InternalError",
            "description": "The server failed to complete the request."
          }
        }
      }
    }
  },
  "webhooks": {},
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "description": "The body of every error response.",
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "description": "What went wrong.",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_request",
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "conflict",
                  "payload_too_large",
                  "rate_limited",
                  "upstream_error",
                  "insufficient_scope",
                  "internal_error"
                ],
                "description": "A machine-readable code. Branch on this, not on the message."
              },
              "message": {
                "type": "string",
                "description": "A sentence for people. The wording can change."
              }
            }
          }
        }
      },
      "Inbox": {
        "type": "object",
        "required": [
          "id",
          "address",
          "name",
          "color",
          "openCount"
        ],
        "description": "An inbox: one connected email address.",
        "properties": {
          "id": {
            "type": "integer",
            "description": "The inbox's id."
          },
          "address": {
            "type": "string",
            "format": "email",
            "description": "The inbox's email address."
          },
          "name": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The inbox's display name, or null when it has none."
          },
          "color": {
            "type": "string",
            "description": "The inbox's colour in Chirp, as a CSS hex colour."
          },
          "openCount": {
            "type": "integer",
            "minimum": 0,
            "description": "How many of its conversations are open."
          }
        }
      },
      "ConversationSummary": {
        "type": "object",
        "required": [
          "id",
          "inboxId",
          "subject",
          "sender",
          "preview",
          "latestAt",
          "messageCount",
          "status",
          "assignee",
          "tags",
          "version"
        ],
        "description": "A conversation as it appears in a list.",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^\\d+$",
            "description": "The conversation's id."
          },
          "inboxId": {
            "type": "integer",
            "description": "The id of the conversation's inbox."
          },
          "subject": {
            "type": "string",
            "description": "The subject of its first message."
          },
          "sender": {
            "type": "string",
            "description": "The customer's address: the latest incoming sender, or the recipient when no message came in."
          },
          "preview": {
            "type": "string",
            "description": "The first line of the latest message."
          },
          "latestAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the latest message arrived or was sent. The list is ordered by this, newest first."
          },
          "messageCount": {
            "type": "integer",
            "minimum": 1,
            "description": "How many messages the conversation holds."
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "pending",
              "snoozed",
              "closed"
            ],
            "description": "The conversation's status."
          },
          "assignee": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "id",
                  "displayName"
                ],
                "description": "A teammate.",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^\\d+$",
                    "description": "The user's id."
                  },
                  "displayName": {
                    "type": "string",
                    "description": "The name shown in Chirp."
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "The assigned teammate, or null when unassigned."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "name"
              ],
              "description": "A tag. Names are lower case, with hyphens for spaces.",
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^\\d+$",
                  "description": "The tag's id."
                },
                "name": {
                  "type": "string",
                  "description": "The tag name, 1–32 characters."
                }
              }
            },
            "description": "Its tags, by name."
          },
          "version": {
            "type": "integer",
            "minimum": 0,
            "description": "Goes up by one with each change. Send it back as expectedVersion to refuse a change made on stale data."
          }
        }
      },
      "Workflow": {
        "type": "object",
        "required": [
          "id",
          "inbox",
          "status",
          "assignee",
          "tags",
          "version"
        ],
        "description": "A conversation's workflow state: what a write call changes.",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^\\d+$",
            "description": "The conversation's id."
          },
          "inbox": {
            "type": "object",
            "required": [
              "id",
              "address"
            ],
            "description": "The conversation's inbox.",
            "properties": {
              "id": {
                "type": "integer",
                "description": "The inbox's id."
              },
              "address": {
                "type": "string",
                "format": "email",
                "description": "The inbox's email address."
              }
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "pending",
              "snoozed",
              "closed"
            ],
            "description": "The conversation's status."
          },
          "assignee": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "id",
                  "displayName"
                ],
                "description": "A teammate.",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^\\d+$",
                    "description": "The user's id."
                  },
                  "displayName": {
                    "type": "string",
                    "description": "The name shown in Chirp."
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "The assigned teammate, or null when unassigned."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "name"
              ],
              "description": "A tag. Names are lower case, with hyphens for spaces.",
              "properties": {
                "id": {
                  "type": "string",
                  "pattern": "^\\d+$",
                  "description": "The tag's id."
                },
                "name": {
                  "type": "string",
                  "description": "The tag name, 1–32 characters."
                }
              }
            },
            "description": "Its tags, by name."
          },
          "version": {
            "type": "integer",
            "minimum": 0,
            "description": "Goes up by one with each change. Send it back as expectedVersion to refuse a change made on stale data."
          }
        }
      },
      "Message": {
        "type": "object",
        "required": [
          "id",
          "direction",
          "from",
          "to",
          "subject",
          "body",
          "createdAt",
          "author",
          "attachments",
          "state"
        ],
        "description": "One email in a conversation.",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^\\d+$",
            "description": "The message's id."
          },
          "direction": {
            "type": "string",
            "enum": [
              "incoming",
              "sent"
            ],
            "description": "incoming from the customer, or sent from the inbox."
          },
          "from": {
            "type": "string",
            "description": "The From address."
          },
          "to": {
            "type": "string",
            "description": "The To address."
          },
          "subject": {
            "type": "string",
            "description": "The subject."
          },
          "body": {
            "type": "string",
            "description": "The plain-text body."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When it arrived or was sent."
          },
          "author": {
            "oneOf": [
              {
                "type": "string",
                "pattern": "^\\d+$"
              },
              {
                "type": "null"
              }
            ],
            "description": "The id of the teammate who sent it, or null for an incoming message."
          },
          "attachments": {
            "type": "array",
            "description": "Its attachments. The API does not serve their content.",
            "items": {
              "type": "object",
              "required": [
                "filename",
                "contentType",
                "size"
              ],
              "description": "An attachment.",
              "properties": {
                "filename": {
                  "type": "string",
                  "description": "The file name."
                },
                "contentType": {
                  "type": "string",
                  "description": "The MIME type."
                },
                "size": {
                  "type": "integer",
                  "minimum": 0,
                  "description": "The size in bytes."
                }
              }
            }
          },
          "state": {
            "oneOf": [
              {
                "type": "string",
                "enum": [
                  "held",
                  "sending",
                  "sent",
                  "failed",
                  "unknown"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "For a sent message, its outbox state: held (in the app's undo window), sending, sent, failed or unknown. Null for an incoming message."
          }
        }
      },
      "Activity": {
        "type": "object",
        "required": [
          "id",
          "kind",
          "body",
          "detail",
          "actor",
          "createdAt",
          "parentNoteId"
        ],
        "description": "One entry in a conversation's timeline: a note or a change.",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^\\d+$",
            "description": "The entry's id."
          },
          "kind": {
            "type": "string",
            "enum": [
              "note",
              "status",
              "assignment",
              "tag_add",
              "tag_remove",
              "reopened",
              "issue_link",
              "issue_unlink",
              "issue_state",
              "automation"
            ],
            "description": "What happened. A note carries its text in body; a change carries its data in detail."
          },
          "body": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "A note's text; null for other kinds."
          },
          "detail": {
            "type": "object",
            "description": "The change's data, which depends on kind (for example from and to for status). Empty for a note."
          },
          "actor": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "id",
                  "displayName"
                ],
                "description": "A teammate.",
                "properties": {
                  "id": {
                    "type": "string",
                    "pattern": "^\\d+$",
                    "description": "The user's id."
                  },
                  "displayName": {
                    "type": "string",
                    "description": "The name shown in Chirp."
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "Who made it, or null for the system (an automation or an incoming message)."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When it happened."
          },
          "parentNoteId": {
            "oneOf": [
              {
                "type": "string",
                "pattern": "^\\d+$"
              },
              {
                "type": "null"
              }
            ],
            "description": "For a reply to a note, the note's id; otherwise null."
          }
        }
      },
      "IssueLink": {
        "type": "object",
        "required": [
          "identifier",
          "url",
          "title",
          "state",
          "stateType"
        ],
        "description": "A Linear issue linked to the conversation.",
        "properties": {
          "identifier": {
            "type": "string",
            "description": "The issue's identifier, such as ENG-42."
          },
          "title": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The issue's title as last seen."
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "The issue's Linear URL."
          },
          "state": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The issue's workflow state name as last seen."
          },
          "stateType": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The state's type in Linear, such as started, completed or canceled."
          }
        }
      },
      "Conversation": {
        "description": "A conversation with its messages, timeline and linked issues.",
        "allOf": [
          {
            "$ref": "#/components/schemas/Workflow"
          },
          {
            "type": "object",
            "required": [
              "subject",
              "messages",
              "activity",
              "issueLinks"
            ],
            "properties": {
              "subject": {
                "type": "string",
                "description": "The subject of its first message."
              },
              "messages": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Message"
                },
                "description": "Its messages, oldest first."
              },
              "activity": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Activity"
                },
                "description": "Its notes and changes, oldest first."
              },
              "issueLinks": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/IssueLink"
                },
                "description": "Its linked Linear issues, oldest link first."
              }
            }
          }
        ]
      },
      "SendResult": {
        "type": "object",
        "required": [
          "conversationId",
          "messageId",
          "state"
        ],
        "description": "The outcome of a send. The response is 201 whenever Chirp recorded the message, even when delivery failed: check state.",
        "properties": {
          "conversationId": {
            "type": "string",
            "pattern": "^\\d+$",
            "description": "The conversation's id."
          },
          "messageId": {
            "type": "string",
            "pattern": "^\\d+$",
            "description": "The sent message's id."
          },
          "state": {
            "type": "string",
            "enum": [
              "sending",
              "sent",
              "failed",
              "unknown"
            ],
            "description": "sent: the mail provider accepted the message. failed: the provider refused it. unknown: the outcome is not known, for example after a timeout; check the conversation before you send again. sending: a retry with the same Idempotency-Key arrived while the first send was still running."
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "The request is not valid.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "invalid_request",
                "message": "Unknown field: priority"
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The API key is missing, invalid, revoked or expired.",
        "headers": {
          "WWW-Authenticate": {
            "$ref": "#/components/headers/WWW-Authenticate"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "unauthorized",
                "message": "Missing API key"
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The key lacks the scope this operation needs.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "insufficient_scope",
                "message": "This key lacks the write scope"
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "The resource does not exist, or the key's user cannot see it.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "not_found",
                "message": "Conversation not found"
              }
            }
          }
        }
      },
      "Conflict": {
        "description": "The request conflicts with the current state.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "conflict",
                "message": "Conversation changed; refresh and retry"
              }
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "The request is too large.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "payload_too_large",
                "message": "JSON body exceeds 64 KB"
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "The key used up its requests for this minute.",
        "headers": {
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "rate_limited",
                "message": "Rate limit reached; retry after the Retry-After seconds"
              }
            }
          }
        }
      },
      "InternalError": {
        "description": "The server failed to complete the request.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": {
                "code": "internal_error",
                "message": "Request failed"
              }
            }
          }
        }
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "description": "Requests this key may make per minute.",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left in the current minute.",
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before the next request.",
        "schema": {
          "type": "integer"
        }
      },
      "WWW-Authenticate": {
        "description": "Always Bearer.",
        "schema": {
          "type": "string"
        }
      }
    },
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key from Settings → API keys, such as chirp_…"
      }
    }
  }
}
