Authentication
API keys and webhook signing secrets.
This guide starts from an empty Wabery project and sends your first WhatsApp reply. A new project can use the shared Wabery sandbox immediately: register your test phone with the project’s join code, send one normal inbound message, then reply from the SDK, CLI, or API.
You need:
wabery login completed, or a server-side key from
Access → API keysYou do not need a dedicated WhatsApp Business number for this quickstart. The shared sandbox is enough to send your first reply.
If you just signed up, Wabery creates your first project during onboarding and shows:
wab-ABC123If you skipped that step, open the dashboard, create a project, then find its
join code in Project settings. From the CLI, wabery projects list and
wabery projects get "project_id" also return join_code plus the effective
enrollment_channel.phone_number to message.
Install the CLI and run wabery login. It opens your browser, asks you to
authorize the CLI, asks you to choose a project, creates a key scoped to that
project, then shows the key once so you can paste it back into the waiting
terminal. The CLI stores it in your local config:
npm i -g @wabery/cliwabery login # opens the browser; use --no-open on a headless boxwabery doctor # confirms the saved key, project, and base URL workNo global install needed — run it on demand instead:
npx wabery login (or pnpm dlx wabery login, bunx wabery login).
Want files for your app too? After login, run:
wabery init --name "My project"That creates .env.wabery, wabery.config.json, and a
wabery-webhook-handler.ts stub without overwriting existing files. You can
skip it for this quickstart; it is just a scaffold for your real app.
Send the project’s join code to the shared Wabery sandbox number from the phone you want to test with. If the dashboard gives you a WhatsApp button, use it; it opens WhatsApp with the code prefilled.
CLI/agent path:
wabery projects listwabery projects get "project_id" # read join_code + enrollment_channel.phone_numberAfter the phone is connected, send a normal message to the same sandbox chat:
hello from quickstartNow verify that the project has a channel and inspect the latest conversation:
wabery channels listwabery conversations listwabery conversations messages "conversation_..."Use the sandbox channel_id and the new conversation_id in the send call
below. If the conversation exists but has no inbound message yet, the join code
registered the phone but the normal test message has not arrived; send one more
WhatsApp message to the sandbox chat.
Use the TypeScript SDK, a normal HTTP library, the CLI, or cURL:
npm install @wabery/sdkPHP 8.1 or newer with the cURL extension is sufficient. No Wabery package is required.
python -m pip install requestsThe wabery CLI installed in step 2 is ready.
cURL is already available on most development systems.
Use the conversation_id from the inbound message you just sent. WhatsApp
free-form replies require an open messaging window; outside that window, send an
approved template instead.
import { Wabery } from "@wabery/sdk";
const wabery = new Wabery();
await wabery.messages.send({ channelId: "channel_...", conversationId: "conversation_...", text: "Thanks for your message",});<?php
$apiKey = getenv("WABERY_API_KEY");if ($apiKey === false || $apiKey === "") { throw new RuntimeException("WABERY_API_KEY is not set");}
$payload = json_encode([ "channel_id" => "channel_...", "conversation_id" => "conversation_...", "text" => "Thanks for your message",], JSON_THROW_ON_ERROR);
$handle = curl_init("https://api.wabery.com/v1/messages");if ($handle === false) { throw new RuntimeException("Could not initialize cURL");}
curl_setopt_array($handle, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => [ "Authorization: Bearer " . $apiKey, "Content-Type: application/json", ], CURLOPT_POSTFIELDS => $payload,]);
$responseBody = curl_exec($handle);if ($responseBody === false) { throw new RuntimeException(curl_error($handle));}
$status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE);if ($status !== 202) { throw new RuntimeException("Wabery returned HTTP {$status}: {$responseBody}");}
$message = json_decode($responseBody, true, 512, JSON_THROW_ON_ERROR);echo $message["id"] . PHP_EOL;import os
import requests
response = requests.post( "https://api.wabery.com/v1/messages", headers={ "Authorization": f"Bearer {os.environ['WABERY_API_KEY']}", }, json={ "channel_id": "channel_...", "conversation_id": "conversation_...", "text": "Thanks for your message", }, timeout=(10, 30),)response.raise_for_status()
print(response.json()["id"])wabery messages send \ --channel-id "channel_..." \ --conversation-id "conversation_..." \ --text "Thanks for your message"curl https://api.wabery.com/v1/messages \ -H "Authorization: Bearer $WABERY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "channel_id": "channel_...", "conversation_id": "conversation_...", "text": "Thanks for your message" }'A successful call returns 202 Accepted with the queued message id. Delivery
happens asynchronously. Within a few seconds, the reply should appear in the
same WhatsApp sandbox chat where you sent the normal test message. Inspect it
with wabery.messages.get("msg_..."),
wabery messages get "msg_...", or the message.status webhook. For proactive
WhatsApp sends to a phone number, use a dedicated channel and record contact
opt-in first.
For reusable clients, error handling, media, and webhook verification, see PHP, Python, and API clients.
Replies (and delivery statuses) arrive at your webhook as signed events — no polling required:
{ "event": "message.received", "payload": { "object": "message", "content": "do you ship internationally?" }}Point an endpoint at Wabery and verify the signature — see Webhooks.
Authentication
API keys and webhook signing secrets.
PHP, Python, and API clients
Reusable REST clients and the importable OpenAPI contract.
Sending messages
Text, templates, and the full send reference.
Registration intents
Link customers from your app with single-use WhatsApp onboarding links.
WhatsApp Flows
Collect structured data with in-chat forms.
Hosted functions
Run server-side TypeScript for workflow and tool logic.
CLI
Ship from your terminal.
WhatsApp MCP Server
Ship by describing it to Claude, ChatGPT, Cursor, or Codex.