{
  "openapi": "3.1.0",
  "info": {
    "title": "Miss Blue API",
    "version": "1.0.0",
    "summary": "Send and receive iMessage from a dedicated business number.",
    "description": "The HTTP API behind Miss Blue. Every request carries a bearer key; a key beginning `mb_test_` acts on the sandbox and sends nothing to a real phone.\n\nThe same reference in prose: https://missblue.dev/api-reference.md",
    "contact": {
      "name": "Miss Blue",
      "url": "https://missblue.dev/contact"
    },
    "termsOfService": "https://missblue.dev/terms"
  },
  "servers": [
    {
      "url": "https://api.missblue.dev",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Identity",
      "description": "What this key is, and which project it holds."
    },
    {
      "name": "Messages",
      "description": "Send, read, react, unsend, and see what failed."
    },
    {
      "name": "Conversations",
      "description": "Threads, and the typing bubble."
    },
    {
      "name": "Lookup",
      "description": "Whether iMessage has reached a handle."
    },
    {
      "name": "Contacts",
      "description": "Names for handles, shared by the project."
    },
    {
      "name": "Attachments",
      "description": "Upload a file, then send it."
    },
    {
      "name": "Numbers",
      "description": "The lines you hold, and naming them."
    },
    {
      "name": "Delivery",
      "description": "Webhooks and per-message callbacks."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key from the Miss Blue console. `Authorization: Bearer <key>`."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Every failure has this shape.",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "A stable, machine-readable reason."
              },
              "message": {
                "type": "string",
                "description": "What went wrong, in prose."
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      }
    }
  },
  "paths": {
    "/v1/me": {
      "get": {
        "operationId": "getV1Me",
        "summary": "What this credential is.",
        "description": "What this credential is.\n\nFor a key: its id, its project, and the organisation above it. `live` is false for an mb_test_ key. There is no person in the answer, because there is no person behind a key.",
        "tags": [
          "Identity"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "What this credential is.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "kind": "key",
                  "key_id": "2aea3032-1631-419a-ba45-05cb965d157c",
                  "project_id": "e3b7c534-7e5d-4669-b90a-5b617bfe77f0",
                  "project_name": "Client Support",
                  "workspace_id": "3480fdd8-2ef3-4edd-9949-bf78b4cd3e35",
                  "workspace_name": "Acme Agency",
                  "live": true
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages": {
      "post": {
        "operationId": "postV1Messages",
        "summary": "Send an iMessage.",
        "description": "Send an iMessage.\n\nText, files, or a reply quoting an earlier message. Sending to an existing group is the same call with the group's chat_id as the recipient.",
        "tags": [
          "Messages"
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "number_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Which of your numbers to send from. GET /v1/numbers lists them."
                  },
                  "recipient": {
                    "type": "string",
                    "description": "A phone number, an Apple ID email, or a chat_id to send into an existing conversation."
                  },
                  "text": {
                    "type": "string",
                    "description": "Optional only if you are sending attachments — a message must carry one or the other."
                  },
                  "attachment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "From POST /v1/attachments. iMessage has no captions, so text and attachments are sent as separate messages."
                  },
                  "reply_to_message_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Our id for the message being quoted, not Apple's."
                  },
                  "status_callback": {
                    "type": "string",
                    "format": "uri",
                    "description": "Where to POST this one message's outcome. See Delivery."
                  },
                  "allow_duplicate": {
                    "type": "boolean",
                    "description": "Send anyway, though the same text went to the same person moments ago."
                  }
                },
                "required": [
                  "number_id",
                  "recipient"
                ]
              },
              "example": {
                "number_id": "8f14e45f-ceea-467a-9a1b-1b1e5f9e2c3d",
                "recipient": "+15555550100",
                "text": "Your table is confirmed for 7pm."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Send an iMessage.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "id": "0b5ed7e8-f7a1-4f6e-9c2a-2a1f0d9e8b77",
                  "status": "sent",
                  "queued": false,
                  "chat_id": "iMessage;-;+15555550100"
                }
              }
            }
          },
          "400": {
            "description": "No text and no attachments, or neither number_id nor agent_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "A number_id your key does not hold. Not 403 — whether it exists is not your business.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The same text went to the same person moments ago. Pass allow_duplicate to mean it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The Mac holding that number is offline. Retry; nothing was sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "getV1Messages",
        "summary": "List messages across your project's numbers, newest first.",
        "description": "List messages across your project's numbers, newest first.",
        "tags": [
          "Messages"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "List messages across your project's numbers, newest first."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/{id}": {
      "get": {
        "operationId": "getV1MessagesById",
        "summary": "One message, including its current delivery state.",
        "description": "One message, including its current delivery state.\n\nPolling this works, but a webhook or a status_callback tells you sooner and costs you nothing. `agent_version` and `macos_version` say which Mac build handled it, stamped when the message was written rather than looked up now — a Mac that is updated must not rewrite what it was running last week, which is exactly the week you are trying to explain.",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The id of the resource.",
            "schema": {
              "type": "string"
            },
            "example": "0b5ed7e8-f7a1-4f6e-9c2a-2a1f0d9e8b77"
          }
        ],
        "responses": {
          "200": {
            "description": "One message, including its current delivery state."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such message, or one on a number your key does not hold.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/thread/{chat_id}": {
      "get": {
        "operationId": "getV1MessagesThreadByChatId",
        "summary": "Every message in one conversation, oldest first.",
        "description": "Every message in one conversation, oldest first.\n\nOrdered by the platform's own sort key, so a message the Mac picked up late still lands where it belongs rather than at the end.",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "name": "chat_id",
            "in": "path",
            "required": true,
            "description": "The chat_id of the resource.",
            "schema": {
              "type": "string"
            },
            "example": "iMessage;-;+15555550100"
          }
        ],
        "responses": {
          "200": {
            "description": "Every message in one conversation, oldest first."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/{id}/react": {
      "post": {
        "operationId": "postV1MessagesByIdReact",
        "summary": "Add or remove a tapback.",
        "description": "Add or remove a tapback.",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The id of the resource.",
            "schema": {
              "type": "string"
            },
            "example": "0b5ed7e8-f7a1-4f6e-9c2a-2a1f0d9e8b77"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reaction_key": {
                    "type": "string",
                    "description": "heart, like, dislike, laugh, emphasize, or question."
                  },
                  "remove": {
                    "type": "boolean",
                    "description": "Take it back rather than add it."
                  }
                },
                "required": [
                  "reaction_key"
                ]
              },
              "example": {
                "reaction_key": "heart"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Add or remove a tapback."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/problems": {
      "get": {
        "operationId": "getV1MessagesProblems",
        "summary": "Messages that failed, or are still waiting for a Mac.",
        "description": "Messages that failed, or are still waiting for a Mac.\n\nThe queue and the failures in one list, because from a caller's side they are the same question: what have I sent that has not arrived. Worth polling on a schedule even if you take webhooks — a webhook that never fired is exactly the case a webhook cannot tell you about.",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "name": "only",
            "in": "query",
            "required": false,
            "description": "queued or failed. Omit for both.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Messages that failed, or are still waiting for a Mac."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages/{id}/unsend": {
      "post": {
        "operationId": "postV1MessagesByIdUnsend",
        "summary": "Unsend a message.",
        "description": "Unsend a message.\n\nApple's two-minute window and its own rules apply. We do not re-check them against our clock — the Mac reports the platform's answer, so a refusal here is Apple's refusal, not ours.",
        "tags": [
          "Messages"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The id of the resource.",
            "schema": {
              "type": "string"
            },
            "example": "0b5ed7e8-f7a1-4f6e-9c2a-2a1f0d9e8b77"
          }
        ],
        "responses": {
          "201": {
            "description": "Unsend a message."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/threads": {
      "get": {
        "operationId": "getV1Threads",
        "summary": "Every conversation on your project's numbers, most recent first.",
        "description": "Every conversation on your project's numbers, most recent first.\n\nunread is always 0 for a key. A key is not a person and has no place in a conversation it has read up to, so reporting every inbound message ever would have read as a backlog nobody has.",
        "tags": [
          "Conversations"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Every conversation on your project's numbers, most recent first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "total": 2,
                  "data": [
                    {
                      "chat_id": "iMessage;-;+15555550100",
                      "handle": "+15555550100",
                      "is_group": false,
                      "last_text": "Perfect, see you then",
                      "last_at": "2026-08-23T09:12:04Z",
                      "last_direction": "inbound",
                      "message_count": 14,
                      "unread": 0,
                      "last_replied_by": "Sam Okafor"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/read-receipts": {
      "post": {
        "operationId": "postV1ReadReceipts",
        "summary": "Tell the customer their message was seen.",
        "description": "Tell the customer their message was seen.\n\nThe \"Read\" that appears under their message on their phone. Distinct from POST /v1/threads/{chat_id}/read, which records where a person has read to so their own unread count is right and which nobody outside this system sees. Nothing sends this automatically: it is a claim that a human looked, and a receipt that fired because a dashboard was open is a lie told at scale.",
        "tags": [
          "Conversations"
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "chat_id": {
                    "type": "string",
                    "description": "The conversation to acknowledge."
                  },
                  "read": {
                    "type": "boolean",
                    "description": "Defaults to true. False marks it unread again."
                  }
                },
                "required": [
                  "chat_id"
                ]
              },
              "example": {
                "chat_id": "iMessage;-;+15555550100",
                "read": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Tell the customer their message was seen."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such conversation on a number your key holds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The Mac is offline. Nothing was acknowledged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/typing": {
      "post": {
        "operationId": "postV1Typing",
        "summary": "Show or hide the typing bubble in a conversation.",
        "description": "Show or hide the typing bubble in a conversation.\n\nDo not block a reply on this. A bubble that arrives after the message it was meant to precede is worse than no bubble — send it, ignore the outcome, and reply. There is no guaranteed stop either: Messages.app clears it on its own after a few seconds of silence, which is the behaviour to rely on.",
        "tags": [
          "Conversations"
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "chat_id": {
                    "type": "string",
                    "description": "The conversation. Not a bare handle — guessing which of several threads was meant would show a stranger that somebody is typing."
                  },
                  "active": {
                    "type": "boolean",
                    "description": "true to start, false to stop."
                  }
                },
                "required": [
                  "chat_id"
                ]
              },
              "example": {
                "chat_id": "iMessage;-;+15555550100",
                "active": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Show or hide the typing bubble in a conversation."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such conversation on a number your key holds.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/lookup": {
      "get": {
        "operationId": "getV1Lookup",
        "summary": "What we know about reaching a handle on iMessage.",
        "description": "What we know about reaching a handle on iMessage.\n\nThis is evidence, not a query to Apple. We cannot ask — so instead this reads what has already happened on your own numbers. An inbound iMessage is proof the handle answers; a send that left the Mac is the same proof from the other side. unknown means we have never tried, and is not a negative answer: routing to SMS on it would be routing on ignorance.",
        "tags": [
          "Lookup"
        ],
        "parameters": [
          {
            "name": "handle",
            "in": "query",
            "required": true,
            "description": "A phone number or Apple ID email.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "What we know about reaching a handle on iMessage.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "handle": "+15555550100",
                  "service": "imessage",
                  "attempts": 3,
                  "detail": "iMessage has worked with this handle before."
                }
              }
            }
          },
          "400": {
            "description": "No handle, an empty one, or one over 256 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/contacts": {
      "get": {
        "operationId": "getV1Contacts",
        "summary": "Every name this project has given a handle.",
        "description": "Every name this project has given a handle.\n\n`reach` says whether iMessage is known to work with each one, from your own traffic. See Lookup for what the three states mean.",
        "tags": [
          "Contacts"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Every name this project has given a handle.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "total": 1,
                  "data": [
                    {
                      "id": "73ef7753-7ca6-493e-b9ff-9eef708828e7",
                      "handle": "+15551230002",
                      "name": "Head of catering",
                      "reach": "reachable",
                      "created_at": "2026-08-23T09:12:04Z",
                      "updated_at": "2026-08-23T09:12:04Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "putV1Contacts",
        "summary": "Name a handle, or rename one already named.",
        "description": "Name a handle, or rename one already named.\n\nPUT rather than POST: naming a handle is idempotent, and naming one that already has a name is a rename rather than a conflict. A caller should not have to know which it is doing.",
        "tags": [
          "Contacts"
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "handle": {
                    "type": "string",
                    "description": "Phone number or Apple ID email."
                  },
                  "name": {
                    "type": "string",
                    "description": "Clearing a name is a delete; there is a route for that."
                  }
                },
                "required": [
                  "handle",
                  "name"
                ]
              },
              "example": {
                "handle": "+15551230002",
                "name": "Head of catering"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Name a handle, or rename one already named."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/contacts/import": {
      "post": {
        "operationId": "postV1ContactsImport",
        "summary": "Name many handles at once.",
        "description": "Name many handles at once.\n\nSaves what is usable and tells you precisely which rows were not, rather than refusing the whole file over one bad line — a five-hundred-row import that fails on row four hundred means somebody edits and retries, several times.",
        "tags": [
          "Contacts"
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "contacts": {
                    "type": "array",
                    "description": "Objects with `handle` and `name`."
                  }
                },
                "required": [
                  "contacts"
                ]
              },
              "example": {
                "contacts": [
                  {
                    "handle": "+15551230002",
                    "name": "Head of catering"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Name many handles at once."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/contacts/{id}": {
      "delete": {
        "operationId": "deleteV1ContactsById",
        "summary": "Remove a name from the project's book.",
        "description": "Remove a name from the project's book.\n\nAnyone on the project may remove any name, not only whoever added it. A shared book that only its author can tidy is a book nobody tidies.",
        "tags": [
          "Contacts"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The id of the resource.",
            "schema": {
              "type": "string"
            },
            "example": "73ef7753-7ca6-493e-b9ff-9eef708828e7"
          }
        ],
        "responses": {
          "200": {
            "description": "Remove a name from the project's book."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such contact in this project's book.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/attachments": {
      "post": {
        "operationId": "postV1Attachments",
        "summary": "Upload a file and get an id to send it with.",
        "description": "Upload a file and get an id to send it with.\n\nThe file name and type ride in headers because a multipart parser is a lot of surface for a form with one field.",
        "tags": [
          "Attachments"
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "description": "The raw bytes of the file (for example `receipt.png`).",
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Upload a file and get an id to send it with.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "id": "7deff905-9038-4e4e-b47c-580a1ab52220",
                  "file_name": "receipt.png",
                  "mime_type": "image/png",
                  "size_bytes": 20481,
                  "sha256": "c414cd0e204de974f73753c7e28d7638e7b3691bb8b1a2bab6b25bb7fed7ce77"
                }
              }
            }
          },
          "400": {
            "description": "An empty file, a missing name, or a type we do not allow.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Over the size limit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-required-headers": {
          "Content-Type": "image/png",
          "X-Miss-Blue-File-Name": "receipt.png"
        }
      }
    },
    "/v1/numbers": {
      "get": {
        "operationId": "getV1Numbers",
        "summary": "Your project's numbers, with the name each has been given.",
        "description": "Your project's numbers, with the name each has been given.\n\nThe Mac behind a number is never named. Which machine holds a line is our problem, and telling you would make it yours.",
        "tags": [
          "Numbers"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Your project's numbers, with the name each has been given."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/numbers/{id}/label": {
      "post": {
        "operationId": "postV1NumbersByIdLabel",
        "summary": "Give a number a name.",
        "description": "Give a number a name.\n\nSo a thread says it arrived on Main support rather than +1 646 555 0142. Coming back to a conversation days later, the digits do not tell you which line you used.",
        "tags": [
          "Numbers"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The id of the resource.",
            "schema": {
              "type": "string"
            },
            "example": "8f14e45f-ceea-467a-9a1b-1b1e5f9e2c3d"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string",
                    "description": "Empty or absent clears it back to the bare handle."
                  }
                }
              },
              "example": {
                "label": "Main support"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Give a number a name."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects/{id}/webhooks": {
      "get": {
        "operationId": "getV1ProjectsByIdWebhooks",
        "summary": "List the endpoints this project sends events to.",
        "description": "List the endpoints this project sends events to.\n\nYour key may manage its own project's endpoints and no others. Another project's id returns 404 rather than 403, because whether it exists is not something a stranger should learn.",
        "tags": [
          "Delivery"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The id of the resource.",
            "schema": {
              "type": "string"
            },
            "example": "3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60"
          }
        ],
        "responses": {
          "200": {
            "description": "List the endpoints this project sends events to."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "postV1ProjectsByIdWebhooks",
        "summary": "Register where events should go.",
        "description": "Register where events should go.\n\nAnswers “tell me about everything on this line”: inbound messages, delivery, read receipts, reactions. Each delivery is signed so you can prove it came from us, and retried with backoff. An endpoint that keeps failing is switched off rather than retried forever, and you are told.",
        "tags": [
          "Delivery"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The id of the resource.",
            "schema": {
              "type": "string"
            },
            "example": "3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Must be https, and must not resolve to a private address."
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Which to receive. Omit for all of them."
                  }
                },
                "required": [
                  "url"
                ]
              },
              "example": {
                "url": "https://example.com/hooks/miss-blue",
                "events": [
                  "message.received",
                  "message.delivered"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Register where events should go."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects/{id}/webhooks/{endpoint_id}/enabled": {
      "post": {
        "operationId": "postV1ProjectsByIdWebhooksByEndpointIdEnabled",
        "summary": "Switch an endpoint on or off without deleting it.",
        "description": "Switch an endpoint on or off without deleting it.\n\nAn endpoint that keeps failing is switched off by us for the same reason you would switch one off during a deploy: to stop hammering something that is not listening. Turning it back on is this call.",
        "tags": [
          "Delivery"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The id of the resource.",
            "schema": {
              "type": "string"
            },
            "example": "3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60"
          },
          {
            "name": "endpoint_id",
            "in": "path",
            "required": true,
            "description": "The endpoint_id of the resource.",
            "schema": {
              "type": "string"
            },
            "example": "b71c0d92-5a83-4e1f-9d70-6c2e8a4b3f15"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": ""
                  }
                },
                "required": [
                  "enabled"
                ]
              },
              "example": {
                "enabled": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Switch an endpoint on or off without deleting it."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects/{id}/webhooks/{endpoint_id}/deliveries": {
      "get": {
        "operationId": "getV1ProjectsByIdWebhooksByEndpointIdDeliveries",
        "summary": "What we tried to send you, and what came back.",
        "description": "What we tried to send you, and what came back.\n\nEvery attempt, its response status, and how many retries it took. This is the first place to look when your endpoint is up but you are not seeing events — it distinguishes “we never sent it” from “we sent it and your server said 500”.",
        "tags": [
          "Delivery"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The id of the resource.",
            "schema": {
              "type": "string"
            },
            "example": "3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60"
          },
          {
            "name": "endpoint_id",
            "in": "path",
            "required": true,
            "description": "The endpoint_id of the resource.",
            "schema": {
              "type": "string"
            },
            "example": "b71c0d92-5a83-4e1f-9d70-6c2e8a4b3f15"
          }
        ],
        "responses": {
          "200": {
            "description": "What we tried to send you, and what came back."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects/{id}/webhooks/{endpoint_id}": {
      "delete": {
        "operationId": "deleteV1ProjectsByIdWebhooksByEndpointId",
        "summary": "Stop sending to an endpoint.",
        "description": "Stop sending to an endpoint.",
        "tags": [
          "Delivery"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The id of the resource.",
            "schema": {
              "type": "string"
            },
            "example": "3c9a1f77-2d64-4b0e-8a5c-7e1d4b2f9a60"
          },
          {
            "name": "endpoint_id",
            "in": "path",
            "required": true,
            "description": "The endpoint_id of the resource.",
            "schema": {
              "type": "string"
            },
            "example": "b71c0d92-5a83-4e1f-9d70-6c2e8a4b3f15"
          }
        ],
        "responses": {
          "200": {
            "description": "Stop sending to an endpoint."
          },
          "401": {
            "description": "The key is missing, malformed, or revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  }
}