Vibrate API Skill · v2026-09-23
دعوا الذكاء الاصطناعي يكتب التكامل مع Vibrate
نسخ ولصق لمرة واحدة يعلّم ChatGPT أو Claude أو Claude Code أو Cursor أو Copilot واجهة API الكاملة في Vibrate لرسائل SMS وواتساب: كل نقطة نهاية، وكل حقل، وكل رمز خطأ، وكل مطب محتمل. اطلبوا التكامل الذي تحتاجونه واحصلوا على كود يعمل.
- REST API، وJSON للطلبات والاستجابات
- SMS + قوالب واتساب للأعمال
- حالة التسليم لكل رسالة
mkdir -p .claude/skills/vibrate-api curl -fsSL https://www.vibrate.co.il/vibrate-api-skill.md -o .claude/skills/vibrate-api/SKILL.md
prompt
Read https://www.vibrate.co.il/vibrate-api-skill.md and treat it as the authoritative reference for the Vibrate SMS & WhatsApp REST API (base URL https://api.vibrate.co.il). Use only the endpoints, fields, status codes and error codes documented there - never invent others. Then help me with the following task:
https://www.vibrate.co.il/vibrate-api-skill.md
طريقة استخدام Skill
ثلاث خطوات، في أقل من دقيقة.
- 01
انسخوا
انسخوا ملف Skill الكامل أدناه (أو الموجّه القصير إذا كان مساعدكم قادرًا على فتح الروابط).
- 02
الصقوا
الصقوه كأول رسالة في ChatGPT أو Claude أو Cursor أو Claude Code. من الآن فصاعدًا، هذا هو التوثيق الذي يعتمد عليه المساعد.
- 03
اطلبوا
"أرسل SMS من خادم Express الخاص بي"، "أرسل قالب واتساب هذا إلى قائمتي"، "تحقق من حالة التسليم واحفظها". وستحصلون على كود يستخدم نقاط النهاية الحقيقية.
يعمل مع جميع مساعدي الذكاء الاصطناعي الرائدين
- ChatGPT
- الصقوا ملف Skill الكامل كأول رسالة (أو الموجّه إذا كان التصفح مفعّلًا).
- Claude
- الصقوا ملف Skill الكامل، أو أضيفوا الملف الخام إلى قسم المعرفة في Project.
- Claude Code
- شغّلوا أمر التثبيت مرة واحدة، وسيتم تحميل Skill تلقائيًا في كل جلسة.
- Cursor
- الصقوا ملف Skill داخل قاعدة (.cursor/rules) أو أضيفوا الرابط عبر @Docs.
- GitHub Copilot
- احفظوا الملف باسم .github/copilot-instructions.md أو الصقوه في الدردشة.
ملف Skill
نص Markdown بسيط. هذا بالضبط ما يقرؤه الذكاء الاصطناعي لديكم.
mkdir -p .claude/skills/vibrate-api curl -fsSL https://www.vibrate.co.il/vibrate-api-skill.md -o .claude/skills/vibrate-api/SKILL.md
---
name: vibrate-api
description: Authoritative reference for the Vibrate SMS & WhatsApp Business REST API (api.vibrate.co.il). Use when writing code that sends SMS or WhatsApp template messages, manages distribution lists (campaigns) and contacts, polls delivery status, or receives signed SMS delivery webhooks through Vibrate. Covers auth, every endpoint, request/response shapes, phone-number rules, billing units, the delivery webhook contract, error codes and known pitfalls.
version: 2026-09-23
source: https://www.vibrate.co.il/vibrate-api-skill.md
---
# Vibrate API Skill
You are helping a developer integrate the **Vibrate** messaging platform (Israel-focused SMS + WhatsApp Business API, vibrate.co.il). This document is the complete, authoritative reference. Follow it exactly:
- Use ONLY the endpoints, fields, headers, status codes and error codes listed here. Do not invent endpoints (there is no `/v1/sms/status`, no `/v1/whatsapp/text`, no `/v2`).
- Base URL: `https://api.vibrate.co.il` - all paths below are relative to it. Always HTTPS. JSON in, JSON out.
- Prefer the most specific endpoint (e.g. `sendBulk` for personalised messages to many recipients, `send` for one message to one or more recipients).
- When the user's language is Hebrew, keep message bodies in Hebrew and explain in Hebrew; keep code, field names and paths exactly as documented.
- Never use an em dash in generated copy; use a regular hyphen.
## 1. Authentication
- Every request needs an access token created in the Vibrate app at **Settings → Access Keys** (`https://www.vibrate.co.il/sms/tokens`).
- Send it in the `Authorization` header. The raw token is accepted; a `Bearer ` prefix is also accepted and stripped. `access_token` header is a legacy alias.
```http
Authorization: YOUR_ACCESS_TOKEN
Content-Type: application/json
```
- Auth failures return `401` with `code` one of `ACCESS_TOKEN_MISSING`, `ACCESS_TOKEN_INVALID`, `ACCESS_TOKEN_INACTIVE`, `USER_DELETED`, `USER_INACTIVE`.
- Tokens are per user account; all campaigns, senders, WhatsApp numbers and templates referenced in a request must belong to that account.
## 2. Response envelope
Success (2xx):
```json
{ "success": true, "data": { ... }, "message": "Human readable" }
```
Error (4xx/5xx):
```json
{
"message": "Insufficient SMS credits",
"code": "SMS_INSUFFICIENT_CREDITS",
"errors": ["..."],
"hints": "You need at least 3 SMS credits, but you only have 1 SMS credits"
}
```
- Branch on the HTTP status and on `code`, never on `message` text.
- Zod validation failures return `400` with `code: "VALIDATION_ERROR"` and `errors` = the Zod issue array (`path`, `message` per issue).
- Bodies over the limit (2 MB; 15 MB for `/v1/sms/sendBulk`) return `413 PAYLOAD_TOO_LARGE`.
## 3. Core rules (read before writing code)
### Phone numbers
- Israel is the implicit country. Accepted input forms, all normalised server-side to E.164 digits without `+`: `0501234567`, `050-123-4567`, `972501234567`, `+972501234567`, `+972 050-123-4567`.
- Foreign numbers must carry their country code with `+` or `00` (`+14155551234`, `00447911123456`) or already be in E.164 digits (`14155551234`).
- Pasted numbers often carry invisible bidi characters (U+200E, U+202C etc.). Strip non-digit / non-`+` characters client-side before sending.
- Recipients on the account's do-not-send (unsubscribe) registry are silently skipped at send time; this is by design and cannot be overridden through the API.
### Sender (SMS only)
- `sender` is REQUIRED on every SMS send and must exactly match an **active sender** on the account (an approved alphanumeric Sender ID such as `MySender`, or a phone number). Otherwise: `400 SMS_INVALID_SENDER`.
- Senders are created and approved in the Vibrate app, not via the API.
### SMS billing units
- 1 SMS unit = up to **256 characters** of the trimmed message, regardless of language or encoding (Hebrew and English cost the same). Units = `ceil(length / 256)`.
- Cost of a request = units × number of recipients. Credits are reserved atomically before queuing; if the balance is short the whole request is rejected with `400 SMS_INSUFFICIENT_CREDITS` and nothing is sent.
- Accounts may carry a rolling 24-hour message cap: `400 SMS_DAILY_LIMIT_EXCEEDED` (`hints` includes limit / used / remaining).
### Asynchronous delivery
- All send endpoints return **`202 Accepted`** once the batch is queued. A 202 means "accepted and billed", NOT "delivered". Track delivery either by push (the SMS delivery webhook, section 5) or by polling `GET /v1/sms/run/{runId}/delivery-status` / `GET /v1/sms/messages?runId=...`.
- Message statuses: `queued` → `in_progress` → `sent` → `delivered`, or `failed`. `statusHistory` on each message is an ordered array of `{ state, occurredAt, error?, errorCodes? }`.
### Idempotency (bulk SMS)
- `POST /v1/sms/sendBulk` accepts an optional `Idempotency-Key` header (1-255 chars, use a UUID). Retrying with the same key within 24 h replays the original 202 (response header `Idempotency-Replayed: true`) instead of sending and billing again. A retry while the first request is still running returns `409 CONFLICT`; retry after a few seconds. Failed requests release the key so a retry re-executes.
## 4. Endpoints
### 4.1 Account
#### GET /v1/user/info
Returns the account behind the token, including the SMS credit balance.
Response `200`:
```json
{ "success": true, "message": "User data retrieved successfully",
"data": { "email": "[email protected]", "name": "ישראל ישראלי", "status": "active", "smsAmount": 38 } }
```
Use `smsAmount` to pre-check credits before a large send.
### 4.2 SMS
#### POST /v1/sms/send
One message body to one or more recipients.
Body:
| field | type | required | notes |
|---|---|---|---|
| `recipients` | string[] | yes | non-empty; each a phone number (see rules) |
| `message` | string | yes | trimmed server-side; units = ceil(len/256) |
| `sender` | string | yes | active sender on the account |
| `campaignId` | string | no | tag the messages to a campaign for reporting |
```bash
curl -X POST https://api.vibrate.co.il/v1/sms/send \
-H "Authorization: YOUR_ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"recipients":["0501231234"],"message":"שלום עולם","sender":"MySender"}'
```
Response `202`:
```json
{ "success": true, "message": "SMS batch queued successfully", "data": { "runId": "6d3c80fc-e2c6-4593-b787-cecafcf84f4b" } }
```
Errors: `400 VALIDATION_ERROR | SMS_NO_RECIPIENTS | SMS_INVALID_SENDER | SMS_DAILY_LIMIT_EXCEEDED | SMS_INSUFFICIENT_CREDITS`, `401`.
#### POST /v1/sms/sendBulk
Different message per recipient (personalised sends). One `sender` for the whole batch. Body limit 15 MB. Supports `Idempotency-Key`.
Body:
| field | type | required | notes |
|---|---|---|---|
| `messages` | { recipient: string, message: string }[] | yes | non-empty |
| `sender` | string | yes | active sender |
| `campaignId` | string | no | |
```bash
curl -X POST https://api.vibrate.co.il/v1/sms/sendBulk \
-H "Authorization: YOUR_ACCESS_TOKEN" -H "Content-Type: application/json" \
-H "Idempotency-Key: 3f1c2b7e-8d1a-4a4c-9d3e-0a1b2c3d4e5f" \
-d '{"sender":"MySender","messages":[{"recipient":"0501231234","message":"הודעה ראשונה"},{"recipient":"0505675678","message":"הודעה שנייה"}]}'
```
Response `202`: same shape as `/send` (`data.runId`). Errors as `/send` plus `400 INVALID_DATA_FORMAT` (bad Idempotency-Key), `409 CONFLICT`, `413 PAYLOAD_TOO_LARGE`.
#### GET /v1/sms/messages
Paginated message log for the account, newest first.
Query params (all optional): `status` (queued|in_progress|sent|delivered|failed), `runId`, `recipient` (substring match), `startDate`, `endDate` (ISO-8601, filter on createdAt), `page` (default 1), `limit` (default 10). `page`/`limit` must be positive integers or you get `400 MISSING_REQUIRED_FIELDS`.
Response `200`:
```json
{ "success": true, "message": "Messages retrieved successfully",
"data": {
"messages": [ { "id": "...", "recipient": "972501231234", "message": "...", "sender": "MySender", "runId": "...", "status": "delivered", "providerId": "...", "statusHistory": [ { "state": "queued", "occurredAt": "2025-06-16T21:25:19.903Z" } ], "createdAt": "...", "updatedAt": "..." } ],
"pagination": { "total": 120, "page": 1, "limit": 10, "totalPages": 12 } } }
```
#### GET /v1/sms/run/{runId}/delivery-status
Delivery summary for one send (the `runId` returned by any send endpoint). `404 RESOURCE_NOT_FOUND` if the run has no messages for this account.
Response `200`:
```json
{ "success": true, "message": "Run delivery status retrieved successfully",
"data": {
"runId": "...", "allDelivered": false,
"summary": { "total": 3, "delivered": 2, "pending": 1 },
"deliveryTimes": [ { "messageId": "...", "recipient": "972501231234", "deliveredAt": "2025-06-16T21:26:02.000Z", "status": "delivered" } ],
"nonDeliveredMessages": [ { "messageId": "...", "recipient": "972505675678", "status": "sent", "lastUpdated": "..." } ] } }
```
`nonDeliveredMessages` is omitted when everything is delivered. Poll with backoff (e.g. every 10-30 s); delivery receipts typically arrive within seconds to a few minutes.
### 4.3 Campaigns (distribution lists)
A **campaign** is a named contact list owned by the account. Contacts carry `name`, `phoneNumber` and free-form `additionalFields` used for personalisation.
#### GET /v1/sms/campaigns
Response `200`: `data` = array of `{ id, name, status, userId, createdAt, updatedAt }`.
#### POST /v1/sms/campaigns
Body: `{ "name": string }` (required, non-empty). Response `201`: `data` = the campaign object above.
#### GET /v1/sms/campaigns/{campaignId}/contacts
Response `200`: `data` = array of contacts `{ id, name, phoneNumber, additionalFields, status, campaignId, createdAt, updatedAt }`.
#### POST /v1/sms/campaigns/{campaignId}/contacts
Body:
| field | type | required |
|---|---|---|
| `phoneNumber` | string | yes |
| `name` | string | yes |
| `additionalFields` | object (string → any) | no |
```json
{ "name": "David Beckham", "phoneNumber": "0505975550", "additionalFields": { "first name": "David", "last name": "Beckham" } }
```
Response `201`: `data` = the created contact. `404` if the campaign is not yours.
#### DELETE /v1/sms/campaigns/{campaignId}/contacts/{contactId}
Response `200` `{ success: true, message: "Contact removed from campaign successfully" }`.
#### POST /v1/sms/campaigns/{campaignId}/run
Send one templated SMS to every **active** contact in the campaign; personalisation happens server-side per contact.
Body:
| field | type | required | notes |
|---|---|---|---|
| `sender` | string | yes | active sender |
| `message` | string | yes | template; `{{name}}` → contact.name, `{{<additionalField key>}}` → that field |
| `addUnsubscribe` | boolean | no (default false) | appends `<unsubscribeText> https://www.vibrate.co.il/r/<contactId>` to each message |
| `nameVar` | string | no | rename the placeholder that maps to contact.name (default `name`) |
| `unsubscribeText` | string | no | label before the unsubscribe link (default `Unsubscribe:`) |
```json
{ "sender": "MySender", "message": "שלום {{name}}, יש לך הזמנה חדשה!", "addUnsubscribe": true, "unsubscribeText": "להסרה:" }
```
Response `202`:
```json
{ "success": true, "message": "Campaign run queued successfully",
"data": { "runId": "...", "campaignId": "...", "recipientCount": 247, "totalSmsUnits": 247 } }
```
Errors: `400 VALIDATION_ERROR | SMS_INVALID_SENDER | CAMPAIGN_NO_ACTIVE_CONTACTS | SMS_DAILY_LIMIT_EXCEEDED | SMS_INSUFFICIENT_CREDITS`, `404 CAMPAIGN_NOT_FOUND`. The unsubscribe suffix counts toward the 256-char units.
### 4.4 WhatsApp
WhatsApp sends go through Meta's Cloud API. Only **approved templates** can be sent through this API (free-text replies are handled in the Vibrate inbox / chatbot, not here).
- `whatsappNumberId`: ID of a WhatsApp number connected to the account - copy it from **WhatsApp → Numbers** (`https://www.vibrate.co.il/whatsapp/numbers`).
- `templateId`: ID of an approved template - copy it from **WhatsApp → Templates** (`https://www.vibrate.co.il/whatsapp/templates`). Both are Vibrate IDs, not Meta IDs.
- Variables are positional: a template body "שלום {{1}}, ההזמנה {{2}} נשלחה" needs `bodyVariables: ["דוד", "ORD-12345"]`. Counts must match the template exactly.
- Header types: TEXT (may use `headerVariables`), IMAGE / VIDEO / DOCUMENT (require a publicly reachable `headerMediaUrl`; Meta fetches it).
#### POST /v1/whatsapp/template/send
Body:
| field | type | required | notes |
|---|---|---|---|
| `whatsappNumberId` | string | yes | |
| `templateId` | string | yes | |
| `recipient` | string | yes | phone, any accepted form |
| `bodyVariables` | string[] | no | one per `{{n}}` in the body, in order |
| `headerVariables` | string[] | no | for TEXT headers with variables |
| `headerMediaUrl` | string | no | public URL for IMAGE/VIDEO/DOCUMENT headers |
| `buttonVariables` | string[] | no | one per dynamic-URL button (`https://x.com/{{1}}`), replaces the placeholder only |
```bash
curl -X POST https://api.vibrate.co.il/v1/whatsapp/template/send \
-H "Authorization: YOUR_ACCESS_TOKEN" -H "Content-Type: application/json" \
-d '{"whatsappNumberId":"your-whatsapp-number-id","templateId":"your-template-id","recipient":"0501234567","bodyVariables":["דוד","ORD-12345"]}'
```
Response `200`:
```json
{ "success": true, "statusCode": 200, "message": "WhatsApp template sent successfully",
"data": { "messageId": "wamid.HBgLOTcyNTA...", "recipient": "972501234567" } }
```
Errors (`400`): `MISSING_REQUIRED_FIELDS`, `INVALID_TEMPLATE` / `WHATSAPP_TEMPLATE_NOT_FOUND` / `WHATSAPP_TEMPLATE_NOT_APPROVED`, `INVALID_PHONE`, `MISSING_VARIABLES` / `WHATSAPP_INVALID_VARIABLES`, `INVALID_MEDIA_URL`, `INVALID_WHATSAPP_NUMBER` / `WHATSAPP_NUMBER_NOT_FOUND`; `WHATSAPP_SEND_FAILED` / `WHATSAPP_GATEWAY_ERROR` for upstream Meta failures.
#### POST /v1/whatsapp/template/sendBulk
Send a template to every contact in a campaign (distribution list), mapping variables from contact fields. Runs in the background.
Body:
| field | type | required | notes |
|---|---|---|---|
| `whatsappNumberId` | string | yes | |
| `templateId` | string | yes | |
| `campaignId` | string | yes | the distribution list |
| `bodyMappings` | { type: "field" \| "custom", value: string }[] | no | positional; `field` reads `name` or `additionalFields.<key>` from each contact, `custom` is a fixed string |
| `headerMappings` | same shape | no | for TEXT header variables |
| `headerMediaUrl` | string | no | |
| `resumeBatchId` | string | no | re-run a previous batch, skipping contacts already sent/delivered/read |
```json
{ "whatsappNumberId": "your-whatsapp-number-id", "templateId": "your-template-id", "campaignId": "your-campaign-id",
"bodyMappings": [ { "type": "field", "value": "name" }, { "type": "custom", "value": "20% הנחה" } ] }
```
Response `202`:
```json
{ "success": true, "statusCode": 202, "message": "Bulk WhatsApp send started in background", "data": { "batchId": "batch_abc123", "totalCount": 2 } }
```
Keep `batchId`; pass it as `resumeBatchId` to safely retry a partially failed batch without double-sending.
## 5. SMS delivery webhook (push delivery reports)
Optional, per account, SMS only. When configured, Vibrate POSTs a signed JSON event to your HTTPS endpoint each time a message's status changes, so you do not have to poll.
**Setup (web app, not API):** **SMS → Logs → Webhook** (`https://www.vibrate.co.il/sms/logs`). Enter an HTTPS URL; the app generates a signing secret (copy it when shown; it can be regenerated, which invalidates the old one). Use **Send test event** to receive a sample event (its `messageId` starts with `test_`). Plain `http://`, localhost, private/link-local IPs and `.local`/`.internal` hosts are rejected. Clearing the URL turns the feature off. There is no API to read or change this config.
**Which events are pushed:** every status transition driven by a carrier delivery receipt or a dispatch failure - in practice `sent`, `delivered` and `failed`. `queued` / `in_progress` are visible through the API only. Code defensively: `status` is always one of the five canonical values from section 3. Events cover messages from every send path on the account (`/send`, `/sendBulk`, campaign runs and sends made in the web app). WhatsApp is not covered.
**Request:** `POST <your URL>`, `Content-Type: application/json`, body =
```json
{
"apiVersion": "v1",
"event": "sms.status",
"messageId": "665f1c2e9a1b2c3d4e5f6a7b",
"runId": "3b9d2f9c-6f0e-4c1a-9e1d-0d8f7a6b5c4e",
"campaignId": null,
"recipient": "972501231234",
"sender": "MySender",
"status": "delivered",
"final": true,
"error": null,
"sentAt": "2025-06-16T21:25:19.903Z",
"occurredAt": "2025-06-16T21:26:02.000Z"
}
```
| Field | Notes |
|---|---|
| `messageId` | same `id` as in `GET /v1/sms/messages`; `test_...` for test events |
| `runId` / `campaignId` | as returned by the send / campaign run; `null` when not applicable |
| `status` | one of `queued`, `in_progress`, `sent`, `delivered`, `failed` |
| `final` | `true` only for `delivered` and `failed` - no further events for this message after a final one |
| `error` | `null` unless `status` is `failed`; then `{ "message": string, "code": string or null }` where `code` is the carrier stat (e.g. `UNDELIV`, `EXPIRED`, `REJECTD`), a carrier submit rejection (`SMPP_THROTTLED`, `SMPP_INVALID_DESTINATION`, `SMPP_QUEUE_FULL`, `SMPP_REJECTED`, ...) or an internal code (e.g. `SMS_GATEWAY_ERROR`). Credits for a `failed` message are refunded automatically |
| `sentAt` | ISO-8601 time the message was accepted; may be `null` |
| `occurredAt` | ISO-8601 time of this transition; identical to the matching `statusHistory` entry |
Headers on every request:
| Header | Value |
|---|---|
| `X-Vibrate-Event` | `sms.status` |
| `X-Vibrate-Message-Id` | same as body `messageId` |
| `X-Vibrate-Timestamp` | Unix seconds (string) when the request was signed |
| `X-Vibrate-Signature` | `sha256=<hex>` where hex = HMAC-SHA256(secret, timestamp + "." + rawBody) |
**Verifying the signature:** compute the HMAC over the **raw request bytes** (not a re-serialised object - key order and whitespace matter), prefix `sha256=`, compare in constant time, and reject timestamps older than a few minutes to block replays.
```ts
import crypto from "node:crypto";
import express from "express";
const app = express();
app.post("/vibrate/sms-status", express.raw({ type: "application/json" }), (req, res) => {
const ts = String(req.header("X-Vibrate-Timestamp") ?? "");
const sig = String(req.header("X-Vibrate-Signature") ?? "");
const raw = (req.body as Buffer).toString("utf8");
const expected = "sha256=" + crypto.createHmac("sha256", process.env.VIBRATE_WEBHOOK_SECRET!).update(ts + "." + raw).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
if (!fresh || sig.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(401).end();
}
const event = JSON.parse(raw) as { messageId: string; status: string; final: boolean; error: { message: string; code: string | null } | null };
// TODO: upsert by (messageId, status) - events may arrive more than once or out of order
res.status(200).end();
});
```
**Delivery semantics (design for these):**
- Respond with any **2xx within 10 seconds**. Do your processing after acknowledging. Non-2xx, timeouts and redirects (redirects are **not** followed) all count as failures.
- A failed delivery is retried **twice**, about 5 s and then 30 s after the previous attempt, then dropped.
- **At-least-once, unordered:** the same event can arrive more than once (e.g. duplicate carrier receipts) and a later status can arrive before an earlier one. De-duplicate on `messageId` + `status` and never let a non-final event overwrite a final one.
- **Best effort, not a ledger:** after 20 consecutive failures to reach your endpoint, forwarding for the account pauses for 60 s and events in that window are dropped; a platform-wide in-flight cap can also shed events during very large bursts. The API (`GET /v1/sms/run/{runId}/delivery-status`, `GET /v1/sms/messages`) is always the source of truth - reconcile any message still non-final after a few minutes against it.
- One URL per account. Use the test event to verify the endpoint before relying on it.
## 6. Error code reference
| HTTP | code | meaning / fix |
|---|---|---|
| 400 | VALIDATION_ERROR | body failed schema; read `errors[]` |
| 400 | MISSING_REQUIRED_FIELDS | a required field or query param is missing/invalid |
| 400 | INVALID_DATA_FORMAT | e.g. malformed Idempotency-Key |
| 400 | SMS_NO_RECIPIENTS | `recipients` empty |
| 400 | SMS_INVALID_SENDER | `sender` missing or not an active sender on this account |
| 400 | SMS_INSUFFICIENT_CREDITS | top up credits; nothing was sent or charged |
| 400 | SMS_DAILY_LIMIT_EXCEEDED | 24 h cap reached; `hints` tells when it resets |
| 400 | CAMPAIGN_NO_ACTIVE_CONTACTS | list is empty or all contacts inactive/unsubscribed |
| 401 | ACCESS_TOKEN_MISSING / ACCESS_TOKEN_INVALID / ACCESS_TOKEN_INACTIVE | fix the Authorization header / create a new key |
| 401 | USER_INACTIVE / USER_DELETED | account problem; contact Vibrate support |
| 404 | CAMPAIGN_NOT_FOUND / RESOURCE_NOT_FOUND | wrong ID or belongs to another account |
| 409 | CONFLICT | same Idempotency-Key still in flight; retry shortly |
| 413 | PAYLOAD_TOO_LARGE | split the batch |
| 5xx | SERVER_ERROR / SMS_GATEWAY_ERROR / SMS_GATEWAY_TIMEOUT / WHATSAPP_GATEWAY_ERROR | transient; retry with backoff and (for bulk SMS) the same Idempotency-Key |
## 7. Recommended integration pattern
1. On startup / before big sends: `GET /v1/user/info` → check `smsAmount` ≥ recipients × units.
2. Normalise phones and de-duplicate recipients client-side.
3. Send with `/v1/sms/sendBulk` + `Idempotency-Key` (or `/send` for a single body). Store the returned `runId`.
4. Track delivery: configure the SMS delivery webhook (section 5) and persist each event by `messageId`; then reconcile anything still non-final after a few minutes with `GET /v1/sms/run/{runId}/delivery-status`. Without a webhook, poll that endpoint with backoff until `allDelivered` or a timeout you choose (e.g. 30 min).
5. Handle `400` codes as permanent (fix input), `409/5xx` as retryable.
6. For WhatsApp, verify the template is APPROVED and the variable counts match before sending; re-runs use `resumeBatchId`.
## 8. Things this API does NOT do
- No **inbound-message** webhooks (replies to your SMS or WhatsApp are not pushed to you). Outbound **SMS delivery status IS pushed** through the delivery webhook in section 5; it is configured in the web app rather than through the API, and it does not cover WhatsApp.
- No free-text WhatsApp sends, no sender/number/template creation, no credit purchase via API - all of that is done in the Vibrate web app (https://www.vibrate.co.il).
- No rate-limit header is returned; keep concurrency modest (a few parallel requests) and batch through `sendBulk` instead of looping `/send`.
## 9. Minimal client (TypeScript)
```ts
const BASE = "https://api.vibrate.co.il";
const headers = { Authorization: process.env.VIBRATE_TOKEN!, "Content-Type": "application/json" };
export async function sendSms(recipients: string[], message: string, sender: string) {
const res = await fetch(`${BASE}/v1/sms/send`, { method: "POST", headers, body: JSON.stringify({ recipients, message, sender }) });
const json = await res.json();
if (!res.ok) throw new Error(`${json.code}: ${json.message} (${json.hints ?? ""})`);
return json.data.runId as string;
}
export async function deliveryStatus(runId: string) {
const res = await fetch(`${BASE}/v1/sms/run/${runId}/delivery-status`, { headers });
const json = await res.json();
if (!res.ok) throw new Error(`${json.code}: ${json.message}`);
return json.data as { allDelivered: boolean; summary: { total: number; delivered: number; pending: number } };
}
```
---
Docs (login required): https://www.vibrate.co.il/docs/sms and https://www.vibrate.co.il/docs/whatsapp. Public copy of this skill: https://www.vibrate.co.il/vibrate-api-skill.md. Site index for AI: https://www.vibrate.co.il/llms.txt.
نقاط النهاية المشمولة
- GET
/v1/user/info
- POST
/v1/sms/send - POST
/v1/sms/sendBulk - GET
/v1/sms/messages - GET
/v1/sms/run/{runId}/delivery-status
- GET
/v1/sms/campaigns - POST
/v1/sms/campaigns - GET
/v1/sms/campaigns/{campaignId}/contacts - POST
/v1/sms/campaigns/{campaignId}/contacts - DELETE
/v1/sms/campaigns/{campaignId}/contacts/{contactId} - POST
/v1/sms/campaigns/{campaignId}/run
- POST
/v1/whatsapp/template/send - POST
/v1/whatsapp/template/sendBulk
الأسئلة الشائعة
مستند Markdown واحد يصف واجهة REST API الكاملة في Vibrate: المصادقة، وكل نقطة نهاية لرسائل SMS وواتساب، وبنية الطلبات والاستجابات، وقواعد أرقام الهواتف، ووحدات الاحتساب، ورموز الأخطاء. تلصقونه في مساعد الذكاء الاصطناعي ليكتب تكاملًا صحيحًا دون تخمين.
مع أي مساعد يقبل النصوص: ChatGPT وClaude وClaude Code وCursor وGitHub Copilot وGemini وغيرها. ويمكن أيضًا تثبيته في Claude Code وCursor كـSkill أو قاعدة (rule) محلية ليتم تحميله تلقائيًا.
نعم. أنشئوا حسابًا مجانيًا، واشحنوا رصيد SMS أو اربطوا رقم واتساب للأعمال، ثم أنشئوا مفتاح وصول من الإعدادات ← مفاتيح الوصول. يُرسل المفتاح في ترويسة Authorization مع كل طلب.
إرسال رسائل SMS فردية أو جماعية، وتشغيل حملات مخصصة لقوائم التوزيع، وإدارة جهات الاتصال، ومتابعة حالة التسليم لكل رسالة، وإرسال قوالب واتساب للأعمال المعتمدة مع متغيرات ووسائط إلى مستلم واحد أو إلى قائمة كاملة.
نعم. يتم إنشاؤه من المصدر نفسه الذي يُنشأ منه توثيق API، ويُتاح عبر رابط ثابت، لذا يعيد كل تنزيل جديد أحدث إصدار.
إنشاء حساب مجاني
انسخوا موجّهًا واحدًا، وسيتعرّف ChatGPT أو Claude أو Claude Code أو Cursor على واجهة API الكاملة في Vibrate: إرسال رسائل SMS وقوالب واتساب، وإدارة القوائم، ومتابعة حالة التسليم. API لرسائل SMS وواتساب للأعمال في إسرائيل.