زاجلي
زاجلي

التواصل متعدد القنوات

Zagaly logo
زاجلي
زاجلي
Toggle sidebar

أدوات المطور

مرجع واجهة برمجة التطبيقات والويب هوكس

أرسل الرسائل، وأدرِ جهات الاتصال، وشغّل الحملات، وتلقّى الأحداث الفورية عبر جميع القنوات من أي لغة برمجة.

المصادقة

تستخدم واجهة برمجة التطبيقات v1 مصادقة زوج المفاتيح. يجب أن يتضمن كل طلب ترويستي HTTP مخصّصتين. قم بإنشاء مفاتيحك من مفاتيح API.

http
X-Public-Key: pk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        X-Secret-Key: sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        Content-Type: application/json

المفتاح العام

يحدّد مساحة العمل الخاصة بك. البادئة: pk_. آمن للتسجيل.

المفتاح السري

يُوقّع الطلبات. البادئة: sk_. يُعرض مرة واحدة فقط — احفظه في مدير أسرار.

يمكن حصر مفاتيح API في قدرات محددة وربطها اختيارياً بجلسة واتساب واحدة. الطلبات بدون القدرة المطلوبة تتلقى HTTP 403 insufficient_ability.

القدرات (النطاقات)

Ability Grants access to
* Full access to all endpoints (wildcard).
sessions.read List and inspect connected sessions and bots.
messages.send POST /api/v1/messages/send.
messages.read GET /api/v1/messages and related read-only endpoints.
contacts.read List and view contacts, groups, and tags.
contacts.write Create and update contacts, groups, and tags.
campaigns.read List and view campaigns, templates, and analytics.
campaigns.write Create, update, start and pause campaigns and automations.
webhooks.read List and view webhooks and delivery logs.
webhooks.write Create, update, delete and test webhooks.

حدود المعدل والحصص

كل مفتاح API لديه ثلاثة حدود مستقلة. يمكن تكوين تجاوزات لكل مفتاح عند إنشاء المفتاح — وإلا فإن الافتراضات العامة للمستأجر أدناه تنطبق.

النطاق الافتراضي On breach
Per-minute burst
120 req / min
429 rate_limit_exceeded
Daily quota
10 000 req / day
429 daily_quota_exceeded
Monthly quota
250 000 req / mo
429 monthly_quota_exceeded

رؤوس الاستجابة

كل استجابة مصادقة تتضمن رؤوس تعرض استخدام الحصة الحالية. عند إرجاع 429، Retry-After يشير الرأس إلى عدد الثواني التي يجب الانتظارها.

Header المعنى
X-RateLimit-Limit Per-minute request ceiling for this key.
X-RateLimit-Daily-Limit Daily request quota for this key.
X-RateLimit-Daily-Remaining Requests still available today.
Retry-After Seconds to wait before retrying (only on 429 responses).

عدم التكرار

Safely retry mutating requests without the fear of creating duplicates. Attach an Idempotency-Key header to any POST, PUT, PATCH or DELETE call — if the same key arrives again within 24 hours on the same endpoint, the original status code and response body are replayed verbatim.

bash
curl -X POST https://zagely.com/api/v1/messages/send \
        -H "X-Public-Key: pk_your_public_key" \
        -H "X-Secret-Key: sk_your_secret_key" \
        -H "Idempotency-Key: 5b2f9e3c-7b8d-4a1e-9f1b-12ab34cd56ef" \
        -H "Content-Type: application/json" \
        -d '{"channel":"whatsapp","to":"+15551234567","message":"hi"}'
Rule القيمة
Applies to POST, PUT, PATCH, DELETE on /api/v1/* mutating endpoints.
Character set A–Z, a–z, 0–9, hyphen and underscore. Max 255 chars.
TTL 24 hours from the first accepted response.
Scope Per API key + request path. Different keys see different results.
Replay marker Replies add Idempotent-Replay: true header.
Malformed key 400 invalid_idempotency_key.

نقاط النهاية العامة

Utility endpoints for monitoring and introspecting your API key.

GET
/api/v1/health
public

Unauthenticated liveness probe. Returns 200 when the database is reachable, 503 otherwise.

json
{ "data": { "status": "ok", "timestamp": "2026-04-22T12:00:00Z" } }
GET
/api/v1/me

Returns the current API key with its abilities, bound session, per-key limits, and today / this-month usage counters.

json
{
            "data": {
            "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
            "name": "Production Key",
            "tenant_id": "...",
            "session_id": null,
            "abilities": ["messages.send", "contacts.read"],
            "expires_at": null,
            "last_used_at": "2026-04-22T11:59:02Z",
            "usage": { "today": 142, "month": 3_812 },
            "limits": { "daily": 10000, "monthly": 250000, "per_minute": 120 },
            "remaining": { "daily": 9858 }
            }
            }

نظرة عامة على القنوات

Every API call that creates or sends a message requires a channel field that tells the platform which messaging channel to use. Each channel also has its own "connection" concept referenced by session_id.

channel value session_id refers to session_id required? Recipient field
whatsapp WhatsApp session UUID
yes
to (phone number)
telegram Telegram bot UUID
yes
contact_id or telegram_chat_id
messenger Messenger page UUID
yes
contact_id (has messenger_psid)
viber Viber bot UUID
yes
contact_id (has viber_user_id)
discord Discord bot UUID
yes
contact_id (has discord_user_id)
instagram Instagram account UUID
yes
contact_id (has instagram_igsid)
webpush Push subscriber UUID
yes
contact_id (subscribed)
email — (not applicable)
no
contact_id (has email)

The v1 API currently supports whatsapp as the channel. Additional channels are available via the Sanctum API (for dashboard-integrated apps) and will be expanded in v1 in future releases.

الرسائل

POST
/api/v1/messages/send

Send a message through the specified channel. The message is queued and dispatched asynchronously — the API returns a message ID immediately.

الحقل النوع مطلوب الوصف
channel string
required
Messaging channel to use. Currently: whatsapp. See Channels Overview for the full list.
to string
required
Recipient phone number (E.164: +15551234567). Auto-normalised — spaces and dashes stripped.
message string
اختياري
Text body. Required when type is text (default). Omit for media-only messages.
type string
اختياري
Message type: text (default), image, video, audio, document.
session_id uuid
اختياري
WhatsApp session UUID. Optional only if the API key is already bound to a session.
media_url string
اختياري
Publicly accessible URL of the media file. Required when type ≠ text.

أرسل رسالة نصية

bash
curl -X POST https://zagely.com/api/v1/messages/send \
            -H "X-Public-Key: pk_your_public_key" \
            -H "X-Secret-Key: sk_your_secret_key" \
            -H "Content-Type: application/json" \
            -d '{
            "channel": "whatsapp",
            "session_id": "11111111-2222-3333-4444-555555555555",
            "to": "+15551234567",
            "message": "Hello from Zagely! 👋"
            }'

أرسل صورة

bash
curl -X POST https://zagely.com/api/v1/messages/send \
                -H "X-Public-Key: pk_your_public_key" \
                -H "X-Secret-Key: sk_your_secret_key" \
                -H "Content-Type: application/json" \
                -d '{
                "channel": "whatsapp",
                "session_id": "11111111-2222-3333-4444-555555555555",
                "to": "+15551234567",
                "type": "image",
                "media_url": "https://example.com/promo.jpg",
                "message": "Check out our latest offer!"
                }'

استجابة 201

json
{
                    "status": "queued",
                    "message_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
                    }
GET
/api/v1/messages

Returns a paginated list of messages (50 per page). All query parameters are optional.

معامل الاستعلام الوصف
channel Filter by channel: whatsapp, telegram, messenger, etc.
session_id Filter by session UUID.
contact_id Filter by contact UUID.
direction inbound or outbound.
json
// 200 Response — paginated list
                {
                "current_page": 1,
                "data": [
                {
                "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
                "channel": "whatsapp",
                "direction": "inbound",
                "type": "text",
                "content": "Hi, I need help!",
                "status": "received",
                "created_at": "2026-04-21T10:00:00Z",
                "contact": { "id": "...", "name": "Jane Doe", "phone_number": "+15551234567" },
                "session": { "id": "...", "name": "My Store Session" }
                }
                ],
                "per_page": 50,
                "total": 1204
                }

الجلسات والبوتات

Every channel requires a connected "session" before messages can be sent. The concept differs per channel:

القناة Connection type Managed via
whatsapp WhatsApp Session (QR scan) API (below) or Dashboard
telegram Telegram Bot (bot token) Dashboard → Channels → Telegram
messenger Messenger Page (Meta OAuth) Dashboard → Channels → Messenger
viber Viber Bot (auth token) Dashboard → Channels → Viber
discord Discord Bot (bot token) Dashboard → Channels → Discord
instagram Instagram Account (Meta OAuth) Dashboard → Channels → Instagram
webpush Push Site Key (VAPID) Dashboard → Channels → Web Push
email Tenant SMTP config Dashboard → Settings → Email

WhatsApp Session API

GET
/api/v1/sessions

Returns all WhatsApp sessions for your workspace with their current status.

json
// 200 Response
            {
            "current_page": 1,
            "data": [
            {
            "id": "11111111-2222-3333-4444-555555555555",
            "name": "Customer Support",
            "phone_number": "+15551234567",
            "status": "connected",
            "daily_limit": 500,
            "warmup_enabled": true,
            "created_at": "2026-01-10T08:00:00Z"
            }
            ],
            "total": 3
            }
GET
/api/v1/sessions/{session}

Fetches the live status of a single session. Possible statuses: connecting, connected, disconnected, banned.

POST
/api/whatsapp/sessions
Sanctum auth

Creates a new WhatsApp session and starts the QR-code connection flow.

الحقل مطلوب الوصف
name
required
Human-readable label for the session (max 255).
phone_number
اختياري
Expected phone number (informational; set automatically on QR scan).
json
// 201 Response
            {
            "id": "11111111-2222-3333-4444-555555555555",
            "name": "Customer Support",
            "status": "connecting",
            "qr_code": "data:image/png;base64,iVBOR..."
            }
PUT
/api/whatsapp/sessions/{session}
Sanctum auth

Updates session settings. All fields optional — only sent fields change.

الحقل النوع الوصف
name string Human-readable label.
description string Optional description.
daily_limit integer Max messages per day (min 1).
warmup_enabled boolean Enable gradual send ramp-up.
warmup_start_limit integer Messages per day at warmup start.
warmup_max_limit integer Messages per day at warmup peak.
warmup_days integer Days to reach peak limit.
simulate_typing boolean Add realistic typing delay before each message.
الطريقة المسار الوصف
GET
/api/whatsapp/sessions/{session}/qr Current QR code image (base64). Poll while status = connecting.
POST
/api/whatsapp/sessions/{session}/disconnect Gracefully disconnect. Status → disconnected.
DELETE
/api/whatsapp/sessions/{session} Disconnect and permanently delete the session.

جهات الاتصال

GET
/api/v1/contacts

Paginated, filterable list of contacts. Default page size is 50.

معامل الاستعلام الوصف
search Partial match on name, phone_number, or email.
tags Array — contacts that have ALL tags. e.g. ?tags[]=vip&tags[]=active.
is_blocked boolean — true returns only blocked contacts.
opted_out boolean — true returns opted-out contacts.
group_id UUID — contacts belonging to this contact group.
sort_by name, phone_number, email, created_at (default), updated_at.
sort_order asc or desc (default).
per_page Items per page 1–200. Default 50.
json
// 200 Response
            {
            "current_page": 1,
            "data": [
            {
            "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
            "name": "Jane Doe",
            "phone_number": "+15551234567",
            "email": "jane@example.com",
            "tags": ["vip", "active"],
            "is_blocked": false,
            "opted_out_at": null,
            "custom_fields": { "city": "New York" },
            "groups": [{ "id": "...", "name": "VIP Customers" }],
            "created_at": "2026-01-15T09:00:00Z"
            }
            ],
            "total": 842,
            "per_page": 50
            }
POST
/api/v1/contacts

Creates a new contact. Returns 422 if a contact with that phone number already exists in your workspace.

الحقل مطلوب الوصف
phone_number
required
E.164 recommended (+15551234567). Non-digit chars stripped automatically.
name
اختياري
Display name. Defaults to phone number if omitted.
email
اختياري
Valid email address.
tags
اختياري
String array: ["vip", "newsletter"].
notes
اختياري
Free-text notes.
custom_fields
اختياري
Key-value object: {"city": "NY", "plan": "gold"}.
bash
curl -X POST https://zagely.com/api/v1/contacts \
            -H "X-Public-Key: pk_your_public_key" \
            -H "X-Secret-Key: sk_your_secret_key" \
            -H "Content-Type: application/json" \
            -d '{
            "phone_number": "+15551234567",
            "name": "Jane Doe",
            "email": "jane@example.com",
            "tags": ["vip", "newsletter"],
            "custom_fields": { "city": "New York", "plan": "gold" }
            }'
الطريقة المسار الوصف
GET
/api/v1/contacts/{contact} Get a contact with last 10 messages and all groups.
PUT
/api/v1/contacts/{contact} Update fields. All fields optional — only sent fields change.
DELETE
/api/v1/contacts/{contact} Permanently delete contact and linked data.
POST
/api/contacts/{contact}/block Block — contact stops receiving campaign messages.
POST
/api/contacts/{contact}/unblock Unblock a previously blocked contact.
POST
/api/contacts/import Bulk import from CSV/XLSX file (async). Returns 202.
POST
/api/contacts/export Start async export with optional filters. Returns 202.

مجموعات جهات الاتصال

Static groups are managed manually. Dynamic groups auto-update based on filters.

POST
/api/v1/contact-groups
الحقل مطلوب الوصف
name
required
Group label (max 255).
type
required
static — manually managed; dynamic — auto-filtered.
description
اختياري
Optional description.
filters
اختياري
Required when type is dynamic. Keys: tags (array), custom_fields (object), created_from, created_to.
json
// Static group
            { "name": "VIP Customers", "type": "static" }

            // Dynamic group
            {
            "name": "New VIPs",
            "type": "dynamic",
            "filters": { "tags": ["vip"], "created_from": "2026-01-01" }
            }
bash
curl -X POST https://zagely.com/api/v1/contact-groups \
            -H "X-Public-Key: pk_your_public_key" \
            -H "X-Secret-Key: sk_your_secret_key" \
            -H "Content-Type: application/json" \
            -d '{ "name": "VIP Customers", "type": "static" }'
bash
# Add contacts to a static group — by UUID or phone number
            curl -X POST https://zagely.com/api/v1/contact-groups/{group_id}/contacts \
            -H "X-Public-Key: pk_your_public_key" \
            -H "X-Secret-Key: sk_your_secret_key" \
            -H "Content-Type: application/json" \
            -d '{ "phone_numbers": ["+201140220996", "+201001234567"] }'
الطريقة المسار الوصف
GET
/api/v1/contact-groups List groups with contacts_count.
GET
/api/v1/contact-groups/{group} Show group and first 100 contacts.
PUT
/api/v1/contact-groups/{group} Update name, description, or filters.
DELETE
/api/v1/contact-groups/{group} Delete group (contacts are not deleted).
POST
/api/v1/contact-groups/{group}/contacts Add members to a static group. Body: contact_ids (UUIDs) and/or phone_numbers (strings).
DELETE
/api/v1/contact-groups/{group}/contacts Remove members from a static group. Body: contact_ids (UUIDs) and/or phone_numbers (strings).

العلامات

Tags are freeform labels stored on contacts. These endpoints let you list, rename, or delete tags across all contacts in your workspace.

الطريقة المسار الوصف
GET
/api/v1/tags List all tags with contacts_count. Optional ?search= filter.
PUT
/api/v1/tags/{tag} Rename a tag across all contacts. Body: { "name": "new-tag" }.
DELETE
/api/v1/tags/{tag} Remove a tag from all contacts.
bash
# List tags
        curl https://zagely.com/api/v1/tags \
        -H "X-Public-Key: pk_your_public_key" \
        -H "X-Secret-Key: sk_your_secret_key"

        # Rename a tag
        curl -X PUT https://zagely.com/api/v1/tags/vip \
        -H "X-Public-Key: pk_your_public_key" \
        -H "X-Secret-Key: sk_your_secret_key" \
        -H "Content-Type: application/json" \
        -d '{ "name": "premium" }'

        # Delete a tag
        curl -X DELETE https://zagely.com/api/v1/tags/vip \
        -H "X-Public-Key: pk_your_public_key" \
        -H "X-Secret-Key: sk_your_secret_key"

القوالب

Reusable message templates with variable substitution. Use {{name}}, {{phone}}, or any custom field key.

POST
/api/templates
الحقل مطلوب الوصف
name
required
Unique template name within your workspace.
channel
required
whatsapp, telegram, messenger, viber, webpush, email, discord, or all.
content
required
Message body. Use {{variable}} placeholders for dynamic values.
media_url
اختياري
Attach media to the template.
buttons
اختياري
Array of quick-reply or CTA button objects.
variables
اختياري
Array of variable names. Auto-extracted from {{...}} in content if omitted.
json
{
            "name": "Order Confirmation",
            "channel": "whatsapp",
            "content": "Hi {{ name }}! Your order #{{ order_id }} is confirmed.
            We ship by {{ ship_date }}. 🎉"
            }

استجابة 201

json
{
                "id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
                "name": "Order Confirmation",
                "channel": "whatsapp",
                "variables": ["name", "order_id", "ship_date"],
                "created_at": "2026-04-21T12:00:00Z"
                }
POST
/api/templates/{template}/preview

Render the template with real values. Pass a contact_id (auto-fills contact fields) or a variables object.

json
// By contact
            { "contact_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" }

            // By values
            { "variables": { "name": "Jane", "order_id": "ORD-001", "ship_date": "April 25" } }

الحملات

POST
/api/v1/campaigns

Creates a campaign in draft status. Start it separately via the /start endpoint. Campaigns cannot be edited once running.

الحقل مطلوب الوصف
name
required
Campaign name (max 255).
channel
اختياري
Sending channel. Default: whatsapp. Values: whatsapp, telegram, messenger, viber, webpush, email, discord.
session_id
اختياري
UUID of the channel session/bot to use. Required for all channels except email.
message_content
required
Message body sent to each recipient.
target_type
required
all — every contact; group — a contact group; filter — custom filters.
media_url
اختياري
Attach a media file to the message.
buttons
اختياري
Array of quick-reply button objects.
target_filters
اختياري
Required when target_type is group or filter. Object with group_id, tags, etc.
scheduled_at
اختياري
ISO 8601 datetime in the future. Campaign launches automatically at this time.
bash
curl -X POST https://zagely.com/api/v1/campaigns \
            -H "X-Public-Key: pk_your_public_key" \
            -H "X-Secret-Key: sk_your_secret_key" \
            -H "Content-Type: application/json" \
            -d '{
            "name": "April Flash Sale",
            "channel": "whatsapp",
            "session_id": "11111111-2222-3333-4444-555555555555",
            "message_content": "Hi {{ name }}! Flash sale — 30% off today only. Shop
            now 👉 https://example.com/sale",
            "target_type": "group",
            "target_filters": { "group_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee" },
            "scheduled_at": "2026-04-25T09:00:00Z"
            }'
GET
/api/campaigns/{campaign}/stats
json
{
            "total_recipients": 1500,
            "sent_count": 1450,
            "delivered_count": 1380,
            "read_count": 920,
            "failed_count": 50,
            "pending_count": 0,
            "delivery_rate": 95.17,
            "read_rate": 66.67
            }

Lifecycle endpoints

POST
/api/v1/campaigns/{campaign}/start draft, scheduled, paused → running
POST
/api/campaigns/{campaign}/pause running → paused
POST
/api/campaigns/{campaign}/resume paused → running
POST
/api/campaigns/{campaign}/cancel scheduled, running, paused → canceled
PUT
/api/campaigns/{campaign} Update (draft/scheduled only)
DELETE
/api/campaigns/{campaign} Delete (draft/scheduled/canceled only)

الأتمتة

Auto-replies triggered when a condition is met. Trigger types: keyword, welcome, away, fallback.

POST
/api/automations
الحقل مطلوب الوصف
session_id
required
WhatsApp session (or bot) this automation listens on.
name
required
Automation label (max 255).
channel
اختياري
Channel scope. Default: whatsapp. Values: whatsapp, telegram, messenger, viber, webpush, email, discord, all.
trigger_type
required
keyword, welcome (first contact), away (outside hours), fallback (no match).
trigger_value
اختياري
Keyword to match. Required when trigger_type is keyword.
response_content
required
Message body to send as the automatic reply.
response_media_url
اختياري
Media attachment for the response.
response_buttons
اختياري
Array of quick-reply buttons.
conditions
اختياري
Extra conditions object.
priority
اختياري
Integer 0–100. Higher priority evaluated first. Default 0.
json
{
            "session_id": "11111111-2222-3333-4444-555555555555",
            "name": "Price Enquiry Reply",
            "channel": "whatsapp",
            "trigger_type": "keyword",
            "trigger_value": "price",
            "response_content": "Hi {{ name }}! Our plans start at $29/month. Visit
            https://example.com/pricing for details.",
            "priority": 10
            }
الطريقة المسار الوصف
GET
/api/automations List automations. Filter: session_id, is_active, trigger_type.
GET
/api/automations/{automation} Show automation with last 50 trigger logs.
PUT
/api/automations/{automation} Update any field.
DELETE
/api/automations/{automation} Delete automation.
POST
/api/automations/{automation}/toggle Toggle is_active on/off.

التحليلات

Read-only endpoints. Date filters use YYYY-MM-DD format.

GET
/api/analytics/overview

High-level snapshot: total contacts, active sessions, campaigns this month, messages today. No parameters.

json
{"total_contacts":4821,"active_sessions":3,"campaigns_this_month":12,"messages_today":347}
GET
/api/analytics/campaigns

Campaign delivery summary. Optional: from_date, to_date.

json
{"summary":{"total_campaigns":12,"total_sent":14800,"avg_delivery_rate":95.94},"campaigns":[...]}
GET
/api/analytics/sessions

Session message stats. Optional: from_date, to_date.

json
{"summary":{"total_sessions":3,"active_sessions":2,"total_messages_sent":8430},"sessions":[...]}
GET
/api/analytics/contacts

Contact growth data. Optional: from_date, to_date (defaults to last 30 days).

json
{"stats":{"total_contacts":4821,"new_contacts":342},"growth_data":[{"date":"2026-04-21","count":15}]}
GET
/api/analytics/message-activity

Hourly message volume by direction. Optional: from_date, to_date (defaults to last 7 days).

json
[{"date":"2026-04-21","hour":9,"direction":"outbound","count":124}]

الويب هوك

Webhooks push real-time event notifications to your server as HTTP POST requests. No polling required.

Managing Webhooks via API

الطريقة نقطة النهاية Purpose
GET
/api/v1/webhooks List all webhooks for the workspace.
POST
/api/v1/webhooks Register a new webhook. Secret auto-generated if omitted.
GET
/api/v1/webhooks/{id} Retrieve a single webhook.
PATCH
/api/v1/webhooks/{id} Update URL, events list, or active state.
DELETE
/api/v1/webhooks/{id} Remove the webhook.
GET
/api/v1/webhooks/{id}/deliveries Paginated delivery log (status, signature, attempts, duration).
POST
/api/v1/webhooks/{id}/test Dispatch a synthetic webhook.test event to the configured URL.

Delivery Headers

Header القيمة
X-Zagely-Event Event name (e.g. message.received).
X-Zagely-Delivery Unique delivery UUID. Use it for idempotency on your side.
X-Zagely-Timestamp Unix seconds when the request was signed.
X-Zagely-Signature sha256=<hex> — HMAC-SHA256 of "{timestamp}.{body}".
Content-Type application/json.

التحقق من التوقيع

The signed string is {timestamp}.{raw_body}. Always use a timing-safe comparison.

php
$timestamp = $_SERVER['HTTP_X_ZAGELY_TIMESTAMP'] ?? '';
            $signature = $_SERVER['HTTP_X_ZAGELY_SIGNATURE'] ?? '';
            $body = file_get_contents('php://input');

            $expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body,
            'your_webhook_secret');

            if (! hash_equals($expected, $signature)) {
            http_response_code(401); exit;
            }

            $event = json_decode($body, true);
javascript
// Node.js / Express — use express.raw() to keep the exact body bytes
                app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
                const timestamp = req.headers['x-zagely-timestamp'];
                const signature = req.headers['x-zagely-signature'];
                const expected  = 'sha256=' + require('crypto')
                .createHmac('sha256', process.env.ZAGELY_WEBHOOK_SECRET)
                .update(timestamp + '.' + req.body.toString('utf8'))
                .digest('hex');

                if (!require('crypto').timingSafeEqual(Buffer.from(signature),
                Buffer.from(expected))) {
                return res.status(401).send('Invalid signature');
                }

                const event = JSON.parse(req.body.toString('utf8'));
                res.sendStatus(200);
                });

أنواع الأحداث

الحدث متى يتم التفعيل
message.received New inbound message on any connected channel.
message.sent Outbound message successfully dispatched.
message.delivered Channel confirms delivery to recipient device.
message.read Recipient reads the message (WhatsApp & Email).
message.failed Delivery failed after all retries.
campaign.started Campaign begins processing recipients.
campaign.completed All recipients have been processed.
campaign.paused Running campaign is paused.
contact.created New contact record is created.
contact.updated Contact fields are updated.
contact.opted_out Contact unsubscribes from email or push.
session.connected Session connects successfully.
session.disconnected Session loses connection.
webhook.test Synthetic event triggered from the dashboard or the /test endpoint.

مثال للحمولة

json
{
            "event": "message.received",
            "tenant_id": "uuid-of-tenant",
            "delivery_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
            "occurred_at": "2026-04-21T10:30:00Z",
            "data": {
            "contact": { "id": "...", "name": "Jane Doe", "phone_number": "+15551234567", "email":
            null },
            "channel": "whatsapp",
            "content": "Hello, I need help with my order."
            }
            }

Retry Policy: Any response other than 2xx (or a timeout after 10 s) triggers a retry. Back-off schedule: 10 s, 60 s, 5 min, 30 min, 2 h. After 5 failed attempts the delivery is marked failed and surfaced in Webhook Logs for manual replay.

Errors

الكل /api/v1/* responses use the same JSON envelope so that clients can handle errors generically.

json
{
        "error": {
        "code": "machine_readable_snake_case",
        "message": "Human-readable description.",
        "details": { /* optional context */ }
        }
        }

HTTP Status Codes

الحالة المعنى Common cause
200 OK Success. Standard success for GET and some POST.
201 Created Resource created. Successful POST that creates a resource.
202 Accepted Job queued. Async operations: import, export, send.
400 Bad Request Malformed request. Invalid JSON or malformed Idempotency-Key.
401 Unauthorized Auth failure. Missing, wrong, inactive, or expired API keys.
403 Forbidden Insufficient permissions. API key lacks the required ability.
404 Not Found Resource not found. Wrong UUID or belongs to another workspace.
422 Unprocessable Validation failed. Missing / invalid field, duplicate unique value.
429 Too Many Requests Rate limit or quota exceeded. Back off. Inspect Retry-After.
500 Internal Error Server error. Contact support with the full response body.
503 Service Unavailable Dependency down. Database or upstream temporarily unreachable.

رموز الأخطاء

error.code الحالة الوصف
credentials_missing 401 No X-Public-Key / X-Secret-Key header pair was sent.
invalid_credentials 401 The public key does not match or the secret is wrong.
key_inactive 401 The key is disabled or past its expires_at.
unauthenticated 401 Session authentication failed (non key-auth endpoints).
insufficient_ability 403 Key lacks the required scope. details.required_ability contains the missing scope.
forbidden 403 Authorization policy denied the request.
not_found 404 Resource does not exist or belongs to another workspace.
validation_failed 422 One or more fields failed validation. details.errors is the errors object.
invalid_idempotency_key 400 The Idempotency-Key header violates length or character rules.
rate_limit_exceeded 429 Per-minute burst limit hit.
daily_quota_exceeded 429 Today's request quota is exhausted.
monthly_quota_exceeded 429 This month's request quota is exhausted.
json
// 422 — validation_failed
        {
        "error": {
        "code": "validation_failed",
        "message": "The given data was invalid.",
        "details": { "errors": { "channel": ["The channel field is required."] } }
        }
        }

        // 403 — insufficient_ability
        {
        "error": {
        "code": "insufficient_ability",
        "message": "The API key does not have the required ability.",
        "details": { "required_ability": "messages.send" }
        }
        }

        // 429 — daily_quota_exceeded
        {
        "error": {
        "code": "daily_quota_exceeded",
        "message": "Daily request quota exceeded.",
        "details": { "limit": 10000 }
        }
        }

Ready to integrate?

Generate your API key pair and start building today.