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, inbound contact identity, managed customer tags, inbox operations, analytics, campaigns, and the boundaries that must not be guessed.
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 sends, webhooks, exact inbound-message contact identity, and same-line contact display-name updates. Customer tag access is a separate, explicitly enabled organization-directory permission.
Workspace admin/operator routes, team management, campaigns, contact imports, Kanban, or the separate evaluation evidence route. Do not assume customer tag access from the token alone.
Workspace API
Authorization: Bearer {idToken}
Logged-in owner, admin, or operator work: lines, QR, tokens, webhooks, inbox, assignments, notes, image sends, contacts, customer tags, analytics, campaigns, Kanban, billing, and signed media reads, subject to route permissions.
Line-token Channel API v1 routes or the separate evaluation evidence route.
Bot evaluation evidence
Authorization: Bearer ezw_eval_...
Approved evaluator only: bounded, audited evidence reads for one token-bound organization and line, limited by the token's operation allowlist.
Messaging, webhooks, customer tags, workspace routes, or self-service access. There is no public token-provisioning route.
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.
Update contact display name by phone
PATCH /api/v1/contacts/by-phone
Line API token
Best-effort exact normalized-phone match on the current line. A miss is a successful no-op.
Read inbound-message contact identity
POST /api/v1/messages/contact-identity
Line API token
Exact retained individual inbound message on the token's line; returns a verified phone or explicit missing/conflict.
Read bounded bot evaluation evidence
POST /api/v1/evaluation/evidence
Dedicated evaluation evidence token
Restricted approved integration. Allowed operations are context_window, context_windows, daily_coverage, and direct_text_reply_pairs.
Read definitions or change customer tags by phone
GET /api/v1/customer-tags, GET/PUT/DELETE /api/v1/contacts/by-phone/tags
Line API token with customer tags access enabled
Organization-shared directory metadata. Exact phone lookup; no implicit customer creation.
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.
Team seen state and bot ownership
POST /api/conversations/{conversationId}/messages/seen, POST /api/conversations/bot-control, GET/POST /api/conversations/{conversationId}/bot-control
Workspace ID token
Seen state is internal team metadata. Bot ownership is available only through the configured Printeam integration; unknown remains unknown.
Contacts
GET/POST /api/contacts
Workspace ID token
Supported for owners/admins.
Manage customer tag definitions and assignments
GET/POST /api/customer-tags, PATCH /api/customer-tags/{tagId}, GET /api/contacts/{contactId}/tags, PUT/DELETE /api/contacts/{contactId}/tags/{tagId}
Workspace ID token
Organization owners/admins manage definitions and assignments. Every active line token can manage tags within its own organization.
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.
Customer-admin analytics
GET /api/admin/analytics, POST /api/admin/analytics/refresh
Workspace ID token
Owners/admins read the last complete snapshot and may queue a durable refresh.
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}'
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."
}'
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.
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. A messages.post payload produced by identity-conflict recovery has _ezw_recovery_kind: "identity_conflict" at its root; record it without treating it as a new customer turn. Ordinary live inbound and delivery retries do not receive that marker.
Use the exact provider message id from an individual inbound message. The line token determines organization and line scope. The request body accepts only providerMessageId; it does not accept a caller-selected line or organization.
providerMessageId must be an unmodified 1–512 character identifier without whitespace or an id: prefix. contactIdentity.state may be resolved, missing, or conflict. For missing or conflict, phoneNumber and source are null. A resolved phone comes only from a provider phone JID or a retained one-to-one provider-observed alias; never derive one from LID digits, display names, imported contacts, or webhook claims. Compare the returned canonical ids with the message being linked. An absent, outbound, or non-individual message returns 404; a tombstoned message returns 410. This read route returns no message body and changes no customer record.
Restricted bot evaluation evidence
POST /api/v1/evaluation/evidence is a separate read-only integration for an approved evaluator. It requires a dedicated ezw_eval_... token bound to one organization and line, with an operation allowlist, expiry, revocation, rate limit, and access audit. Line and workspace tokens are rejected. There is no public provisioning route or UI; an authorized operator must provision and transfer the credential before use. The request does not select another organization or line.
The supported operations are context_window, context_windows, daily_coverage, and direct_text_reply_pairs. This route does not grant contact identity or message-send access. Approved evaluators should use the exact request contract supplied during credential provisioning.
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.
After inbound messages are actually visible to an operator, submit 1–100 unique message UUIDs from the same visible conversation. The latest supplied message advances the team seen frontier through older inbound history. The first human viewer is retained; repeated calls are idempotent. This does not send a WhatsApp read receipt to the customer.
Read bot ownership separately from inbox assignment
For visible individual conversations, a bounded list request checks up to 50 unique conversation UUIDs. The selected-conversation read requires scope=selected. Ownership may be bot, human, or unknown when the configured Printeam integration cannot prove a state. Inbox assignment is a separate Platform record and must not be inferred from bot ownership.
On the configured Printeam line, an active member with conversations.claim may call POST /api/conversations/{conversationId}/bot-control to request return to bot. Treat it as complete only when the response explicitly confirms bot ownership. The request does not clear the Platform assignment.
Contacts
Contacts are customer organization data. A line API token can set the final operator-facing display name through an exact phone match on its own line. Owners and admins can separately list or import directory contacts.
Use an international number beginning with +. Platform searches only the line resolved from the token, updates every exact phone match, and never creates a contact. HTTP 200 with matched: false is an expected completed no-op.
External display-name updates are idempotent at the stored-value level: an exact match already holding the final name is not rewritten or audited again. For workspace imports, 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.
New canonical individual messages can create one organization directory customer when the counterpart has a validated international phone identity. Opaque LIDs, group messages, quarantined identities, and rejected sends do not create one. Message-created customers do not overwrite existing customer details or tag assignments. A directory row is metadata for enrichment; it is not proof of WhatsApp identity, marketing consent, or campaign eligibility.
Managed customer tags
Managed definitions have stable UUIDs and belong to the organization directory. They are separate from free-text tags on contact imports. Renaming preserves assignments; archiving keeps existing assignments visible and blocks new attachments until restoration. Operators can see managed tags in the inbox, while owners/admins manage definitions and assignments.
Use DELETE /api/contacts/{contactId}/tags/{tagId} to remove that one assignment. Attach/remove responses include changed; an already satisfied operation returns false.
Use any active line token for customer tags
Every active line token can access shared organization directory tags, including customers associated with other lines in its organization. Token rotation keeps this capability; revocation or line archival ends it. Message and provider permissions do not change.
Use the returned tag id, not its display name. The line-token routes GET/PUT/DELETE /api/v1/contacts/by-phone/tags use an exact international directory phone number beginning with +. A missing directory customer returns 404 and is never created by tagging. See copy-ready examples for encoded phone queries and write bodies.
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.
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.
Customer-admin analytics snapshots
Owners/admins can read the last complete snapshot for 7, 30, or 90 days, optionally scoped to one active organization line. A queued or failed refresh does not erase an earlier successful snapshot. A manual refresh queues durable worker work or coalesces with an existing request; it does not calculate results in the web request.
Read responses may contain analytics: null while a first snapshot is queued. Inspect refresh.state, snapshot.generatedAt, and snapshot.dataThrough before using the figures.
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.
Use "statusId": null to remove a conversation from Kanban. A stale expectedVersion returns a conflict with current state.
Common status codes
Status
Meaning
200
Read or update succeeded.
201
Token, webhook, contact import, customer tag, campaign draft, or Kanban field was created.
202
Send was durably queued or an idempotent attempt already existed.
400
Invalid query, payload, target, media, campaign, webhook, or idempotency key.
401
Missing or invalid bearer token.
403
Authenticated actor is not allowed to perform the operation.
404
Tenant-scoped resource was not visible or does not exist.
409
Optimistic conflict, campaign state conflict, or line not currently sendable.
410
The exact inbound message requested for contact identity was tombstoned.
501
Required 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 for messaging and provider-contact reads. Customer tag access is a separate, explicitly enabled organization-directory capability.
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.