Migrate from Z-API to the official WhatsApp API

In short: to move a bot or an automation from Z-API's QR-code product to Meta's official WhatsApp Cloud API, connect the number to ZapClaw through Coexistence, send through ZapClaw's REST API instead of /send-text, and read ZapClaw's webhook instead of ReceivedCallback. The number keeps working in the WhatsApp Business app on your phone. Below: the steps, the text and media send fields and the main webhook fields side by side, and what has no equivalent on the official API.

ZapClaw connects your number to Meta's Cloud API through Coexistence, with no QR-code session and no Meta app of your own.

Business plan: US$ 29 per month, with unlimited numbers

Start for free See the field mapping

Why move to the official API

Meta's policies still apply on the official API: Meta can restrict or disable an account that breaks them, and ZapClaw's API then answers 403 with the reason (errors).

What you need to connect

You do not need a company tax ID or a verified business to connect. The full checklist.

The move, step by step

  1. Map your code first. Change your send calls and your webhook handler with the tables below, or put the shim in front of your flow so it keeps its Z-API calls while you rewrite it.
  2. Back up your chats in the WhatsApp Business app and check the requirements above.
  3. Connect the number. Create your ZapClaw account and connect the number in Meta's window from the dashboard. Meta then unlinks every linked device from the account, the Z-API session included. Disconnect the instance in Z-API afterwards, so nobody reads its QR code again and two systems answer the same customer.
  4. Set the webhook. In the dashboard, under Webhook, paste your public HTTPS URL, pick the events and copy the signing secret. Tick Messages sent from the WhatsApp Business app if your flow read callbacks with fromMe: true.
  5. Create an API key and send with it in the X-API-Key header to https://api.zapclaw.app. The from field names the number, so one key sends from every number of the account, with no instance id or token in the path.
  6. Create your templates. Outside the 24-hour window, and to write first, Meta accepts only an approved template. Create them in the dashboard or with POST /api/v1/send/templates, and pass language when you send one: the API's default is pt_BR.
  7. Test. Press Send test event on the dashboard's Webhook page, then write to your number from another phone and check that your flow receives the message and replies.

Sending: Z-API calls and their ZapClaw equivalent

Z-API's paths start with https://api.z-api.io/instances/{instanceId}/token/{token}, shortened below to …. ZapClaw's start with https://api.zapclaw.app.

Z-API ZapClaw
Send text POST …/send-text POST /api/v1/send/text
Send media …/send-image, …/send-video, …/send-audio, …/send-sticker, …/send-document/{extension} POST /api/v1/send/media
Authentication Instance id and token in the path, plus an optional Client-Token header X-API-Key header
Your number The instance, in the path from, in the body
Recipient phone: a number or a group id to: a number, digits only
Text message text, up to 4,096 characters
Media file image, video, audio, sticker or document: a link or base64 type, plus link (a public https URL) or mediaId from POST /api/v1/media
Document name The extension in the path, fileName in the body filename, with its extension
Caption caption caption (image, video, document)
Reply to a message messageId, on text and media sends replyTo: the wamid of the message
Delays delayMessage, delayTyping No equivalent. POST /api/v1/send/typing shows a typing indicator on a message you received.
View once viewOnce Not supported on a Coexistence number
Edit a sent message editMessageId No equivalent
Answer zaapId, messageId, id 201 with message.id and message.wamid

Location, contact cards and reactions go through POST /api/v1/send/location, /send/contacts and /send/reaction (API reference). The shim further down does not convert those calls yet: send them straight to ZapClaw.

Receiving: ReceivedCallback and its ZapClaw equivalent

Z-API posts each message as a flat object with type: "ReceivedCallback". ZapClaw posts event, timestamp, data and account to one URL for all your numbers.

Z-API ZapClaw
Event type: "ReceivedCallback" event: "message.received", also in the X-ZapClaw-Event header
Which of your numbers instanceId, connectedPhone account.phone_number_id, account.display_phone_number
Sender phone data.from, and data.user_id, the contact's durable id
Sent by you fromMe: true A separate message.echo event, with data.to, for messages sent from the WhatsApp Business app or WhatsApp Web (opt-in)
Message id messageId data.message_id, a wamid
Contact name senderName, chatName data.contact_name
Time momment: milliseconds in the documentation's examples data.timestamp: seconds, as a string
Text text.message data.text.body
Image image.imageUrl, image.mimeType, image.caption data.image.media_url, data.image.mime_type, data.image.caption
Audio audio.audioUrl, audio.ptt data.audio.media_url, data.audio.voice
Video video.videoUrl, video.caption data.video.media_url, data.video.caption
Document document.documentUrl, document.fileName data.document.media_url, data.document.filename
Sticker sticker.stickerUrl data.sticker.media_url
Location location.latitude, location.longitude data.location.latitude, data.location.longitude
Button reply buttonsResponseMessage.buttonId data.interactive.button_reply.id; a template's quick reply arrives as data.button.payload
List reply listResponseMessage.selectedRowId data.interactive.list_reply.id
Quoted message referenceMessageId data.context.id
Groups isGroup, participantPhone Always a one-to-one chat
Delivery status MessageStatusCallback, with SENT, RECEIVED, READ, READ_BY_ME or PLAYED message.status, with data.status set to sent, delivered, read or failed
Send result DeliveryCallback, with error when the send failed message.status with failed, and Meta's reason in data.errors
Media storage Z-API keeps files for 30 days Meta keeps received media for around 30 days; download it with your X-API-Key
Request authentication None described in the webhook documentation X-ZapClaw-Signature: an HMAC-SHA256 of the raw body, keyed with your signing secret (how to verify it)

Z-API reports a failed send in its DeliveryCallback webhook, in the error field; its MessageStatusCallback has no failed status. On ZapClaw a message.status with failed carries Meta's reason in data.errors: move your DeliveryCallback error handling there. Deliveries can arrive out of order: when order matters, sort by the top-level sequence.

What has no equivalent, and what to do instead

Keep using the WhatsApp Business app on the phone: Meta disconnects a Coexistence number whose app goes about 14 days without use, and ZapClaw tells your webhook with an account.account_update event (details).

A short adapter

Two functions for Node.js 18 or newer, with no dependency: the first sends where your code called send-text, the second turns a ZapClaw message.received into the ReceivedCallback fields your flow already reads. Check the X-ZapClaw-Signature header before you trust a body.

// Sending: what was POST .../instances/{instanceId}/token/{token}/send-text
const sendText = async ({ phone, message }) => {
  const res = await fetch('https://api.zapclaw.app/api/v1/send/text', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': process.env.ZAPCLAW_API_KEY,
    },
    body: JSON.stringify({ from: process.env.ZAPCLAW_FROM, to: phone, text: message }),
  });
  if (!res.ok) throw new Error(`ZapClaw answered ${res.status}`);
  const sent = (await res.json()).message;
  return { zaapId: sent.id, messageId: sent.wamid, id: sent.wamid };
};

// Receiving: the ReceivedCallback fields, taken from a message.received body
const fromZapClaw = (body) => ({
  phone: body.data.from,
  messageId: body.data.message_id,
  senderName: body.data.contact_name,
  momment: Number(body.data.timestamp) * 1000,
  text: body.data.text ? { message: body.data.text.body } : undefined,
});

The full version is on GitHub: zapclaw/migrate-to-whatsapp-official-api converts text, media, statuses and webhooks and ships a small shim. A call it does not convert, such as a location or a contact card, is refused with a clear error code: send those calls straight to ZapClaw's API. Point your flow's Z-API base URL at the shim: it forwards the calls it converts to ZapClaw and turns ZapClaw's signed webhooks back into ReceivedCallback, MessageStatusCallback and, for a failed send, DeliveryCallback, so you can move first and rewrite later. MIT license.

If your setup is different

What it costs

Meta bills its WhatsApp message fees to you directly, with no ZapClaw markup. Every plan and price.

Related

Sources

Checked on 15 October 2026. Z-API changes its API over time, so check its documentation before you move.

Move your Z-API flows to the official API

Start on the free plan: one number, no card. Move to the Business plan when you need more numbers or more messages.

Start for free Read the webhook docs