EZWhatsApp Docs

Current API reference

Build against the current EZWhatsApp Platform API.

This is the public implementation reference for customers, agencies, and developers using EZWhatsApp. It covers the line-token Channel API, logged-in Workspace API, webhook setup, inbox reads, campaigns, contacts, Kanban status updates, and the operating boundaries that must not be guessed.

Quickstart

1. Log in

Get a workspace ID token.

Workspace routes act as a logged-in customer user. Use this token for admin, inbox, campaign, contact, Kanban, and line-management operations.

curl -X POST 'https://app.ezw.solutions/api/auth/login' \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "owner@example.com",
    "password": "password"
  }'
2. Create a line token

Create or rotate a line-scoped API token.

Channel API routes use a line API token beginning with ezw_line_. It is scoped to exactly one WhatsApp line.

curl -X POST 'https://app.ezw.solutions/api/lines/{lineId}/api-token' \
  -H 'Authorization: Bearer {idToken}'
3. Check health

Confirm the line is sendable.

wakeup=false makes the check read-only. Do not flatten the result into only connected or disconnected.

curl 'https://app.ezw.solutions/api/v1/health?wakeup=false&channel_type=web' \
  -H 'Authorization: Bearer {lineApiToken}'
4. Send idempotently

Queue one durable outbound attempt.

Send routes return 202 because delivery is asynchronous. Use a deterministic idempotency key for retry-safe sends.

curl -X POST 'https://app.ezw.solutions/api/v1/messages/text' \
  -H 'Authorization: Bearer {lineApiToken}' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: client-order-123' \
  -d '{
    "to": "+972501234567",
    "body": "Your order is ready"
  }'

Base URLs

Production

Workspace API: https://app.ezw.solutions
Channel API v1: https://app.ezw.solutions/api/v1

Local operator runtime

Workspace API: http://127.0.0.1:3010
Channel API v1: http://127.0.0.1:3010/api/v1

Authentication

API family Token Use for Do not use for
Channel API v1 Authorization: Bearer ezw_line_... Machine-to-machine health, direct or group text sends, direct or group URL media sends, and webhook configuration for one line. Workspace admin/operator routes, team management, cross-line operations, campaigns, contacts, or Kanban.
Workspace API Authorization: Bearer {idToken} Logged-in owner, admin, or operator work: lines, QR, tokens, webhooks, inbox, assignments, notes, image sends, contacts, campaigns, Kanban, billing, and signed media reads. Channel API v1 routes.

Current support matrix

Capability Endpoint Auth Status
Check line health GET /api/v1/health Line API token Supported.
Send direct or group text POST /api/v1/messages/text Line API token Supported. Direct/conversation text may include CTA URL; group text may not.
Send direct or group URL media POST /api/v1/messages/media Line API token Supported for external URL image, video, audio, and document. No conversationId target on this route.
Configure line webhooks GET/POST /api/v1/webhooks Line API token Supported for the current line.
Create or rotate line token POST /api/lines/{lineId}/api-token Workspace ID token Supported for allowed workspace users.
Request QR / reconnect line POST /api/lines/{lineId}/qr Workspace ID token Supported when the line state allows QR request.
Workspace send POST /api/lines/{lineId}/send Workspace ID token Supported for text, one image upload, or forwarding a stored message.
Read inbox data GET /api/conversations, GET /api/conversations/{conversationId}/messages Workspace ID token Supported with organization and line visibility checks.
Contacts GET/POST /api/contacts Workspace ID token Supported for owners/admins.
Campaigns GET/POST /api/campaigns, POST /api/campaigns/{campaignId}/launch, POST /api/campaigns/{campaignId}/cancel Workspace ID token Supported for owners/admins in the scoped customer-admin campaign surface.
Kanban fields and conversation status GET/POST /api/contact-statuses, PATCH /api/conversations/{conversationId}/status Workspace ID token Supported with optimistic version checks.
Live update hints GET /api/live/stream Workspace ID token Supported as SSE hints only. Refetch DB APIs for truth.

Target model

Target Fields Allowed on Rules
Direct phone recipient to on Channel API, recipientPhoneNumber or phoneNumber on Workspace API Channel text, Channel URL media, Workspace send. Use an explicit phone number. Do not derive a phone number from provider-only ids.
WhatsApp group groupId, group_id, groupJid, or group_jid Channel text and Channel URL media only. Must be an exact WhatsApp group JID ending in @g.us. Not a Workspace send target.
Existing Platform conversation conversationId or conversation_id Channel text and Workspace send. Use this for replies to an existing Platform conversation. Channel URL media does not accept conversationId.

Check channel health

Use this before sending and before diagnosing automation failures. wakeup=false makes the check read-only; wakeup=true may queue a wakeup when the line needs one.

curl -X GET 'https://app.ezw.solutions/api/v1/health?wakeup=false&channel_type=web' \
  -H 'Authorization: Bearer {lineApiToken}'
{
  "channelId": "{lineId}",
  "channelType": "web",
  "wakeupRequested": false,
  "health": {
    "status": {
      "code": 4,
      "text": "AUTH"
    }
  },
  "line": {
    "displayName": "Billing",
    "phoneNumber": "+972...",
    "lifecycleState": "connected",
    "effectiveSendState": "sendable",
    "sendable": true
  }
}

Send text with Channel API v1

Use this for machine-to-machine text sends from the line resolved by the line API token. Use exactly one of to, groupId, or conversationId.

Direct phone recipient

curl -X POST 'https://app.ezw.solutions/api/v1/messages/text' \
  -H 'Authorization: Bearer {lineApiToken}' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: send-once-123' \
  -d '{
    "to": "+972501234567",
    "body": "Test message",
    "ctaUrlButton": {
      "displayText": "Open offer",
      "url": "https://example.com/offers/123"
    }
  }'

Group target

curl -X POST 'https://app.ezw.solutions/api/v1/messages/text' \
  -H 'Authorization: Bearer {lineApiToken}' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: group-send-once-123' \
  -d '{
    "groupId": "120363012345678901@g.us",
    "body": "Group update"
  }'

Existing conversation

curl -X POST 'https://app.ezw.solutions/api/v1/messages/text' \
  -H 'Authorization: Bearer {lineApiToken}' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: conversation-reply-123' \
  -d '{
    "conversationId": "{conversationId}",
    "body": "Thanks, I am checking this now."
  }'
Field Required Notes
to Conditional Direct recipient phone number. Use exactly one target field.
groupId, group_id, groupJid, or group_jid Conditional Exact WhatsApp group JID ending @g.us. CTA URL buttons are not supported for group sends.
conversationId or conversation_id Conditional Existing Platform conversation id. Use this for webhook replies when available.
body Yes Text body. Empty strings are rejected.
ctaUrlButton or cta_url_button No Direct/conversation text only. Requires displayText or text, plus an HTTP/HTTPS URL.
Idempotency-Key header or idempotencyKey body field Recommended Header takes precedence. Use a deterministic key for retry-safe sends.

Send URL media with Channel API v1

Use this when the media is already available at a public HTTP/HTTPS URL. This route accepts direct phone or group targets only.

Direct URL media

curl -X POST 'https://app.ezw.solutions/api/v1/messages/media' \
  -H 'Authorization: Bearer {lineApiToken}' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: media-once-123' \
  -d '{
    "to": "+972501234567",
    "type": "document",
    "media": {
      "url": "https://cdn.example.com/invoice.pdf",
      "mime_type": "application/pdf",
      "filename": "invoice.pdf"
    },
    "caption": "Invoice"
  }'

Group URL media

curl -X POST 'https://app.ezw.solutions/api/v1/messages/media' \
  -H 'Authorization: Bearer {lineApiToken}' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: group-media-once-123' \
  -d '{
    "groupId": "120363012345678901@g.us",
    "type": "image",
    "mediaUrl": "https://cdn.example.com/photo.jpg",
    "contentType": "image/jpeg",
    "caption": "Photo update"
  }'
Field Required Notes
to Conditional Direct recipient phone number. Use exactly one of to or groupId.
groupId, group_id, groupJid, or group_jid Conditional Exact WhatsApp group JID ending @g.us.
type or mediaKind Yes One of image, video, audio, or document.
media.url, mediaUrl, or url Yes External HTTP/HTTPS media URL. Localhost, loopback, and private IP targets are rejected.
media.mime_type, mimeType, or contentType Yes MIME type such as image/jpeg or application/pdf.
media.filename or fileName Required for documents Preserved as the document filename sent through the provider.
caption or body No Caption for image, video, or document media. Audio captions are ignored by the provider.

Workspace send

Workspace sends act as a logged-in user and enforce organization scope, line visibility, line sendability, and membership permissions. Idempotency-Key is required.

Reply to an existing conversation

curl -X POST 'https://app.ezw.solutions/api/lines/{lineId}/send' \
  -H 'Authorization: Bearer {idToken}' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: workspace-send-001' \
  -d '{
    "conversationId": "{conversationId}",
    "bodyText": "Thanks, I am checking this now."
  }'

Send to a direct phone number

curl -X POST 'https://app.ezw.solutions/api/lines/{lineId}/send' \
  -H 'Authorization: Bearer {idToken}' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: workspace-direct-001' \
  -d '{
    "recipientPhoneNumber": "+972501234567",
    "bodyText": "Thanks, I am checking this now."
  }'

Send one uploaded image

curl -X POST 'https://app.ezw.solutions/api/lines/{lineId}/send' \
  -H 'Authorization: Bearer {idToken}' \
  -H 'Idempotency-Key: image-send-001' \
  -F 'conversationId={conversationId}' \
  -F 'bodyText=Optional caption text' \
  -F 'media=@/absolute/path/photo.jpg;type=image/jpeg'

Forward a stored message

curl -X POST 'https://app.ezw.solutions/api/lines/{lineId}/send' \
  -H 'Authorization: Bearer {idToken}' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: forward-001' \
  -d '{
    "recipientPhoneNumber": "+972501234567",
    "forwardedFromMessageId": "{messageId}"
  }'
  • Use exactly one target: conversationId or recipientPhoneNumber. phoneNumber is accepted as an alias.
  • Send content must be one of bodyText, one image in media, or forwardedFromMessageId.
  • Forwarded sends preserve the selected message content and cannot include replacement text, media, or CTA content.
  • Workspace multipart upload accepts one image file up to 25 MB. Generic documents, PDFs, video, audio, and arbitrary files are not supported on this route.
  • There is no Workspace API group send route.

Webhooks

Webhooks let customer or agency systems receive durable event deliveries for the configured line. Configure by line token or workspace token.

Line token

Configure current line webhook

curl -X POST 'https://app.ezw.solutions/api/v1/webhooks' \
  -H 'Authorization: Bearer {lineApiToken}' \
  -H 'Content-Type: application/json' \
  -d '{
    "targetUrl": "https://agency.example.com/ezw/webhook",
    "events": ["messages.post", "statuses.post", "chats.post"],
    "payloadFormat": "whapi",
    "enabled": true,
    "persistent": true
  }'
Workspace token

Configure webhook for a managed line

curl -X POST 'https://app.ezw.solutions/api/lines/{lineId}/webhooks' \
  -H 'Authorization: Bearer {idToken}' \
  -H 'Content-Type: application/json' \
  -d '{
    "targetUrl": "https://agency.example.com/ezw/webhook",
    "events": ["messages.post", "statuses.post"],
    "enabled": true
  }'

Deliveries include X-EZW-Webhook-Event, X-EZW-Line-Id, X-EZW-Webhook-Id, and X-EZW-Delivery-Id headers. Return any 2xx status to accept a delivery. Store delivery ids to make receivers idempotent.

Read the webhook guide

Read inbox data

Use workspace tokens for inbox reads. Routes are organization-scoped and enforce line visibility. SSE events are hints only; clients must refetch durable DB-backed APIs.

curl 'https://app.ezw.solutions/api/conversations?lineId={lineId}&limit=25' \
  -H 'Authorization: Bearer {idToken}'

curl 'https://app.ezw.solutions/api/conversations/{conversationId}/messages?limit=50' \
  -H 'Authorization: Bearer {idToken}'

curl -N 'https://app.ezw.solutions/api/live/stream' \
  -H 'Authorization: Bearer {idToken}' \
  -H 'Accept: text/event-stream'

Contacts

Contacts are customer organization data. Owners and admins can list or import contacts. Imports remain auditable through source filename, batch, rows, invalid rows, and summary counts.

List contacts

curl 'https://app.ezw.solutions/api/contacts?query=Acme&limit=100' \
  -H 'Authorization: Bearer {idToken}'

Import contacts

curl -X POST 'https://app.ezw.solutions/api/contacts' \
  -H 'Authorization: Bearer {idToken}' \
  -H 'Content-Type: application/json' \
  -d '{
    "sourceFilename": "customers.csv",
    "rows": [
      {
        "phoneNumber": "+972501234567",
        "displayName": "Dana Levi",
        "company": "Acme",
        "email": "dana@example.com",
        "tags": ["vip", "orders"],
        "externalId": "crm-123"
      }
    ]
  }'

Accepted row aliases include phone for phoneNumber, name for displayName, external_id for externalId, and semicolon/comma-separated string tags. Imports must contain 1 to 5000 rows.

Campaigns

Campaigns are a scoped customer-admin workflow for owners and admins. They are separate from inbox replies and direct-send loops. Do not use this surface for scraped, purchased, unrelated, or cold outbound recipient lists.

Create draft

curl -X POST 'https://app.ezw.solutions/api/campaigns' \
  -H 'Authorization: Bearer {idToken}' \
  -F 'name=May offers' \
  -F 'lineId={lineId}' \
  -F 'bodyText=Hello, {name}. Your offer is ready.' \
  -F 'recipientsCsv=phone,name,company
+972501234567,Dana Levi,Acme' \
  -F 'sourceFilename=may-offers.csv' \
  -F 'minDelaySeconds=30' \
  -F 'maxDelaySeconds=120' \
  -F 'hourlyCap=60'

Launch, inspect, cancel

curl 'https://app.ezw.solutions/api/campaigns?limit=100' \
  -H 'Authorization: Bearer {idToken}'

curl -X POST 'https://app.ezw.solutions/api/campaigns/{campaignId}/launch' \
  -H 'Authorization: Bearer {idToken}'

curl -X POST 'https://app.ezw.solutions/api/campaigns/{campaignId}/cancel' \
  -H 'Authorization: Bearer {idToken}'
  • Required draft fields: name, lineId, bodyText, and recipientsCsv.
  • Optional fields: sourceFilename, minDelaySeconds, maxDelaySeconds, hourlyCap, and image file field image.
  • Campaign images must be PNG, JPEG, WebP, or GIF and 25 MB or smaller.
  • maxDelaySeconds must be greater than or equal to minDelaySeconds. hourlyCap must be positive.
  • A campaign can launch only from draft status and only when valid recipients exist. Terminal campaigns cannot be cancelled.

Kanban and conversation status

Kanban fields and conversation status changes are durable workspace state. Use expectedVersion when moving conversations so stale clients do not overwrite newer state.

List or create fields

curl 'https://app.ezw.solutions/api/contact-statuses' \
  -H 'Authorization: Bearer {idToken}'

curl -X POST 'https://app.ezw.solutions/api/contact-statuses' \
  -H 'Authorization: Bearer {idToken}' \
  -H 'Content-Type: application/json' \
  -d '{ "name": "Waiting for payment" }'

Move a conversation

curl -X PATCH 'https://app.ezw.solutions/api/conversations/{conversationId}/status' \
  -H 'Authorization: Bearer {idToken}' \
  -H 'Content-Type: application/json' \
  -d '{
    "statusId": "{statusId}",
    "expectedVersion": 7
  }'

Use "statusId": null to remove a conversation from Kanban. A stale expectedVersion returns a conflict with current state.

Common status codes

Status Meaning
200Read or update succeeded.
201Token, webhook, contact import, campaign draft, or Kanban field was created.
202Send was durably queued or an idempotent attempt already existed.
400Invalid query, payload, target, media, campaign, webhook, or idempotency key.
401Missing or invalid bearer token.
403Authenticated actor is not allowed to perform the operation.
404Tenant-scoped resource was not visible or does not exist.
409Optimistic conflict, campaign state conflict, or line not currently sendable.
501Required PostgreSQL source of truth or media bucket is not configured.

Non-negotiable implementation boundaries

  • The API is for support inbox workflows, client notifications, and scoped customer-admin campaign workflows, not cold outbound tooling.
  • Every customer-owned record is organization-scoped.
  • Line API tokens are scoped to exactly one WhatsApp line.
  • Outbound sends are idempotent and asynchronous.
  • Provider echoes must not be treated as authoritative assignment or ownership state.
  • Redis and live events are never the source of truth. Refetch DB-backed APIs.
  • Session credentials and provider runtime details stay behind EZWhatsApp provider contracts.
  • Campaigns require explicit recipient-source, consent, opt-out, policy, and compliance review by the customer.