{
  "openapi": "3.1.0",
  "info": {
    "title": "Nodium API",
    "version": "v1",
    "description": "Nodium 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://nodium.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",
              "messenger",
              "instagram"
            ]
          },
          "channelName": {
            "type": "string",
            "description": "The channel's name in Nodium."
          },
          "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",
              "messenger",
              "instagram"
            ]
          },
          "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 Nodium.",
            "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",
              "messenger",
              "instagram"
            ]
          },
          "name": {
            "type": "string",
            "description": "Its name in Nodium."
          },
          "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 Nodium 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-Nodium-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, `nod_…`."
          },
          "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 nod_…`. 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 Nodium.",
        "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": {
                        "contacts": {
                          "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": {
                        "attributes": {
                          "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. Each one says what the AI collaborator does with the conversations it holds.\n\nRequires a key with the `admin` role or above.",
        "operationId": "getApiV1InboxCategories",
        "responses": {
          "200": {
            "description": "The labels, with their name, colour and collaborator behaviour.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The labels.",
                      "properties": {
                        "categories": {
                          "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."
                              },
                              "agentMode": {
                                "type": "string",
                                "description": "What the collaborator does on conversations with this label.",
                                "enum": [
                                  "reply",
                                  "propose",
                                  "handover"
                                ]
                              }
                            }
                          }
                        },
                        "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 narrow the list: by folder, by channel, by label, by status. Views that mean \"assigned to me\" match nothing when called with an API key — a key is not a person.",
        "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. Defaults to a sensible page size.",
            "schema": {
              "type": "integer",
              "description": "How many to return. Defaults to a sensible page size."
            }
          },
          {
            "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": "status",
            "in": "query",
            "required": false,
            "description": "Only conversations in this state.",
            "schema": {
              "type": "string",
              "description": "Only conversations in this state.",
              "enum": [
                "open",
                "pending",
                "closed"
              ]
            }
          },
          {
            "name": "channelId",
            "in": "query",
            "required": false,
            "description": "Only conversations on this channel.",
            "schema": {
              "type": "string",
              "description": "Only conversations on this channel."
            }
          }
        ]
      },
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Write first to someone who has not written yet",
        "description": "Opens a conversation and sends the first message. If that person already wrote in the past, their existing thread is reused — you never end up with two threads for one person on one channel.\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.\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` — That key already served another message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`template_holes_unfilled` — A placeholder has no value; `data.issues` lists them.\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."
                  },
                  "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 position: `{ \"1\": \"Karim\" }`."
                  },
                  "text": {
                    "type": "string",
                    "description": "Email: the body."
                  },
                  "subject": {
                    "type": "string",
                    "description": "Email: the subject line."
                  },
                  "queue": {
                    "type": "boolean",
                    "description": "Return at once and send within a minute (HTTP 202). Default false."
                  }
                },
                "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 Nodium'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. 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. 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": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "userId": {
                    "type": "string",
                    "description": "The workspace member who takes it over."
                  }
                },
                "required": [
                  "userId"
                ]
              }
            }
          }
        }
      }
    },
    "/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; Nodium keeps its own copy so it stays readable after Meta 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\nA received email 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": "before",
            "in": "query",
            "required": false,
            "description": "Return messages older than this one.",
            "schema": {
              "type": "string",
              "description": "Return messages older than this one."
            }
          }
        ]
      },
      "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"
                }
              }
            }
          },
          "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.",
            "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. Also the readable body when you send a form."
                  },
                  "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."
                  },
                  "queue": {
                    "type": "boolean",
                    "description": "Return at once and send within a minute (HTTP 202). Default false."
                  }
                },
                "required": [
                  "text",
                  "idempotencyKey"
                ]
              }
            }
          }
        }
      }
    },
    "/inbox/conversations/{id}/needs-human": {
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Flag a conversation as needing a person",
        "description": "Raises the thread in the Nodium inbox so an advisor picks it up. Use it when your software decides it cannot handle a case on its own.\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"
                }
              }
            }
          },
          "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, in one line. Shown to the advisor."
                  }
                },
                "required": []
              }
            }
          }
        }
      }
    },
    "/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.",
            "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 position: `{ \"1\": \"Karim\" }`."
                  },
                  "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 Nodium.\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/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. Nodium keeps its own copy: Meta deletes received media after seven days, so this stays available afterwards.",
        "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."
            }
          }
        ]
      }
    },
    "/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"
                }
              }
            }
          }
        }
      }
    },
    "/phone-numbers": {
      "get": {
        "tags": [
          "Status"
        ],
        "summary": "List your WhatsApp numbers as Meta sees them",
        "description": "Returns the numbers with what Meta says about them: verified name, quality rating, and messaging limit tier — how many people you may start a conversation with per day.\n\nA number whose quality drops will eventually be restricted by Meta. Watch this.\n\nRequires a key with the `admin` role or above.",
        "operationId": "getApiV1PhoneNumbers",
        "responses": {
          "200": {
            "description": "The numbers, with quality and limits, and any channel that could not be read.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The numbers.",
                      "properties": {
                        "numbers": {
                          "type": "array",
                          "description": "Each number as Meta describes it, with `channel` when it serves one.",
                          "items": {
                            "type": "object",
                            "description": "A number."
                          }
                        },
                        "orphans": {
                          "type": "array",
                          "description": "Numbers connected in Nodium that Meta did not return.",
                          "items": {
                            "type": "object",
                            "description": "A number."
                          }
                        },
                        "unreachable": {
                          "type": "array",
                          "description": "Channels that could not be read, with the reason.",
                          "items": {
                            "type": "object",
                            "description": "`{ channel, reason }`."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "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"
                }
              }
            }
          }
        }
      }
    },
    "/phone-numbers/{id}": {
      "get": {
        "tags": [
          "Status"
        ],
        "summary": "Get one WhatsApp number",
        "description": "Same detail for a single number. The identifier is Meta's `phone_number_id`, not the number people dial, and not a Nodium id.\n\nRequires a key with the `admin` role or above.",
        "operationId": "getApiV1PhoneNumbersId}",
        "responses": {
          "200": {
            "description": "The number, with its verified name, quality rating and messaging limit.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "description": "The number as Meta describes it.",
                      "properties": {
                        "number": {
                          "type": "object",
                          "description": "Its id, display number, verified name, `qualityRating`, `messagingLimitTier`, `accountMode` and `wabaId`, as Meta gives them."
                        },
                        "channel": {
                          "type": "object",
                          "description": "The Nodium channel it serves (`channelId`, `channelName`, `isActive`), 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": "Meta's phone number id.",
            "schema": {
              "type": "string",
              "description": "Meta's phone number id.",
              "example": "1233522149840389"
            }
          }
        ]
      }
    },
    "/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`, `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": {
                        "templates": {
                          "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 Nodium 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 Nodium 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 Nodium 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 Nodium."
                  }
                },
                "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 Nodium."
                  },
                  "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 Nodium 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",
    "conversation.needs_human",
    "contact.opted_in",
    "contact.opted_out",
    "approval.requested",
    "approval.decided",
    "mission.closed",
    "dossier.changed",
    "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 nod_…`.",
    "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."
  }
}
