openapi: 3.0.3
info:
title: Zavu Unified Messaging Layer 10DLC Senders API
version: 0.2.0
description: 'Unified multi-channel messaging API for Zavu.
Supported channels:
- **SMS**: Simple text messages
- **WhatsApp**: Rich messaging with media, buttons, lists, CTA URL buttons, and templates
- **Telegram**: Bot messaging with text, media, and interactive elements
- **Email**: Transactional emails via Amazon SES
Design goals:
- Simple `send()` entrypoint for developers
- Project-level authentication via Bearer token
- Support for all WhatsApp message types (text, image, video, audio, document, sticker, location, contact, buttons, list, cta_url, reaction, template)
- If a non-text message type is sent, WhatsApp channel is used automatically
- 24-hour WhatsApp conversation window enforcement
- Universal `to` field accepts phone numbers (E.164), email addresses, or numeric chat IDs (Telegram/Instagram/Messenger)
'
servers:
- url: https://api.zavu.dev
security:
- bearerAuth: []
tags:
- name: Senders
paths:
/v1/senders:
get:
summary: List senders
operationId: listSenders
parameters:
- name: limit
in: query
schema:
type: integer
default: 50
maximum: 100
- name: cursor
in: query
schema:
type: string
responses:
'200':
description: List of sender profiles.
content:
application/json:
schema:
type: object
required:
- items
properties:
items:
type: array
items:
$ref: '#/components/schemas/Sender'
nextCursor:
type: string
nullable: true
'401':
description: Unauthorized.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- bearerAuth: []
tags:
- Senders
post:
summary: Create sender
operationId: createSender
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SenderCreateRequest'
responses:
'201':
description: Sender created.
content:
application/json:
schema:
$ref: '#/components/schemas/Sender'
'400':
description: Invalid sender.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- bearerAuth: []
tags:
- Senders
/v1/senders/{senderId}:
get:
summary: Get sender
operationId: getSender
parameters:
- $ref: '#/components/parameters/SenderIdParam'
responses:
'200':
description: Sender details.
content:
application/json:
schema:
$ref: '#/components/schemas/Sender'
'401':
description: Unauthorized.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Sender not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- bearerAuth: []
tags:
- Senders
patch:
summary: Update sender
operationId: updateSender
parameters:
- $ref: '#/components/parameters/SenderIdParam'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SenderUpdateRequest'
responses:
'200':
description: Sender updated.
content:
application/json:
schema:
$ref: '#/components/schemas/Sender'
'400':
description: Invalid update.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Sender not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- bearerAuth: []
tags:
- Senders
delete:
summary: Delete sender
operationId: deleteSender
parameters:
- $ref: '#/components/parameters/SenderIdParam'
responses:
'204':
description: Sender deleted.
'400':
description: Cannot delete default sender.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Sender not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- bearerAuth: []
tags:
- Senders
/v1/senders/{senderId}/webhook/secret:
post:
summary: Regenerate webhook secret
description: Regenerate the webhook secret for a sender. The old secret will be invalidated immediately.
operationId: regenerateSenderWebhookSecret
parameters:
- $ref: '#/components/parameters/SenderIdParam'
responses:
'200':
description: New webhook secret generated.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookSecretResponse'
'400':
description: Sender has no webhook configured.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Sender not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- bearerAuth: []
tags:
- Senders
/v1/senders/{senderId}/profile:
get:
summary: Get WhatsApp Business profile
description: Get the WhatsApp Business profile for a sender. The sender must have a WhatsApp Business Account connected.
operationId: getSenderProfile
parameters:
- $ref: '#/components/parameters/SenderIdParam'
responses:
'200':
description: WhatsApp Business profile.
content:
application/json:
schema:
$ref: '#/components/schemas/WhatsAppBusinessProfileResponse'
'400':
description: Sender does not have a WhatsApp Business Account connected.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Sender not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- bearerAuth: []
tags:
- Senders
patch:
summary: Update WhatsApp Business profile
description: Update the WhatsApp Business profile for a sender. The sender must have a WhatsApp Business Account connected.
operationId: updateSenderProfile
parameters:
- $ref: '#/components/parameters/SenderIdParam'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WhatsAppBusinessProfileUpdateRequest'
example:
about: Succulent specialists!
description: We specialize in providing high-quality succulents.
email: contact@example.com
websites:
- https://www.example.com
vertical: RETAIL
responses:
'200':
description: Profile updated successfully.
content:
application/json:
schema:
type: object
required:
- success
- profile
properties:
success:
type: boolean
profile:
$ref: '#/components/schemas/WhatsAppBusinessProfile'
'400':
description: Invalid request or sender does not have a WhatsApp Business Account connected.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Sender not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- bearerAuth: []
tags:
- Senders
/v1/senders/{senderId}/profile/picture:
post:
summary: Upload WhatsApp Business profile picture
description: Upload a new profile picture for the WhatsApp Business profile. The image will be uploaded to Meta and set as the profile picture.
operationId: uploadSenderProfilePicture
parameters:
- $ref: '#/components/parameters/SenderIdParam'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WhatsAppBusinessProfilePictureRequest'
example:
imageUrl: https://example.com/profile.jpg
mimeType: image/jpeg
responses:
'200':
description: Profile picture uploaded successfully.
content:
application/json:
schema:
type: object
required:
- success
- profile
properties:
success:
type: boolean
profile:
$ref: '#/components/schemas/WhatsAppBusinessProfile'
'400':
description: Invalid request or sender does not have a WhatsApp Business Account connected.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Unauthorized.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: Sender not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- bearerAuth: []
tags:
- Senders
components:
schemas:
WhatsAppBusinessProfilePictureRequest:
type: object
description: Request body to upload a profile picture.
required:
- imageUrl
- mimeType
properties:
imageUrl:
type: string
format: uri
description: URL of the image to upload.
mimeType:
type: string
description: MIME type of the image.
enum:
- image/jpeg
- image/png
SenderUpdateRequest:
type: object
properties:
name:
type: string
setAsDefault:
type: boolean
webhookUrl:
type: string
format: uri
nullable: true
description: HTTPS URL for webhook events. Set to null to remove webhook.
webhookEvents:
type: array
items:
$ref: '#/components/schemas/WebhookEvent'
description: Events to subscribe to.
webhookActive:
type: boolean
description: Whether the webhook is active.
emailReceivingEnabled:
type: boolean
description: Enable or disable inbound email receiving for this sender.
emailCatchAllEnabled:
type: boolean
description: Enable or disable domain catch-all. When enabled (with emailReceivingEnabled true), this sender receives email for any address at its domain. Ignored (treated as false) if receiving is not enabled.
emailAddress:
type: string
format: email
description: Attach or change the sender's email from-address (e.g. noreply@yourdomain.com). The domain must be a verified email domain in your project.
example: noreply@yourdomain.com
emailDomainId:
type: string
description: ID of the verified email domain to attach. Optional — resolved from `emailAddress`'s domain when omitted.
emailFromName:
type: string
maxLength: 100
description: Display name shown in the recipient's inbox for the email channel.
WhatsAppBusinessProfileResponse:
type: object
required:
- profile
properties:
profile:
$ref: '#/components/schemas/WhatsAppBusinessProfile'
WhatsAppBusinessProfile:
type: object
description: WhatsApp Business profile information.
properties:
about:
type: string
description: Short description of the business (max 139 characters).
maxLength: 139
example: Succulent specialists!
address:
type: string
description: Physical address of the business (max 256 characters).
maxLength: 256
example: 1 Hacker Way, Menlo Park, CA 94025
description:
type: string
description: Extended description of the business (max 512 characters).
maxLength: 512
example: At Lucky Shrub, we specialize in providing a diverse range of high-quality succulents.
email:
type: string
format: email
description: Business email address.
example: contact@example.com
profilePictureUrl:
type: string
format: uri
description: URL of the business profile picture.
example: https://pps.whatsapp.net/v/t61.24...
websites:
type: array
description: Business website URLs (maximum 2).
maxItems: 2
items:
type: string
format: uri
example:
- https://www.example.com/
vertical:
$ref: '#/components/schemas/WhatsAppBusinessProfileVertical'
WebhookEvent:
type: string
description: 'Type of event that triggers the webhook.
**Message lifecycle events:**
- `message.queued`: Message created and queued for sending. `data.status` = `queued`
- `message.sent`: Message accepted by the provider. `data.status` = `sent`
- `message.delivered`: Message delivered to recipient. `data.status` = `delivered`
- `message.read`: Message was read by the recipient (WhatsApp only). `data.status` = `read`
- `message.failed`: Message failed to send. `data.status` = `failed`
**Inbound events:**
- `message.inbound`: New message received from a contact. Reactions are delivered as `message.inbound` with `messageType=''reaction''`. When the contact replied to (quoted) an earlier message, `data.content` carries the reply context: `replyToMessageId`, `replyToProviderMessageId`, `replyToFrom`, `replyToText`, and `replyToMessageType`. `data.providerTimestamp` is the provider''s original receive time in Unix milliseconds (the moment the channel received the message from the contact — WhatsApp, Telegram, Instagram, Messenger; `null` for SMS and email). Compare it against the top-level `timestamp` (when Zavu dispatched the webhook) to detect and ignore delayed deliveries.
- `message.status`: A contact posted a WhatsApp status/story (currently WhatsApp Alternative only). It is NOT a conversation message and never enters the inbox — it is delivered only if you subscribe to `message.status`. `data` carries `from` (the author in E.164), `messageType` (`text`, `image`, `video`, `audio`), `text` (caption/text when present), `mimetype` (for media stories), and `providerTimestamp`. Media bytes are not included.
- `message.unsupported`: Received a message type that is not supported
**Broadcast events:**
- `broadcast.status_changed`: Broadcast status changed (pending_review, approved, rejected, sending, completed, cancelled)
**Other events:**
- `conversation.new`: New conversation started with a contact
- `template.status_changed`: WhatsApp template approval status changed
**Partner events:**
- `invitation.status_changed`: A partner invitation status changed (pending, in_progress, completed, cancelled)
**Custom domain events:**
- `domain.verified`: A custom email domain passed verification (DKIM, and SPF/DMARC/MAIL FROM if enhanced records are enabled)
- `domain.failed`: A custom email domain failed verification or is partially verified'
enum:
- message.queued
- message.sent
- message.delivered
- message.read
- message.failed
- message.inbound
- message.status
- message.unsupported
- broadcast.status_changed
- conversation.new
- template.status_changed
- invitation.status_changed
- domain.verified
- domain.failed
WhatsAppBusinessProfileVertical:
type: string
description: Business category for WhatsApp Business profile.
enum:
- UNDEFINED
- OTHER
- AUTO
- BEAUTY
- APPAREL
- EDU
- ENTERTAIN
- EVENT_PLAN
- FINANCE
- GROCERY
- GOVT
- HOTEL
- HEALTH
- NONPROFIT
- PROF_SERVICES
- RETAIL
- TRAVEL
- RESTAURANT
- NOT_A_BIZ
Sender:
type: object
required:
- id
- name
- phoneNumber
properties:
id:
type: string
example: sender_12345
name:
type: string
example: Primary sender
phoneNumber:
type: string
description: Phone number in E.164 format.
example: '+13125551212'
isDefault:
type: boolean
description: Whether this sender is the project's default.
default: false
webhook:
$ref: '#/components/schemas/SenderWebhook'
whatsapp:
type: object
description: WhatsApp Business Account information. Only present if a WABA is connected.
properties:
phoneNumberId:
type: string
description: WhatsApp phone number ID from Meta.
displayPhoneNumber:
type: string
description: Display phone number.
example: '+14155551234'
paymentStatus:
type: object
description: Payment configuration status from Meta.
properties:
setupStatus:
type: string
description: Payment setup status (COMPLETE, NOT_STARTED, etc.).
example: COMPLETE
methodStatus:
type: string
description: Payment method status (VALID, NONE, etc.).
example: VALID
canSendTemplates:
type: boolean
description: Whether template messages can be sent. Requires setupStatus=COMPLETE and methodStatus=VALID.
emailAddress:
type: string
description: From-address for the email channel, if configured.
example: noreply@yourdomain.com
emailReceivingEnabled:
type: boolean
description: Whether inbound email receiving is enabled for this sender.
default: false
emailCatchAllEnabled:
type: boolean
description: Whether catch-all receiving is enabled. When true (and emailReceivingEnabled is true), this sender receives email addressed to any local part at its domain, not just its own address. The original recipient is delivered in the message.inbound webhook's data.to.
default: false
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
WebhookSecretResponse:
type: object
required:
- secret
properties:
secret:
type: string
description: The new webhook secret.
example: whsec_abc123...
SenderWebhook:
type: object
description: Webhook configuration for the sender.
required:
- url
- events
- active
properties:
url:
type: string
format: uri
description: HTTPS URL that will receive webhook events.
example: https://api.example.com/webhooks/zavu
events:
type: array
items:
$ref: '#/components/schemas/WebhookEvent'
description: List of events the webhook is subscribed to.
secret:
type: string
description: Webhook secret for signature verification. Only returned on create or regenerate.
example: whsec_abc123...
active:
type: boolean
description: Whether the webhook is active.
default: true
Error:
type: object
required:
- code
- message
properties:
code:
type: string
example: invalid_request
message:
type: string
example: Phone number is invalid
details:
type: object
additionalProperties: true
SenderCreateRequest:
type: object
description: Create a sender. Provide `phoneNumber` for an SMS/WhatsApp sender, or `emailAddress` (with a verified email domain) for an email sender — at least one is required.
required:
- name
properties:
name:
type: string
phoneNumber:
type: string
description: Phone number in E.164 format. Required for phone-based channels (SMS, WhatsApp). Omit for an email-only sender.
setAsDefault:
type: boolean
default: false
webhookUrl:
type: string
format: uri
description: HTTPS URL for webhook events.
webhookEvents:
type: array
items:
$ref: '#/components/schemas/WebhookEvent'
description: Events to subscribe to.
emailAddress:
type: string
format: email
description: From-address for the email channel (e.g. noreply@yourdomain.com). The address's domain must be a verified email domain in your project. Setting this attaches the email channel to the sender.
example: noreply@yourdomain.com
emailDomainId:
type: string
description: ID of the verified email domain to attach. Optional — resolved from `emailAddress`'s domain when omitted.
emailFromName:
type: string
maxLength: 100
description: Display name shown in the recipient's inbox for the email channel.
emailReceivingEnabled:
type: boolean
description: Enable inbound email receiving on this sender. Requires a verified MX record on the domain; ignored otherwise.
WhatsAppBusinessProfileUpdateRequest:
type: object
description: Request body to update WhatsApp Business profile.
properties:
about:
type: string
description: Short description of the business (max 139 characters).
maxLength: 139
address:
type: string
description: Physical address of the business (max 256 characters).
maxLength: 256
description:
type: string
description: Extended description of the business (max 512 characters).
maxLength: 512
email:
type: string
format: email
description: Business email address.
websites:
type: array
description: Business website URLs (maximum 2).
maxItems: 2
items:
type: string
format: uri
vertical:
$ref: '#/components/schemas/WhatsAppBusinessProfileVertical'
parameters:
SenderIdParam:
name: senderId
in: path
required: true
schema:
type: string
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT