---
title: "Send API"
source: https://sumgenius.ai/docs/send-api
generated: 2026-09-14
---

# Send API

The original message-sending endpoint, used by production integrations today and fully supported. New integrations should prefer [`POST /v1/messages`](https://sumgenius.ai/docs/rest-api#send-message) on the REST API: it drives the same sending engine with the same request body, and returns the standard v1 envelope.

Webhook & API Add-on · $29/month · Requires Creator+ plan

### The endpoint

One authenticated `POST` handles all five platforms and every action. Choose the recipient via `conversation_id`, `platform_user_id`, or `to_phone`. [Authentication](https://sumgenius.ai/docs/api-authentication) is the same API key as everything else.

Send API vs POST /v1/messages

- request bodyidentical Same fields, same actions, same routing, same idempotency. Moving between the two is a URL change.
- responsediffers This endpoint returns a flat body with a `success` boolean and a local-time `sent_at`. `POST /v1/messages` returns the v1 envelope (`{"data": ...}` / `{"error": ...}`) with UTC ISO-8601 timestamps and stable machine-readable error codes.
- existing integrations Nothing to change. This endpoint is stable; both endpoints share one engine, so behavior and capabilities never drift apart.

Actions

- send_message All five platforms. Free-form text, or approved WhatsApp templates.
- react · unreact Facebook and Instagram DMs only.
- typing_on Facebook, Instagram, and Telegram.

**Endpoint**

```
POST https://sumgenius.ai/api/meta/webhook-send.php
```

**Recommended for new integrations**

```
POST https://sumgenius.ai/api/v1/messages
// same body, v1 envelope; see the REST API page
```

## Actions

### POST action: send_message

Send a text message to a customer. This is the default action when `action` is omitted.

Body

- idempotency_keystringrequired Your unique key for this request (max 128 chars). Submitting the same key twice returns the original result without resending.
- conversation_idinteger The `conversation.id` from a received webhook event. Use this, or the routing below.
- platform_user_idstring The customer's PSID for Facebook or Instagram, or the `chat_id` for Telegram. Requires `platform` when used without `conversation_id`.
- to_phonestring Recipient phone in E.164 format, for SMS or WhatsApp. Auto-normalized. For WhatsApp also set `platform: "whatsapp"`; for SMS the platform auto-sets.
- platformenumoptional `facebook` · `instagram` · `sms` · `whatsapp` · `telegram`. Required when routing by `platform_user_id`, and for WhatsApp with `to_phone`.
- message_textstringrequired Max 1,000 chars on Instagram, 2,000 on Facebook, 1,600 on SMS (GSM-7) or 1,530 (UCS-2), 4,096 on WhatsApp and Telegram. See [Encoding & Segments](https://sumgenius.ai/docs/webhook-sms#encoding) for SMS segmentation. For WhatsApp templates use `template_name` instead.
- template_name · template_language · template_componentsoptional **WhatsApp only.** Send an approved template any time, no 24h window required. `template_language` defaults to `en_US`; components pass through to Meta verbatim. See [WhatsApp Templates](https://sumgenius.ai/docs/webhook-whatsapp#templates).

> **Note**
>
> **Cold outbound.** SMS, WhatsApp, and Telegram can send to a recipient with no existing conversation; the thread is auto-created. Facebook and Instagram require the customer to have messaged first. A failed cold send still creates the conversation and contact; see the [note on the REST page](https://sumgenius.ai/docs/rest-api#send-message).

**Request**

```
curl -X POST https://sumgenius.ai/api/meta/webhook-send.php \
  -H "X-SumGenius-Api-Key: sgwh_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "idempotency_key": "confirm-appt-10482-001",
    "conversation_id": 10482,
    "message_text": "Your appointment is confirmed for Tuesday at 2pm. See you then!"
  }'
```

**Node.js**

```javascript
const response = await fetch('https://sumgenius.ai/api/meta/webhook-send.php', {
  method: 'POST',
  headers: {
    'X-SumGenius-Api-Key': process.env.CHATGENIUS_API_KEY,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    idempotency_key: 'confirm-appt-10482-001',
    conversation_id: 10482,
    message_text: 'Your appointment is confirmed for Tuesday at 2pm.'
  })
});
const result = await response.json();
```

**PHP**

```php
$payload = json_encode([
    'idempotency_key' => 'confirm-appt-10482-001',
    'conversation_id' => 10482,
    'message_text'    => 'Your appointment is confirmed for Tuesday at 2pm.'
]);

$ch = curl_init('https://sumgenius.ai/api/meta/webhook-send.php');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $payload,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'X-SumGenius-Api-Key: ' . getenv('CHATGENIUS_API_KEY'),
        'Content-Type: application/json'
    ]
]);
$result = json_decode(curl_exec($ch), true);
```

### POST send_message · private reply to a comment

Reply privately to an Instagram or Facebook **comment** — the one DM Meta allows a page to send to a commenter who has never messaged you. A comment does not open the messaging window; this endpoint is the sanctioned way around it. Pair it with the [`comment.received`](https://sumgenius.ai/docs/webhook-events#comment-received) event, which hands you both identifiers you need.

Body (in addition to `idempotency_key` and `message_text` above)

- comment_idstringrequired The comment to reply to — `comment.id` from the `comment.received` event. Its presence switches the send into private-reply mode.
- platformenumrequired `instagram` or `facebook` — the platform the comment lives on.
- platform_user_idstringrequired The commenter — `customer.platform_user_id` from the event. Used to create the conversation record (a commenter usually has none yet).
- platform_user_namestringoptional The commenter's username or display name from the event, stored on the new conversation so it shows a name instead of an ID.
- buttonsarrayoptional Up to 3 buttons. Each: `{"type": "postback", "title": "...", "payload": "..."}` or `{"type": "url", "title": "...", "url": "https://..."}`. Titles max 20 chars; postback payloads max 1,000 chars. When the customer taps a postback button, the payload comes back to your endpoint as a [`message.postback`](https://sumgenius.ai/docs/webhook-events#message-postback) event, and the tap opens the 24h window so you can continue with regular sends.

> **Warning**
>
> **One private reply per comment.** Meta enforces it. A second attempt returns `409` with a clear error. The reply must be sent within 7 days of the comment. `message_text` is capped at 1,000 chars (640 with buttons). The reply can only target comments on your own connected account's posts.

**Request**

```
curl -X POST https://sumgenius.ai/api/meta/webhook-send.php \
  -H "X-SumGenius-Api-Key: sgwh_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "idempotency_key": "reply-cmt-18013620254946776",
    "platform": "instagram",
    "comment_id": "18013620254946776",
    "platform_user_id": "1869400170313836",
    "platform_user_name": "jaredr.media",
    "message_text": "Here is the guide for that post: https://example.com/guide",
    "buttons": [
      { "type": "postback", "title": "Send me more", "payload": "more_guides" },
      { "type": "url", "title": "Open the guide", "url": "https://example.com/guide" }
    ]
  }'
```

**The loop**

```
1. comment.received arrives at your server
   (source_post.id, comment.text_raw, customer)
2. Your server picks the resource for that post
3. POST back here with comment_id + the link
4. Tap on a postback button → message.postback
   event + the 24h window opens
5. Continue with regular send_message calls
```

### POST action: react

Add an emoji reaction to a specific message. Facebook and Instagram DMs only; other platforms reject it with HTTP 422. Use the `message.id` from a received webhook event as `target_message_id`.

Body

- actionrequired Must be `"react"`.
- idempotency_keyrequired
- conversation_id or platform_user_id (+ platform)required `platform` is `facebook` or `instagram`.
- target_message_idrequired The platform message id to react to.
- reactionoptional `love` ❤️ · `smile` 😆 · `wow` 😮 · `sad` 😢 · `angry` 😡 · `yes` 👍 · `no` 👎. Defaults to `love` when omitted.

**Request**

```
curl -X POST https://sumgenius.ai/api/meta/webhook-send.php \
  -H "X-SumGenius-Api-Key: sgwh_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "react",
    "idempotency_key": "react-10482-msg-001",
    "conversation_id": 10482,
    "target_message_id": "mid.1234567890abcdef",
    "reaction": "love"
  }'
```

### POST action: unreact

Remove a previously added reaction. Same routing and fields as [react](#react), with `action: "unreact"` and no `reaction` field.

**Request**

```
curl -X POST https://sumgenius.ai/api/meta/webhook-send.php \
  -H "X-SumGenius-Api-Key: sgwh_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "unreact",
    "idempotency_key": "unreact-10482-msg-001",
    "conversation_id": 10482,
    "target_message_id": "mid.1234567890abcdef"
  }'
```

### POST action: typing_on

Show a typing indicator (the "..." bubble) to the customer. Lasts about 20 seconds or until you send a message. Supported on Facebook, Instagram, and Telegram (where it maps to `sendChatAction`).

Body

- actionrequired Must be `"typing_on"`.
- idempotency_keyrequired
- conversation_id or platform_user_id (+ platform)required No `message_text` needed.

> **Note**
>
> Typing indicators are ephemeral. They are **not retried** on failure: a failed send goes straight to `dead_letter`. On Meta DM the messaging-window check is skipped, since Meta allows `sender_action` outside the window.

**Request**

```
curl -X POST https://sumgenius.ai/api/meta/webhook-send.php \
  -H "X-SumGenius-Api-Key: sgwh_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "typing_on",
    "idempotency_key": "typing-10482-001",
    "conversation_id": 10482
  }'
```

## Behavior

### Idempotency

Every request requires an `idempotency_key` (1 to 128 characters). Submitting the same key again returns the original result without resending, which makes retries safe under network failures. A reliable pattern: `{your-reference}-{conversation_id}-{sequence}`.

**Replay of a processed key**

```
{
  "success": true,
  "status": "duplicate",
  "request_id": "msgreq_c8d83cf861ec1f48",
  "message": "Request already processed with this idempotency key."
}
```

### Response format

Responses are JSON. This endpoint's shape predates the v1 envelope: a flat body with a `success` boolean.

- successboolean
- statusenum `sent`: delivered to the platform · `queued_retry`: immediate send failed, queued for automatic retry · `duplicate`: this idempotency key was already processed · `dead_letter`: permanently failed.
- request_id ChatGenius request id. Provide it when contacting support.
- conversation_id · platform · action The resolved conversation, platform (any of the five), and executed action.
- target_message_id · reaction Present on `react` / `unreact`.
- sent_at Local-time string on this endpoint. `POST /v1/messages` returns the same moment as UTC ISO-8601.

**Success**

```
{
  "success":         true,
  "status":          "sent",
  "request_id":      "msgreq_c8d83cf861ec1f48",
  "conversation_id": 10482,
  "platform":        "instagram",
  "action":          "send_message",
  "sent_at":         "2026-02-20 08:15:32",
  "message":         "Message sent successfully."
}
```

### Errors

Errors return `"success": false` with an `error` string describing what went wrong. (`POST /v1/messages` returns the same failures with stable machine-readable codes; branch on those if you need programmatic handling.)

| HTTP | When |
| --- | --- |
| 200 | Request accepted. Always check `status`: a 200 may also carry `queued_retry` or `duplicate`. |
| 400 | Invalid or missing request fields, or a non-JSON body |
| 401 | Missing or invalid API key |
| 403 | Valid key, but the add-on is not active |
| 405 | Wrong HTTP method; only POST is accepted |
| 422 | Validation passed but the request cannot be fulfilled: message too long, unsupported action for the platform, expired messaging window |
| 500 | Unexpected server error; contact support with your `request_id` |

**Error**

```
{
  "success": false,
  "status":  "error",
  "error":   "message_text is required for action=send_message."
}
```

### Limits

These apply to sending through either endpoint; the platforms enforce them, not us.

Message length

- instagram1,000 chars
- facebook2,000 chars
- sms1,600 GSM-7 · 1,530 UCS-2 Auto-split into segments (160 chars GSM-7, 70 UCS-2). See [Encoding & Segments](https://sumgenius.ai/docs/webhook-sms#encoding).
- whatsapp4,096 chars
- telegram4,096 chars Sent as plain text, never parsed as HTML or Markdown.

Platform rate limits

- Instagram DM 100 messages/second, 750 private replies/hour.
- Facebook Messenger 300 messages/second, 750 private replies/hour.
- SMS (Twilio) In practice your plan's monthly SMS allowance is the limit you reach first (500/month on Professional, 2,500 on Business, plus any volume packs), well before any carrier ceiling. Above that sits the A2P 10DLC layer: we register a low-volume campaign, which T‑Mobile caps at roughly 2,000 message segments per day. That cap counts T‑Mobile traffic only (including Sprint and MetroPCS), applies per registered brand across every provider sending for that business, and resets at midnight Pacific. Your exact daily limit and per-second throughput are set by your brand's Trust Score from The Campaign Registry, not by us, and are visible in Twilio's Trust Hub once your brand is approved. US and Canadian carriers require your business to be registered before this number can send at all, which you complete once from Settings → SMS in your portal. We handle the submission and tell you when the carriers approve it.

If a platform returns a rate-limit error, the request is queued and retried automatically (`status: queued_retry`).

**Length check before sending**

```
// per-platform caps
const LIMITS = {
  instagram: 1000,
  facebook:  2000,
  sms:       1600,
  whatsapp:  4096,
  telegram:  4096
};
```
