NUACOM API

Messaging

Conversations / WhatsApp

WhatsApp is the second channel of Conversations (channel_type 2). It is sent through the same thread endpoints as SMS, with three WhatsApp rules on top: the sender is a phone number connected to a Meta business account, a conversation that has had no inbound message for 24 hours accepts approved templates only, and media is moderated before it leaves. This group holds the template and media endpoints; the sender itself is connected in the portal.

Before your first message

  1. Add-on and sender: the account needs the WhatsApp for Business add-on and at least one connected number. Both are set up in the portal under Integrations → WhatsApp for Business: payment, a Twilio sub-account and Meta's Embedded Signup run in the browser, so those endpoints are not part of the public reference. Accounts that bring their own Twilio carrier (BYOC) cannot use WhatsApp.
  2. Find your senders: GET /v2/sms/sender_names?filter[channel]=whatsapp lists the connected numbers; sender_name is the value to send as sender_id. Up to 10 numbers per account.
  3. Token: the template and media endpoints need an account admin token. Without an active sender every endpoint of this group answers 403 "No active WhatsApp sender found for your account".

Templates (what the portal does)

  1. List: GET /v2/integrations/whatsapp/templates (status, category, language, search, page, per_page up to 100) or the shared GET /v2/sms-templates?filter[channel]=2, which is what the portal's template picker uses (filter[status]=1 for sendable ones, filter[exclude_dynamic_variables]=true for automations that cannot fill placeholders).
  2. Create: POST /v2/integrations/whatsapp/templates with name, category (1 Authentication, 2 Marketing, 3 Utility: Meta prices and reviews them differently), language, sender_id, body (text with {{1}}…{{N}} and one sample per placeholder in variables), optional header (type text, image, video or document; media as a media_id from the upload endpoint, a public media_url or an inline media file), footer and up to 10 buttons. The template is created locally with status 6 (Not Submitted); submit_after_approval: true submits it as soon as its header media passes moderation.
  3. Edit or delete: PUT is allowed while permissions.can_edit is true (Not Submitted, Rejected, Disabled or Media Rejected); an approved template is immutable, create a new one. DELETE works in every status and also removes the template from Twilio.
  4. Submit: POST /v2/integrations/whatsapp/templates/{smsTemplate}/submit sends it to Meta. 200 with status: "submitted" and the template_sid; 202 with status: "pending_submit" when the header media is still being moderated (it is submitted automatically afterwards); 403 when the template is already approved or pending; 422 when a placeholder has no sample.
  5. Wait for approval: Meta decides asynchronously, usually within minutes, sometimes a day. Poll GET …/templates/{smsTemplate}: status is an integer and status_label its text: 0 Pending (submitted, awaiting Meta's review), 1 Approved, 2 Error (the submission failed; status_label reads "Unknown"), 3 Rejected, 4 Disabled, 5 Paused, 6 Not Submitted, 7 Pending Moderation (header media still being checked), 8 Pending Submit (queued for submission), 9 Media Rejected, 10 Deleted from Twilio (removed on the provider side; create a new one). Only 1 can be sent. An approved template can later become Paused or Disabled by Meta for quality reasons.

Template lifecycle

stateDiagram-v2
    [*] --> NotSubmitted: created locally (status 6 Not Submitted)
    NotSubmitted --> PendingModeration: submit_after_approval, header media uploaded (status 7)
    PendingModeration --> PendingSubmit: media approved (status 8)
    PendingModeration --> MediaRejected: media rejected (status 9)
    PendingSubmit --> Pending: auto-submitted to Meta (status 0)
    NotSubmitted --> Pending: POST …/submit, 200 submitted (status 0)
    NotSubmitted --> PendingSubmit: POST …/submit, 202 pending_submit (status 8)
    Pending --> Approved: Meta approves (status 1)
    Pending --> Rejected: Meta rejects (status 3)
    Pending --> Error: submission failed (status 2)
    Approved --> Paused: Meta pauses for quality (status 5)
    Approved --> Disabled: Meta disables (status 4)
    Rejected --> [*]: DELETE, create a new one
    MediaRejected --> [*]: DELETE, create a new one
    Approved --> [*]: Deleted from Twilio (status 10)

Media

  • Upload: POST /v2/integrations/whatsapp/media (multipart file; JPEG, PNG, MP4, AAC, MP3, AMR, 3GP, PDF, DOC, DOCX, PPTX, XLSX, VCF; 16 MB, images 5 MB) answers media_id, a signed preview url, type, message_type and moderation_status. Images and videos are moderated asynchronously (pending → approved or rejected); audio and documents are approved on upload. Rejected media cannot be sent or used in a template header. Template headers accept JPEG, PNG, MP4 and PDF only; an inline header.media file is checked on its real MIME type, not its extension, and the same 16 MB / 5 MB limits apply.
  • Read: an inbound or outbound file is the message's single attachments[] entry (id, content_url, thumbnail_url, media_type, mime_type, file_size, filename), and content_url and thumbnail_url are repeated on the message itself. content_url is a signed URL that expires. GET /v2/whatsapp/media/{media}/download-url with the attachment's id issues a fresh one: 200 with url (valid for 15 minutes, usable directly in <img>, <video> and <audio>, no token needed), expires_at, media_type and mime_type; 202 with status: "pending" while the file is still being moderated, so poll until 200; 403 when it was rejected or belongs to another account; 404 when the id does not exist.
  • Previews: for an image or video NUACOM also builds a JPEG thumbnail of at most 320 × 320 px in the background. thumbnail_url is a signed link that needs no token and stays the same until the end of the calendar year, so it can be cached; it answers 404 until the thumbnail exists and 403 once the media is rejected. It is null for audio, documents and messages without media.

Sending

  • Start or reply with POST /v2/conversations (channel_type: 2, sender_id, one participants[] entry of type phone_number) or POST /v2/conversations/{conversation}/messages. A message is exactly one of: text in content; media as content_url (a public URL NUACOM downloads and re-hosts), content_file (multipart) or a media_id-backed upload, with content as the caption; a template as metadata.template_id plus metadata.template_variables (one value per {{N}}, in order); a location as metadata.location (latitude, longitude, label). The answer is 201 with the message.
  • The 24-hour window: a contact's inbound message opens a window that ends 24 hours after their last message; expires_at on the conversation shows when. Inside it any message type is allowed; outside it the API answers 422 (metadata.template_id "The Customer Service Window is closed…", or error: WHATSAPP_CSW_CLOSED on a new conversation) unless the message is an approved template. Templates can always be sent and reopen the conversation.
  • Campaigns: POST /v2/sms/overview with channel_type: 2, wa_template_id and wa_parameters (JSON keyed by placeholder number) quotes a WhatsApp campaign and flags numbers that cannot receive WhatsApp as invalid; the portal's New Message page then sends the staged draft. Through the API, send to each recipient with POST /v2/conversations.
  • Scheduling uses scheduled_for like SMS; a scheduled message is cancellable until delivery.

Reading and delivery

  • Threads and messages come from GET /v2/conversations (channel=2) and GET /v2/conversations/{conversation}; statuses[] move through sent, delivered and read as Meta reports them; a failed status refunds the message. An inbound file that could not be fetched arrives with metadata.media_error.code (MEDIA_TOO_LARGE above about 16 MB, MEDIA_TOO_LARGE_PROVIDER when Twilio rejected it, UNSUPPORTED_MEDIA_TYPE, MEDIA_DOWNLOAD_FAILED, MEDIA_PROCESSING_FAILED).
  • message_received and message_sent webhooks fire for WhatsApp as for SMS.

Pricing

Each message is charged at request time (scheduled messages at delivery, Marketing and Authentication templates up front) from the SMS balance at the WhatsApp rate of the recipient's country, by category: a Marketing or Authentication template at its own rate; a Utility template and a free-form message at the cheaper "service" rate while the 24-hour window is open, the Utility rate outside it. GET /v2/account/messaging-rates?country_code= lists them per wa_message_type. A message Meta reports as failed or undelivered is refunded once. 402 means the balance is empty.

Roles and access

  • Template and media endpoints: account admin with the WhatsApp for Business add-on and an active sender. Reading and sending through Conversations: any authenticated user on an account with the add-on.
  • Without the add-on WhatsApp messages are also hidden from the conversation and inbox endpoints.

Pitfalls

  • Recipients are phone numbers today (participant_type: phone_number, international format). Meta is rolling out WhatsApp usernames: a contact who hides their number will arrive with a business-scoped id instead, so do not parse participant_id as a phone number.
  • status and category are integers on the WhatsApp endpoints; template_id in metadata is the integer id of the template, not its template_sid.
  • A template's placeholders are positional: send template_variables in {{1}}, {{2}} order with every value present, or the send is rejected.
  • content_url must be fetchable by NUACOM without authentication; it is downloaded, moderated and re-hosted, so a private or expiring link fails with 422.
  • Inbound files larger than about 95 MB never reach NUACOM: Meta drops them with no error on either side, so tell contacts the limit.
  • A sender number must not be in use on another WhatsApp Business account; connecting it moves it. Removing a number in the portal ends its threads' sending ability but keeps the history.