Quick Start
Three steps to your first request.
- 1
Create an access key
Go to the Access Keys page and create a new key
Create Access Token - 2
Add the key to the request header
Authorization: YOUR_ACCESS_… - 3
Send your first request
Use the code examples below
Vibrate API Skill
Teach your AI assistant the entire Vibrate API in one paste. Works with ChatGPT, Claude, Claude Code, Cursor and GitHub Copilot.
- 01Copy the skill (or the short prompt if your tool can open links).
- 02Paste it into ChatGPT, Claude, Cursor or Claude Code as the first message.
- 03Ask for what you need: "send an SMS from my Node server", "poll delivery status", "send a WhatsApp template to my list".
- Works with
- ChatGPT ·Claude ·Claude Code ·Cursor ·GitHub Copilot
mkdir -p .claude/skills/vibrate-api curl -fsSL https://www.vibrate.co.il/vibrate-api-skill.md -o .claude/skills/vibrate-api/SKILL.md
Overview
Send template messages via WhatsApp Business API
whatsappNumberId — Go to WhatsApp Channels and copy the desired number's ID
templateId — Go to Message Templates and copy the desired template's ID
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
whatsappNumberId | string | Yes | WhatsApp channel ID to send from |
templateId | string | Yes | Template ID (including language) |
recipient | string | Yes | Recipient phone number (any format) |
bodyVariables | string[] | No | Array of values for body variables ({{1}}, {{2}}) |
headerVariables | string[] | No | Array of values for header variables |
headerMediaUrl | string | No | URL for media (image/video/document) |
Phone Number Format
You can send phone numbers in any format — the system will automatically convert to the correct format:
0501234567972501234567+972501234567Endpoints
These two endpoints live on www.vibrate.co.il, not api.vibrate.co.il, with the same access token.
Template Variables
שלום {{1}}, ההזמנה שלך מספר {{2}} נשלחה!
שלום דוד, ההזמנה שלך מספר ORD-12345 נשלחה!
Important: The order of values in the bodyVariables array must match the order of variables in the template.
Header Types
TEXT
Plain text, can contain variables
IMAGE
JPG/PNG, up to 5MB
VIDEO
MP4, up to 16MB
DOCUMENT
PDF recommended, up to 100MB
Common Errors
INVALID_TEMPLATETemplate not found or not approvedINVALID_PHONEInvalid phone numberMISSING_VARIABLESMissing required variablesINVALID_MEDIA_URLMedia URL is not accessible or invalidINVALID_WHATSAPP_NUMBERInvalid WhatsApp channel IDDelivery status webhook
Instead of polling, Vibrate can POST a signed event to you on every WhatsApp status change: sent, delivered, read or failed. It is the same webhook as for SMS, same URL and same signing secret, only with a different event name.
- 1Save an HTTPS URL on the WhatsApp → API & Webhooks page. The secret is shown once.
- 2On the same page turn on "Also send WhatsApp status events" and send a WhatsApp test event.
- 3Verify every request: HMAC-SHA256 over "timestamp.body" with the secret, compared to the X-Vibrate-Signature header. The code that verifies SMS events works unchanged.
Event body
{
"apiVersion": "v1",
"event": "whatsapp.status",
"messageId": "wamid.HBgLOTcyNTAxMjM0NTY3...",
"whatsappNumberId": "c1f7c0e4-2b6e-4f3a-9d2a-5e8b1a2c3d4e",
"templateId": "8a4f2d1e-6c3b-4a5d-9e8f-1b2c3d4e5f6a",
"batchId": "batch_abc123",
"campaignId": null,
"subAccountId": null,
"recipient": "972501234567",
"status": "failed",
"final": true,
"error": { "message": "Message undeliverable", "code": "131026" },
"sentAt": "2026-10-13T10:12:00.000Z",
"occurredAt": "2026-10-13T10:12:07.000Z"
}read is terminal. failed is not always: Meta sometimes revises a failed message to delivered or read later, and another event is sent. Let a later status overwrite failed.
Respond 2xx within 10 seconds. A failure is retried twice (after about 5 and 30 seconds). Events can arrive more than once and out of order; de-duplicate on messageId + status.
Sub-accounts
A sub-account key works on every WhatsApp endpoint, but only for numbers assigned to its sub-account: any other whatsappNumberId is rejected with 403 FORBIDDEN, and the list endpoints return only those numbers and their templates. Status events for those numbers carry subAccountId and go to the sub-account's own webhook when it has one, otherwise to the account's webhook. Numbers are assigned on the WhatsApp → API & Webhooks page.
Automatic sends from an APINo code
Instead of writing code that calls the Vibrate API, let Vibrate call yours. An automation pulls a list of people from any URL that returns JSON on a schedule, and sends each of them an approved WhatsApp template - once. Built for appointment reminders, order updates and lead follow-ups.
Open automationsHow it works
1.Source
The API URL (GET or POST), query parameters, headers that carry your API key, and a request body. Header values are encrypted and never shown again after saving.
2.Extraction
Where the list lives in the response, where the phone number is inside each item, a display name, filters, and a "same person" key that prevents double sends.
3.Message
A WhatsApp number, an approved template, and one variable per {{N}} in the template. Each variable is text with placeholders taken from the item.
4.Schedule
How often to call the API (5 minutes minimum), a send window in Israel time, and how often the same person may be messaged again.
5.Approval
By default every batch waits for approval: you see each person and the exact message, then press Send. In auto mode messages go out as soon as the list is ready.
Example: a reminder for tomorrow's appointment
Say your booking system answers /appointments?from={{today+1}}&to={{today+1}} with this response:
{
"data": [
{
"id": "apt_18233",
"status": "confirmed",
"startTime": "2026-09-09T07:00:00.000Z",
"customer": {
"firstName": "דנה",
"lastName": "לוי",
"mobile": "050-1234567"
}
},
{
"id": "apt_18234",
"status": "cancelled",
"startTime": "2026-09-09T08:30:00.000Z",
"customer": {
"firstName": "יוסי",
"lastName": "כהן",
"mobile": "052-7654321"
}
}
]
}Settings that match this response
| Setting | Value |
|---|---|
| Items path | data |
| Phone field | customer.mobile |
| Name | {{item.customer.firstName}} {{item.customer.lastName}} |
| "Same person" key | {{item.id}} |
| Filter | status equals confirmed |
| Body variables | {{item.customer.firstName}} · {{item.startTime|date}} · {{item.startTime|time}} |
Result: Dana receives "Hi Dana, a reminder for your appointment tomorrow, 09/09/2026 at 10:00". The Preview tab shows exactly this before anything is sent.
Date placeholders in the request
Most APIs expect a date range, and every vendor spells it differently. Write the vendor's format around placeholders that are filled at request time, in Israel time. They work in the URL, query parameters, headers and body. Each one takes a day offset: {{today+1}}, {{startOfDay-7}}.
| Placeholder | Example value | |
|---|---|---|
{{now}} | 2026-09-08T11:05:00.000Z | Current instant in UTC (ISO) |
{{unix}} | 1788001500 | Current instant as epoch seconds |
{{today}} | 2026-09-08 | Calendar day in Israel |
{{today+1}} | 2026-09-09 | Calendar day in Israel |
{{startOfDay}} | 2026-09-07T21:00:00.000Z | Israel midnight, as UTC |
{{endOfDay}} | 2026-09-08T20:59:59.999Z | Israel 23:59:59.999, as UTC |
{{startOfDay+1}} | 2026-09-08T21:00:00.000Z | Israel midnight, as UTC |
{{endOfDay+1}} | 2026-09-09T20:59:59.999Z | Israel 23:59:59.999, as UTC |
{{startOfDayLocal}} | 2026-09-08T00:00:00.000 | Israel midnight, wall clock with no zone |
{{endOfDayLocal}} | 2026-09-08T23:59:59.999 | Israel end of day, wall clock with no zone |
Placeholders in the message
The name, the "same person" key and the template variables are free text with three kinds of placeholder:
{{fieldKey}}A field you defined under "Fields to extract". Resolved first, so {{firstName}} is shorter than a raw path and also becomes a column in the recipients list.{{item.path}}Any path inside the raw item, arrays included: {{item.customer.phones[0]}}.{{index}}The item's position in the response, starting at 1.
Formatters
Append a pipe and a formatter to any placeholder. Dates (ISO or epoch) render in Israel time, so 2026-09-09T07:00:00.000Z reaches the customer as 10:00, never as a raw string.
{{item.startTime|date}}09/09/2026{{item.startTime|time}}10:00{{item.startTime|datetime}}09/09/2026 10:00{{item.startTime|weekday}}Wednesday (in Hebrew){{item.startTime|dayMonth}}9/9{{item.startTime|upper}}Upper case{{item.startTime|lower}}Lower case{{item.startTime|first}}First word onlyWhy someone does not get a message
Every item read from the response goes through these checks, in order. The first reason that matches is shown next to the item in the preview and in the run, so the preview is exactly what the run will do.
- 1
filteredOne of the filters rejected the item. - 2
invalid_phoneNothing dialable at the phone field. The raw value is shown so you can see why. - 3
suppressedThe number is on the account's do-not-send list (an unsubscribe). - 4
duplicate_in_batchThe same "same person" key appeared earlier in this response. - 5
already_sentThis key was already messaged and the repeat policy says "not yet". - 6
sendThe message is sent (or waits for approval).
Limits and safety
- Interval between calls: 5 minutes to 90 days. The scheduler ticks every 5 minutes, so a run can start up to 5 minutes late.
- Up to 300 messages per run. Whatever is left goes out in a follow-up run 5 minutes later.
- Up to 5,000 items are read from one response. The response is capped at 8MB and 20 seconds.
- Every recipient is written to the ledger before WhatsApp is called. Even if a run crashes halfway, the same person never receives the message twice.
- A batch waiting for approval expires after 7 days, and no new batch is prepared while one is open.
- Test mode sends everything to your own number, 5 messages per run, without marking customers as messaged.
- Approved non-OTP templates only, on the same business account as the number. Messages go through the same path as the send API and show up in sent messages and in the inbox.