{"openapi":"3.1.0","info":{"title":"IntelliPortal Partner API","version":"1.6.0","description":"Connect client WhatsApp and Instagram accounts, manage their message templates, send messages on their behalf, and meter usage through a versioned partner API.","contact":{"email":"support@intelliconcierge.com"}},"servers":[{"url":"https://api.intelliconcierge.dev/v1","description":"Production"}],"tags":[{"name":"Messages","description":"Send through a connected client."},{"name":"Clients","description":"Onboard and inspect channel connections."},{"name":"Templates","description":"Create WhatsApp message templates on a client's behalf and follow them through Meta's review."},{"name":"Usage","description":"Read monthly message consumption."},{"name":"Platform","description":"Probe public edge health and version."}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Scoped partner API key prefixed with ik_test_ or ik_live_."}},"schemas":{"ErrorResponse":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","examples":["bad_request","unauthorized","rate_limited"]},"message":{"type":"string"},"details":{"type":"object","additionalProperties":true,"description":"Optional structured validation or provider context."}}}}},"SendMessageRequest":{"type":"object","required":["client_ref","to","type"],"description":"Meta-compatible free-form message. The type value selects the same-named payload field.","properties":{"client_ref":{"type":"string","description":"Stable client identifier from your own system."},"to":{"type":"string","description":"WhatsApp recipient number or Instagram-scoped user ID."},"type":{"type":"string","enum":["text","image","video","audio","document","sticker","location","contacts","interactive","reaction"]},"text":{"type":"object","properties":{"body":{"type":"string"},"preview_url":{"type":"boolean"}}},"image":{"$ref":"#/components/schemas/CaptionedMedia"},"video":{"$ref":"#/components/schemas/CaptionedMedia"},"audio":{"$ref":"#/components/schemas/Media"},"document":{"allOf":[{"$ref":"#/components/schemas/CaptionedMedia"},{"type":"object","properties":{"filename":{"type":"string"}}}]},"sticker":{"$ref":"#/components/schemas/Media"},"location":{"type":"object","required":["latitude","longitude"],"properties":{"latitude":{"type":"number"},"longitude":{"type":"number"},"name":{"type":"string"},"address":{"type":"string"}}},"contacts":{"type":"array","items":{"type":"object","additionalProperties":true}},"interactive":{"type":"object","required":["type"],"additionalProperties":true,"properties":{"type":{"type":"string"}}},"reaction":{"type":"object","required":["message_id","emoji"],"properties":{"message_id":{"type":"string"},"emoji":{"type":"string","description":"An empty value removes an existing reaction."}}},"dry_run":{"type":"boolean","description":"Validate without delivery. Always true for ik_test_ keys."}}},"Media":{"type":"object","description":"Provide either a public HTTPS link or an uploaded media id.","properties":{"link":{"type":"string","format":"uri"},"id":{"type":"string"}}},"CaptionedMedia":{"allOf":[{"$ref":"#/components/schemas/Media"},{"type":"object","properties":{"caption":{"type":"string"}}}]},"SendTemplateRequest":{"type":"object","required":["client_ref","to"],"description":"Name the template with template_ref (recommended — the template is resolved, approval-checked, and its components are built server-side) or pass a raw Meta template object. Exactly one of template_ref / template.","properties":{"client_ref":{"type":"string"},"to":{"type":"string"},"template_ref":{"type":"string","description":"Template id from the Templates API, or the template name. Disambiguate a name that exists in several languages with `language`."},"language":{"type":"string","description":"Language code (e.g. en_US) used to disambiguate template_ref by name."},"parameters":{"description":"Body variable values: an array fills {{1}}, {{2}}… in order; an object matches named variables or numbered keys.","oneOf":[{"type":"array","items":{"type":"string"}},{"type":"object","additionalProperties":{"type":"string"}}]},"header":{"type":"object","description":"Header variable when the template has one: {\"text\": ...} for text headers; {\"link\": ...} or {\"id\": ...} (plus optional \"filename\") for image/video/document; {\"location\": {...}} for location headers.","additionalProperties":true},"buttons":{"type":"array","items":{"type":"string"},"description":"Values for dynamic URL buttons, in button order."},"cards":{"type":"array","description":"Carousel templates only: one object per card, in card order. Every card needs its header media at send time.","items":{"type":"object","properties":{"header":{"type":"object","description":"The card's header media: {\"link\": ...} or {\"id\": ...}.","additionalProperties":true},"parameters":{"description":"The card's body variable values.","oneOf":[{"type":"array","items":{"type":"string"}},{"type":"object","additionalProperties":{"type":"string"}}]},"buttons":{"type":"array","items":{"type":"string"},"description":"Values for the card's quick replies (webhook payload) and dynamic URL buttons (suffix), in button order."}}}},"template":{"type":"object","required":["name"],"description":"Raw Meta template object, forwarded unchanged. Alternative to template_ref.","properties":{"name":{"type":"string"},"language":{"type":"object","properties":{"code":{"type":"string","default":"en_US"}}},"components":{"type":"array","items":{"type":"object","additionalProperties":true}}}},"dry_run":{"type":"boolean"}}},"MessageResponse":{"type":"object","required":["success","message_id"],"properties":{"success":{"type":"boolean","const":true},"message_id":{"type":"string"},"dry_run":{"type":"boolean","description":"Present and true when no delivery was attempted."},"warnings":{"type":"array","items":{"type":"string"},"description":"Present when the message was sent but the channel could not carry part of it — a caption on an Instagram attachment, for example. The send succeeded and needs no retry, but the listed content never reached the recipient."},"template":{"type":"object","additionalProperties":true,"description":"On a template_ref dry-run: the assembled Meta template object a live send would carry."},"hint":{"type":"string","description":"Present on a simulated test-key send — explains that delivery only happens to registered test recipients."}}},"SessionRequest":{"type":"object","required":["client_ref"],"properties":{"client_ref":{"type":"string","description":"Stable, unique identifier from your own system."}}},"SessionResponse":{"type":"object","required":["session_id","url","expires_in"],"properties":{"session_id":{"type":"string"},"url":{"type":"string","format":"uri"},"expires_in":{"type":"integer","const":600}}},"ClientSignupRequest":{"type":"object","required":["client_ref","auth_code"],"description":"Advanced Meta signup completion. Most integrations should use the hosted session flow.","properties":{"client_ref":{"type":"string"},"auth_code":{"type":"string"}}},"Client":{"type":"object","required":["client_ref","channel"],"additionalProperties":true,"properties":{"client_ref":{"type":"string"},"channel":{"type":"string","enum":["whatsapp","instagram"]},"business_name":{"type":"string"},"phone_number":{"type":"string"},"phone_number_id":{"type":"string"},"waba_id":{"type":"string"}}},"UsagePeriod":{"type":"object","required":["period","inbound_messages","outbound_messages","outbound_template_messages","total_messages"],"properties":{"period":{"type":"string","examples":["2026-07"]},"inbound_messages":{"type":"integer"},"outbound_messages":{"type":"integer"},"outbound_template_messages":{"type":"integer"},"total_messages":{"type":"integer"}}},"UsageResponse":{"type":"object","required":["current","history"],"properties":{"current":{"$ref":"#/components/schemas/UsagePeriod"},"history":{"type":"array","items":{"$ref":"#/components/schemas/UsagePeriod"}}}},"TemplateHeader":{"type":"object","description":"Optional header. Media formats need either media_handle (from POST /v1/templates/media) or media_url, which Intelli downloads and uploads to Meta for you.","properties":{"format":{"type":"string","enum":["TEXT","IMAGE","VIDEO","DOCUMENT","LOCATION"]},"text":{"type":"string","description":"TEXT headers only. May contain a single {{1}}."},"examples":{"type":"array","items":{"type":"string"}},"media_handle":{"type":"string"},"media_url":{"type":"string","format":"uri"}}},"TemplateButton":{"type":"object","required":["type","text"],"properties":{"type":{"type":"string","enum":["QUICK_REPLY","URL","PHONE_NUMBER","COPY_CODE","OTP"]},"text":{"type":"string","maxLength":25},"url":{"type":"string","description":"URL buttons. A trailing {{1}} may be appended at send time."},"example":{"type":"array","items":{"type":"string"}},"phone_number":{"type":"string","description":"PHONE_NUMBER buttons."}}},"CreateTemplateRequest":{"type":"object","required":["client_ref","name","body"],"description":"Either supply header/body/footer/buttons and let Intelli assemble Meta's payload, or pass a raw `components` array if you already build it yourself. `components` takes precedence.","properties":{"client_ref":{"type":"string","description":"The client whose WhatsApp Business Account owns the template."},"name":{"type":"string","description":"Normalized to lower_snake_case. Must be unique per language for the client.","examples":["order_update"]},"language":{"type":"string","default":"en_US"},"category":{"type":"string","enum":["MARKETING","UTILITY","AUTHENTICATION"],"default":"MARKETING"},"header":{"$ref":"#/components/schemas/TemplateHeader"},"body":{"type":"object","required":["text"],"properties":{"text":{"type":"string","maxLength":1024},"examples":{"type":"array","items":{"type":"string"},"description":"One sample per {{n}} placeholder. Meta rejects templates whose placeholders have no example, so missing entries are auto-filled."}}},"footer":{"type":"object","properties":{"text":{"type":"string","maxLength":60}}},"buttons":{"type":"array","maxItems":10,"items":{"$ref":"#/components/schemas/TemplateButton"}},"components":{"type":"array","description":"Raw Meta component array. Overrides the fields above.","items":{"type":"object","additionalProperties":true}}}},"Template":{"type":"object","required":["id","client_ref","name","language","status"],"properties":{"id":{"type":"string","nullable":true,"description":"Meta's template id. Use it as {template_ref}."},"client_ref":{"type":"string"},"name":{"type":"string"},"language":{"type":"string"},"category":{"type":"string"},"status":{"type":"string","enum":["PENDING","APPROVED","REJECTED","PAUSED","DISABLED","IN_APPEAL","FLAGGED"],"description":"Only APPROVED templates can be sent."},"rejected_reason":{"type":"string","nullable":true,"description":"Meta's reason. Non-null only when status is REJECTED."},"template_type":{"type":"string"},"components":{"type":"array","items":{"type":"object","additionalProperties":true}},"variables":{"type":"object","description":"Placeholders each component expects, so you know what to supply on send.","properties":{"header":{"type":"array","items":{"type":"string"}},"body":{"type":"array","items":{"type":"string"}},"buttons":{"type":"array","items":{"type":"string"}}}},"created_at":{"type":"string","format":"date-time","nullable":true},"updated_at":{"type":"string","format":"date-time","nullable":true}}},"TemplateList":{"type":"object","required":["results","count"],"properties":{"results":{"type":"array","items":{"$ref":"#/components/schemas/Template"}},"count":{"type":"integer"},"warning":{"type":"string","description":"Present when Meta could not be reached to refresh and the last known state is being served."}}},"TemplateMediaResponse":{"type":"object","required":["handle","format"],"properties":{"handle":{"type":"string","description":"Pass as header.media_handle when creating a template."},"format":{"type":"string","enum":["IMAGE","VIDEO","DOCUMENT"]},"filename":{"type":"string"},"size":{"type":"integer"}}}}},"paths":{"/media":{"post":{"operationId":"uploadSendMedia","summary":"Upload send media","description":"Upload media to a client's WhatsApp number and get back the media id sends can reference — header.id on a template send, image.id and friends on a free-form message. Meta keeps it 30 days, so one upload serves many sends; prefer an id over link in production, since link is re-fetched by Meta at every delivery and fails it silently when unreachable. WhatsApp-only: Instagram takes media as a public URL on the send itself.","tags":["Messages"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["client_ref","file"],"properties":{"client_ref":{"type":"string"},"file":{"type":"string","format":"binary","description":"image/jpeg or png (≤5 MB), video/mp4 or 3gpp (≤16 MB), audio aac/mp4/mpeg/amr/ogg (≤16 MB), documents pdf/Office/txt (≤100 MB), sticker webp (≤500 KB)."}}}}}},"responses":{"201":{"description":"Uploaded — reference media_id from sends.","headers":{"X-Request-Id":{"description":"Opaque request correlation ID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Edge request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current edge window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","required":["success","media_id"],"properties":{"success":{"type":"boolean","const":true},"media_id":{"type":"string"},"kind":{"type":"string","description":"What WhatsApp classified the upload as: image, video, audio, document or sticker."},"expires_in_days":{"type":"integer","const":30}}}}}},"400":{"description":"Unsupported media type, file too large, or the client is not a WhatsApp client.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing messages:send scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown client.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Meta rejected the upload.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/media/{media_id}":{"get":{"operationId":"downloadMedia","summary":"Download inbound media","description":"Stream media a customer sent in. WhatsApp names inbound media by id only and resolving it needs the client's Meta credentials, which partners never hold — call this instead. Instagram does not need it: its webhooks already carry a fetchable URL at attachments[].payload.url, short-lived, so download on receipt. Meta keeps inbound media for 30 days.","tags":["Messages"],"parameters":[{"name":"media_id","in":"path","required":true,"schema":{"type":"string"},"description":"The id from the webhook, e.g. messages[].image.id."},{"name":"client_ref","in":"query","required":true,"schema":{"type":"string"},"description":"Which of your clients received the media. Selects the credentials used to fetch it."}],"responses":{"200":{"description":"The file, streamed with its real Content-Type. X-Media-Sha256 carries the checksum to verify against the webhook.","headers":{"X-Media-Sha256":{"description":"SHA-256 of the file as reported by Meta.","schema":{"type":"string"}}},"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"client_ref missing (client_ref_required), or the client's channel delivers media inline (channel_media_inline).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing clients:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown client_ref, or Meta does not recognise the media id for this client (media_not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The client has no channel credentials on file.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Meta was unreachable or refused the download.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/messages/send":{"post":{"operationId":"sendMessage","summary":"Send a free-form message","description":"Send a Meta-compatible payload through the channel connected to client_ref. WhatsApp carries every type below. Instagram carries text, image, video, audio and document — media must be a public HTTPS URL in <type>.link, since Instagram fetches the file itself and cannot use an uploaded media id (code media_link_required); caption and filename are returned in `warnings` because an Instagram attachment has nowhere to put them. Templates, sticker, location, contacts, interactive and reaction are WhatsApp-only (code channel_type_unsupported).","tags":["Messages"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageRequest"}}}},"responses":{"200":{"description":"Message accepted or dry-run validated.","headers":{"X-Request-Id":{"description":"Opaque request correlation ID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Edge request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current edge window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Trial and grace have ended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing messages:send scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown client_ref.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Channel provider rejected the message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/messages/send-template":{"post":{"operationId":"sendTemplateMessage","summary":"Send a WhatsApp template","tags":["Messages"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendTemplateRequest"}}}},"responses":{"200":{"description":"Template send accepted or dry-run validated.","headers":{"X-Request-Id":{"description":"Opaque request correlation ID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Edge request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current edge window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponse"}}}},"400":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Trial and grace have ended.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing messages:send scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown client or template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"422":{"description":"Channel provider rejected the message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/embedded-signup/sessions":{"post":{"operationId":"createWhatsAppSignupSession","summary":"Create a hosted WhatsApp signup session","description":"Returns a single-use Intelli-hosted URL valid for 10 minutes.","tags":["Clients"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionRequest"}}}},"responses":{"201":{"description":"Session created.","headers":{"X-Request-Id":{"description":"Opaque request correlation ID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Edge request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current edge window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing clients:write scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Client reference or number is already connected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/instagram-connect/sessions":{"post":{"operationId":"createInstagramConnectSession","summary":"Create a hosted Instagram connect session","description":"Returns a single-use Intelli-hosted URL valid for 10 minutes.","tags":["Clients"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionRequest"}}}},"responses":{"201":{"description":"Session created.","headers":{"X-Request-Id":{"description":"Opaque request correlation ID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Edge request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current edge window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing clients:write scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Client reference or account is already connected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/clients":{"get":{"operationId":"listClients","summary":"List connected clients","tags":["Clients"],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":20,"minimum":1}},{"name":"offset","in":"query","schema":{"type":"integer","default":0,"minimum":0}}],"responses":{"200":{"description":"Paginated client records.","headers":{"X-Request-Id":{"description":"Opaque request correlation ID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Edge request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current edge window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"oneOf":[{"type":"array","items":{"$ref":"#/components/schemas/Client"}},{"type":"object","additionalProperties":true}]}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing clients:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/clients/{client_ref}":{"get":{"operationId":"getClient","summary":"Get a client by reference","tags":["Clients"],"parameters":[{"name":"client_ref","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Connected client.","headers":{"X-Request-Id":{"description":"Opaque request correlation ID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Edge request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current edge window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Client"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing clients:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown client_ref.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/clients/signup":{"post":{"operationId":"completeWhatsAppSignup","summary":"Complete a Meta signup code exchange","description":"Advanced endpoint used to complete Meta Embedded Signup. Most integrations should use /embedded-signup/sessions.","tags":["Clients"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientSignupRequest"}}}},"responses":{"201":{"description":"Client connected.","headers":{"X-Request-Id":{"description":"Opaque request correlation ID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Edge request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current edge window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Client"}}}},"400":{"description":"Invalid code or request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing clients:write scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Client or number is already connected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/templates":{"get":{"operationId":"listTemplates","summary":"List a client's message templates","description":"Templates are registered per client WhatsApp Business Account, so client_ref is required. Results are served from Intelli's mirror of the WABA, refreshed from Meta when it goes stale; pass refresh=true to force a pull.","tags":["Templates"],"parameters":[{"name":"client_ref","in":"query","required":true,"schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["PENDING","APPROVED","REJECTED","PAUSED","DISABLED"]}},{"name":"category","in":"query","schema":{"type":"string","enum":["MARKETING","UTILITY","AUTHENTICATION"]}},{"name":"language","in":"query","schema":{"type":"string"}},{"name":"search","in":"query","description":"Case-insensitive match on the template name.","schema":{"type":"string"}},{"name":"refresh","in":"query","description":"Force a pull from Meta before responding.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"The client's templates.","headers":{"X-Request-Id":{"description":"Opaque request correlation ID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Edge request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current edge window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateList"}}}},"400":{"description":"client_ref is missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing templates:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Client not found or inactive.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"post":{"operationId":"createTemplate","summary":"Submit a template for review","description":"Creates the template on the client's WhatsApp Business Account. It comes back with status PENDING — Meta reviews asynchronously and only APPROVED templates can be sent. Listen for the `template.status` webhook, or poll GET /templates/{template_ref}.","tags":["Templates"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTemplateRequest"}}}},"responses":{"201":{"description":"Template submitted for review.","headers":{"X-Request-Id":{"description":"Opaque request correlation ID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Edge request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current edge window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"400":{"description":"Meta rejected the template, or the payload is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing templates:write scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Client not found or inactive.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"A template with this name and language already exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/templates/media":{"post":{"operationId":"uploadTemplateMedia","summary":"Upload header media","description":"Uploads a sample image, video or document and returns a handle to pass as header.media_handle. Only needed when you hold the bytes — if the file already sits on a public URL, pass header.media_url on the create instead.","tags":["Templates"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["client_ref","file"],"properties":{"client_ref":{"type":"string"},"file":{"type":"string","format":"binary"},"format":{"type":"string","enum":["IMAGE","VIDEO","DOCUMENT"],"description":"Inferred from the file's content type when omitted."}}}}}},"responses":{"201":{"description":"Media uploaded.","headers":{"X-Request-Id":{"description":"Opaque request correlation ID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Edge request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current edge window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateMediaResponse"}}}},"400":{"description":"Missing file, unsupported type, or over the size limit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing templates:write scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/templates/{template_ref}":{"parameters":[{"name":"template_ref","in":"path","required":true,"description":"Meta's template id, or the template name — add ?language= when the name exists in several languages.","schema":{"type":"string"}},{"name":"client_ref","in":"query","required":true,"schema":{"type":"string"}},{"name":"language","in":"query","schema":{"type":"string"}}],"get":{"operationId":"getTemplate","summary":"Get a template and its review status","description":"Poll this to follow a template through review if you'd rather not consume the `template.status` webhook.","tags":["Templates"],"parameters":[{"name":"refresh","in":"query","description":"Force a pull from Meta before responding.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"The template.","headers":{"X-Request-Id":{"description":"Opaque request correlation ID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Edge request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current edge window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing templates:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Template or client not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The name matches several languages — pass ?language=.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateTemplate","summary":"Edit a template","description":"Updates the template's components on Meta, which sends it back through review. Name and language are immutable — create a new template to change either. Meta limits how often an approved template may be edited.","tags":["Templates"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTemplateRequest"}}}},"responses":{"200":{"description":"Template updated and resubmitted for review.","headers":{"X-Request-Id":{"description":"Opaque request correlation ID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Edge request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current edge window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"400":{"description":"Meta rejected the edit, or name/language was changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing templates:write scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Template or client not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"The template is in a state Meta will not accept edits for.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteTemplate","summary":"Delete a template","description":"Deletes the template from the client's WhatsApp Business Account. Sends referencing it will fail afterwards. Meta blocks re-using the same name for 30 days.","tags":["Templates"],"responses":{"204":{"description":"Template deleted."},"400":{"description":"Meta refused the delete.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Missing templates:write scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Template or client not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/usage":{"get":{"operationId":"getUsage","summary":"Get monthly message usage","description":"Returns the current UTC month and recent closed months. Test-key and dry-run traffic is excluded.","tags":["Usage"],"responses":{"200":{"description":"Usage rollups.","headers":{"X-Request-Id":{"description":"Opaque request correlation ID.","schema":{"type":"string","format":"uuid"}},"X-RateLimit-Limit":{"description":"Edge request ceiling for the current window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current edge window.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageResponse"}}}},"401":{"description":"Invalid or missing API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/status":{"get":{"operationId":"getStatus","summary":"Probe public API edge health and version","tags":["Platform"],"security":[],"responses":{"200":{"description":"The API edge is reachable.","content":{"application/json":{"schema":{"type":"object","required":["status","version"],"properties":{"status":{"type":"string","const":"ok"},"version":{"type":"string","const":"v1"}}}}}}}}}}}