{
  "openapi": "3.1.0",
  "info": {
    "title": "Coplace API",
    "version": "v1",
    "description": "Coplace connects a business's own software to its customers on WhatsApp and email.\n\nThis API gives your software everything it needs: send and receive messages, manage contacts and approved templates, subscribe to webhooks, and hand conversations to — or take them back from — the AI collaborator.\n\nEvery call is scoped to one workspace: the key decides which one, and nothing crosses over.\n\nEvery success is `{ \"data\": … }`; every refusal is `{ \"error\": { \"status\", \"code\", \"message\", \"detail\", \"requestId\" } }`. Test `error.code`, never the text."
  },
  "servers": [
    {
      "url": "https://coplace.io/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "components": {
    "schemas": {
      "Conversation": {
        "type": "object",
        "description": "A conversation: one person on one channel (WhatsApp), or one email thread.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The conversation id."
          },
          "channelId": {
            "type": "string",
            "description": "The channel it lives on."
          },
          "channelType": {
            "type": "string",
            "description": "The kind of channel.",
            "enum": [
              "whatsapp",
              "email"
            ]
          },
          "channelName": {
            "type": "string",
            "description": "The channel's name in Coplace."
          },
          "status": {
            "type": "string",
            "description": "Where it stands.",
            "enum": [
              "open",
              "pending",
              "closed"
            ]
          },
          "subject": {
            "type": "string",
            "description": "Email subject; `null` elsewhere."
          },
          "category": {
            "type": "string",
            "description": "The label key it is filed under, or `null`."
          },
          "aiEnabled": {
            "type": "boolean",
            "description": "Whether the AI collaborator may answer in this conversation."
          },
          "needsHuman": {
            "type": "boolean",
            "description": "Someone was asked to take over."
          },
          "assignedUserId": {
            "type": "string",
            "description": "The member it is assigned to, or `null`."
          },
          "agentName": {
            "type": "string",
            "description": "The collaborator's name on this conversation, or `null`."
          },
          "contact": {
            "type": "object",
            "description": "Who you are talking to.",
            "properties": {
              "id": {
                "type": "string",
                "description": "The contact id."
              },
              "displayName": {
                "type": "string",
                "description": "Their name, or `null`."
              },
              "identity": {
                "type": "string",
                "description": "Their phone number (WhatsApp) or email address."
              },
              "identityKind": {
                "type": "string",
                "description": "What `identity` is.",
                "enum": [
                  "phone",
                  "email",
                  "external"
                ]
              },
              "blocked": {
                "type": "boolean",
                "description": "Blocked contacts are kept out of the inbox."
              }
            }
          },
          "window": {
            "type": "object",
            "description": "The WhatsApp 24-hour customer service window.",
            "properties": {
              "applies": {
                "type": "boolean",
                "description": "`false` outside WhatsApp."
              },
              "open": {
                "type": "boolean",
                "description": "Whether free text can be sent now. Always `true` when the rule does not apply."
              },
              "expiresAt": {
                "type": "string",
                "description": "When it closes, ISO 8601, or `null`."
              },
              "minutesLeft": {
                "type": "integer",
                "description": "Minutes left, `0` when closed, `null` when the rule does not apply."
              }
            }
          },
          "unreadCount": {
            "type": "integer",
            "description": "Messages from the contact not yet read by your team."
          },
          "lastMessageAt": {
            "type": "string",
            "description": "ISO 8601, or `null`."
          },
          "lastInboundAt": {
            "type": "string",
            "description": "When the contact last wrote, ISO 8601. This opens the 24-hour window."
          },
          "createdAt": {
            "type": "string",
            "description": "ISO 8601."
          },
          "lastMessage": {
            "type": "object",
            "description": "The latest message, or `null`.",
            "properties": {
              "id": {
                "type": "string",
                "description": "The message id."
              },
              "direction": {
                "type": "string",
                "description": "From the contact, or to them.",
                "enum": [
                  "inbound",
                  "outbound"
                ]
              },
              "textContent": {
                "type": "string",
                "description": "Its text."
              },
              "status": {
                "type": "string",
                "description": "Its delivery status."
              },
              "createdAt": {
                "type": "string",
                "description": "ISO 8601."
              }
            }
          }
        }
      },
      "Message": {
        "type": "object",
        "description": "One message, in either direction.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The message id."
          },
          "conversationId": {
            "type": "string",
            "description": "Its conversation."
          },
          "channelType": {
            "type": "string",
            "description": "The kind of channel.",
            "enum": [
              "whatsapp",
              "email"
            ]
          },
          "direction": {
            "type": "string",
            "description": "`inbound` from the contact, `outbound` to them.",
            "enum": [
              "inbound",
              "outbound"
            ]
          },
          "sender": {
            "type": "string",
            "description": "Who speaks: the contact, the AI collaborator, your side (a person or your software), or Coplace.",
            "enum": [
              "contact",
              "agent_ai",
              "agent_human",
              "system"
            ]
          },
          "senderName": {
            "type": "string",
            "description": "The person's name, or your key's label."
          },
          "apiKeyId": {
            "type": "string",
            "description": "The key that sent it, when it went through the API."
          },
          "type": {
            "type": "string",
            "description": "`text`, `template`, `interactive`, `image`, `audio`, `document`…"
          },
          "textContent": {
            "type": "string",
            "description": "Its text. For a template, the rendered text."
          },
          "transcript": {
            "type": "string",
            "description": "The text of a received voice note, when transcribed."
          },
          "mediaUrl": {
            "type": "string",
            "description": "Where to download its file (`GET /inbox/messages/{id}/media`), or `null`."
          },
          "subject": {
            "type": "string",
            "description": "Email subject, or `null`."
          },
          "status": {
            "type": "string",
            "description": "Delivery status. `queued`: waiting to leave (`queue: true`).",
            "enum": [
              "queued",
              "sending",
              "sent",
              "delivered",
              "read",
              "failed"
            ]
          },
          "providerMessageId": {
            "type": "string",
            "description": "The id at the messaging provider (WhatsApp `wamid`), or `null`."
          },
          "error": {
            "type": "string",
            "description": "Why it failed, as the provider said, or `null`."
          },
          "form": {
            "type": "object",
            "description": "The buttons or list sent with it, or `null`."
          },
          "reply": {
            "type": "object",
            "description": "For a tapped button or chosen list row: `{ id, title }`. Otherwise `null`."
          },
          "location": {
            "type": "object",
            "description": "A location the contact sent: `{ latitude, longitude, name, address }`, or `null`."
          },
          "createdAt": {
            "type": "string",
            "description": "ISO 8601."
          }
        }
      },
      "SendResult": {
        "type": "object",
        "description": "What a send returns.",
        "properties": {
          "message": {
            "$ref": "#/components/schemas/Message"
          },
          "duplicate": {
            "type": "boolean",
            "description": "`true` when this `idempotencyKey` was already used: the message returned is the first one, nothing was sent again."
          }
        }
      },
      "Contact": {
        "type": "object",
        "description": "A person you talk to.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The contact id."
          },
          "displayName": {
            "type": "string",
            "description": "Their name, or `null`."
          },
          "locale": {
            "type": "string",
            "description": "Their language, e.g. `fr`, or `null`."
          },
          "phone": {
            "type": "string",
            "description": "Their phone number, E.164 digits, or `null`."
          },
          "email": {
            "type": "string",
            "description": "Their email address, or `null`."
          },
          "externalId": {
            "type": "string",
            "description": "Your own id for them, or `null`."
          },
          "tags": {
            "type": "array",
            "description": "Free tags.",
            "items": {
              "type": "string",
              "description": "A tag."
            }
          },
          "attributes": {
            "type": "object",
            "description": "Custom field values, by attribute key."
          },
          "optInAt": {
            "type": "string",
            "description": "When they agreed to receive messages, or `null`."
          },
          "optOutAt": {
            "type": "string",
            "description": "When they withdrew — on WhatsApp by tapping \"Stop\" — or `null`. It overrides `optInAt`."
          },
          "blocked": {
            "type": "boolean",
            "description": "Blocked contacts receive nothing."
          },
          "conversationCount": {
            "type": "integer",
            "description": "How many conversations they have."
          },
          "lastMessageAt": {
            "type": "string",
            "description": "ISO 8601, or `null`."
          },
          "createdAt": {
            "type": "string",
            "description": "ISO 8601."
          },
          "updatedAt": {
            "type": "string",
            "description": "ISO 8601."
          }
        }
      },
      "ContactAttribute": {
        "type": "object",
        "description": "A custom field defined for contacts.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The attribute id."
          },
          "key": {
            "type": "string",
            "description": "The key used in `attributes`."
          },
          "label": {
            "type": "string",
            "description": "Its name, in your words."
          },
          "dataType": {
            "type": "string",
            "description": "`text`, `number`, `boolean` or `date`."
          },
          "isArchived": {
            "type": "boolean",
            "description": "Archived fields keep their values but are no longer shown."
          }
        }
      },
      "ContactNote": {
        "type": "object",
        "description": "A free-text note about a contact.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The note id."
          },
          "body": {
            "type": "string",
            "description": "The note."
          },
          "author": {
            "type": "string",
            "description": "The person's name or the key's label; `null` when the author no longer exists."
          },
          "authorKind": {
            "type": "string",
            "description": "Who wrote it.",
            "enum": [
              "user",
              "api_key"
            ]
          },
          "createdAt": {
            "type": "string",
            "description": "ISO 8601."
          }
        }
      },
      "Channel": {
        "type": "object",
        "description": "A number or mailbox the workspace speaks from.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The channel id — the `channelId` you send from."
          },
          "type": {
            "type": "string",
            "description": "The kind of channel.",
            "enum": [
              "whatsapp",
              "email"
            ]
          },
          "name": {
            "type": "string",
            "description": "Its name in Coplace."
          },
          "identity": {
            "type": "string",
            "description": "Its public identity: the business phone number id, or the mailbox address."
          },
          "isActive": {
            "type": "boolean",
            "description": "An inactive channel neither sends nor receives."
          },
          "qualityRating": {
            "type": "string",
            "description": "WhatsApp quality rating: `GREEN`, `YELLOW`, `RED`, or `null`."
          },
          "agentId": {
            "type": "string",
            "description": "The collaborator answering on it; `null` when your software answers."
          },
          "messagingLimit": {
            "type": "string",
            "description": "Meta's sending tier, or `null`."
          }
        }
      },
      "Member": {
        "type": "object",
        "description": "A person of the workspace.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The member id — what `assign` takes."
          },
          "fullName": {
            "type": "string",
            "description": "Their name."
          },
          "email": {
            "type": "string",
            "description": "Their email address."
          },
          "role": {
            "type": "string",
            "description": "Their role.",
            "enum": [
              "owner",
              "admin",
              "agent",
              "member"
            ]
          },
          "canBeAssigned": {
            "type": "boolean",
            "description": "Whether a conversation can be assigned to them."
          }
        }
      },
      "Template": {
        "type": "object",
        "description": "A WhatsApp message template.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The template id — the `templateId` you send."
          },
          "name": {
            "type": "string",
            "description": "Its name at Meta."
          },
          "language": {
            "type": "string",
            "description": "Its language code, e.g. `fr`."
          },
          "category": {
            "type": "string",
            "description": "Meta's category. Marketing ones are refused to contacts who opted out.",
            "enum": [
              "UTILITY",
              "MARKETING",
              "AUTHENTICATION"
            ]
          },
          "status": {
            "type": "string",
            "description": "Only `approved` can be sent.",
            "enum": [
              "draft",
              "submitted",
              "approved",
              "rejected",
              "paused",
              "disabled"
            ]
          },
          "body": {
            "type": "string",
            "description": "Its text, with `{{1}}`, `{{2}}`… placeholders."
          },
          "rejectionReason": {
            "type": "string",
            "description": "Why Meta refused it, or `null`."
          },
          "createdAt": {
            "type": "string",
            "description": "ISO 8601."
          },
          "updatedAt": {
            "type": "string",
            "description": "ISO 8601."
          }
        }
      },
      "WebhookSubscription": {
        "type": "object",
        "description": "An address Coplace calls, and for which events.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The subscription id."
          },
          "url": {
            "type": "string",
            "description": "The https address called."
          },
          "events": {
            "type": "array",
            "description": "The events delivered; `*` means all.",
            "items": {
              "type": "string",
              "description": "An event name."
            }
          },
          "label": {
            "type": "string",
            "description": "Its name."
          },
          "isActive": {
            "type": "boolean",
            "description": "A paused subscription receives nothing."
          },
          "lastDeliveredAt": {
            "type": "string",
            "description": "The last successful call, ISO 8601, or `null`."
          },
          "lastError": {
            "type": "string",
            "description": "What went wrong last time, or `null`."
          },
          "previousSecretValidUntil": {
            "type": "string",
            "description": "During a rotation: until when the old secret still signs. `null` otherwise."
          },
          "createdAt": {
            "type": "string",
            "description": "ISO 8601."
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "description": "One event delivered — or to deliver — to one subscription.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The delivery id, also sent as `X-Coplace-Delivery`."
          },
          "subscriptionId": {
            "type": "string",
            "description": "The subscription."
          },
          "event": {
            "type": "string",
            "description": "The event name."
          },
          "eventId": {
            "type": "string",
            "description": "The event's own id (`evt_…`), the same across redeliveries: deduplicate on it."
          },
          "status": {
            "type": "string",
            "description": "`failed` after six attempts.",
            "enum": [
              "pending",
              "delivered",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer",
            "description": "Attempts so far."
          },
          "responseStatus": {
            "type": "integer",
            "description": "The HTTP status your server answered, or `null`."
          },
          "lastError": {
            "type": "string",
            "description": "What went wrong, or `null`."
          },
          "nextAttemptAt": {
            "type": "string",
            "description": "When the next attempt is due, for a pending delivery."
          },
          "createdAt": {
            "type": "string",
            "description": "ISO 8601."
          },
          "deliveredAt": {
            "type": "string",
            "description": "ISO 8601, or `null`."
          }
        }
      },
      "ApiRequest": {
        "type": "object",
        "description": "One call made with an API key.",
        "properties": {
          "id": {
            "type": "string",
            "description": "The log line id."
          },
          "apiKeyId": {
            "type": "string",
            "description": "The key used."
          },
          "keyLabel": {
            "type": "string",
            "description": "The key's label."
          },
          "keyPrefix": {
            "type": "string",
            "description": "The key's public prefix, `cop_…`."
          },
          "method": {
            "type": "string",
            "description": "HTTP method."
          },
          "path": {
            "type": "string",
            "description": "The address called, without its query string."
          },
          "status": {
            "type": "integer",
            "description": "The HTTP status returned."
          },
          "errorCode": {
            "type": "string",
            "description": "The error `code` returned, or `null`."
          },
          "durationMs": {
            "type": "integer",
            "description": "How long it took."
          },
          "requestId": {
            "type": "string",
            "description": "The request id, also returned in errors."
          },
          "createdAt": {
            "type": "string",
            "description": "ISO 8601."
          }
        }
      },
      "Error": {
        "type": "object",
        "description": "Every refusal of `/api/v1`.",
        "properties": {
          "error": {
            "type": "object",
            "description": "What went wrong.",
            "properties": {
              "status": {
                "type": "integer",
                "description": "The HTTP status, repeated."
              },
              "code": {
                "type": "string",
                "description": "A stable code. Test this, never the text."
              },
              "message": {
                "type": "string",
                "description": "What it means, in English."
              },
              "detail": {
                "type": "string",
                "description": "The specific reason, when there is one. May be in French."
              },
              "requestId": {
                "type": "string",
                "description": "Quote it to us if you need help."
              },
              "data": {
                "type": "object",
                "description": "Extra facts attached to some refusals (e.g. `issues`)."
              }
            }
          }
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Send `Authorization: Bearer cop_…`. A key belongs to ONE workspace and acts as itself, not as the person who created it: removing that person does not revoke the key, and messages it sends are attributed to the key's label."
      }
    }
  },
  "paths": {
    "/api-requests": {
      "get": {
        "tags": [
          "API keys"
        ],
        "summary": "List recent API calls",
        "description": "Every call made to `/api/v1` with one of this workspace's keys over the last 30 days: method, address, status, error code and duration. Newest first. Use it to see why a call failed without asking us.\n\nNeither bodies, headers nor query strings are kept. A call refused because the key itself was wrong is not listed: we cannot tell whose it was.\n\nRequires a key with the `admin` role or above.",
        "operationId": "getApiV1ApiRequests",
        "responses": {
          "200": {
            "description": "A page of calls, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "A page of calls.",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "The calls.",
                          "items": {
                            "$ref": "#/components/schemas/ApiRequest"
                          }
                        },
                        "nextCursor": {
                          "type": "string",
                          "description": "Pass it as `cursor` for the next page; `null` at the end."
                        },
                        "hasMore": {
                          "type": "boolean",
                          "description": "Whether another page exists."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "apiKeyId",
            "in": "query",
            "required": false,
            "description": "Only calls made with this key.",
            "schema": {
              "type": "string",
              "description": "Only calls made with this key."
            }
          },
          {
            "name": "errorsOnly",
            "in": "query",
            "required": false,
            "description": "`true` keeps only calls that did not succeed (status 400 and above).",
            "schema": {
              "type": "boolean",
              "description": "`true` keeps only calls that did not succeed (status 400 and above)."
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 100. Default 50.",
            "schema": {
              "type": "integer",
              "description": "Page size, 1 to 100. Default 50."
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `nextCursor` of the previous page.",
            "schema": {
              "type": "string",
              "description": "The `nextCursor` of the previous page."
            }
          }
        ]
      }
    },
    "/inbox/channels": {
      "get": {
        "tags": [
          "Channels"
        ],
        "summary": "List the channels of the workspace",
        "description": "Returns every WhatsApp number and mailbox the workspace can speak from, with the identifier you need everywhere else. **Start here**: opening a conversation requires a `channelId`, and there is no other way to obtain one.\n\nA channel that is switched off still appears, with `isActive: false`. Nothing is sent or received on it until someone switches it back on in Coplace.",
        "operationId": "getApiV1InboxChannels",
        "responses": {
          "200": {
            "description": "The channels, each with its id, type, public identity, and whether it is active.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The channels.",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "The channels.",
                          "items": {
                            "$ref": "#/components/schemas/Channel"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/contacts": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "List contacts",
        "description": "Returns the people this workspace has talked to, searchable by name, phone number or email. Custom attributes come back alongside, so you can map them into your own records.",
        "operationId": "getApiV1Contacts",
        "responses": {
          "200": {
            "description": "A page of contacts, and the attribute definitions that describe their custom fields.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "A page of contacts.",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "The contacts.",
                          "items": {
                            "$ref": "#/components/schemas/Contact"
                          }
                        },
                        "attributes": {
                          "type": "array",
                          "description": "The custom field definitions.",
                          "items": {
                            "$ref": "#/components/schemas/ContactAttribute"
                          }
                        },
                        "nextCursor": {
                          "type": "string",
                          "description": "Pass it as `cursor` for the next page; `null` at the end."
                        },
                        "total": {
                          "type": "integer",
                          "description": "How many match, on the first page only; `null` after."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search by name, phone number or email.",
            "schema": {
              "type": "string",
              "description": "Search by name, phone number or email."
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many to return.",
            "schema": {
              "type": "integer",
              "description": "How many to return."
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from the previous page.",
            "schema": {
              "type": "string",
              "description": "Opaque cursor from the previous page."
            }
          }
        ]
      },
      "post": {
        "tags": [
          "Contacts"
        ],
        "summary": "Create a contact",
        "description": "Creates someone before they have written. A contact needs at least one identity — a phone number or an email address — otherwise nothing can ever reach them.\n\nIf that identity is already known, the existing contact is returned rather than duplicated.\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1Contacts",
        "responses": {
          "200": {
            "description": "The contact.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The contact and how to read its custom fields.",
                      "properties": {
                        "contact": {
                          "$ref": "#/components/schemas/Contact"
                        },
                        "attributes": {
                          "type": "array",
                          "description": "The custom field definitions.",
                          "items": {
                            "$ref": "#/components/schemas/ContactAttribute"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`no_identity` — Neither a phone number nor an email address was given.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "fullName": {
                    "type": "string",
                    "description": "Their name, as you want it displayed."
                  },
                  "phone": {
                    "type": "string",
                    "description": "Phone number in E.164 form.",
                    "example": "+33612345678"
                  },
                  "email": {
                    "type": "string",
                    "description": "Email address."
                  },
                  "attributes": {
                    "type": "object",
                    "description": "Your custom fields, by key. See `GET /contacts/attributes`."
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/contacts/{id}": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "Get one contact",
        "description": "Returns the contact with all their identities and custom attributes.",
        "operationId": "getApiV1ContactsId}",
        "responses": {
          "200": {
            "description": "The contact, and the attribute definitions describing their custom fields.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The contact and how to read its custom fields.",
                      "properties": {
                        "contact": {
                          "$ref": "#/components/schemas/Contact"
                        },
                        "attributes": {
                          "type": "array",
                          "description": "The custom field definitions.",
                          "items": {
                            "$ref": "#/components/schemas/ContactAttribute"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The contact id.",
            "schema": {
              "type": "string",
              "description": "The contact id."
            }
          }
        ]
      },
      "patch": {
        "tags": [
          "Contacts"
        ],
        "summary": "Update a contact",
        "description": "Changes the name or the custom attributes. Fields you leave out keep their value.\n\nIdentities — phone numbers and email addresses — are not edited here: they are established by real conversations, and rewriting them would silently merge two people.\n\nRequires a key with the `agent` role or above.",
        "operationId": "patchApiV1ContactsId}",
        "responses": {
          "200": {
            "description": "The contact, updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The contact and how to read its custom fields.",
                      "properties": {
                        "contact": {
                          "$ref": "#/components/schemas/Contact"
                        },
                        "attributes": {
                          "type": "array",
                          "description": "The custom field definitions.",
                          "items": {
                            "$ref": "#/components/schemas/ContactAttribute"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The contact id.",
            "schema": {
              "type": "string",
              "description": "The contact id."
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "fullName": {
                    "type": "string",
                    "description": "Their name."
                  },
                  "attributes": {
                    "type": "object",
                    "description": "Custom fields to set, by key."
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/contacts/{id}/notes": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "List notes on a contact",
        "description": "Free-text notes your team or your software wrote about this person, newest first. Notes are never shown to the contact and never read by the collaborator.",
        "operationId": "getApiV1ContactsIdNotes",
        "responses": {
          "200": {
            "description": "The notes, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The notes.",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "Up to 200 notes, newest first.",
                          "items": {
                            "$ref": "#/components/schemas/ContactNote"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The contact id.",
            "schema": {
              "type": "string",
              "description": "The contact id."
            }
          }
        ]
      },
      "post": {
        "tags": [
          "Contacts"
        ],
        "summary": "Add a note to a contact",
        "description": "Writes a free-text note about this person — what your CRM knows, a call summary, a warning for the team. It is signed with your key's label. The contact never sees it.\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1ContactsIdNotes",
        "responses": {
          "200": {
            "description": "The note.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ContactNote"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The contact id.",
            "schema": {
              "type": "string",
              "description": "The contact id."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "description": "The note, 1 to 5000 characters."
                  }
                },
                "required": [
                  "body"
                ]
              }
            }
          }
        }
      }
    },
    "/contacts/{id}/notes/{noteId}": {
      "delete": {
        "tags": [
          "Contacts"
        ],
        "summary": "Delete a note",
        "description": "Deletes a note. Only its author — the same key, or the same person — or an `admin` key can.\n\nRequires a key with the `agent` role or above.",
        "operationId": "deleteApiV1ContactsIdNotesNoteId}",
        "responses": {
          "200": {
            "description": "Confirmation that it is gone.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Confirmation.",
                      "properties": {
                        "deleted": {
                          "type": "boolean",
                          "description": "Always `true`."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The contact id.",
            "schema": {
              "type": "string",
              "description": "The contact id."
            }
          },
          {
            "name": "noteId",
            "in": "path",
            "required": true,
            "description": "The note id.",
            "schema": {
              "type": "string",
              "description": "The note id."
            }
          }
        ]
      }
    },
    "/contacts/attributes": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "List the custom contact fields",
        "description": "Returns the fields this account defined on its contacts: their key, label and type. Read this before writing `attributes` on a contact — an unknown key is refused.\n\nThese belong to the account, so every workspace of the same client shares them.",
        "operationId": "getApiV1ContactsAttributes",
        "responses": {
          "200": {
            "description": "The attribute definitions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The custom field definitions.",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "The definitions.",
                          "items": {
                            "$ref": "#/components/schemas/ContactAttribute"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/privacy/contacts/{id}": {
      "delete": {
        "tags": [
          "Contacts"
        ],
        "summary": "Erase a person, for good",
        "description": "**Irreversible.** Deletes the contact, their identities, their messages and their conversations. Meant for a right-to-erasure request.\n\nWhat is kept is what carries no personal data: counters, and the record that an erasure happened. The call returns a report of what was removed.\n\nRequires a key with the `admin` role or above.",
        "operationId": "deleteApiV1PrivacyContactsId}",
        "responses": {
          "200": {
            "description": "A report: what was erased, and how much of it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "What was erased.",
                      "properties": {
                        "erased": {
                          "type": "boolean",
                          "description": "Always `true`."
                        },
                        "removed": {
                          "type": "object",
                          "description": "Counts removed: `identities`, `conversations`, `messages`."
                        },
                        "emptied": {
                          "type": "object",
                          "description": "Counts kept for deduplication but emptied of their content."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The contact id.",
            "schema": {
              "type": "string",
              "description": "The contact id."
            }
          }
        ]
      }
    },
    "/inbox/categories": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "List the labels of the inbox",
        "description": "Labels are how threads are actually sorted — more than the open/closed state. A label can also keep its threads out of the queue (`skipProcedure`); what the AI collaborator does lives on the procedure each thread follows.\n\nRequires a key with the `admin` role or above.",
        "operationId": "getApiV1InboxCategories",
        "responses": {
          "200": {
            "description": "The labels, with their name and colour.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The labels.",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "The labels.",
                          "items": {
                            "type": "object",
                            "description": "A label.",
                            "properties": {
                              "key": {
                                "type": "string",
                                "description": "What `category` holds on a conversation."
                              },
                              "name": {
                                "type": "string",
                                "description": "Its name."
                              },
                              "color": {
                                "type": "string",
                                "description": "Its colour."
                              },
                              "skipProcedure": {
                                "type": "boolean",
                                "description": "The procedure skips these threads: out of the queue at reception."
                              }
                            }
                          }
                        },
                        "counts": {
                          "type": "object",
                          "description": "Conversations per label key over the last 30 days."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/inbox/conversations": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "List conversations",
        "description": "Returns conversations, most recently active first, with a cursor for the next page.\n\nFilters combine. `folder` picks the view (default `inbox`); `mine` and `starred` are personal and refused for a key (422 `personal_folder`) — a key is not a person. Trial conversations are excluded unless `folder=trials` or `sandbox` says otherwise. Counts per folder, label and channel: `GET /inbox/overview`.",
        "operationId": "getApiV1InboxConversations",
        "responses": {
          "200": {
            "description": "A page of conversations and the cursor for the next one.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "A page of conversations.",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "The conversations, most recent activity first.",
                          "items": {
                            "$ref": "#/components/schemas/Conversation"
                          }
                        },
                        "nextCursor": {
                          "type": "string",
                          "description": "Pass it as `cursor` for the next page; `null` at the end."
                        },
                        "hasMore": {
                          "type": "boolean",
                          "description": "Whether another page exists."
                        },
                        "total": {
                          "type": "integer",
                          "description": "How many match, on the first page only; `null` after."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many to return, 100 at most. Defaults to 50.",
            "schema": {
              "type": "integer",
              "description": "How many to return, 100 at most. Defaults to 50."
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from the previous page. Omit for the first page.",
            "schema": {
              "type": "string",
              "description": "Opaque cursor from the previous page. Omit for the first page."
            }
          },
          {
            "name": "folder",
            "in": "query",
            "required": false,
            "description": "The view. Defaults to `inbox`.",
            "schema": {
              "type": "string",
              "description": "The view. Defaults to `inbox`.",
              "enum": [
                "inbox",
                "missions",
                "waiting",
                "sent",
                "done",
                "spam",
                "trials",
                "all"
              ]
            }
          },
          {
            "name": "box",
            "in": "query",
            "required": false,
            "description": "Only conversations on this channel id (`GET /inbox/channels`).",
            "schema": {
              "type": "string",
              "description": "Only conversations on this channel id (`GET /inbox/channels`)."
            }
          },
          {
            "name": "label",
            "in": "query",
            "required": false,
            "description": "Only conversations filed under this label key (`GET /inbox/categories`).",
            "schema": {
              "type": "string",
              "description": "Only conversations filed under this label key (`GET /inbox/categories`)."
            }
          },
          {
            "name": "agent",
            "in": "query",
            "required": false,
            "description": "Only conversations of this AI collaborator (its id).",
            "schema": {
              "type": "string",
              "description": "Only conversations of this AI collaborator (its id)."
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Search in message text, contact name, phone or email, reference and subject.",
            "schema": {
              "type": "string",
              "description": "Search in message text, contact name, phone or email, reference and subject."
            }
          },
          {
            "name": "externalId",
            "in": "query",
            "required": false,
            "description": "Only conversations linked to this record of your software (`link.externalId`), exact. Use `folder=all` to include closed ones.",
            "schema": {
              "type": "string",
              "description": "Only conversations linked to this record of your software (`link.externalId`), exact. Use `folder=all` to include closed ones."
            }
          },
          {
            "name": "reference",
            "in": "query",
            "required": false,
            "description": "Only conversations whose reference or link label equals this, case-insensitive.",
            "schema": {
              "type": "string",
              "description": "Only conversations whose reference or link label equals this, case-insensitive."
            }
          },
          {
            "name": "email",
            "in": "query",
            "required": false,
            "description": "Only conversations of the contact holding this email address, exact.",
            "schema": {
              "type": "string",
              "description": "Only conversations of the contact holding this email address, exact."
            }
          },
          {
            "name": "phone",
            "in": "query",
            "required": false,
            "description": "Only conversations of the contact holding this phone number (any format; digits are compared).",
            "schema": {
              "type": "string",
              "description": "Only conversations of the contact holding this phone number (any format; digits are compared)."
            }
          },
          {
            "name": "sandbox",
            "in": "query",
            "required": false,
            "description": "Trial conversations: leave them out, show only them, or show all.",
            "schema": {
              "type": "string",
              "description": "Trial conversations: leave them out, show only them, or show all.",
              "enum": [
                "exclude",
                "only",
                "all"
              ]
            }
          }
        ]
      },
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Write first to someone who has not written yet",
        "description": "Opens a conversation and sends the first message. **On WhatsApp**, if that person already wrote in the past, their existing thread is reused — one thread per person and channel.\n\n**On email, each call opens a new thread** (a thread is a subject). To add to an existing thread, pass its id as `threadId` (find it with `GET /inbox/conversations?externalId=…&folder=all`) or use `POST /inbox/conversations/{id}/messages`.\n\n**On WhatsApp, only an approved template can be sent here.** The 24-hour service window is closed by definition when nobody has written to you. This is Meta's rule, not ours: free text would be rejected by the gateway after letting you believe it had been sent. Pass `templateId` and its `variables`.\n\nOn email, send `text` and `subject` instead — or `html` for a formatted body (bold, italics, lists, quotes, links; anything else is removed). Attachments: `attachments` lists files your software hosts (fetched by Coplace), and/or one file rides along when the call is `multipart/form-data`: the JSON body in a `payload` field and the file in `file` (4 MB at most).\n\nWith `queue: true` the call returns at once (HTTP 202) and the message leaves within a minute.\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1InboxConversations",
        "responses": {
          "200": {
            "description": "The message as stored, its delivery status, and the id of the conversation it belongs to.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The message, and its conversation.",
                      "properties": {
                        "message": {
                          "$ref": "#/components/schemas/Message"
                        },
                        "duplicate": {
                          "type": "boolean",
                          "description": "`true` when this `idempotencyKey` was already used: the message returned is the first one, nothing was sent again."
                        },
                        "conversationId": {
                          "type": "string",
                          "description": "The conversation it belongs to — new or reused."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`template_required` — WhatsApp: a first message must be an approved template.\n\n`consent_missing` — A marketing template to a contact who opted out.\n\n`idempotency_key_conflict` — Two calls with the same key crossed at the same instant; retry, the first message comes back.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "`file_too_large` — An attachment is over 4 MB, or they total over 10 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "`file_type_refused` — That type of file is not accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`template_holes_unfilled` — A placeholder has no value; `data.issues` lists them.\n\n`template_buttons_invalid` — A button value is missing or does not match the template's buttons; the message says which.\n\n`not_email` — `html`, `cc`, `bcc`, `attachments` and `threadId` only exist on email.\n\n`invalid_recipient` — `to` is not a readable email address or an international number (WhatsApp), or an address in `cc` or `bcc` is unreadable, or there are more than 10.\n\n`invalid_attachment` — An attachment has no `url`, or there are more than 5.\n\n`attachment_unreachable` — An attachment address could not be read; `data.url` names it.\n\n`invalid_thread` — `threadId` is not a thread of this channel with this contact.\n\n`attachment_unsupported` — WhatsApp: a first message is a template, without attachment.\n\n`invalid_link` — `link` is not an object, or has no `externalId`.\n\n`idempotency_key_required` — The idempotency key is missing or outside 8-200 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "channelId": {
                    "type": "string",
                    "description": "Which number or mailbox to speak from. See `GET /inbox/channels`."
                  },
                  "to": {
                    "type": "string",
                    "description": "Phone number in E.164 form, or an email address.",
                    "example": "+33612345678"
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "description": "Your own key, 8 to 200 characters. Sending twice with the same key sends once and returns the first message (`duplicate: true`) — even if the body differs."
                  },
                  "templateId": {
                    "type": "string",
                    "description": "WhatsApp: the approved template to send. This or `templateName` is required there."
                  },
                  "templateName": {
                    "type": "string",
                    "description": "WhatsApp: the template, by its name at Meta, instead of `templateId`."
                  },
                  "templateLanguage": {
                    "type": "string",
                    "description": "With `templateName`: its language code. Default `fr`.",
                    "example": "fr"
                  },
                  "variables": {
                    "type": "object",
                    "description": "WhatsApp: the values filling the template placeholders — by number for `{{1}}`, by name for `{{client_name}}`: `{ \"1\": \"Karim\" }` or `{ \"client_name\": \"Karim\" }`."
                  },
                  "buttons": {
                    "type": "array",
                    "description": "WhatsApp: the values of the template's dynamic buttons: `{ index, type, … }` — `index` is the button's position in the template (from 0), `type` is `url` (with `text`, the link suffix filling `{{1}}`), `quick_reply` (with `payload`) or `copy_code` (with `code`, 15 characters at most). A link button with `{{1}}` and a copy-code button must be given."
                  },
                  "text": {
                    "type": "string",
                    "description": "Email: the body, as plain text."
                  },
                  "html": {
                    "type": "string",
                    "description": "Email: the body as HTML. From an API key, a full layout is kept — tables, inline styles, `https` images (no `<style>` block, no script). From the console, only bold, italics, lists, quotes and links. Cleaned on arrival; `text` is then derived from it."
                  },
                  "withSignature": {
                    "type": "boolean",
                    "description": "Email: `false` leaves the mailbox signature off this message. Default true."
                  },
                  "subject": {
                    "type": "string",
                    "description": "Email: the subject line."
                  },
                  "threadId": {
                    "type": "string",
                    "description": "Email: add this message to that existing thread instead of opening a new one. It must be a thread of this channel with the same contact."
                  },
                  "cc": {
                    "type": "array",
                    "description": "Email only: addresses in copy, visible to the recipient. At most 10.",
                    "items": {
                      "type": "string",
                      "description": "An email address."
                    }
                  },
                  "bcc": {
                    "type": "array",
                    "description": "Email only: addresses in hidden copy.",
                    "items": {
                      "type": "string",
                      "description": "An email address."
                    }
                  },
                  "attachments": {
                    "type": "array",
                    "description": "Email only: up to 5 files your software hosts, fetched by Coplace (public addresses only, 4 MB each, 10 MB in all). A file that cannot be read refuses the whole email (nothing is sent). Sent files are kept in the thread: they come back in the message `attachments`. The call is synchronous — the files are fetched, then the email sent — so it cannot be combined with `queue`.",
                    "items": {
                      "type": "object",
                      "description": "A file to attach.",
                      "properties": {
                        "url": {
                          "type": "string",
                          "description": "Where to fetch it, `https://`."
                        },
                        "name": {
                          "type": "string",
                          "description": "The file name shown to the recipient. Defaults to the end of the address."
                        },
                        "mime": {
                          "type": "string",
                          "description": "Its type, when the host serves a generic one (`application/pdf`)."
                        }
                      },
                      "required": [
                        "url"
                      ]
                    }
                  },
                  "queue": {
                    "type": "boolean",
                    "description": "Return at once and send within a minute (HTTP 202). Default false."
                  },
                  "link": {
                    "type": "object",
                    "description": "Ties the thread to a record in your system, so you can find it back by `GET /inbox/conversations?externalId=…`. A link set by hand in the console is never replaced.",
                    "properties": {
                      "externalId": {
                        "type": "string",
                        "description": "Your identifier for the record — the booking id.",
                        "example": "ca23babb-af88-467d-8a3e-2526e7dc62e8"
                      },
                      "label": {
                        "type": "string",
                        "description": "The reference the customer quotes — the booking reference. Matched case-insensitively.",
                        "example": "2XSRXG9"
                      }
                    },
                    "required": [
                      "externalId"
                    ]
                  }
                },
                "required": [
                  "channelId",
                  "to",
                  "idempotencyKey"
                ]
              }
            }
          }
        }
      }
    },
    "/inbox/conversations/{id}": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "Get one conversation",
        "description": "Returns the conversation with its contact, channel, status, labels, and whether the AI collaborator is answering on it.",
        "operationId": "getApiV1InboxConversationsId}",
        "responses": {
          "200": {
            "description": "The conversation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ]
      }
    },
    "/inbox/conversations/{id}/ai": {
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Let the AI collaborator answer this thread, or stop it",
        "description": "**This is how you choose who replies.** Set `enabled: false` and Coplace's collaborator goes quiet on this conversation: every message still reaches you by webhook, and your software answers. Set it back to `true` to hand the thread over again.\n\nThe setting touches this conversation only — not the collaborator, not the channel, not other threads.\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1InboxConversationsIdAi",
        "responses": {
          "200": {
            "description": "The conversation, with the collaborator on or off.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "description": "True: the collaborator answers. False: it stays silent and you answer."
                  }
                },
                "required": [
                  "enabled"
                ]
              }
            }
          }
        }
      }
    },
    "/inbox/conversations/{id}/assign": {
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Hand a conversation to someone",
        "description": "Assigns the thread to a member of the workspace. **An API key must name the person** — it has no \"me\" to assign to — either with `userId`, or with the `X-Coplace-Acting-User` header (a member id or email), in which case an empty body means \"that person takes it\". Get the candidates from `GET /inbox/members`.\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1InboxConversationsIdAssign",
        "responses": {
          "200": {
            "description": "The conversation, assigned.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`assignee_required` — No target given, and `X-Coplace-Acting-User` is absent or not an active member who answers customers. A key cannot assign a thread to itself.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "userId": {
                    "type": "string",
                    "description": "The workspace member who takes it over; `null` hands it back. Optional with `X-Coplace-Acting-User`."
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/inbox/conversations/{id}/block": {
      "delete": {
        "tags": [
          "Conversations"
        ],
        "summary": "Unblock the contact of a conversation",
        "description": "The contact's threads leave the spam folder and its next messages are received normally.\n\nRequires a key with the `agent` role or above.",
        "operationId": "deleteApiV1InboxConversationsIdBlock",
        "responses": {
          "200": {
            "description": "The conversation, its contact unblocked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ]
      },
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Block the contact of a conversation",
        "description": "Moves every thread of the contact to the spam folder and drops its next messages on arrival, without any answer. Nothing is deleted: `DELETE` undoes it.\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1InboxConversationsIdBlock",
        "responses": {
          "200": {
            "description": "The conversation, its contact blocked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "description": "Why, 300 characters at most."
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/inbox/conversations/{id}/category": {
      "delete": {
        "tags": [
          "Conversations"
        ],
        "summary": "Clear the label of a conversation",
        "description": "Removes the label and lifts the lock: the AI collaborator may file the thread again.\n\nRequires a key with the `agent` role or above.",
        "operationId": "deleteApiV1InboxConversationsIdCategory",
        "responses": {
          "200": {
            "description": "The conversation, without label.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ]
      },
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Put a conversation in a queue",
        "description": "Files the thread under one of the workspace labels (`GET /inbox/categories`), or under none with `category: null`. A human filing locks the label: the AI collaborator can no longer move it (`DELETE` lifts the lock).\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1InboxConversationsIdCategory",
        "responses": {
          "200": {
            "description": "The conversation, filed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`invalid_category` — The label does not exist in the workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "category": {
                    "type": "string",
                    "description": "The label key, or `null` for no queue."
                  },
                  "always": {
                    "type": "boolean",
                    "description": "Also file this sender's next messages there. Needs a person: ignored without `X-Coplace-Acting-User`."
                  }
                },
                "required": [
                  "category"
                ]
              }
            }
          }
        }
      }
    },
    "/inbox/conversations/{id}/link": {
      "delete": {
        "tags": [
          "Conversations"
        ],
        "summary": "Detach the record of a conversation",
        "description": "Clears the link and lifts its lock. Nothing is deleted in your software.\n\nRequires a key with the `agent` role or above.",
        "operationId": "deleteApiV1InboxConversationsIdLink",
        "responses": {
          "200": {
            "description": "The conversation, unlinked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ]
      },
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Attach a record to a conversation",
        "description": "Attaches the record of your software (a booking, an order) to an existing thread. It locks the link: no automatic rule replaces it afterwards. To link a thread at the moment you open it, pass `link` to `POST /inbox/conversations` instead. Find it back with `GET /inbox/conversations?externalId=…`.\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1InboxConversationsIdLink",
        "responses": {
          "200": {
            "description": "The conversation, linked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "externalId": {
                    "type": "string",
                    "description": "Your identifier for the record — the booking id."
                  },
                  "label": {
                    "type": "string",
                    "description": "The reference the customer quotes — the booking reference."
                  },
                  "url": {
                    "type": "string",
                    "description": "A link to the record, `http://` or `https://`."
                  },
                  "payload": {
                    "type": "object",
                    "description": "A display copy of the record, 64 KB at most. Never the source of truth."
                  }
                },
                "required": [
                  "externalId"
                ]
              }
            }
          }
        }
      }
    },
    "/inbox/conversations/{id}/media": {
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Send a file in a conversation",
        "description": "Uploads an image, video, audio, document or voice note and sends it. Send it as `multipart/form-data`.\n\nThe 24-hour window applies as it does to text. Files are at most 4 MB; Coplace keeps its own copy so it stays readable after WhatsApp deletes theirs.\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1InboxConversationsIdMedia",
        "responses": {
          "200": {
            "description": "The message as stored, with its delivery status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SendResult"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`window_closed` — More than 24 hours since the customer last wrote.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`caption_unsupported` — WhatsApp shows no caption under audio — send the text separately.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "description": "The file itself, as a multipart part."
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "description": "Your own key, 8 to 200 characters."
                  },
                  "caption": {
                    "type": "string",
                    "description": "A caption shown under the file. WhatsApp shows none under audio."
                  },
                  "voice": {
                    "type": "boolean",
                    "description": "True to send audio as a voice note, with its waveform."
                  }
                },
                "required": [
                  "file",
                  "idempotencyKey"
                ]
              }
            }
          }
        }
      }
    },
    "/inbox/conversations/{id}/messages": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "List the messages of a conversation",
        "description": "Returns messages oldest first; paging walks back into the past.\n\nAn email, received or sent, whose layout was kept carries `hasHtml: true`; fetch the HTML separately with `GET /inbox/messages/{id}/html` — it is never inlined in the thread.",
        "operationId": "getApiV1InboxConversationsIdMessages",
        "responses": {
          "200": {
            "description": "A page of messages.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "A page of messages, oldest to newest.",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "The messages, oldest first.",
                          "items": {
                            "$ref": "#/components/schemas/Message"
                          }
                        },
                        "nextCursor": {
                          "type": "string",
                          "description": "Pass it as `cursor` to go further back; `null` at the start of the thread."
                        },
                        "hasMore": {
                          "type": "boolean",
                          "description": "Whether older messages exist."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many messages to return.",
            "schema": {
              "type": "integer",
              "description": "How many messages to return."
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `nextCursor` of the previous page, to go further back.",
            "schema": {
              "type": "string",
              "description": "The `nextCursor` of the previous page, to go further back."
            }
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "Delta mode: an ISO date or an epoch (seconds or milliseconds). Returns what arrived after it, plus the outbound messages of the last 7 days not yet `read` or `failed`, so their ticks refresh. Merge by `id`; use the newest `createdAt` you hold as the next `since`. `hasMore: true` means more than `limit` changed: raise `limit`. `cursor` is ignored.",
            "schema": {
              "type": "string",
              "description": "Delta mode: an ISO date or an epoch (seconds or milliseconds). Returns what arrived after it, plus the outbound messages of the last 7 days not yet `read` or `failed`, so their ticks refresh. Merge by `id`; use the newest `createdAt` you hold as the next `since`. `hasMore: true` means more than `limit` changed: raise `limit`. `cursor` is ignored."
            }
          }
        ]
      },
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Reply in a conversation",
        "description": "Sends free text, quick-reply buttons, a list, or a location request.\n\n**The 24-hour window is enforced here.** On WhatsApp you may only send free text within 24 hours of the customer's last message. Outside it you get `window_closed` and must send an approved template instead (`POST /inbox/conversations/{id}/template`). Interactive forms count as free text for Meta — they are refused outside the window too.\n\nThe message is attributed to your key: the conversation shows the key's label, never a person who did nothing.\n\nWith `queue: true` the call returns at once with the message `queued` (HTTP 202) and it leaves within a minute; follow it with the `message.sent` / `message.failed` webhooks. Use it when you send many messages and do not want to wait for each round trip to WhatsApp.\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1InboxConversationsIdMessages",
        "responses": {
          "200": {
            "description": "The message as stored, with its delivery status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SendResult"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`window_closed` — More than 24 hours since the customer last wrote. Send an approved template instead.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "`file_too_large` — An attachment is over 4 MB, or they total over 10 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "`file_type_refused` — An attachment type is not accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`no_recipient` — This contact has no usable phone number or email address.\n\n`idempotency_key_required` — The idempotency key is missing or outside 8-200 characters.\n\n`invalid_form` — The form breaks a WhatsApp limit; `detail` says which.\n\n`not_whatsapp` — Forms only exist on WhatsApp.\n\n`not_email` — `html`, `cc`, `bcc` and `attachments` only exist on email.\n\n`invalid_recipient` — An address in `cc` or `bcc` is unreadable, or there are more than 10.\n\n`invalid_attachment` — An attachment has no `url`, or there are more than 5.\n\n`attachment_unreachable` — An attachment address could not be read; `data.url` names it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string",
                    "description": "What to say, as plain text. Also the readable body when you send a form. Required unless `html` is given."
                  },
                  "html": {
                    "type": "string",
                    "description": "Email: the body as HTML. From an API key, a full layout is kept — tables, inline styles, `https` images (no `<style>` block, no script). From the console, only bold, italics, lists, quotes and links. Cleaned on arrival; `text` is then derived from it."
                  },
                  "withSignature": {
                    "type": "boolean",
                    "description": "Email only: `false` leaves the mailbox signature off this message. Default true."
                  },
                  "subject": {
                    "type": "string",
                    "description": "Email only: the subject line. Defaults to the thread subject."
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "description": "Your own key, 8 to 200 characters. Sending twice with the same key sends once."
                  },
                  "form": {
                    "type": "object",
                    "description": "An interactive form, WhatsApp only: up to 3 buttons, a list of up to 10 rows, or a location request.",
                    "properties": {
                      "kind": {
                        "type": "string",
                        "description": "Which form.",
                        "enum": [
                          "buttons",
                          "list",
                          "location_request"
                        ]
                      },
                      "body": {
                        "type": "string",
                        "description": "The text above the form, 1024 characters at most. Defaults to `text`."
                      },
                      "header": {
                        "type": "string",
                        "description": "A line above the body, 60 characters at most."
                      },
                      "footer": {
                        "type": "string",
                        "description": "A line below the body, 60 characters at most."
                      },
                      "buttons": {
                        "type": "array",
                        "description": "For `buttons`: 1 to 3. The reply you receive carries the chosen `id`.",
                        "items": {
                          "type": "object",
                          "description": "A button.",
                          "properties": {
                            "id": {
                              "type": "string",
                              "description": "Your identifier, returned when tapped."
                            },
                            "title": {
                              "type": "string",
                              "description": "20 characters at most."
                            }
                          },
                          "required": [
                            "title"
                          ]
                        }
                      },
                      "buttonLabel": {
                        "type": "string",
                        "description": "For `list`: the label of the button that opens it, 20 characters at most."
                      },
                      "sections": {
                        "type": "array",
                        "description": "For `list`: sections of rows, 10 rows in all.",
                        "items": {
                          "type": "object",
                          "description": "A section.",
                          "properties": {
                            "title": {
                              "type": "string",
                              "description": "The section title."
                            },
                            "rows": {
                              "type": "array",
                              "description": "Its rows.",
                              "items": {
                                "type": "object",
                                "description": "A row.",
                                "properties": {
                                  "id": {
                                    "type": "string",
                                    "description": "Your identifier, returned when chosen."
                                  },
                                  "title": {
                                    "type": "string",
                                    "description": "24 characters at most."
                                  },
                                  "description": {
                                    "type": "string",
                                    "description": "72 characters at most."
                                  }
                                },
                                "required": [
                                  "title"
                                ]
                              }
                            }
                          }
                        }
                      }
                    },
                    "required": [
                      "kind"
                    ]
                  },
                  "replyToId": {
                    "type": "string",
                    "description": "Quote an earlier message of this thread."
                  },
                  "cc": {
                    "type": "array",
                    "description": "Email only: addresses in copy, visible to the recipient. At most 10.",
                    "items": {
                      "type": "string",
                      "description": "An email address."
                    }
                  },
                  "bcc": {
                    "type": "array",
                    "description": "Email only: addresses in hidden copy.",
                    "items": {
                      "type": "string",
                      "description": "An email address."
                    }
                  },
                  "attachments": {
                    "type": "array",
                    "description": "Email only: up to 5 files your software hosts, fetched by Coplace (public addresses only, 4 MB each, 10 MB in all). A file that cannot be read refuses the whole email (nothing is sent). Sent files are kept in the thread: they come back in the message `attachments`. The call is synchronous — the files are fetched, then the email sent — so it cannot be combined with `queue`.",
                    "items": {
                      "type": "object",
                      "description": "A file to attach.",
                      "properties": {
                        "url": {
                          "type": "string",
                          "description": "Where to fetch it, `https://`."
                        },
                        "name": {
                          "type": "string",
                          "description": "The file name shown to the recipient. Defaults to the end of the address."
                        },
                        "mime": {
                          "type": "string",
                          "description": "Its type, when the host serves a generic one (`application/pdf`)."
                        }
                      },
                      "required": [
                        "url"
                      ]
                    }
                  },
                  "queue": {
                    "type": "boolean",
                    "description": "Return at once and send within a minute (HTTP 202). Default false."
                  }
                },
                "required": [
                  "idempotencyKey"
                ]
              }
            }
          }
        }
      }
    },
    "/inbox/conversations/{id}/needs-human": {
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Flag a conversation as needing a person",
        "description": "Raises the thread in the Coplace inbox so an advisor picks it up, and silences the AI collaborator on it. Use it when your software decides it cannot handle a case on its own. `false` clears the flag but does not wake the collaborator (`POST /inbox/conversations/{id}/ai`).\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1InboxConversationsIdNeedsHuman",
        "responses": {
          "200": {
            "description": "The conversation, flagged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`invalid_request` — `needsHuman` is missing or not a boolean.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "needsHuman": {
                    "type": "boolean",
                    "description": "`true` calls a person (the AI collaborator goes quiet on this thread); `false` says it is handled."
                  }
                },
                "required": [
                  "needsHuman"
                ]
              }
            }
          }
        }
      }
    },
    "/inbox/conversations/{id}/read": {
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Mark a conversation as read",
        "description": "Clears the unread count. On WhatsApp this also sends a read receipt to the customer — their two ticks turn blue — for the last message they sent.\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1InboxConversationsIdRead",
        "responses": {
          "200": {
            "description": "The conversation, with its unread count back to zero.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ]
      }
    },
    "/inbox/conversations/{id}/status": {
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Change the state of a conversation",
        "description": "Moves a conversation between open, pending and closed. Closing does not stop anything: a new message from the customer reopens the thread by itself.\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1InboxConversationsIdStatus",
        "responses": {
          "200": {
            "description": "The conversation in its new state.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "description": "The new state.",
                    "enum": [
                      "open",
                      "pending",
                      "closed"
                    ]
                  }
                },
                "required": [
                  "status"
                ]
              }
            }
          }
        }
      }
    },
    "/inbox/conversations/{id}/template": {
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Send an approved template",
        "description": "**The only thing that reaches a WhatsApp customer outside the 24-hour window.** A template is text Meta reviewed and approved in advance; its placeholders are filled at send time.\n\nList what is available with `GET /templates`. A template that is not yet approved is refused.\n\nA **marketing** template is refused for a contact who opted out — on WhatsApp by tapping \"Stop\", or through `PATCH /contacts/{id}` — with `consent_missing`.\n\nWith `queue: true` the call returns at once (HTTP 202) and the message leaves within a minute.\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1InboxConversationsIdTemplate",
        "responses": {
          "200": {
            "description": "The message as stored, with the rendered text and its delivery status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/SendResult"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`template_not_applicable` — Templates only exist on WhatsApp.\n\n`consent_missing` — A marketing template to a contact who opted out.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`template_not_approved` — The template is not approved by Meta yet.\n\n`template_holes_unfilled` — A placeholder has no value; `data.issues` lists them.\n\n`template_buttons_invalid` — A button value is missing or does not match the template's buttons; the message says which.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "templateId": {
                    "type": "string",
                    "description": "The approved template to send."
                  },
                  "variables": {
                    "type": "object",
                    "description": "The values filling its placeholders: by number for `{{1}}` (`{ \"1\": \"Karim\" }`), by name for `{{client_name}}` (`{ \"client_name\": \"Karim\" }`). A named placeholder left out takes the field of the same name from the record linked to the conversation."
                  },
                  "buttons": {
                    "type": "array",
                    "description": "WhatsApp: the values of the template's dynamic buttons: `{ index, type, … }` — `index` is the button's position in the template (from 0), `type` is `url` (with `text`, the link suffix filling `{{1}}`), `quick_reply` (with `payload`) or `copy_code` (with `code`, 15 characters at most). A link button with `{{1}}` and a copy-code button must be given."
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "description": "Your own key, 8 to 200 characters."
                  },
                  "queue": {
                    "type": "boolean",
                    "description": "Return at once and send within a minute (HTTP 202). Default false."
                  }
                },
                "required": [
                  "templateId",
                  "idempotencyKey"
                ]
              }
            }
          }
        }
      }
    },
    "/inbox/conversations/{id}/typing": {
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Show \"typing…\" to the customer",
        "description": "Shows the typing indicator in the customer's WhatsApp while your software prepares the answer. It disappears when your message arrives, or after 25 seconds — call it again for a longer wait.\n\nWhatsApp ties it to the read receipt: the customer's last message also turns blue. Nothing changes in Coplace.\n\nRequires a key with the `agent` role or above.",
        "operationId": "postApiV1InboxConversationsIdTyping",
        "responses": {
          "200": {
            "description": "Whether the indicator was shown.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Whether the indicator was shown.",
                      "properties": {
                        "shown": {
                          "type": "boolean",
                          "description": "`false` when the customer has not written yet: there is nothing to attach it to."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`not_whatsapp` — Only WhatsApp has a typing indicator.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "`upstream_error` — WhatsApp refused it; `detail` carries its reason.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The conversation id.",
            "schema": {
              "type": "string",
              "description": "The conversation id."
            }
          }
        ]
      }
    },
    "/inbox/members": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "List the people who can take a conversation",
        "description": "Returns the active members of the workspace, to be used as the target of an assignment. A key sees every member: it belongs to no team.",
        "operationId": "getApiV1InboxMembers",
        "responses": {
          "200": {
            "description": "The members, with their id, name and role.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The members.",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "The members.",
                          "items": {
                            "$ref": "#/components/schemas/Member"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/inbox/overview": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "Count the folders, labels and mailboxes",
        "description": "The numbers of the inbox sidebar in one call: how many threads and how many unread per folder, per label, and per channel (`boxes`, joined to `GET /inbox/channels` by `channelId` — group by channel `type` for a per-network total).\n\nEach count is exactly what the same folder returns in `GET /inbox/conversations`. Trials and blocked contacts never count. `mine` and `starred` are personal: a key has no \"me\", they are `0`.",
        "operationId": "getApiV1InboxOverview",
        "responses": {
          "200": {
            "description": "The counts by folder, label and channel.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The counts.",
                      "properties": {
                        "folders": {
                          "type": "object",
                          "description": "Counts per folder.",
                          "properties": {
                            "inbox": {
                              "type": "object",
                              "description": "Open threads of the folder.",
                              "properties": {
                                "total": {
                                  "type": "integer",
                                  "description": "How many threads."
                                },
                                "unread": {
                                  "type": "integer",
                                  "description": "How many have unread messages."
                                }
                              }
                            },
                            "mine": {
                              "type": "object",
                              "description": "Open threads of the folder.",
                              "properties": {
                                "total": {
                                  "type": "integer",
                                  "description": "How many threads."
                                },
                                "unread": {
                                  "type": "integer",
                                  "description": "How many have unread messages."
                                }
                              }
                            },
                            "missions": {
                              "type": "object",
                              "description": "Open threads of the folder.",
                              "properties": {
                                "total": {
                                  "type": "integer",
                                  "description": "How many threads."
                                },
                                "unread": {
                                  "type": "integer",
                                  "description": "How many have unread messages."
                                }
                              }
                            },
                            "waiting": {
                              "type": "object",
                              "description": "Open threads of the folder.",
                              "properties": {
                                "total": {
                                  "type": "integer",
                                  "description": "How many threads."
                                },
                                "unread": {
                                  "type": "integer",
                                  "description": "How many have unread messages."
                                }
                              }
                            },
                            "starred": {
                              "type": "object",
                              "description": "Open threads of the folder.",
                              "properties": {
                                "total": {
                                  "type": "integer",
                                  "description": "How many threads."
                                },
                                "unread": {
                                  "type": "integer",
                                  "description": "How many have unread messages."
                                }
                              }
                            }
                          }
                        },
                        "labels": {
                          "type": "array",
                          "description": "The labels, with their counts.",
                          "items": {
                            "type": "object",
                            "description": "A label.",
                            "properties": {
                              "key": {
                                "type": "string",
                                "description": "The label key."
                              },
                              "name": {
                                "type": "string",
                                "description": "Its display name."
                              },
                              "unread": {
                                "type": "integer",
                                "description": "Open threads with unread messages."
                              },
                              "recent": {
                                "type": "integer",
                                "description": "Threads active in the last 30 days."
                              }
                            }
                          }
                        },
                        "boxes": {
                          "type": "array",
                          "description": "Unread open threads per channel.",
                          "items": {
                            "type": "object",
                            "description": "A channel count.",
                            "properties": {
                              "channelId": {
                                "type": "string",
                                "description": "The channel id."
                              },
                              "unread": {
                                "type": "integer",
                                "description": "Open threads with unread messages."
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/inbox/messages/{id}/html": {
      "get": {
        "tags": [
          "Messages"
        ],
        "summary": "Get the layout of a received email",
        "description": "Returns the cleaned HTML of an email, kept apart from the thread so listing messages stays light. A message with no kept layout returns 404.",
        "operationId": "getApiV1InboxMessagesIdHtml",
        "responses": {
          "200": {
            "description": "`{ html }`, sanitised.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The email layout.",
                      "properties": {
                        "html": {
                          "type": "string",
                          "description": "Sanitised HTML, without scripts."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The message id.",
            "schema": {
              "type": "string",
              "description": "The message id."
            }
          }
        ]
      }
    },
    "/inbox/messages/{id}/media": {
      "get": {
        "tags": [
          "Messages"
        ],
        "summary": "Download the file attached to a message",
        "description": "Returns the file itself. A WhatsApp file is read from the gateway that keeps it; once the gateway no longer has it, the route answers 410 `media_expired`.",
        "operationId": "getApiV1InboxMessagesIdMedia",
        "responses": {
          "200": {
            "description": "The file, with its content type.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "string",
                      "description": "The file itself — not wrapped in `data`. Its type is in `Content-Type`."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The message id.",
            "schema": {
              "type": "string",
              "description": "The message id."
            }
          },
          {
            "name": "piece",
            "in": "query",
            "required": false,
            "description": "For an email with attachments, which one (0-based).",
            "schema": {
              "type": "integer",
              "description": "For an email with attachments, which one (0-based)."
            }
          }
        ]
      }
    },
    "/health": {
      "get": {
        "tags": [
          "Status"
        ],
        "summary": "Is everything working",
        "description": "Returns the state of the workspace and what is wrong, if anything: a channel that has gone silent, one missing its credentials, a number whose quality is red, a queue that is not moving. Each finding says what it is and how serious it is.\n\nPoll this rather than guessing from failures.\n\nRequires a key with the `admin` role or above.",
        "operationId": "getApiV1Health",
        "responses": {
          "200": {
            "description": "An overall status and the list of findings.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The state of the workspace.",
                      "properties": {
                        "healthy": {
                          "type": "boolean",
                          "description": "`true` when nothing is wrong."
                        },
                        "alerts": {
                          "type": "array",
                          "description": "What is wrong, most serious first.",
                          "items": {
                            "type": "object",
                            "description": "A finding.",
                            "properties": {
                              "level": {
                                "type": "string",
                                "description": "How serious.",
                                "enum": [
                                  "warning",
                                  "critical"
                                ]
                              },
                              "code": {
                                "type": "string",
                                "description": "A stable code, e.g. `channel_silent`."
                              },
                              "message": {
                                "type": "string",
                                "description": "What it is, in words."
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/usage": {
      "get": {
        "tags": [
          "Status"
        ],
        "summary": "What has been consumed",
        "description": "Returns monthly counters for this workspace — conversations handled by the collaborator, messages in and out, templates sent — over the last 3 months (`?months=` 1 to 12).\n\nPlan limits are counted across the whole client account; a key only ever sees its own workspace's counters.\n\nRequires a key with the `admin` role or above.",
        "operationId": "getApiV1Usage",
        "responses": {
          "200": {
            "description": "The counters and the limits they run against.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Monthly counters.",
                      "properties": {
                        "months": {
                          "type": "array",
                          "description": "The months covered, `YYYY-MM`.",
                          "items": {
                            "type": "string",
                            "description": "A month."
                          }
                        },
                        "workspaces": {
                          "type": "array",
                          "description": "For a key: its own workspace only.",
                          "items": {
                            "type": "object",
                            "description": "A workspace and its counters.",
                            "properties": {
                              "id": {
                                "type": "string",
                                "description": "The workspace id."
                              },
                              "byMonth": {
                                "type": "array",
                                "description": "Per month: `conversations` handled by the collaborator, `messagesIn`, `messagesOut` (counted as the plan counts its monthly messages: sent from Coplace, outside tests), `templates`, `aiCalls`.",
                                "items": {
                                  "type": "object",
                                  "description": "A month."
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "months",
            "in": "query",
            "required": false,
            "description": "How many months back, 1 to 12. Default 3.",
            "schema": {
              "type": "integer",
              "description": "How many months back, 1 to 12. Default 3."
            }
          }
        ]
      }
    },
    "/templates": {
      "get": {
        "tags": [
          "Templates"
        ],
        "summary": "List message templates",
        "description": "Returns the templates of the workspace with their approval state: approved ones can be sent outside the 24-hour window, the others cannot yet.\n\nA template is identified at Meta by its name AND its language — the same name in two languages is two templates.",
        "operationId": "getApiV1Templates",
        "responses": {
          "200": {
            "description": "The templates, with their status, category, body and placeholders.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The templates.",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "The templates, recently changed first.",
                          "items": {
                            "$ref": "#/components/schemas/Template"
                          }
                        },
                        "summary": {
                          "type": "object",
                          "description": "Counts: `total`, `draft`, `submitted`, `approved`, `rejected`."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Templates"
        ],
        "summary": "Draft a template",
        "description": "Writes a template on the Coplace side. **Nothing is sent to Meta yet** — drafting and submitting are two steps on purpose, because submission reserves the name at Meta and cannot be undone. Submit with `POST /templates/{id}/submit`.\n\nRequires a key with the `admin` role or above.",
        "operationId": "postApiV1Templates",
        "responses": {
          "200": {
            "description": "The template as drafted, not yet submitted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The template.",
                      "properties": {
                        "template": {
                          "$ref": "#/components/schemas/Template"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Lowercase letters, digits and underscores. Reserved at Meta on submission."
                  },
                  "language": {
                    "type": "string",
                    "description": "Language tag, for example `fr` or `en_US`.",
                    "example": "fr"
                  },
                  "category": {
                    "type": "string",
                    "description": "What Meta charges it as.",
                    "enum": [
                      "UTILITY",
                      "MARKETING",
                      "AUTHENTICATION"
                    ]
                  },
                  "body": {
                    "type": "string",
                    "description": "The text, with `{{1}}` style placeholders."
                  },
                  "header": {
                    "type": "string",
                    "description": "An optional one-line header."
                  },
                  "footer": {
                    "type": "string",
                    "description": "An optional footer."
                  },
                  "buttons": {
                    "type": "array",
                    "description": "Up to three buttons: url, quick reply, or copy code.",
                    "items": {
                      "type": "object",
                      "description": "A button."
                    }
                  }
                },
                "required": [
                  "name",
                  "language",
                  "category",
                  "body"
                ]
              }
            }
          }
        }
      }
    },
    "/templates/{id}": {
      "get": {
        "tags": [
          "Templates"
        ],
        "summary": "Get one template",
        "description": "Returns the template with its current approval state and full definition.",
        "operationId": "getApiV1TemplatesId}",
        "responses": {
          "200": {
            "description": "The template.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The template and how it is used.",
                      "properties": {
                        "template": {
                          "$ref": "#/components/schemas/Template"
                        },
                        "editable": {
                          "type": "boolean",
                          "description": "Whether it can still be changed (draft or rejected)."
                        },
                        "usage": {
                          "type": "object",
                          "description": "`{ sent, skipped }`: how often it went out."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The template id.",
            "schema": {
              "type": "string",
              "description": "The template id."
            }
          }
        ]
      },
      "patch": {
        "tags": [
          "Templates"
        ],
        "summary": "Edit a draft template",
        "description": "Changes a template that has not been submitted. Once submitted, its name and language are fixed at Meta — edit the text and submit again as a new version instead.\n\nRequires a key with the `admin` role or above.",
        "operationId": "patchApiV1TemplatesId}",
        "responses": {
          "200": {
            "description": "The template, updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The template.",
                      "properties": {
                        "template": {
                          "$ref": "#/components/schemas/Template"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The template id.",
            "schema": {
              "type": "string",
              "description": "The template id."
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "body": {
                    "type": "string",
                    "description": "The text."
                  },
                  "header": {
                    "type": "string",
                    "description": "The header line."
                  },
                  "footer": {
                    "type": "string",
                    "description": "The footer."
                  },
                  "category": {
                    "type": "string",
                    "description": "What Meta charges it as.",
                    "enum": [
                      "UTILITY",
                      "MARKETING",
                      "AUTHENTICATION"
                    ]
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/templates/{id}/refresh": {
      "post": {
        "tags": [
          "Templates"
        ],
        "summary": "Ask Meta where a submitted template stands",
        "description": "Re-reads the approval state at Meta and stores it. Returns `changed: false` when nothing moved, so polling is cheap.\n\nRequires a key with the `admin` role or above.",
        "operationId": "postApiV1TemplatesIdRefresh",
        "responses": {
          "200": {
            "description": "The template and whether its state changed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The template, re-read from Meta.",
                      "properties": {
                        "template": {
                          "$ref": "#/components/schemas/Template"
                        },
                        "changed": {
                          "type": "boolean",
                          "description": "Whether its state changed."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`never_submitted` — This template was never submitted — there is nothing to ask about.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The template id.",
            "schema": {
              "type": "string",
              "description": "The template id."
            }
          }
        ]
      }
    },
    "/templates/{id}/submit": {
      "post": {
        "tags": [
          "Templates"
        ],
        "summary": "Submit a template to Meta for approval",
        "description": "**External and final.** The name is reserved at Meta and a submitted template cannot be withdrawn this way.\n\nMeta almost always answers `submitted` (pending) and reviews later — poll with `POST /templates/{id}/refresh`. A rejection comes back with Meta's own reason.\n\nRequires a key with the `admin` role or above.",
        "operationId": "postApiV1TemplatesIdSubmit",
        "responses": {
          "200": {
            "description": "The template with its new state: submitted, or rejected with a reason.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The template.",
                      "properties": {
                        "template": {
                          "$ref": "#/components/schemas/Template"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The template id.",
            "schema": {
              "type": "string",
              "description": "The template id."
            }
          }
        ]
      }
    },
    "/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List your webhook subscriptions",
        "description": "Returns where Coplace calls you and for which events, plus the list of every event name you can subscribe to. Secrets are never returned.\n\nRequires a key with the `admin` role or above.",
        "operationId": "getApiV1Webhooks",
        "responses": {
          "200": {
            "description": "The subscriptions, the event catalogue and the latest deliveries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Subscriptions and the event catalogue.",
                      "properties": {
                        "events": {
                          "type": "array",
                          "description": "Every event name you can subscribe to.",
                          "items": {
                            "type": "string",
                            "description": "An event name."
                          }
                        },
                        "subscriptions": {
                          "type": "array",
                          "description": "Your subscriptions, newest first.",
                          "items": {
                            "$ref": "#/components/schemas/WebhookSubscription"
                          }
                        },
                        "deliveries": {
                          "type": "array",
                          "description": "The 50 most recent deliveries. The full log is `GET /webhooks/deliveries`.",
                          "items": {
                            "$ref": "#/components/schemas/WebhookDelivery"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Subscribe to events",
        "description": "Registers an HTTPS address that Coplace calls when something happens. **Without this your software has to poll**, which is slower and heavier for both of us.\n\nThe response carries the signing secret **once and never again**: store it now. Each call is signed with it, and is retried up to six times with growing delays (1 min, 5 min, 30 min, 2 h, 12 h).\n\nRequires a key with the `admin` role or above.",
        "operationId": "postApiV1Webhooks",
        "responses": {
          "200": {
            "description": "The subscription id and its signing secret — shown only here.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The new subscription.",
                      "properties": {
                        "id": {
                          "type": "string",
                          "description": "The subscription id."
                        },
                        "secret": {
                          "type": "string",
                          "description": "The signing secret, `whsec_…`. Shown only here."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`webhook_url_not_https` — The address must start with https://.\n\n`invalid_request` — No event, or an unknown event name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "Where to call. Must start with https://."
                  },
                  "events": {
                    "type": "array",
                    "description": "Which events to receive. `*` means all of them.",
                    "items": {
                      "type": "string",
                      "description": "An event name."
                    }
                  },
                  "label": {
                    "type": "string",
                    "description": "A name for you, shown in Coplace."
                  }
                },
                "required": [
                  "url",
                  "events"
                ]
              }
            }
          }
        }
      }
    },
    "/webhooks/{id}": {
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Unsubscribe",
        "description": "Removes the subscription. Nothing more is delivered to that address.\n\nRequires a key with the `admin` role or above.",
        "operationId": "deleteApiV1WebhooksId}",
        "responses": {
          "200": {
            "description": "Confirmation that it is gone.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "Confirmation.",
                      "properties": {
                        "ok": {
                          "type": "boolean",
                          "description": "Always `true`."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription id.",
            "schema": {
              "type": "string",
              "description": "The subscription id."
            }
          }
        ]
      },
      "patch": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Change a subscription",
        "description": "Moves the address, changes the events, renames, or pauses the subscription. Send only the fields you change. The secret does not change — rotate it with `POST /webhooks/{id}/rotate-secret`.\n\nRequires a key with the `admin` role or above.",
        "operationId": "patchApiV1WebhooksId}",
        "responses": {
          "200": {
            "description": "The subscription, updated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookSubscription"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`webhook_url_not_https` — The address must start with https://.\n\n`invalid_request` — Nothing to change, an unknown event, or a field of the wrong type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription id.",
            "schema": {
              "type": "string",
              "description": "The subscription id."
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "A new https address."
                  },
                  "events": {
                    "type": "array",
                    "description": "The events to receive from now on. `*` means all of them.",
                    "items": {
                      "type": "string",
                      "description": "An event name."
                    }
                  },
                  "label": {
                    "type": "string",
                    "description": "A name for you, shown in Coplace."
                  },
                  "isActive": {
                    "type": "boolean",
                    "description": "False pauses delivery without losing the subscription."
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/webhooks/{id}/rotate-secret": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Rotate the signing secret",
        "description": "Issues a new signing secret, returned **once**. For the next 24 hours every call carries two signatures — `v1=` with the new secret, then `v1=` with the old one — so you can deploy the new secret without missing a call. Accept a call when **any** `v1` matches.\n\nRotating twice in a row forgets the oldest secret straight away.\n\nRequires a key with the `admin` role or above.",
        "operationId": "postApiV1WebhooksIdRotateSecret",
        "responses": {
          "200": {
            "description": "The new secret, and until when the old one still signs.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The new secret.",
                      "properties": {
                        "secret": {
                          "type": "string",
                          "description": "The new signing secret, `whsec_…`. Shown only here."
                        },
                        "previousValidUntil": {
                          "type": "string",
                          "description": "ISO date until which the old secret still signs."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription id.",
            "schema": {
              "type": "string",
              "description": "The subscription id."
            }
          }
        ]
      }
    },
    "/webhooks/{id}/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Send a test call",
        "description": "Fires one signed call at your address right now and reports what came back: status code, round trip, and the error if there was one. Use it to prove your endpoint and your signature check before going live.\n\nRequires a key with the `admin` role or above.",
        "operationId": "postApiV1WebhooksIdTest",
        "responses": {
          "200": {
            "description": "What your endpoint answered: status, duration, and any error.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "What your endpoint answered.",
                      "properties": {
                        "ok": {
                          "type": "boolean",
                          "description": "Whether it answered 2xx."
                        },
                        "status": {
                          "type": "integer",
                          "description": "The HTTP status, or `null` when unreachable."
                        },
                        "ms": {
                          "type": "integer",
                          "description": "The round trip."
                        },
                        "error": {
                          "type": "string",
                          "description": "What went wrong, or `null`."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The subscription id.",
            "schema": {
              "type": "string",
              "description": "The subscription id."
            }
          }
        ]
      }
    },
    "/webhooks/deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List deliveries",
        "description": "Every call Coplace made — or is about to make — to your addresses: which event, how many attempts, what your server answered, and when the next attempt is due. Newest first.\n\nThis is what settles \"we never received it\": a `failed` delivery with `lastError` tells you what your endpoint said; no delivery at all means the event did not happen.\n\nRequires a key with the `admin` role or above.",
        "operationId": "getApiV1WebhooksDeliveries",
        "responses": {
          "200": {
            "description": "A page of deliveries, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "A page of deliveries.",
                      "properties": {
                        "items": {
                          "type": "array",
                          "description": "The deliveries.",
                          "items": {
                            "$ref": "#/components/schemas/WebhookDelivery"
                          }
                        },
                        "nextCursor": {
                          "type": "string",
                          "description": "Pass it as `cursor` for the next page; `null` at the end."
                        },
                        "hasMore": {
                          "type": "boolean",
                          "description": "Whether another page exists."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found, or outside this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "subscriptionId",
            "in": "query",
            "required": false,
            "description": "Only this subscription.",
            "schema": {
              "type": "string",
              "description": "Only this subscription."
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Only deliveries in this state.",
            "schema": {
              "type": "string",
              "description": "Only deliveries in this state.",
              "enum": [
                "pending",
                "delivered",
                "failed"
              ]
            }
          },
          {
            "name": "event",
            "in": "query",
            "required": false,
            "description": "Only this event, e.g. `message.received`.",
            "schema": {
              "type": "string",
              "description": "Only this event, e.g. `message.received`."
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size, 1 to 100. Default 50.",
            "schema": {
              "type": "integer",
              "description": "Page size, 1 to 100. Default 50."
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `nextCursor` of the previous page.",
            "schema": {
              "type": "string",
              "description": "The `nextCursor` of the previous page."
            }
          }
        ]
      }
    },
    "/webhooks/deliveries/{id}/redeliver": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Redeliver an event",
        "description": "Queues the same event again for the same subscription — typically after your endpoint was down and the delivery ended `failed`. It goes out within a minute, with fresh retries.\n\nThe payload is identical, **including its `id`**: if you already processed it, your deduplication on `id` will skip it.\n\nRequires a key with the `admin` role or above.",
        "operationId": "postApiV1WebhooksDeliveriesIdRedeliver",
        "responses": {
          "200": {
            "description": "The new delivery, pending.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookDelivery"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed or revoked API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key does not have the required role.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not_found` — No such delivery in this workspace, or its subscription is paused or gone.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — 600 calls per minute per key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The delivery id.",
            "schema": {
              "type": "string",
              "description": "The delivery id."
            }
          }
        ]
      }
    }
  },
  "x-webhook-events": [
    "message.received",
    "message.sent",
    "message.delivered",
    "message.read",
    "message.failed",
    "conversation.opened",
    "conversation.closed",
    "contact.opted_in",
    "contact.opted_out",
    "approval.requested",
    "approval.decided",
    "mission.closed",
    "timer.fired"
  ],
  "x-error-codes": {
    "bad_request": "The request could not be read. Check that the body is valid JSON.",
    "unauthorized": "Missing or invalid API key. Send `Authorization: Bearer cop_…`.",
    "forbidden": "This key's role does not allow this call.",
    "not_found": "Not found — or it belongs to another workspace.",
    "method_not_allowed": "This method is not allowed on this address.",
    "conflict": "The request conflicts with the current state of the resource.",
    "payload_too_large": "The request body is too large.",
    "unsupported_media_type": "This content type is not accepted here.",
    "invalid_request": "A field is missing or invalid. `detail` says which one.",
    "rate_limited": "Too many calls: 600 per minute per key. Wait and retry.",
    "internal_error": "Something went wrong on our side. Retry; if it persists, send us the `requestId`.",
    "upstream_error": "The messaging provider refused or failed. `detail` carries its reason.",
    "unavailable": "The service is temporarily unavailable. Retry shortly.",
    "upstream_timeout": "The messaging provider did not answer in time. Retry.",
    "api_key_revoked": "This API key has been revoked. Create a new one in the console.",
    "api_keys_not_installed": "API keys are not available on this instance.",
    "window_closed": "The 24-hour customer service window is closed: only an approved template can be sent.",
    "idempotency_key_required": "`idempotencyKey` is required: 8 to 200 characters, unique per message.",
    "idempotency_key_conflict": "This `idempotencyKey` was already used for a different message.",
    "no_recipient": "The contact has no address on this channel.",
    "template_required": "A template is required here.",
    "template_not_approved": "This template is not approved by Meta yet.",
    "template_wrong_channel": "This template belongs to another WhatsApp number.",
    "template_variables_invalid": "The template variables do not match its placeholders.",
    "template_holes_unfilled": "Some template placeholders have no value.",
    "template_not_applicable": "This template cannot be sent on this conversation.",
    "template_locked": "This template is under review and cannot be changed now.",
    "already_submitted": "This template was already submitted.",
    "never_submitted": "This template was never submitted to Meta.",
    "plan_limit": "Your plan's limit is reached for this month.",
    "queue_not_supported": "This message cannot be queued. Send it without `queue`.",
    "webhook_url_not_https": "The webhook address must start with https://.",
    "file_missing": "No file was sent.",
    "file_too_large": "The file is too large for this channel.",
    "empty_file": "The file is empty.",
    "unsupported_file_type": "This file type is not accepted on this channel.",
    "file_type_refused": "This file type is refused.",
    "caption_unsupported": "This kind of file cannot carry a caption.",
    "channel_required": "`channelId` is required.",
    "unknown_channel": "No such channel in this workspace.",
    "ambiguous_channel": "Several channels match: pass `channelId`.",
    "no_channel": "No active channel can reach this contact.",
    "channel_not_meta": "This channel is not a WhatsApp number connected through Meta.",
    "meta_token_missing": "The WhatsApp connection is not configured on this instance.",
    "assignee_required": "Choose who the conversation is assigned to.",
    "identity_taken": "Another contact already has this phone number or email address.",
    "attribute_exists": "An attribute with this key already exists.",
    "confirmation_required": "Erasing a contact needs the confirmation field.",
    "confirmation_mismatch": "The confirmation does not match the contact.",
    "consent_missing": "The contact has not agreed to receive marketing messages, or has opted out.",
    "contact_blocked": "This contact is blocked.",
    "not_whatsapp": "This only works on WhatsApp conversations.",
    "store_failed": "The event could not be stored. It will be retried.",
    "already_approved": "This template is already approved: it cannot be submitted again.",
    "attachment_unreachable": "An attachment address could not be read; `data.url` names it.",
    "attachment_unsupported": "That type of attachment is not accepted on this channel.",
    "invalid_attachment": "An attachment has no `url`, or there are more than 5.",
    "invalid_category": "That label does not exist in the workspace.",
    "invalid_form": "The interactive form breaks a WhatsApp limit; `detail` says which.",
    "invalid_link": "The `link` is unreadable: it needs an `externalId`.",
    "invalid_recipient": "An address in `cc` or `bcc` is unreadable, or there are more than 10.",
    "invalid_since": "`since` is not a readable date or epoch.",
    "media_expired": "The file is no longer available.",
    "no_identity": "The contact needs a phone number or an email address.",
    "not_email": "`cc`, `bcc` and `attachments` only exist on email.",
    "quote_unavailable": "The quoted message has no identifier at the provider and cannot be quoted.",
    "retention_not_installed": "Data retention is not installed on this workspace."
  }
}
