SocialHook
Docs/Outbound API

Outbound API

Send WhatsApp, Facebook, and Instagram messages from your own server through SocialHook. One signed endpoint covers all three platforms — your code doesn't change when you add a new platform.

When to use this

→
You want to message contacts from a CRM, helpdesk, or custom dashboard.
→
You're running an automation (n8n, Make, Zapier, your own worker) that needs to fan out replies.
→
You're building a chatbot whose brain lives on your servers.
For one-off replies, just type in the SocialHook inbox — no API needed. Your webhook can also respond with { "reply": "..." } to auto-send a message back without calling this endpoint at all. See Auto-reply below.

1. Get an API key

Open API key in your dashboard and click Generate. You'll see the full key once — copy it now and store it somewhere safe (e.g. your secrets manager). If you lose it, generate a new one; the old one stops working immediately.

Keys start with sk_live_ followed by 64 hex characters. Treat it like a password: never commit it to git, never expose it in browser code.

2. Endpoint

HTTP
POST https://api.socialhook.io/api/v1/outbound/<your_user_id>/send
Content-Type: application/json

Your user_id is shown in the URL on the API key page. The path is user-scoped on purpose so a leaked key can only impact one account.

3. Authentication

Choose the method that fits your setup. Both work with the same endpoint.

Method A — Simple key (n8n, Make, Zapier, no-code tools)

Add a single header. No signing required.

HTTP
X-Api-Key: <your_api_key>
n8n / HTTP Request node
# Method: POST
# URL:    https://api.socialhook.io/api/v1/outbound/<your_user_id>/send
# Header: X-Api-Key  →  sk_live_…
# Body (JSON):
{
  "platform": "whatsapp",
  "to": "447911123456",
  "message_text": "Hello from n8n!"
}

Method B — Signed request (HMAC-SHA256, custom servers)

Build the signature by HMAC-SHA256-hashing <timestamp>.<raw_body> with your API key, then send the lowercase hex digest in the X-Socialhook-Signature header prefixed with sha256=. Requests with timestamps more than 5 minutes from server time are rejected.

HTTP
X-Socialhook-Timestamp: <unix_seconds>
X-Socialhook-Signature: sha256=<hmac_hex>
Node.js
const crypto = require('crypto');

const API_KEY = process.env.SOCIALHOOK_API_KEY;     // sk_live_…
const USER_ID = process.env.SOCIALHOOK_USER_ID;     // shown on the API key page

const body = JSON.stringify({
  platform: 'whatsapp',
  to: '447911123456',
  message_text: 'Hello from your SocialHook integration!',
});
const ts  = Math.floor(Date.now() / 1000).toString();
const sig = crypto
  .createHmac('sha256', API_KEY)
  .update(`${ts}.${body}`)
  .digest('hex');

const res = await fetch(
  `https://api.socialhook.io/api/v1/outbound/${USER_ID}/send`,
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Socialhook-Timestamp': ts,
      'X-Socialhook-Signature': `sha256=${sig}`,
    },
    body,
  }
);

if (!res.ok) throw new Error(await res.text());
cURL
# Drop your real API_KEY and USER_ID in here
TS=$(date +%s)
BODY='{"platform":"whatsapp","to":"447911123456","message_text":"Hello!"}'
SIG=$(printf "%s.%s" "$TS" "$BODY" | openssl dgst -sha256 -hmac "$API_KEY" | awk '{print $2}')

curl -X POST https://api.socialhook.io/api/v1/outbound/$USER_ID/send \
  -H "Content-Type: application/json" \
  -H "X-Socialhook-Timestamp: $TS" \
  -H "X-Socialhook-Signature: sha256=$SIG" \
  -d "$BODY"
Important: sign the exact raw body bytes you send. Re-serializing JSON between signing and POSTing changes the bytes and breaks the signature.

4. Request body per platform

WhatsApp

JSON
{
  "platform": "whatsapp",
  "to": "447911123456",
  "message_text": "Hello from your integration!"
}

"to" is the recipient phone number in E.164 format (no leading +).

Facebook

JSON
{
  "platform": "facebook",
  "to": "<recipient_psid>",
  "page_id": "<your_facebook_page_id>",
  "message_text": "Hi from SocialHook"
}

"to" is the page-scoped user id (PSID). "page_id" is required so we know which page to send from.

Instagram

JSON
{
  "platform": "instagram",
  "to": "<recipient_psid>",
  "page_id": "<your_facebook_page_id>",
  "message_text": "Hey from SocialHook"
}

"to" is the IG-scoped user id. "page_id" is the linked Facebook Page id (Instagram piggybacks the FB OAuth).

5. Response codes

CodeMeaningWhat to do
200SentMessage accepted by the platform; appears in the chat thread.
400Bad requestMissing or invalid field. Check platform/to/message_text/page_id.
401UnauthorizedX-Api-Key missing/invalid, or X-Socialhook-Signature did not match. Recompute it with the exact raw body.
403Window expired24-hour messaging window closed (FB / IG / WA). Wait for an inbound message before replying.
408Timestamp too oldX-Socialhook-Timestamp is more than 5 minutes from server time. Resend with a fresh timestamp.
429Rate limitedToo many requests. Back off and retry.

Auto-reply (skip the endpoint)

If your only outbound need is responding to incoming messages, you don't need to call this endpoint at all. When SocialHook delivers a webhook to you, your server can respond with one of:

JSON
// Single reply
{ "reply": "Thanks — got your message!" }

// Or multiple in order
{ "replies": ["Thanks!", "We'll get back to you within an hour."] }

SocialHook sends each reply on the right platform automatically and records it in the chat thread. Reply failures don't block delivery acknowledgement — your webhook is still marked DELIVERED.

Replay protection (signed requests)

→
Timestamps older than 5 minutes are rejected (clock-skew tolerant).
→
Signatures are compared in constant time.
→
Path is user-scoped, so a leaked key can only impact one account.
→
Revoking the key (or generating a new one) blocks any further requests with the old key immediately.

What's next

→
Generate your API key if you haven't already.
→
Read the payload reference to understand what your webhook receives — useful when wiring auto-reply.
→
Review signature verification for inbound webhook deliveries (different signing scheme — same idea).