WhatsApp API v1

WhatsApp API Documentation

Send messages, manage templates, and access contacts from any application.

Quick Start

1. Get an API Key

Log into the platform at social.slick.company, go to Settings, and create an API key under "WhatsApp API Keys".

2. Authenticate

Include your API key in every request:

# Header (recommended)
X-API-Key: slick_your_api_key_here

# Or query parameter
?api_key=slick_your_api_key_here

3. Set Business ID

If your API key is scoped to a business, it's automatic. Otherwise pass business_id in the request body, query string, or X-Business-ID header.

Available businesses: warpoint, notebook

Base URL

https://social.slick.company/api/v1/whatsapp

Template Lifecycle

WhatsApp requires message templates to be approved by Meta before use. The lifecycle is:

  1. Create a template via API → status: draft
  2. Submit to Meta for review → status: submitted
  3. Wait 24-48h. Platform auto-polls every 30 min → status: approved or rejected
  4. Send messages using the approved template

Free-form text messages (without templates) can only be sent within a 24-hour window after the customer messages you first.

Endpoints

POST /send Send a single message

Send a WhatsApp message to one recipient using a template or free-form text.

ParameterTypeRequiredDescription
business_idstringYesBusiness slug, e.g. "notebook"
tostringYesPhone number in international format, e.g. "+96512345678"
template_namestring*Meta-approved template name. Use this OR template_id.
template_idstring*Template ID from this platform (name resolved automatically)
languagestringNoTemplate language code. Default: "ar"
parametersobjectNoTemplate variables: {"body": ["value1", "value2"]}
textstringNoFree-form text (24h window only). Overrides template fields.

Example

curl -X POST https://social.slick.company/api/v1/whatsapp/send \
  -H "X-API-Key: slick_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "business_id": "notebook",
    "to": "+96512345678",
    "template_name": "hello_world",
    "language": "en"
  }'

Response

{
  "ok": true,
  "result": {
    "messages": [{"id": "wamid.HBgLOTY1MTIzNDU2NzgVAgA..."}]
  }
}
POST /send-bulk Send bulk messages

Send a template message to multiple recipients. Provide a list of phone numbers OR a segment name to pull contacts from the database. Runs in the background.

ParameterTypeRequiredDescription
business_idstringYesBusiness slug
template_namestringYesApproved template name
languagestringNoDefault: "ar"
recipientsstring[]NoPhone numbers array. If empty, uses segment.
segmentstringNoContact segment: "customers", "vip", "all"

Example — send to a segment from the database

curl -X POST https://social.slick.company/api/v1/whatsapp/send-bulk \
  -H "X-API-Key: slick_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "business_id": "notebook",
    "template_name": "promo_sale",
    "segment": "customers"
  }'

Example — send to specific numbers

curl -X POST https://social.slick.company/api/v1/whatsapp/send-bulk \
  -H "X-API-Key: slick_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "business_id": "warpoint",
    "template_name": "tournament_invite",
    "language": "ar",
    "recipients": ["+96565965089", "+96599887766", "+96555443322"]
  }'

Response

{
  "ok": true,
  "campaign_id": "api-1a2b3c4d",
  "recipients": 1103,
  "status": "sending",
  "message": "Sending to 1103 recipients in background"
}
GET /templates List message templates

Get all WhatsApp message templates. Filter by status to get only approved ones ready for sending.

Query ParamDescription
business_idFilter by business slug
statusdraft, submitted, approved, or rejected
curl "https://social.slick.company/api/v1/whatsapp/templates?business_id=notebook&status=approved" \
  -H "X-API-Key: slick_your_key"

Response

{
  "templates": [
    {
      "id": "tpl-abc123",
      "name": "hello_world",
      "category": "MARKETING",
      "language": "en",
      "body_text": "Hello! Welcome to Notebook.",
      "status": "approved",
      "meta_template_id": "123456789"
    }
  ],
  "count": 1
}
POST /templates Create a message template

Create a new template as a draft. Use {{1}}, {{2}} for variables in the body.

ParameterTypeRequiredDescription
business_idstringYesBusiness slug
namestringYesTemplate name (lowercase, underscores, no spaces)
body_textstringYesMessage body (max 1024 chars)
categorystringNoMARKETING (default), UTILITY, AUTHENTICATION
languagestringNoDefault: "ar"
header_typestringNo"text", "image", "video", or empty
footer_textstringNoFooter (max 60 chars)
curl -X POST https://social.slick.company/api/v1/whatsapp/templates \
  -H "X-API-Key: slick_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "business_id": "notebook",
    "name": "order_update",
    "body_text": "Hi {{1}}, your order #{{2}} is on its way! Track: {{3}}",
    "category": "UTILITY",
    "language": "en"
  }'
POST /templates/{id}/submit Submit template to Meta for approval

Submit a draft template to Meta for review. Approval typically takes 24-48 hours. The platform polls every 30 minutes and updates status automatically.

curl -X POST https://social.slick.company/api/v1/whatsapp/templates/tpl-abc123/submit \
  -H "X-API-Key: slick_your_key"

Response

{"ok": true, "status": "submitted", "meta_template_id": "123456789"}
GET /contacts List contacts

Retrieve contacts from the database. Contacts are synced daily from business databases and can be added via API.

Query ParamDescription
business_idFilter by business
segmentFilter by segment (e.g. "customers", "vip")
curl "https://social.slick.company/api/v1/whatsapp/contacts?business_id=warpoint" \
  -H "X-API-Key: slick_your_key"

Response

{
  "contacts": [
    {"id": "c-xxx", "phone": "+96565965089", "name": "Ahmed", "segment": "customers", "opted_in": true}
  ],
  "count": 1030
}
POST /trigger Trigger template via webhook (OTP, notifications)

Fire-and-forget endpoint for sending a template with variables. Ideal for OTPs, order confirmations, booking reminders. Language is auto-detected from the template database. For AUTHENTICATION templates (like OTP), the copy-code button parameter is automatically included.

ParameterTypeRequiredDescription
tostringYesPhone number in international format
template_namestringYesApproved template name (e.g. "send_otp")
variablesstring[]NoValues for {{1}}, {{2}}, etc.
languagestringNoAuto-detected from DB if omitted

Example — Send OTP

curl -X POST https://social.slick.company/api/v1/whatsapp/trigger \
  -H "X-API-Key: slick_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+96566477000",
    "template_name": "send_otp",
    "variables": ["847291"]
  }'

Example — Booking Reminder

curl -X POST https://social.slick.company/api/v1/whatsapp/trigger \
  -H "X-API-Key: slick_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+96599887766",
    "template_name": "system_booking_reminder_new",
    "variables": ["Ahmed", "VR Arena", "Tomorrow 6 PM"]
  }'

Example — Discount Notification

curl -X POST https://social.slick.company/api/v1/whatsapp/trigger \
  -H "X-API-Key: slick_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+96566477000",
    "template_name": "discount_reminder",
    "variables": ["Fahad", "15", "2026-05-01", "66477000", "Fahad", "15", "May 1st 2026", "66477000"]
  }'

Response

{"ok": true, "result": {"messages": [{"id": "wamid.HBgL..."}]}}

How to use for OTP in your app

// Node.js example - send OTP from your backend
const otp = Math.floor(100000 + Math.random() * 900000); // 6-digit OTP

await fetch('https://social.slick.company/api/v1/whatsapp/trigger', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'slick_your_warpoint_key'
  },
  body: JSON.stringify({
    to: '+96566477000',
    template_name: 'send_otp',
    variables: [String(otp)]
  })
});
POST /contacts Add a contact
ParameterTypeRequiredDescription
business_idstringYesBusiness slug
phonestringYesInternational format
namestringNoContact name
segmentstringNoe.g. "customers", "leads", "vip"
curl -X POST https://social.slick.company/api/v1/whatsapp/contacts \
  -H "X-API-Key: slick_your_key" \
  -H "Content-Type: application/json" \
  -d '{"business_id": "warpoint", "phone": "+96599887766", "name": "Fahad", "segment": "vip"}'

Ads API

Read spend and campaigns across Meta (Facebook & Instagram), Google Ads, TikTok and Snapchat for one business, and pause, resume or re-budget its campaigns. Use the same business-scoped X-API-Key as the WhatsApp API.

Base URL: https://social.slick.company/api/v1/ads

Connecting an ad account

Each platform is connected once per business with the platform's own login (OAuth), then you choose which ad account to use. One login often reaches several ad accounts, so the account is never guessed.

  1. POST /connect/{platform} with your callback_url → returns data.auth_url.
  2. Send the user to auth_url. They sign in to the platform and approve access.
  3. After sign-in the browser returns to your callback_url with code and state (for every platform, including TikTok).
  4. POST /connect/{platform}/callback with that code and state. If the login reaches exactly one ad account it is selected automatically; otherwise the response has "needs_account_selection": true and the list of accounts.
  5. POST /connect/{platform}/account with the chosen account_id. (GET /connect/{platform}/accounts lists them again at any time.)

Nothing to register. The ad platforms always return the user to Slick, and Slick forwards the browser to your callback_url. Any http(s) URL you control works.

{platform} is one of facebook, google, tiktok, snapchat.

POST /connect/{platform} Start connecting a platform
FieldTypeRequiredDescription
callback_urlstringYesYour page that receives ?code=…&state=… after sign-in (Slick forwards to it)

Response

{"success": true, "data": {"auth_url": "https://www.facebook.com/v21.0/dialog/oauth?..."}}
POST /connect/{platform}/callback Finish connecting
FieldTypeRequiredDescription
codestringYesFrom the redirect to your callback_url
statestringYesFrom the redirect; must match the one Slick issued

Response

{
  "success": true,
  "platform": "google",
  "account_id": "",
  "account_name": "",
  "needs_account_selection": true,
  "accounts": [
    {"account_id": "5182527639", "account_name": "Warpoint", "currency": "USD", "timezone": "Asia/Kuwait"},
    {"account_id": "1344141276", "account_name": "Another business", "currency": "USD", "timezone": "Asia/Kuwait"}
  ]
}
GET /connect/{platform}/accounts List reachable ad accounts
{"success": true, "data": {"accounts": [ ...same shape as above... ], "selected_account_id": "5182527639"}}
POST /connect/{platform}/account Choose the ad account
FieldTypeRequiredDescription
account_idstringYesOne of the listed accounts. Meta ids may be sent with or without act_.

Response

{"success": true, "data": {"account_id": "5182527639", "account_name": "Warpoint", "currency": "USD"}}

Returns 404 if the connected login can't reach that account.

DELETE /connect/{platform} Disconnect a platform
{"success": true}
GET /platforms Connection status
{
  "platforms": [
    {"platform": "google", "is_connected": true, "account_id": "5182527639", "account_name": "Warpoint",
     "currency": "USD", "needs_account_selection": false, "last_synced": "2026-10-03 09:12:44"},
    {"platform": "snapchat", "is_connected": false}
  ]
}

A platform that is connected but still needs_account_selection returns no spend or campaigns until an account is chosen.

GET /spend Daily spend per campaign
QueryRequiredDescription
start_date, end_dateYesYYYY-MM-DD, inclusive, in each ad account's own time zone. Any length of range works in one call; Slick pages through each platform and splits long ranges where a platform requires it. An invalid or reversed range returns 400.
platformsNoComma-separated subset, e.g. google,tiktok. Default: all connected.

Response

{
  "data": [
    {"platform": "google", "date": "2026-10-02", "campaign_id": "12345678901",
     "spend": 5.286, "clicks": 382, "impressions": 9411, "conversions": 0,
     "revenue": 0, "revenue_source": "reported by Google Ads"}
  ]
}

Snapchat: at account level Snapchat reports spend only, so its rows have clicks and impressions of 0.

Errors per platform: if a platform can't be read (expired login, API error), the others are still returned and the response adds an errors object, for example "errors": {"tiktok": "tiktok: Access token is expired"}. The field is absent when everything succeeded, so a platform with no rows and no entry in errors genuinely had no spend.

GET /campaigns Campaigns and budgets
{
  "campaigns": [
    {"platform_campaign_id": "12345678901", "platform": "google",
     "name": "Warpoint VR Arena — Kuwait Gamers & Activities", "status": "active",
     "objective": "SEARCH", "daily_budget": 4.92, "lifetime_budget": 0, "currency": "KWD"}
  ]
}

Meta, Google and TikTok, every campaign (all pages). status is active or paused. A platform that fails is listed in an errors object, as for /spend.

POST /campaigns/{id}/pause Pause a campaign

Body: {"platform": "google"}

POST /campaigns/{id}/resume Resume a campaign

Body: {"platform": "google"}

POST /campaigns/{id}/budget Change the daily budget

Body: {"platform": "tiktok", "daily_budget": 5}. The budget is in KWD and converted to the ad account's currency. Not available for Snapchat yet.

Errors

Failures return an HTTP error status with {"success": false, "error": "..."}. Calls to each platform are lightly throttled per business; a burst of requests waits briefly instead of failing.

Integration Examples

Node.js

const API_KEY = 'slick_your_key_here';
const BASE = 'https://social.slick.company/api/v1/whatsapp';

async function sendWhatsApp(to, templateName, bizId = 'notebook') {
  const res = await fetch(`${BASE}/send`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY },
    body: JSON.stringify({ business_id: bizId, to, template_name: templateName, language: 'ar' }),
  });
  return res.json();
}

// Usage
const result = await sendWhatsApp('+96512345678', 'order_confirmation');

Python

import requests

API_KEY = 'slick_your_key_here'
BASE = 'https://social.slick.company/api/v1/whatsapp'

def send_whatsapp(to, template_name, biz_id='notebook'):
    return requests.post(f'{BASE}/send',
        headers={'X-API-Key': API_KEY, 'Content-Type': 'application/json'},
        json={'business_id': biz_id, 'to': to, 'template_name': template_name, 'language': 'ar'}
    ).json()

result = send_whatsapp('+96512345678', 'order_confirmation')

PHP

$apiKey = 'slick_your_key_here';
$base = 'https://social.slick.company/api/v1/whatsapp';

$ch = curl_init("$base/send");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', "X-API-Key: $apiKey"],
    CURLOPT_POSTFIELDS => json_encode([
        'business_id' => 'notebook', 'to' => '+96512345678',
        'template_name' => 'order_confirmation', 'language' => 'ar',
    ]),
    CURLOPT_RETURNTRANSFER => true,
]);
$result = json_decode(curl_exec($ch), true);

Go

package main

import (
    "bytes"
    "encoding/json"
    "net/http"
)

func sendWhatsApp(to, template, bizID string) (map[string]interface{}, error) {
    body, _ := json.Marshal(map[string]string{
        "business_id": bizID, "to": to, "template_name": template, "language": "ar",
    })
    req, _ := http.NewRequest("POST", "https://social.slick.company/api/v1/whatsapp/send", bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-API-Key", "slick_your_key_here")

    resp, err := http.DefaultClient.Do(req)
    if err != nil { return nil, err }
    defer resp.Body.Close()

    var result map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&result)
    return result, nil
}

Error Codes

HTTPMeaningCommon Cause
401UnauthorizedMissing or invalid API key
400Bad RequestMissing required fields
404Not FoundTemplate or endpoint not found
500Server ErrorMeta API failure

All errors include an "error" field. If WhatsApp is not configured: {"error":"WhatsApp not configured","status":"not_configured"}

Rate Limits & Notes

Home Create account Privacy Policy Terms of Service Slick