# SendRCS API - complete documentation > Everything needed to use the SendRCS RCS and SMS API, in one file: the guide, every endpoint, every schema and every webhook event. Written for language models and coding agents - no links need to be followed. - API version: 2.0.1 - Base URL: https://api.sendrcs.eu/api/rcs - Machine-readable source of truth: https://docs.sendrcs.eu/openapi.yaml - Shorter index: https://docs.sendrcs.eu/llms.txt - Interactive reference: https://docs.sendrcs.eu/ - Sign up for an API key: https://app.sendrcs.eu/auth/signup Derived from the OpenAPI specification above - the same source as the interactive reference. ## Contents 1. Guide and quick start - the same content as llms.txt 2. Endpoint reference - every path, parameter, body and response 3. Webhook events 4. Schemas 5. Assistant setup notes --- # Part 1 - Guide and quick start > SendRCS API for sending rich, interactive messages via RCS (Rich Communication Services) with SMS fallback. Machine-readable specification: https://docs.sendrcs.eu/openapi.yaml ## Quick Start Base URL: `https://api.sendrcs.eu/api/rcs` Authentication: Bearer token or API key in `X-API-Key` header (not needed for the sandbox endpoints below). ## Try it without an account (Sandbox) You can validate and preview any message body before you have an API key. The sandbox runs the same validator as `POST /send`, returns the same error shape, and gives you a phone-mockup preview link. Nothing is sent, no credits are used, and no auth header is needed. - `POST https://api.sendrcs.eu/api/rcs/sandbox/validate` - validation only - `POST https://api.sendrcs.eu/api/rcs/sandbox/preview` - validation plus a `share_url` (expires after 24 hours) - `GET https://api.sendrcs.eu/api/rcs/sandbox/samples` - sample images, a video and a PDF you may reference Send the exact body you would send to `/send` or `/send-batch`. Recipient and sender fields (`phoneNumber`, `sendernameId`, `scheduleAt`, ...) are accepted and listed under `sandbox.ignored_fields`. Only media from `/sandbox/samples` is rendered; other `https` URLs still pass validation but show as a placeholder in the preview. ```bash curl -X POST https://api.sendrcs.eu/api/rcs/sandbox/preview \ -H "Content-Type: application/json" \ -d '{ "messageType": "richCard", "content": { "title": "Your order is on its way", "description": "Track the parcel or talk to us.", "media": { "height": "TALL", "contentInfo": { "fileUrl": "https://sendrcs.eu/images/rcsexamples/shoes-1.jpg", "contentType": "image/jpeg" } }, "cardActions": [ { "type": "openUrl", "text": "Track order", "openUrlAction": { "url": "https://example.com/track" } }, { "type": "dial", "text": "Call us", "dialAction": { "phoneNumber": "+4512345678" } } ] } }' ``` Read `valid`, `errors`, `warnings`, `preview.share_url` and `preview.expires_at`. Open `share_url` to see the message on a phone mockup (Android and iOS). Rate limit: 20 requests per minute and 100 per day per IP. Sandbox preview ids cannot be used to send. When you are ready, create a free account at https://app.sendrcs.eu/auth/signup and call `/send` with your API key. ## Core Endpoints ### Send Single Message POST /api/rcs/send Send an RCS message to one recipient. Supports text, rich cards, carousels, and media with interactive buttons. ### Send Batch Messages POST /api/rcs/send-batch Send to up to 10,000 recipients. Automatic disabled contact filtering and progress tracking. ### Send Standalone SMS POST /api/rcs/send-sms Send a plain SMS (no RCS involved). Max 1530 characters, billed per segment. Supports the full merge field set (see Available Merge Fields) including `{{unsubscribe_link}}` - placeholders are replaced per recipient before segments are calculated. Supports `scheduleAt`/`timeZone` scheduling. When a Shopify store is connected, links to that store are replaced by short tracked links at send time (clicks and purchases show up in statistics); the billed segment count reflects the final text. ### Cancel a Scheduled Message DELETE /api/messages/{id} Cancel one scheduled message before it is sent, RCS or SMS alike. Only messages still in `SCHEDULED` status can be cancelled; anything already sending or sent returns `404`. Pass the `messageId` returned by a scheduled `/send` or `/send-sms`, or an `id` from `GET /api/messages/log?status=SCHEDULED`. Cancelling one recipient of a scheduled batch drops only that message and leaves the rest of the batch on schedule - to cancel a whole batch use `DELETE /api/rcs/batch/{jobId}` instead. Requires the `messages:write` permission. ## Message Types - **text**: Text with up to 11 chip suggestions (reply buttons) - **textBasic**: Plain text only (no interactive elements) - **richCard**: Card with image, title, description, and up to 4 action buttons + 4 chip suggestions - **carousel**: Up to 10 rich cards in horizontal scroll - **media**: Standalone image or video with optional suggestions - **file**: Send files (PDF, documents, etc.) - **image**: Send images (JPG, PNG, GIF, etc.) - **video**: Send videos (MP4, etc.) - **audio**: Send audio files (MP3, etc.) ## Key Features - **Interactive Buttons**: Chip suggestions (11 max) and card actions (3 max per card) - **SMS Fallback**: Automatic fallback when RCS unavailable - **Scheduling**: Timezone-aware scheduling with `scheduleAt` and `timeZone` - **skipContactCreation**: Send without creating contact records - **Conversation Billing**: 24-hour conversation windows for cost optimization ## Example: Send Text with Buttons ```json POST /api/rcs/send { "phoneNumber": "+4512345678", "messageType": "text", "sendernameId": "60f1b2c3d4e5f6a7b8c9d0e1", "content": { "text": "Hello! How can we help?", "suggestions": [ { "reply": { "text": "Book Appointment", "postbackData": "book", "webhookUrl": "https://yourapp.com/webhook" } }, { "action": { "text": "Call Us", "type": "dial", "dialAction": { "phoneNumber": "+4512345678" } } } ] } } ``` ## Example: Rich Card with Actions ```json { "phoneNumber": "+4512345678", "messageType": "richCard", "sendernameId": "60f1b2c3d4e5f6a7b8c9d0e1", "content": { "title": "Special Offer", "description": "50% off today!", "media": { "height": "MEDIUM", "contentInfo": { "fileUrl": "https://example.com/offer.jpg" } }, "cardActions": [ { "text": "Shop Now", "type": "openUrl", "openUrlAction": { "url": "https://shop.example.com" } }, { "text": "Details", "type": "postback", "postbackData": "details", "webhookUrl": "https://yourapp.com/webhook" } ] }, "smsFallback": { "message": "50% off! Visit shop.example.com", "sendernameId": "60f1b2c3d4e5f6a7b8c9d0e2" } } ``` ## Example: Send File/Image/Video/Audio ```json { "phoneNumber": "+4512345678", "messageType": "file", "sendernameId": "60f1b2c3d4e5f6a7b8c9d0e1", "content": { "media": { "contentInfo": { "fileUrl": "https://example.com/document.pdf" } } } } ``` Use `messageType`: `file`, `image`, `video`, or `audio` depending on the content type. ## Limits - Max chip suggestions: 11 (any message type, any mix of reply and action chips) - Max card actions: 4 per card - Max carousel cards: 10 - Max batch recipients: 10,000 - Max text length: 3,072 characters - Max button text: 25 characters - Max SMS fallback: 1,530 characters ## Action Types for Card Buttons (cardActions) - `openUrl` - Open URL in browser - `openUrlInWebview` - Open URL in in-app webview (`viewMode`: FULL, TALL, or HALF) - `dial` - Open phone dialer - `postback` - Send postback data to webhook (card-only; on chips use a `reply` with postbackData) - `addToCalendar` - Add to calendar via `calendarAction` (card-only; chips use `createCalendarEvent`) - `viewLocation` - Open maps - `shareLocation` - Share location - `unsubscribeLink` - Open per-recipient unsubscribe URL (`{{unsubscribe_link}}`) ## Action Types for Chips - `reply` - Send a text reply with postbackData to webhook - `dial` - Open phone dialer - `openUrl` - Open URL - `openUrlInWebview` - Open URL in in-app webview (`viewMode`: FULL, TALL, or HALF) - `createCalendarEvent` - Add to calendar via `createCalendarEventAction` (chip-only; card buttons use `addToCalendar`) - `viewLocation` - Open maps - `shareLocation` - Share location - `unsubscribeLink` - Open per-recipient unsubscribe URL (`{{unsubscribe_link}}`) - `unsubscribe` - Send stop keyword as reply to trigger unsubscribe flow (chip-only, uses `unsubscribeAction: { stopKeyword: "STOP" }`). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. ## Webhooks Your webhook URL receives button clicks: ```json { "event": "button_interaction", "interaction": { "type": "button_click", "buttonText": "Book Appointment", "postbackData": "book_appointment" }, "contact": { "phone": "+4512345678", "name": "John Doe" } } ``` ### Form submission webhooks Configure one webhook per form in Settings > Webhooks (Form Submission Webhooks tab). The webhook receives two events: - `form_submission` — fired on every form submit, before opt-in confirmation. `optIn.status` is `PENDING`; `data` contains the submitted values (custom fields include labels); `optIn.confirmationSent` is `false` when the "submit once" throttle suppressed a repeat opt-in message. - `form_optin_confirmed` — fired once when the visitor confirms the double opt-in. `optIn.status` is `CONFIRMED`; the contact is now active and (when `optIn.dataApplied` is true) the submitted data and groups have been written to it. ```json { "event": "form_submission", "form": { "id": "...", "name": "Newsletter signup", "slug": "..." }, "submission": { "action": "created", "submittedAt": "2024-01-15T10:30:00Z" }, "optIn": { "status": "PENDING", "confirmationSent": true, "confirmedAt": null }, "contact": { "id": "...", "phone": "+4512345678", "optInStatus": "PENDING" }, "data": { "phone": "+4512345678", "firstName": "John", "email": "john@example.com", "customFields": [{ "id": "...", "label": "Company", "value": "Acme Inc" }] } } ``` ## Contacts API Base URL: `https://api.sendrcs.eu/api/v1/contacts` Requires `contacts:read` / `contacts:write` and `custom-fields:read` / `custom-fields:write` API key permissions. ### Contact CRUD - **GET /api/v1/contacts** — List contacts with cursor pagination, search (`?search=`), phone lookup (`?phone=`), filters (`?disabled=`, `?group=`), and optional custom fields (`?include=customFields`) - **GET /api/v1/contacts/count** — Get contact count with optional filters - **GET /api/v1/contacts/:id** — Get single contact - **POST /api/v1/contacts** — Create contact (phone required, must be unique) - **PUT /api/v1/contacts/:id** — Update contact (partial updates) - **DELETE /api/v1/contacts/:id** — Delete contact and all associated data ### Bulk Operations - **POST /api/v1/contacts/bulk** — Bulk create/upsert up to 1,000 contacts. Set `upsert: true` to update existing by phone. - **PATCH /api/v1/contacts/bulk/disable** — Soft-delete multiple contacts - **PATCH /api/v1/contacts/bulk/enable** — Re-enable disabled contacts ### Custom Field Definitions - **GET /api/v1/contacts/fields** — List all custom merge field definitions - **POST /api/v1/contacts/fields** — Create custom field (name + tag, tag used as `{{tag}}` in messages) - **GET /api/v1/contacts/fields/:fieldId** — Get single field definition - **PUT /api/v1/contacts/fields/:fieldId** — Update field definition - **DELETE /api/v1/contacts/fields/:fieldId** — Delete field (`?force=true` to delete even if in use) ### Per-Contact Custom Field Values - **GET /api/v1/contacts/:id/fields** — Get all custom field values for a contact - **PUT /api/v1/contacts/:id/fields** — Bulk set values (by fieldId or tag) - **PUT /api/v1/contacts/:id/fields/:fieldId** — Set single value - **DELETE /api/v1/contacts/:id/fields/:fieldId** — Delete value ### Example: Create Contact with Custom Fields ```json POST /api/v1/contacts { "firstName": "Peter", "lastName": "Thomsen", "phone": "4512345678", "email": "peter@example.com", "groups": ["60f1b2c3d4e5f6a7b8c9d0e1"], "customFields": [ { "fieldId": "665ghi789jkl012mno345pqr", "value": "Premium" } ] } ``` ### Example: Bulk Upsert ```json POST /api/v1/contacts/bulk { "contacts": [ { "phone": "4512345678", "firstName": "Peter", "customFields": [{ "tag": "customer_status", "value": "Gold" }] }, { "phone": "4587654321", "firstName": "Anna" } ], "upsert": true, "groups": ["60f1b2c3d4e5f6a7b8c9d0e1"] } ``` ## Groups API Base URL: `https://api.sendrcs.eu/api/v1/groups` Requires `groups:read` / `groups:write` API key permissions. ### Group CRUD - **GET /api/v1/groups** — List all groups with contact counts, sorted by newest first - **GET /api/v1/groups/:id** — Get single group. Add `?includeContacts=true&limit=50&offset=0` for paginated contact list - **POST /api/v1/groups** — Create group (name required, optional description) - **PUT /api/v1/groups/:id** — Update group name and/or description - **DELETE /api/v1/groups/:id** — Delete group (does NOT delete contacts, only removes group references) ### Group Membership - **POST /api/v1/groups/:id/contacts** — Add contacts to group with bidirectional sync. Cascades to linked campaigns/flows. Duplicates silently ignored. - **DELETE /api/v1/groups/:id/contacts** — Remove contacts from group with bidirectional sync. Cascades removal from linked entities. Contacts NOT deleted. Both membership endpoints accept `{ "contactIds": ["id1", "id2", ...] }` (up to 10,000 IDs). ### Example: Create Group and Add Contacts ```json POST /api/v1/groups { "name": "VIP Customers", "description": "High-value customers" } ``` ```json POST /api/v1/groups/60f1b2c3d4e5f6a7b8c9d0e1/contacts { "contactIds": ["665abc123def456ghi789jkl", "665abc123def456ghi789jkm"] } ``` ## Templates API Base URL: `https://api.sendrcs.eu/api/v1/templates` Requires `templates:read` / `templates:write` API key permissions. ### Template CRUD - **GET /api/v1/templates** — List templates with pagination. Filters: `?type=` (SMS/RCS), `?rcsMessageType=`, `?templateGroupId=` (a template group id, or `none` for ungrouped), `?context=` (event/flow/general), `?search=`. Pagination: `?page=1&limit=20`. Sort: `?sort=name&order=asc` - **GET /api/v1/templates/:id** — Get single template. RCS templates include `parsedContent` with structured message data - **POST /api/v1/templates** — Create template. For RCS: pass content as object + rcsMessageType. For SMS: content as string. Optional `templateGroupId` places it in a template group (folder) - **PUT /api/v1/templates/:id** — Update template (partial update). RCS content re-wrapped if rcsMessageType changes - **DELETE /api/v1/templates/:id** — Delete template permanently ### Template Groups Template groups are folders for organizing message templates. They are NOT contact groups. Each template belongs to at most one template group. - **GET /api/v1/templates/groups** — List all template groups with `templateCount` each, plus `ungroupedTemplateCount` - **POST /api/v1/templates/groups** — Create a template group (`name` required, unique per account, max 100 chars) - **PUT /api/v1/templates/groups/:id** — Rename a template group - **DELETE /api/v1/templates/groups/:id** — Delete a template group. Its templates are NOT deleted, they become ungrouped - **POST /api/v1/templates/groups/assign** — Move templates into a group: `{ "templateIds": [...], "templateGroupId": "..." }`. Pass `"templateGroupId": null` to ungroup ### Example: Create SMS Template ```json POST /api/v1/templates { "name": "Welcome SMS", "type": "SMS", "content": "Welcome {{contact_first_name}}! Thanks for signing up.", "context": "general" } ``` ### Example: Create RCS Template ```json POST /api/v1/templates { "name": "Product Card", "type": "RCS", "rcsMessageType": "richCard", "content": { "title": "New Product Available", "description": "Check out our latest product!", "media": { "height": "TALL", "contentInfo": { "fileUrl": "https://example.com/image.jpg" } }, "cardActions": [ { "text": "View Product", "type": "openUrl", "openUrlAction": { "url": "https://example.com/product" } } ] }, "templateGroupId": "665ghi789jkl012mno345abc" } ``` ## MCP Tools for Contacts, Groups & Templates The SendRCS MCP server (at `https://api.sendrcs.eu/api/mcp`) also exposes contact, group, custom field, and template management as MCP tools. These can be used by AI assistants via the Model Context Protocol. ### Contact Tools (MCP) - **list_contacts** — List with cursor pagination, search, phone lookup, filters (disabled, group), sorting, optional custom fields - **count_contacts** — Count with optional filters (disabled, group, search) - **get_contact** — Get single contact by ID with groups and custom field values - **create_contact** — Create contact (phone required, unique). Can assign groups and custom fields - **update_contact** — Partial update (name, phone, email, disabled, groups, custom fields) - **delete_contact** — Hard delete with cascading cleanup (messages, flows, events, groups, fields) - **bulk_create_contacts** — Create/upsert up to 1,000 contacts with groups and custom fields - **bulk_disable_contacts** — Soft-disable contacts, cancels scheduled messages and flow enrollments - **bulk_enable_contacts** — Re-enable disabled contacts ### Group Tools (MCP) - **list_groups** — List all groups with contact counts - **get_group** — Get group by ID, optionally with paginated contact list - **create_group** — Create group (name required, optional description) - **update_group** — Update group name/description - **delete_group** — Delete group (does NOT delete contacts, only removes group references) - **add_contacts_to_group** — Add contacts with bidirectional sync, cascades to linked campaigns/flows - **remove_contacts_from_group** — Remove contacts with bidirectional sync, cascades to linked entities ### Custom Field Tools (MCP) - **list_custom_fields** — List all custom merge field definitions (tags become `{{tag}}` placeholders) - **create_custom_field** — Create field definition (name + tag, tag must be unique) - **delete_custom_field** — Delete field definition (requires `force: true` if in use) - **set_contact_custom_fields** — Set or delete values on a contact (by fieldId or tag, null to delete) ### Template Tools (MCP) - **list_templates** — List templates with pagination, filtering by type (SMS/RCS), rcsMessageType, template group, context, and name search - **get_template** — Get single template by ID. RCS templates include parsed content in rcs_send format - **create_template** — Create template. For RCS: pass content as an object (same format as rcs_send), the tool handles storage - **update_template** — Partial update of template fields (name, content, template group, etc.) - **delete_template** — Delete a template permanently ### Template Group Tools (MCP) Template groups are folders for message templates, NOT contact groups. - **list_template_groups** — List all template groups with template counts, plus the count of ungrouped templates - **create_template_group** — Create a template group (name required, unique per account) - **update_template_group** — Rename a template group - **delete_template_group** — Delete a template group. Templates inside are NOT deleted, they become ungrouped - **assign_templates_to_template_group** — Move templates into a group, or pass `templateGroupId: null` to ungroup them ### Template-based Preview (MCP) The `rcs_preview` tool accepts an optional `templateId` parameter. When provided, the template's stored content and messageType are loaded automatically — no need to pass content or messageType separately. Only `sendernameId` is required alongside `templateId`. ## Available Merge Fields ### System merge fields (always available) - `{{contact_first_name}}`, `{{contact_last_name}}`, `{{contact_phone}}`, `{{contact_email}}` - `{{today}}` - `{{company_name}}`, `{{company_email}}`, `{{company_phone}}`, `{{company_website}}` - `{{company_address}}`, `{{company_city}}`, `{{company_country}}` - `{{contact_person}}`, `{{business_first_name}}`, `{{business_last_name}}` - `{{gdpr_email}}`, `{{gdpr_phone}}` - `{{unsubscribe_link}}` ### Custom merge fields Use the tag from your custom field definitions: `{{customer_status}}`, `{{loyalty_points}}`, etc. ## Full Documentation - OpenAPI Spec: /openapi.yaml - AI Instructions: /ai-instructions/ --- # Part 2 - Endpoint reference Authentication: - `ApiKeyAuth` (apiKey, header `X-API-Key`) - API key for programmatic access. Obtain your API key by logging into your SendRCS account and navigating to Settings > Api Keys. Servers: `https://api.sendrcs.eu/api/rcs` 61 endpoints in 14 groups. ## Messaging Send RCS messages to single or multiple recipients ### POST /send - Send single RCS message operationId `sendMessage` · tag Messaging · requires `X-API-Key` Send an RCS message to a single recipient. Supports all message types (text, richCard, carousel, media) with optional SMS fallback and scheduling. **New Features:** - `skipContactCreation`: Send without creating a contact record - `skipDisabled`: Skip sending to disabled contacts (default: true) - Test phone detection (no charge for test sender phones) - Interactive buttons with webhook callbacks Request body (`application/json`, required): Schema: `SendMessageRequest` - `phoneNumber` (string, required) - Recipient phone number (E.164 format) - `messageType` (string, required, one of `text`, `textBasic`, `richCard`, `carousel`, `media`, `file`, `image`, `video`, `audio`) - Type of RCS message - `content` (MessageContent, required) - Message content varies by messageType: - **text**: `text` string + optional `suggestions` (max 11 chips) - **textBasic**: `text` string only (no suggestions, max 160 characters) - **richCard**: At least `title` or `description` required, optional `media` (omitted if no fileUrl), `cardActions` (max 4), optional `suggestions` (max 11) - **carousel**: `cardContents` array (max 10 cards, each requiring at least `title` or `description`, optional `media`) + optional `suggestions` - **media**: `url` + optional `caption` (sent as rich card if caption present), or `media` object + optional `suggestions` - **file**: `url` or `media` object with file URL (PDFs, documents, etc.) - **image**: `url` or `media` object with image URL + optional `caption` - **video**: `url` or `media` object with video URL + optional `caption` - **audio**: `url` or `media` object with audio URL **Merge fields:** text, card titles/descriptions and button/chip URLs support `{{merge_field}}` placeholders (contact/company fields, custom tags, and `{{unsubscribe_link}}`), replaced per recipient at send time. See "Merge Fields & Unsubscribe Link" in the API overview. - `url` (string (uri), maxLength 2000) - Direct URL to media file (for media/image/video/audio/file). Alternative to using the media object. - `caption` (string, maxLength 2000) - Caption text for media messages. When present, the message is sent as a rich card with the caption as description. - `text` (string, maxLength 3072) - Text content (for text messages) - `title` (string, maxLength 200) - Card title (for richCard). At least title or description is required. - `description` (string, maxLength 2000) - Card description (for richCard). At least title or description is required. - `cardOrientation` (string, one of `VERTICAL`, `HORIZONTAL`, default `"VERTICAL"`) - Card layout orientation (for richCard) - `imageAlignment` (string, one of `LEFT`, `RIGHT`, default `"RIGHT"`) - Image alignment for horizontal cards (for richCard) - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - Card action buttons (max 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `cardContents` (array of CardContent, maxItems 10) - Carousel cards (min 2, max 10) - `title` (string, maxLength 200) - Card title. At least title or description is required. - `description` (string, maxLength 2000) - Card description. At least title or description is required. - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestions` (array of Suggestion, maxItems 11) - Chip suggestions (max 11) - `reply` (object) - `text` (string, maxLength 25) - `postbackData` (string, maxLength 2048) - `webhookUrl` (string (uri)) - `action` (CardAction) - Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `sendernameId` (string, required) - MongoDB ObjectId of approved RCS sender - `contactId` (string) - Optional existing contact ID - `skipContactCreation` (boolean, default `false`) - Skip creating a contact record (not allowed for conversation-based senders) - `skipDisabled` (boolean, default `true`) - Skip sending to disabled contacts - `campaignName` (string, maxLength 100) - Campaign name for tracking - `scheduleAt` (string) - ISO 8601 date-time for scheduling - `timeZone` (string) - IANA timezone for scheduling - `smsFallback` (SmsFallback) - SMS fallback when RCS is unavailable - `message` (string, required, maxLength 1530) - SMS fallback text. Supports the same merge fields as the RCS body (contact, company and custom fields, `{{today}}`, `{{unsubscribe_link}}`), substituted per recipient when the fallback is sent. `{{unsubscribe_link}}` becomes a unique per-recipient opt-out URL when the recipient is a contact. - `sendernameId` (string, required) - SMS sender ID Example - Rich card with image and buttons: ```json { "phoneNumber": "+4512345678", "messageType": "richCard", "sendernameId": "60f1b2c3d4e5f6a7b8c9d0e1", "content": { "title": "Special Offer - 50% Off!", "description": "Limited time offer on premium products.", "cardOrientation": "VERTICAL", "media": { "height": "MEDIUM", "contentInfo": { "fileUrl": "https://sendrcs.eu/images/rcsexamples/tech.jpg" }, "thumbnailUrl": "https://sendrcs.eu/images/rcsexamples/tech.jpg" }, "cardActions": [ { "text": "Shop Now", "type": "openUrl", "openUrlAction": { "url": "https://shop.example.com" } }, { "text": "Get Details", "type": "postback", "postbackData": "offer_details", "webhookUrl": "https://yourapp.com/webhooks/rcs" } ] }, "smsFallback": { "message": "50% Off! Shop: shop.example.com", "sendernameId": "60f1b2c3d4e5f6a7b8c9d0e2" } } ``` Example - Rich card with unsubscribe button (marketing): ```json { "phoneNumber": "+4512345678", "messageType": "richCard", "sendernameId": "60f1b2c3d4e5f6a7b8c9d0e1", "content": { "title": "Special Offer - 50% Off!", "description": "Limited time offer on premium products.", "cardOrientation": "VERTICAL", "media": { "height": "MEDIUM", "contentInfo": { "fileUrl": "https://sendrcs.eu/images/rcsexamples/tech.jpg" }, "thumbnailUrl": "https://sendrcs.eu/images/rcsexamples/tech.jpg" }, "cardActions": [ { "text": "Shop Now", "type": "openUrl", "openUrlAction": { "url": "https://shop.example.com" } }, { "text": "Unsubscribe", "type": "unsubscribeLink", "openUrlAction": { "url": "{{unsubscribe_link}}" } } ] } } ``` Responses: - `200` - Message sent or scheduled successfully Body: `SendMessageResponse`. - `400` - Validation error Body: `ErrorResponse`. - `401` - Unauthorized - `500` - Server error ### POST /send-batch - Send batch RCS messages operationId `sendBatch` · tag Messaging · requires `X-API-Key` Send the same RCS message to multiple recipients (up to 10,000). **Features:** - Automatic disabled contact filtering - Test phone detection (no charge) - Bulk contact lookup optimization - Progress tracking via job ID Request body (`application/json`, required): Schema: `SendBatchRequest` - `phoneNumbers` (array of string, required, maxItems 10000) - Array of recipient phone numbers - `messageType` (string, required, one of `text`, `textBasic`, `richCard`, `carousel`, `media`, `file`, `image`, `video`, `audio`) - `content` (MessageContent, required) - Message content varies by messageType: - **text**: `text` string + optional `suggestions` (max 11 chips) - **textBasic**: `text` string only (no suggestions, max 160 characters) - **richCard**: At least `title` or `description` required, optional `media` (omitted if no fileUrl), `cardActions` (max 4), optional `suggestions` (max 11) - **carousel**: `cardContents` array (max 10 cards, each requiring at least `title` or `description`, optional `media`) + optional `suggestions` - **media**: `url` + optional `caption` (sent as rich card if caption present), or `media` object + optional `suggestions` - **file**: `url` or `media` object with file URL (PDFs, documents, etc.) - **image**: `url` or `media` object with image URL + optional `caption` - **video**: `url` or `media` object with video URL + optional `caption` - **audio**: `url` or `media` object with audio URL **Merge fields:** text, card titles/descriptions and button/chip URLs support `{{merge_field}}` placeholders (contact/company fields, custom tags, and `{{unsubscribe_link}}`), replaced per recipient at send time. See "Merge Fields & Unsubscribe Link" in the API overview. - `url` (string (uri), maxLength 2000) - Direct URL to media file (for media/image/video/audio/file). Alternative to using the media object. - `caption` (string, maxLength 2000) - Caption text for media messages. When present, the message is sent as a rich card with the caption as description. - `text` (string, maxLength 3072) - Text content (for text messages) - `title` (string, maxLength 200) - Card title (for richCard). At least title or description is required. - `description` (string, maxLength 2000) - Card description (for richCard). At least title or description is required. - `cardOrientation` (string, one of `VERTICAL`, `HORIZONTAL`, default `"VERTICAL"`) - Card layout orientation (for richCard) - `imageAlignment` (string, one of `LEFT`, `RIGHT`, default `"RIGHT"`) - Image alignment for horizontal cards (for richCard) - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - Card action buttons (max 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `cardContents` (array of CardContent, maxItems 10) - Carousel cards (min 2, max 10) - `title` (string, maxLength 200) - Card title. At least title or description is required. - `description` (string, maxLength 2000) - Card description. At least title or description is required. - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestions` (array of Suggestion, maxItems 11) - Chip suggestions (max 11) - `reply` (object) - `text` (string, maxLength 25) - `postbackData` (string, maxLength 2048) - `webhookUrl` (string (uri)) - `action` (CardAction) - Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `sendernameId` (string, required) - `skipContactCreation` (boolean, default `false`) - `skipDisabled` (boolean, default `true`) - `campaignName` (string, maxLength 100) - `scheduleAt` (string) - `timeZone` (string) - `smsFallback` (SmsFallback) - SMS fallback when RCS is unavailable - `message` (string, required, maxLength 1530) - SMS fallback text. Supports the same merge fields as the RCS body (contact, company and custom fields, `{{today}}`, `{{unsubscribe_link}}`), substituted per recipient when the fallback is sent. `{{unsubscribe_link}}` becomes a unique per-recipient opt-out URL when the recipient is a contact. - `sendernameId` (string, required) - SMS sender ID - `batchSize` (integer, default `100`, minimum 1, maximum 1000) - Number of messages per batch - `delayMs` (integer, default `50`, minimum 0, maximum 10000) - Delay between batches in milliseconds Example - Basic batch send: ```json { "phoneNumbers": [ "+4512345678", "+4587654321", "+4511223344" ], "messageType": "text", "sendernameId": "60f1b2c3d4e5f6a7b8c9d0e1", "content": { "text": "Hello! This is a batch message." }, "campaignName": "February Campaign" } ``` Example - Batch with SMS fallback: ```json { "phoneNumbers": [ "+4512345678", "+4587654321" ], "messageType": "richCard", "sendernameId": "60f1b2c3d4e5f6a7b8c9d0e1", "content": { "title": "Special Offer", "description": "Limited time discount", "media": { "height": "MEDIUM", "contentInfo": { "fileUrl": "https://sendrcs.eu/images/rcsexamples/tech-3.jpg" } }, "cardActions": [ { "text": "Shop Now", "type": "openUrl", "openUrlAction": { "url": "https://shop.example.com" } } ] }, "smsFallback": { "message": "Special offer! Visit shop.example.com", "sendernameId": "60f1b2c3d4e5f6a7b8c9d0e2" }, "skipDisabled": true, "batchSize": 100, "delayMs": 50 } ``` Responses: - `200` - Batch scheduled for later delivery (returned when scheduleAt is provided) Body: `SendBatchScheduledResponse`. - `202` - Batch queued for immediate background processing Body: `SendBatchResponse`. - `400` - Validation error - `401` - Unauthorized ## SMS Send standalone SMS messages independently of RCS. Specify your SMS sender ID and message content. Billed at standard SMS rate per message segment. ### POST /send-sms - Send standalone SMS message operationId `sendSms` · tag SMS · requires `X-API-Key` Send an SMS message to a single recipient, independently of RCS. Specify your SMS sender ID and message content. Billed at standard SMS rate per message segment. **Features:** - Standalone SMS — no RCS sending or fallback involved - Full merge field support: `{{contact_first_name}}`, `{{company_name}}`, custom field tags, `{{today}}`, and `{{unsubscribe_link}}` - replaced per recipient before segments are calculated - Tracked store links: when a Shopify store is connected, links to that store are replaced by short tracked links at send time, so clicks and purchases show up in statistics. The billed segment count reflects the final text. Requires a contact (not with `skipContactCreation`); can be turned off on the Shopify settings page. - Timezone-aware scheduling - `skipContactCreation`: Send without creating a contact record - `skipDisabled`: Skip sending to disabled contacts (default: true) Request body (`application/json`, required): Schema: `SendSmsRequest` - `phoneNumber` (string, required) - Recipient phone number (E.164 format) - `message` (string, required, maxLength 1530) - SMS message text (max 1530 characters / 10 segments). Supports the full merge field set: contact fields (`{{contact_first_name}}`, `{{contact_last_name}}`, `{{contact_phone}}`, `{{contact_email}}`), company fields (`{{company_name}}`, `{{company_phone}}`, `{{company_website}}`, ...), `{{today}}`, custom field tags (`{{your_tag}}`), and `{{unsubscribe_link}}` - all replaced before segments are calculated, so billing reflects the resolved length. Contact and custom fields resolve from the recipient's contact record (auto-created unless `skipContactCreation: true`); unknown or unresolvable placeholders are replaced with an empty string. `{{unsubscribe_link}}` is replaced with a unique per-recipient opt-out URL and requires a contact - it is left as-is when `skipContactCreation: true` is used for a number that is not already a contact. - `sendernameId` (string, required) - ID or name of an approved SMS sender - `campaignName` (string, maxLength 100) - Campaign name for tracking - `skipContactCreation` (boolean, default `false`) - Skip creating a contact record (message still logged with phone number) - `skipDisabled` (boolean, default `true`) - Skip sending to disabled contacts - `scheduleAt` (string) - ISO 8601 date-time for scheduling - `timeZone` (string) - IANA timezone for scheduling Example - Basic SMS message: ```json { "phoneNumber": "+4512345678", "message": "Hello! How can we help you today? Visit our website or call +4593700401", "sendernameId": "60f1b2c3d4e5f6a7b8c9d0e2", "campaignName": "My SMS Campaign" } ``` Example - Marketing SMS with unsubscribe link: ```json { "phoneNumber": "+4512345678", "message": "Summer sale! 20% off all items this weekend only. Unsubscribe: {{unsubscribe_link}}", "sendernameId": "60f1b2c3d4e5f6a7b8c9d0e2", "campaignName": "Summer Sale" } ``` Responses: - `200` - SMS sent or scheduled successfully Body: `SendSmsResponse`. - `400` - Validation error Body: `ErrorResponse`. - `401` - Unauthorized - `500` - Server error Example response: ```json { "success": true, "messageId": "42", "scheduled": false, "queued": true, "smsSegments": 1, "smsEncoding": "GSM-7", "creditsEstimated": 1 } ``` ## Scheduling Timezone-aware message scheduling ### GET /timezones - Get available timezones operationId `getTimezones` · tag Scheduling · requires `X-API-Key` Returns list of supported timezones with current times and user's configured timezone. Responses: - `200` - Timezone information Body: `TimezonesResponse`. ### POST /validate-schedule-time - Validate schedule time operationId `validateScheduleTime` · tag Scheduling · requires `X-API-Key` Validate a schedule time before sending. Returns parsed UTC time and timezone information. Request body (`application/json`, required): - `scheduleAt` (string, required) - Schedule time (ISO 8601 format) - `timeZone` (string) - IANA timezone Responses: - `200` - Validation result Body: `ValidateScheduleResponse`. ## Status Check service and job status ### GET /status - Get RCS service status operationId `getStatus` · tag Status · requires `X-API-Key` Returns current status of the RCS messaging service and validation limits for message content (text lengths, card limits, supported media formats, etc.). Responses: - `200` - Service status and validation limits ### GET /health - Health check operationId `healthCheck` · tag Status · requires `X-API-Key` Simple health check endpoint for monitoring services like statuspage.io. Returns HTTP 200 when healthy, HTTP 503 when down. Responses: - `200` - Service is healthy - `503` - Service is down ### GET /queue/status - Get queue status operationId `getQueueStatus` · tag Status · requires `X-API-Key` Returns current message queue status including pending jobs. Responses: - `200` - Queue status ### GET /batch/{jobId}/status - Get batch job status operationId `getBatchStatus` · tag Status · requires `X-API-Key` Returns detailed status of a batch sending job. Parameters: - `jobId` (path, string, required) - Batch job ID Responses: - `200` - Job status Body: `BatchJobStatus`. - `404` - Job not found ### DELETE /batch/{jobId} - Cancel batch job operationId `cancelBatch` · tag Status · requires `X-API-Key` Cancel a pending or in-progress batch job. Parameters: - `jobId` (path, string, required) Responses: - `200` - Job cancelled - `404` - Job not found ### GET /jobs - List jobs operationId `listJobs` · tag Status · requires `X-API-Key` Returns list of batch and scheduled RCS jobs with filtering and pagination. Parameters: - `status` (query, string) - Filter by job status - `type` (query, string) - Filter by job type - `page` (query, integer) - Page number - `limit` (query, integer) - Results per page - `startDate` (query, string (date-time)) - Filter jobs created after this date (ISO 8601) - `endDate` (query, string (date-time)) - Filter jobs created before this date (ISO 8601) Responses: - `200` - List of jobs ### GET /stats - Get messaging statistics operationId `getStats` · tag Status · requires `X-API-Key` Returns RCS messaging statistics for the authenticated user, filtered by time period. Parameters: - `period` (query, string) - Predefined time period - `startDate` (query, string (date-time)) - Custom start date (overrides period) - `endDate` (query, string (date-time)) - Custom end date (overrides period) Responses: - `200` - Messaging statistics ## Validation Validate messages before sending ### POST /validate - Validate message operationId `validateMessage` · tag Validation · requires `X-API-Key` Validate a message structure before sending. Returns validation errors and warnings. Request body (`application/json`, required): Schema: `ValidateMessageRequest` - `messageType` (string) - `content` (MessageContent) - Message content varies by messageType: - **text**: `text` string + optional `suggestions` (max 11 chips) - **textBasic**: `text` string only (no suggestions, max 160 characters) - **richCard**: At least `title` or `description` required, optional `media` (omitted if no fileUrl), `cardActions` (max 4), optional `suggestions` (max 11) - **carousel**: `cardContents` array (max 10 cards, each requiring at least `title` or `description`, optional `media`) + optional `suggestions` - **media**: `url` + optional `caption` (sent as rich card if caption present), or `media` object + optional `suggestions` - **file**: `url` or `media` object with file URL (PDFs, documents, etc.) - **image**: `url` or `media` object with image URL + optional `caption` - **video**: `url` or `media` object with video URL + optional `caption` - **audio**: `url` or `media` object with audio URL **Merge fields:** text, card titles/descriptions and button/chip URLs support `{{merge_field}}` placeholders (contact/company fields, custom tags, and `{{unsubscribe_link}}`), replaced per recipient at send time. See "Merge Fields & Unsubscribe Link" in the API overview. - `url` (string (uri), maxLength 2000) - Direct URL to media file (for media/image/video/audio/file). Alternative to using the media object. - `caption` (string, maxLength 2000) - Caption text for media messages. When present, the message is sent as a rich card with the caption as description. - `text` (string, maxLength 3072) - Text content (for text messages) - `title` (string, maxLength 200) - Card title (for richCard). At least title or description is required. - `description` (string, maxLength 2000) - Card description (for richCard). At least title or description is required. - `cardOrientation` (string, one of `VERTICAL`, `HORIZONTAL`, default `"VERTICAL"`) - Card layout orientation (for richCard) - `imageAlignment` (string, one of `LEFT`, `RIGHT`, default `"RIGHT"`) - Image alignment for horizontal cards (for richCard) - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - Card action buttons (max 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `cardContents` (array of CardContent, maxItems 10) - Carousel cards (min 2, max 10) - `title` (string, maxLength 200) - Card title. At least title or description is required. - `description` (string, maxLength 2000) - Card description. At least title or description is required. - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestions` (array of Suggestion, maxItems 11) - Chip suggestions (max 11) - `reply` (object) - `text` (string, maxLength 25) - `postbackData` (string, maxLength 2048) - `webhookUrl` (string (uri)) - `action` (CardAction) - Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. Responses: - `200` - Validation result Body: `ValidationResult`. ### GET /button-guidelines - Get button guidelines operationId `getButtonGuidelines` · tag Validation · requires `X-API-Key` Returns guidelines and limits for interactive buttons. Responses: - `200` - Button guidelines Body: `ButtonGuidelines`. ## Sandbox Validate and preview messages without an account. Public, rate limited (20 requests per minute and 100 per day per IP), nothing is ever sent. Post the exact body you would send to `/send` and get the same validation result plus a phone-mockup preview link that expires after 24 hours. Only sample media from `/sandbox/samples` is rendered; other URLs pass validation but show as a placeholder. Sandbox preview ids cannot be used to send. ### POST /sandbox/validate - Validate a message without an account operationId `sandboxValidate` · tag Sandbox · no authentication Runs the exact validator behind `POST /send` on the body you post, with no API key and no account. Always returns `200` once the body is structurally valid; read `valid`, `errors` and `warnings`. Fields the real send needs (`phoneNumber`, `sendernameId`, `scheduleAt`, ...) are accepted and echoed under `sandbox.ignored_fields`. Nothing is sent. Rate limit per IP: 20 requests per minute, 100 per day (`429` with code `SANDBOX_RATE_LIMITED`). Request body (`application/json`, required): Schema: `SandboxRequest` - `messageType` (string, required, one of `text`, `textBasic`, `richCard`, `carousel`, `media`, `file`, `image`, `video`, `audio`) - `content` (MessageContent, required) - Message content varies by messageType: - **text**: `text` string + optional `suggestions` (max 11 chips) - **textBasic**: `text` string only (no suggestions, max 160 characters) - **richCard**: At least `title` or `description` required, optional `media` (omitted if no fileUrl), `cardActions` (max 4), optional `suggestions` (max 11) - **carousel**: `cardContents` array (max 10 cards, each requiring at least `title` or `description`, optional `media`) + optional `suggestions` - **media**: `url` + optional `caption` (sent as rich card if caption present), or `media` object + optional `suggestions` - **file**: `url` or `media` object with file URL (PDFs, documents, etc.) - **image**: `url` or `media` object with image URL + optional `caption` - **video**: `url` or `media` object with video URL + optional `caption` - **audio**: `url` or `media` object with audio URL **Merge fields:** text, card titles/descriptions and button/chip URLs support `{{merge_field}}` placeholders (contact/company fields, custom tags, and `{{unsubscribe_link}}`), replaced per recipient at send time. See "Merge Fields & Unsubscribe Link" in the API overview. - `url` (string (uri), maxLength 2000) - Direct URL to media file (for media/image/video/audio/file). Alternative to using the media object. - `caption` (string, maxLength 2000) - Caption text for media messages. When present, the message is sent as a rich card with the caption as description. - `text` (string, maxLength 3072) - Text content (for text messages) - `title` (string, maxLength 200) - Card title (for richCard). At least title or description is required. - `description` (string, maxLength 2000) - Card description (for richCard). At least title or description is required. - `cardOrientation` (string, one of `VERTICAL`, `HORIZONTAL`, default `"VERTICAL"`) - Card layout orientation (for richCard) - `imageAlignment` (string, one of `LEFT`, `RIGHT`, default `"RIGHT"`) - Image alignment for horizontal cards (for richCard) - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - Card action buttons (max 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `cardContents` (array of CardContent, maxItems 10) - Carousel cards (min 2, max 10) - `title` (string, maxLength 200) - Card title. At least title or description is required. - `description` (string, maxLength 2000) - Card description. At least title or description is required. - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestions` (array of Suggestion, maxItems 11) - Chip suggestions (max 11) - `reply` (object) - `text` (string, maxLength 25) - `postbackData` (string, maxLength 2048) - `webhookUrl` (string (uri)) - `action` (CardAction) - Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `smsFallback` (object) - Only `message` is used (shown under the phone); `sendernameId` is ignored. - `message` (string, maxLength 1530) - `sendernameId` (string) - `phoneNumber` (string) - Accepted and ignored - `phoneNumbers` (array of string) - Accepted and ignored - `sendernameId` (string) - Accepted and ignored - `contactId` (string) - Accepted and ignored - `campaignName` (string) - Accepted and ignored - `scheduleAt` (string) - Accepted and ignored - `timeZone` (string) - Accepted and ignored - `skipDisabled` (boolean) - Accepted and ignored - `skipContactCreation` (boolean) - Accepted and ignored - `batchSize` (integer) - Accepted and ignored - `delayMs` (integer) - Accepted and ignored Example - Rich card with a sample image: ```json { "messageType": "richCard", "content": { "title": "Your order is on its way", "description": "Track the parcel or talk to us.", "media": { "height": "TALL", "contentInfo": { "fileUrl": "https://sendrcs.eu/images/rcsexamples/shoes-1.jpg", "contentType": "image/jpeg" } }, "cardActions": [ { "type": "openUrl", "text": "Track order", "openUrlAction": { "url": "https://example.com/track" } }, { "type": "dial", "text": "Call us", "dialAction": { "phoneNumber": "+4512345678" } } ] } } ``` Responses: - `200` - Validation result (check `valid`) Body: `SandboxValidateResponse`. - `400` - Structurally invalid body (missing `messageType` or `content`) Body: `ErrorResponse`. - `413` - `content` larger than 256000 bytes - `429` - Sandbox rate limit reached Body: `SandboxRateLimited`. ### POST /sandbox/preview - Validate and preview a message without an account operationId `sandboxPreview` · tag Sandbox · no authentication Same validation as `/sandbox/validate`, plus a stored anonymous preview with a `share_url` that shows the message on a phone mockup (Android and iOS) with SendRCS branding. The preview expires after 24 hours. Identical input returns the same link. Invalid content returns `400` with the same body as `POST /send`, so you learn the real error shape before you have an account. Only media listed by `GET /sandbox/samples` is rendered. Any other `https` URL passes validation but is replaced by a placeholder on the preview page; each replacement is reported in `sandbox.media`. Sandbox preview ids (`sbx_...`) cannot be used to send. Request body (`application/json`, required): Schema: `SandboxRequest` - `messageType` (string, required, one of `text`, `textBasic`, `richCard`, `carousel`, `media`, `file`, `image`, `video`, `audio`) - `content` (MessageContent, required) - Message content varies by messageType: - **text**: `text` string + optional `suggestions` (max 11 chips) - **textBasic**: `text` string only (no suggestions, max 160 characters) - **richCard**: At least `title` or `description` required, optional `media` (omitted if no fileUrl), `cardActions` (max 4), optional `suggestions` (max 11) - **carousel**: `cardContents` array (max 10 cards, each requiring at least `title` or `description`, optional `media`) + optional `suggestions` - **media**: `url` + optional `caption` (sent as rich card if caption present), or `media` object + optional `suggestions` - **file**: `url` or `media` object with file URL (PDFs, documents, etc.) - **image**: `url` or `media` object with image URL + optional `caption` - **video**: `url` or `media` object with video URL + optional `caption` - **audio**: `url` or `media` object with audio URL **Merge fields:** text, card titles/descriptions and button/chip URLs support `{{merge_field}}` placeholders (contact/company fields, custom tags, and `{{unsubscribe_link}}`), replaced per recipient at send time. See "Merge Fields & Unsubscribe Link" in the API overview. - `url` (string (uri), maxLength 2000) - Direct URL to media file (for media/image/video/audio/file). Alternative to using the media object. - `caption` (string, maxLength 2000) - Caption text for media messages. When present, the message is sent as a rich card with the caption as description. - `text` (string, maxLength 3072) - Text content (for text messages) - `title` (string, maxLength 200) - Card title (for richCard). At least title or description is required. - `description` (string, maxLength 2000) - Card description (for richCard). At least title or description is required. - `cardOrientation` (string, one of `VERTICAL`, `HORIZONTAL`, default `"VERTICAL"`) - Card layout orientation (for richCard) - `imageAlignment` (string, one of `LEFT`, `RIGHT`, default `"RIGHT"`) - Image alignment for horizontal cards (for richCard) - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - Card action buttons (max 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `cardContents` (array of CardContent, maxItems 10) - Carousel cards (min 2, max 10) - `title` (string, maxLength 200) - Card title. At least title or description is required. - `description` (string, maxLength 2000) - Card description. At least title or description is required. - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestions` (array of Suggestion, maxItems 11) - Chip suggestions (max 11) - `reply` (object) - `text` (string, maxLength 25) - `postbackData` (string, maxLength 2048) - `webhookUrl` (string (uri)) - `action` (CardAction) - Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `smsFallback` (object) - Only `message` is used (shown under the phone); `sendernameId` is ignored. - `message` (string, maxLength 1530) - `sendernameId` (string) - `phoneNumber` (string) - Accepted and ignored - `phoneNumbers` (array of string) - Accepted and ignored - `sendernameId` (string) - Accepted and ignored - `contactId` (string) - Accepted and ignored - `campaignName` (string) - Accepted and ignored - `scheduleAt` (string) - Accepted and ignored - `timeZone` (string) - Accepted and ignored - `skipDisabled` (boolean) - Accepted and ignored - `skipContactCreation` (boolean) - Accepted and ignored - `batchSize` (integer) - Accepted and ignored - `delayMs` (integer) - Accepted and ignored Example - Rich card with a sample image: ```json { "messageType": "richCard", "content": { "title": "Your order is on its way", "description": "Track the parcel or talk to us.", "media": { "height": "TALL", "contentInfo": { "fileUrl": "https://sendrcs.eu/images/rcsexamples/shoes-1.jpg", "contentType": "image/jpeg" } }, "cardActions": [ { "type": "openUrl", "text": "Track order", "openUrlAction": { "url": "https://example.com/track" } }, { "type": "dial", "text": "Call us", "dialAction": { "phoneNumber": "+4512345678" } } ] } } ``` Example - Carousel posted with the fields a real send would carry: ```json { "phoneNumber": "+4512345678", "sendernameId": "507f1f77bcf86cd799439011", "messageType": "carousel", "content": { "cardContents": [ { "title": "Trainers", "description": "Light and fast.", "media": { "height": "MEDIUM", "contentInfo": { "fileUrl": "https://sendrcs.eu/images/rcsexamples/shoes-1.jpg" } }, "cardActions": [ { "type": "openUrl", "text": "Shop trainers", "openUrlAction": { "url": "https://example.com/trainers" } } ] }, { "title": "Road trip", "description": "Book your next weekend.", "media": { "height": "MEDIUM", "contentInfo": { "fileUrl": "https://sendrcs.eu/images/rcsexamples/travel-1.jpg" } }, "cardActions": [ { "type": "openUrl", "text": "See offers", "openUrlAction": { "url": "https://example.com/travel" } } ] } ] } } ``` Responses: - `200` - Valid message with a preview link Body: `SandboxPreviewResponse`. - `400` - Structurally invalid body, or content that failed validation (same body as `POST /send`) - `413` - `content` larger than 256000 bytes - `429` - Sandbox rate limit reached Body: `SandboxRateLimited`. ### GET /sandbox/samples - List the sample media the sandbox renders operationId `sandboxSamples` · tag Sandbox · no authentication Images, a video and a PDF you may reference in sandbox requests. These are the only media URLs rendered on sandbox preview pages. `usage` shows where to put a URL for each message type. Responses: - `200` - Sample media list Body: `SandboxSamplesResponse`. ### GET /sandbox/preview/{slug} - Read a sandbox preview operationId `sandboxGetPreview` · tag Sandbox · no authentication The JSON behind a sandbox `share_url`. The slug is the last path segment of the `share_url`. Returns the rendered content (placeholders already applied), the validation warnings and the expiry. Foreign media URLs are never returned, only their host. Parameters: - `slug` (path, string, required) Responses: - `200` - Sandbox preview Body: `SandboxSharedPreview`. - `404` - Unknown or expired preview Body: `ErrorResponse`. ## Sender Management List and inspect available sender identities (SMS and RCS) ### GET /senders - List sender names operationId `listSenders` · tag Sender Management · requires `X-API-Key` List all SMS and RCS sender names available to the authenticated user. Use the returned `id` as `sendernameId` when calling `/send`, `/send-batch`, or `/send-sms`. Status meanings: - **APPROVED** — ready to send. If `isTest` is true, can only send free test messages to numbers in `testPhones`. - **PENDING** — waiting for approval, cannot send yet. - **REJECTED** — not approved. Check `rejectionReason`. - **DRAFT** — not submitted yet. Parameters: - `type` (query, string) - Filter by sender type. Omit to list all. Responses: - `200` - List of sender names Example response: ```json { "success": true, "count": 2, "sendernames": [ { "id": "60f1b2c3d4e5f6a7b8c9d0e1", "name": "MyBrand RCS", "type": "RCS", "status": "APPROVED", "isDefault": true, "isTest": false, "supportsConversationBilling": true }, { "id": "60f1b2c3d4e5f6a7b8c9d0e2", "name": "MyBrand SMS", "type": "SMS", "status": "APPROVED", "isDefault": true, "isTest": false, "supportsConversationBilling": false } ] } ``` ## Conversations Conversation billing and analytics ### GET /conversations - List conversations operationId `listConversations` · tag Conversations · requires `X-API-Key` Returns your conversations with billing information, newest activity first. Without a `status` filter, both active and expired conversations are returned. A conversation is `ACTIVE` while its 24-hour window is open and `EXPIRED` once the window has passed. Status is derived from `sessionExpires` at request time. Parameters: - `status` (query, string) - Only return conversations with this status. Omit to return all. - `page` (query, integer) - `limit` (query, integer) Responses: - `200` - List of conversations ### GET /conversations/{phoneNumber}/status - Get conversation status operationId `getConversationStatus` · tag Conversations · requires `X-API-Key` Returns conversation status for a specific phone number. Parameters: - `phoneNumber` (path, string, required) Responses: - `200` - Conversation status Body: `ConversationStatus`. ### GET /conversations/{phoneNumber}/rate-limit - Get rate limit for conversation operationId `getConversationRateLimit` · tag Conversations · requires `X-API-Key` Returns rate limit information for a specific conversation. Parameters: - `phoneNumber` (path, string, required) Responses: - `200` - Rate limit info ### POST /conversations/billing-preview - Preview billing for multiple recipients operationId `previewConversationBilling` · tag Conversations · requires `X-API-Key` Check, before sending, which recipients already have an open 24-hour conversation window (free) and which will be charged. Read-only - nothing is sent and no credits are consumed. Up to 1,000 phone numbers per call. Recipients whose status cannot be determined are reported with `reason: "ERROR_CHECKING"` and counted as chargeable, so the estimate stays conservative. Request body (`application/json`, required): - `phoneNumbers` (array of string, required, maxItems 1000) - Recipient phone numbers to price. Example - Price three recipients: ```json { "phoneNumbers": [ "+4512345678", "+4587654321", "+4593700401" ] } ``` Responses: - `200` - Billing preview - `400` - Missing phoneNumbers, or more than 1,000 numbers Body: `ErrorResponse`. ## Message Log Retrieve your message history with filtering and cursor-based pagination. **Base URL:** `https://api.sendrcs.eu/api/messages` ### GET /messages/log - Get message log operationId `getMessageLog` · tag Message Log · requires `X-API-Key` Retrieve your message history with filtering and cursor-based pagination. Supports filtering by phone number, campaign name, direction, status, and message type. **Single message lookup:** Pass `messageId` to retrieve a specific message by ID (no date range needed). **Date range:** Defaults to last 7 days if not provided. Maximum range is 31 days per request. **Pagination:** Uses cursor-based pagination. Follow the `nextCursor` value from each response to fetch the next page. When `hasNext` is `false`, you've reached the end. **Base URL:** `https://api.sendrcs.eu/api/messages/log` **Example request:** ``` GET https://api.sendrcs.eu/api/messages/log?startDate=2026-05-01T00:00:00Z&endDate=2026-05-07T23:59:59Z&status=DELIVERED,READ&limit=200 ``` Parameters: - `messageId` (query, string) - Look up a single message by ID. When provided, date range is not required. Matches the `messageRecordId` from `/send` responses and webhook payloads. - `startDate` (query, string (date-time)) - Start of date range (ISO 8601). Defaults to 7 days ago if omitted. - `endDate` (query, string (date-time)) - End of date range (ISO 8601). Max 31 days from startDate. Defaults to now if omitted. - `phoneNumber` (query, string) - Filter by exact phone number. Accepts with or without `+` prefix (e.g. `+4512345678` or `4512345678`) - `campaignName` (query, string) - Filter by exact campaign name - `direction` (query, string) - Filter by message direction - `status` (query, string) - Comma-separated statuses: SCHEDULED, SENDING, SENT, DELIVERED, READ, RECEIVED, FAILED, BLOCKED, REJECTED - `type` (query, string) - Comma-separated message types: sms, rcs, mms - `limit` (query, integer) - Number of messages per page (1-500) - `cursor` (query, string) - Pagination cursor from previous response's `pagination.nextCursor` Responses: - `200` - Message log page Body: `MessageLogResponse`. - `400` - Validation error (missing dates, invalid range, bad cursor, etc.) Body: `ErrorResponse`. - `401` - Unauthorized — missing or invalid API key / JWT token Example response: ```json { "messages": [ { "id": "507f1f77bcf86cd799439011", "type": "RCS", "status": "DELIVERED", "direction": "OUTBOUND", "source": "API_MESSAGE", "phoneNumber": "+4512345678", "content": "Hello! Your order #1234 has been shipped.", "campaignName": "Order Updates", "sendernameId": "663f1a2b4e1c8d0012a45678", "smsFallback": null, "creditsCost": 0.025, "createdAt": "2026-05-01T10:00:00Z", "sentAt": "2026-05-01T10:00:01Z", "deliveredAt": "2026-05-01T10:00:03Z", "readAt": null, "scheduledAt": null, "failedAt": null, "error": null }, { "id": "507f1f77bcf86cd799439012", "type": "SMS", "status": "FAILED", "direction": "OUTBOUND", "source": "DIRECT_MESSAGE", "phoneNumber": "+1601234567890", "content": "Reminder: appointment tomorrow at 10:00", "campaignName": null, "sendernameId": "663f1a2b4e1c8d0012a45679", "smsFallback": null, "creditsCost": null, "createdAt": "2026-05-01T09:30:00Z", "sentAt": "2026-05-01T09:30:02Z", "deliveredAt": null, "readAt": null, "scheduledAt": null, "failedAt": "2026-05-01T09:30:02Z", "error": "Country not supported" } ], "pagination": { "nextCursor": "eyJzZW50QXQiOiIyMDI2LTA1LTAxVDA5OjMwOjAyWiIsIl9pZCI6IjUwN2YxZjc3YmNmODZjZDc5OTQzOTAxMiJ9", "hasNext": true, "limit": 100, "count": 100 } } ``` ### GET /messages/{id}/content - Get message content operationId `getMessageContent` · tag Message Log · requires `X-API-Key` Retrieve the full, reusable content of a single message by its ID — so you can resend or adapt a previously sent RCS or SMS message. Unlike the [message log](#operation/getMessageLog) (which returns a flat text summary), this returns the complete, structured, re-sendable content: - **RCS:** returns `messageType` (text, textBasic, richCard, carousel, …) and the full `content` object — pass them straight to the RCS send/preview endpoints to recreate the message. The SMS fallback configuration is included when one was set. - **SMS:** returns the message text in `content`, ready to resend. The `content` field is a structured object for RCS and a plain string for SMS; `text` always holds the flat text representation (the SMS body, or the RCS fallback/display text). **Base URL:** `https://api.sendrcs.eu/api/messages/{id}/content` **Example request:** ``` GET https://api.sendrcs.eu/api/messages/507f1f77bcf86cd799439011/content ``` Parameters: - `id` (path, string, required) - The message record ID. Matches the `messageRecordId` from `/send` responses, the `id` from the message log, and webhook payloads. Responses: - `200` - Message content Body: `MessageContentResponse`. - `400` - Invalid message ID format Body: `ErrorResponse`. - `401` - Unauthorized — missing or invalid API key / JWT token - `404` - Message not found Body: `ErrorResponse`. Example response: ```json { "message": { "id": "507f1f77bcf86cd799439011", "type": "RCS", "status": "DELIVERED", "direction": "OUTBOUND", "source": "API_MESSAGE", "phoneNumber": "+4512345678", "messageType": "richCard", "content": { "title": "Your order shipped", "description": "Order #1234 is on its way", "media": { "height": "TALL", "contentInfo": { "fileUrl": "https://example.com/box.jpg" } }, "suggestions": [ { "reply": { "text": "Track", "postbackData": "track_1234" } } ] }, "text": "Your order #1234 has been shipped.", "error": null, "smsFallback": { "message": "Your order #1234 has been shipped.", "sendernameId": "663f1a2b4e1c8d0012a45679" }, "campaignName": "Order Updates", "sendernameId": "663f1a2b4e1c8d0012a45678", "createdAt": "2026-05-01T10:00:00Z", "sentAt": "2026-05-01T10:00:01Z" } } ``` ### DELETE /messages/{id} - Cancel a scheduled message operationId `cancelScheduledMessage` · tag Message Log · requires `X-API-Key` Cancel one scheduled message before it is sent, by its message ID. Works the same way for RCS and SMS - a scheduled `/send` and a scheduled `/send-sms` both produce one cancellable message record. Only messages still in `SCHEDULED` status can be cancelled. Anything already sending or sent returns `404` - once a message has left, it cannot be called back. The scheduled job is cancelled and the message record is removed, so the message no longer appears in the message log. **Cancelling one recipient of a scheduled batch** works too: pass that recipient's message ID and only their message is dropped. The rest of the batch is untouched and still goes out on schedule. To cancel the whole batch instead, use [`DELETE /batch/{jobId}`](#operation/cancelBatch) - do not loop this endpoint over every recipient. **Finding the ID:** a scheduled send (`/send` or `/send-sms` with `scheduleAt`) returns `{ "scheduled": true, "jobId": "...", "messageId": "..." }` - `messageId` is the ID to pass here. Note that scheduled responses do not carry `messageRecordId`; that field appears only on immediate sends. You can also list everything still pending with [`GET /messages/log?status=SCHEDULED`](#operation/getMessageLog) and use the `id` of a row. Requires the `messages:write` permission. Parameters: - `id` (path, string, required) - The message ID - `messageId` from a scheduled send response, or `id` from the message log. Responses: - `200` - The scheduled message was cancelled - `400` - Invalid message ID format Body: `ErrorResponse`. - `403` - The API key lacks the `messages:write` permission Body: `ErrorResponse`. - `404` - No scheduled message with that ID on this account (already sent, already cancelled, or not yours) Body: `ErrorResponse`. ## Contacts Full CRUD for contacts with cursor-based pagination, phone number lookup, search, and bulk operations. Includes soft-delete (disable/enable) and group management. **Base URL:** `https://api.sendrcs.eu/api/v1/contacts` **Required permissions:** `contacts:read` for read operations, `contacts:write` for create/update/delete. ### GET /v1/contacts - List contacts operationId `listContacts` · tag Contacts · requires `X-API-Key` List contacts with cursor-based pagination, search, and filtering. **Phone lookup:** Pass `?phone=4512345678` for an exact-match lookup on the unique `{user, phone}` index. Returns 0 or 1 results - pagination is bypassed. The number must include the country code and is accepted with or without a prefix: `4512345678`, `+4512345678` or `004512345678`. In query strings, URL-encode `+` as `%2B` (an unencoded `+` is tolerated too). **Search:** Pass `?search=peter` to search across firstName, lastName, phone, and email (case-insensitive regex). **Custom fields:** Pass `?include=customFields` to embed each contact's custom field values in the response. This uses a single batch query — no N+1 overhead. **Pagination:** Uses cursor-based pagination (like the Message Log). Follow `nextCursor` from each response to fetch the next page. When `hasNext` is `false`, you've reached the end. Use the separate `/count` endpoint if you need a total count. **Example requests:** ``` GET /api/v1/contacts?limit=50&search=peter&include=customFields GET /api/v1/contacts?phone=4512345678 GET /api/v1/contacts?disabled=false&group=60f1b2c3d4e5f6a7b8c9d0e1&sort=lastName&order=asc ``` Parameters: - `limit` (query, integer) - Number of contacts per page (1-200) - `cursor` (query, string) - Pagination cursor from previous response's `pagination.nextCursor` - `search` (query, string) - Search across firstName, lastName, phone, email (case-insensitive) - `phone` (query, string) - Exact phone number lookup (country code required, `+`/`00` prefix accepted) - bypasses pagination, returns 0 or 1 results - `disabled` (query, string) - Filter by disabled status. Omit to include all. - `group` (query, string) - Filter by group ID (MongoDB ObjectId) - `sort` (query, string) - Sort field - `order` (query, string) - Sort order - `include` (query, string) - Pass `customFields` to embed custom field values in each contact Responses: - `200` - Paginated list of contacts Body: `ContactListResponse`. - `400` - Validation error Body: `ErrorResponse`. - `401` - Unauthorized Example response: ```json { "data": [ { "id": "665abc123def456ghi789jkl", "firstName": "Peter", "lastName": "Thomsen", "phone": "4512345678", "email": "peter@example.com", "disabled": false, "groups": [ { "id": "60f1b2c3d4e5f6a7b8c9d0e1", "name": "VIP Customers" } ], "createdAt": "2025-01-15T10:00:00Z", "customFields": [ { "fieldId": "665ghi789jkl012mno345pqr", "tag": "customer_status", "name": "Customer Status", "value": "Premium" } ] } ], "pagination": { "nextCursor": "eyJjcmVhdGVkQXQiOiIyMDI1LTAxLTE1VDEwOjAwOjAwWiIsIl9pZCI6IjY2NWFiYzEyM2RlZjQ1NmdoaTc4OWprbCJ9", "hasNext": true, "limit": 50, "count": 50 } } ``` ### POST /v1/contacts - Create contact operationId `createContact` · tag Contacts · requires `X-API-Key` Create a new contact. The phone number must be unique per user account. Optionally assign to groups and set custom field values in the same request. Groups are managed via bidirectional relationships — group contact counts are updated automatically. Returns `409 Conflict` if a contact with the same phone number already exists. Request body (`application/json`, required): Schema: `CreateContactRequest` - `firstName` (string, maxLength 100) - `lastName` (string, maxLength 100) - `phone` (string, required) - Phone number with country code (6-20 digits, optional + or 00 prefix - stored without the prefix). Must be unique per account. - `email` (string (email)) - `groups` (array of string) - Array of group IDs to assign the contact to - `customFields` (array of object) - Custom field values to set on creation - `fieldId` (string) - Custom field definition ID - `value` (string) Example: ```json { "firstName": "Peter", "lastName": "Thomsen", "phone": "4512345678", "email": "peter@example.com", "groups": [ "60f1b2c3d4e5f6a7b8c9d0e1" ], "customFields": [ { "fieldId": "665ghi789jkl012mno345pqr", "value": "Premium" } ] } ``` Responses: - `201` - Contact created - `400` - Validation error, or the phone's country is outside your price list (`code: COUNTRY_NOT_SUPPORTED`). Numbers you cannot message are never stored as contacts; this applies to every way a contact can b… Body: `ErrorResponse`. - `401` - Unauthorized - `409` - A contact with this phone number already exists ### GET /v1/contacts/count - Count contacts operationId `countContacts` · tag Contacts · requires `X-API-Key` Get the total number of contacts matching optional filters. More efficient than listing all contacts when you only need the count. Parameters: - `disabled` (query, string) - Filter by disabled status - `group` (query, string) - Filter by group ID - `search` (query, string) - Search filter (same as list endpoint) Responses: - `200` - Contact count - `401` - Unauthorized Example response: ```json { "count": 1542 } ``` ### POST /v1/contacts/bulk - Bulk create or upsert contacts operationId `bulkCreateContacts` · tag Contacts · requires `X-API-Key` Create or upsert up to 1,000 contacts in a single request. **Create mode** (`upsert: false`, default): Creates new contacts only. Contacts with duplicate phone numbers are skipped and reported in the `errors` array. **Upsert mode** (`upsert: true`): Matches existing contacts by phone number. If found, updates firstName/lastName/email. If not found, creates a new contact. Uses MongoDB `bulkWrite` for efficient processing. Custom fields can be referenced by `fieldId` or by `tag` (the tag on the custom field definition). Optionally assign all contacts to one or more groups. Numbers whose country is outside your price list are never stored (created or upserted). They are counted in `failed` and listed in `errors` with the reason, e.g. "Country not supported in your price list". Request body (`application/json`, required): Schema: `BulkContactRequest` - `contacts` (array of object, required, maxItems 1000) - `phone` (string, required) - `firstName` (string, maxLength 100) - `lastName` (string, maxLength 100) - `email` (string (email)) - `customFields` (array of object) - `fieldId` (string) - Custom field definition ID (use this or `tag`) - `tag` (string) - Custom field tag (use this or `fieldId`) - `value` (string) - `upsert` (boolean, default `false`) - When `true`, existing contacts (matched by phone) are updated. When `false`, duplicates are skipped. - `groups` (array of string) - Group IDs to assign all contacts to Example: ```json { "contacts": [ { "phone": "4512345678", "firstName": "Peter", "lastName": "Thomsen", "email": "peter@example.com", "customFields": [ { "tag": "customer_status", "value": "Premium" } ] }, { "phone": "4587654321", "firstName": "Anna", "lastName": "Jensen" } ], "upsert": true, "groups": [ "60f1b2c3d4e5f6a7b8c9d0e1" ] } ``` Responses: - `200` - Bulk operation results Body: `BulkContactResponse`. - `400` - Validation error Body: `ErrorResponse`. - `401` - Unauthorized Example response: ```json { "created": 1, "updated": 1, "failed": 1, "errors": [ { "phone": "99912345678", "error": "Country not supported in your price list" } ] } ``` ### PATCH /v1/contacts/bulk/disable - Bulk disable contacts operationId `bulkDisableContacts` · tag Contacts · requires `X-API-Key` Soft-delete multiple contacts by setting `disabled: true`. Disabled contacts cannot receive messages. All scheduled messages for these contacts are cancelled, and active flow enrollments are deactivated. Request body (`application/json`, required): - `contactIds` (array of string, required) - Array of contact IDs to disable - `reason` (string, maxLength 500) - Optional reason for disabling Example: ```json { "contactIds": [ "665abc123def456ghi789jkl", "665abc123def456ghi789jkm" ], "reason": "Unsubscribed via API" } ``` Responses: - `200` - Disable results - `400` - No valid contacts to disable - `401` - Unauthorized Example response: ```json { "message": "2 contacts disabled", "disabledCount": 2, "requestedCount": 2, "cancelledMessages": 3 } ``` ### PATCH /v1/contacts/bulk/enable - Bulk enable contacts operationId `bulkEnableContacts` · tag Contacts · requires `X-API-Key` Re-enable previously disabled contacts so they can receive messages again. Request body (`application/json`, required): - `contactIds` (array of string, required) - Array of contact IDs to enable Example: ```json { "contactIds": [ "665abc123def456ghi789jkl" ] } ``` Responses: - `200` - Enable results - `400` - No valid disabled contacts to enable - `401` - Unauthorized Example response: ```json { "message": "1 contacts enabled", "enabledCount": 1, "requestedCount": 1 } ``` ### GET /v1/contacts/{id} - Get contact operationId `getContact` · tag Contacts · requires `X-API-Key` Get a single contact by ID or by phone number. Pass `?include=customFields` to embed custom field values. The `{id}` path parameter accepts either a contact ID or a phone number. The phone number must include the country code and is accepted with or without a prefix: `4512345678`, `+4512345678` or `004512345678`. ``` GET /api/v1/contacts/665abc123def456ghi789jkl GET /api/v1/contacts/4512345678 GET /api/v1/contacts/+4512345678 ``` Parameters: - `id` (path, string, required) - Contact ID, or phone number with country code (`45...`, `+45...` or `0045...`) - `include` (query, string) - Pass `customFields` to embed custom field values Responses: - `200` - Contact details - `404` - Contact not found - `401` - Unauthorized ### PUT /v1/contacts/{id} - Update contact operationId `updateContact` · tag Contacts · requires `X-API-Key` Update a contact. All fields are optional — only provided fields are updated. **Group changes:** When `groups` is provided, the contact's group memberships are updated using bidirectional relationship management. Contacts are automatically synced to/from linked campaigns and message flows. **Disable/enable:** Set `disabled: true` to soft-delete or `disabled: false` to re-enable. Parameters: - `id` (path, string, required) - Contact ID Request body (`application/json`, required): Schema: `UpdateContactRequest` - `firstName` (string, maxLength 100) - `lastName` (string, maxLength 100) - `phone` (string) - `email` (string (email)) - `groups` (array of string) - Replace the contact's group memberships with this list - `customFields` (array of object) - `fieldId` (string) - `value` (string) - `disabled` (boolean) - Set to `true` to soft-delete, `false` to re-enable Example: ```json { "firstName": "Peter", "lastName": "Thomsen", "email": "peter.new@example.com", "groups": [ "60f1b2c3d4e5f6a7b8c9d0e1", "60f1b2c3d4e5f6a7b8c9d0e2" ], "customFields": [ { "fieldId": "665ghi789jkl012mno345pqr", "value": "Gold" } ] } ``` Responses: - `200` - Updated contact - `400` - Validation error - `404` - Contact not found - `409` - Phone number already exists on another contact - `401` - Unauthorized ### DELETE /v1/contacts/{id} - Delete contact operationId `deleteContact` · tag Contacts · requires `X-API-Key` Permanently delete a contact and all associated data: - Scheduled messages are cancelled - Flow enrollments are removed - Event participations are removed - Group memberships are removed (counts updated) - Custom field values are deleted Parameters: - `id` (path, string, required) - Contact ID Responses: - `200` - Contact deleted - `404` - Contact not found - `401` - Unauthorized Example response: ```json { "message": "Contact deleted", "id": "665abc123def456ghi789jkl" } ``` ## Custom Fields Manage custom merge field definitions and per-contact custom field values. Custom fields use tags (e.g., `customer_status`) that can be used as merge fields in messages via `{{customer_status}}`. **Base URL:** `https://api.sendrcs.eu/api/v1/contacts` **Required permissions:** `custom-fields:read` for read operations, `custom-fields:write` for create/update/delete. ### GET /v1/contacts/fields - List custom field definitions operationId `listCustomFields` · tag Custom Fields · requires `X-API-Key` List all custom merge field definitions for your account. These define the available custom fields — use the `tag` value as `{{tag}}` in message templates. Responses: - `200` - List of custom field definitions - `401` - Unauthorized Example response: ```json { "data": [ { "id": "665ghi789jkl012mno345pqr", "name": "Customer Status", "tag": "customer_status", "description": "Customer tier level", "category": "Business", "createdAt": "2025-01-10T08:00:00Z" }, { "id": "665ghi789jkl012mno345pqs", "name": "Loyalty Points", "tag": "loyalty_points", "description": null, "category": "Custom", "createdAt": "2025-02-20T14:00:00Z" } ] } ``` ### POST /v1/contacts/fields - Create custom field definition operationId `createCustomField` · tag Custom Fields · requires `X-API-Key` Create a new custom merge field definition. The `tag` must be unique per account and can only contain letters, numbers, and underscores (e.g., `customer_status`). Once created, you can set values per contact and use `{{tag}}` in message templates. Request body (`application/json`, required): Schema: `CreateCustomFieldRequest` - `name` (string, required, minLength 1, maxLength 100) - Display name. Letters, numbers, spaces, underscores, hyphens. - `tag` (string, required, minLength 1, maxLength 50) - Unique tag for merge fields. Letters, numbers, underscores only. - `description` (string, maxLength 500) - `category` (string, maxLength 50) - Category for organization (default: "Custom") Example: ```json { "name": "Customer Status", "tag": "customer_status", "description": "Customer tier level", "category": "Business" } ``` Responses: - `201` - Custom field created - `400` - Validation error or tag already exists Body: `ErrorResponse`. - `401` - Unauthorized ### GET /v1/contacts/fields/{fieldId} - Get custom field definition operationId `getCustomField` · tag Custom Fields · requires `X-API-Key` Get a single custom merge field definition by ID. Parameters: - `fieldId` (path, string, required) - Custom field definition ID Responses: - `200` - Custom field definition - `404` - Custom field not found - `401` - Unauthorized ### PUT /v1/contacts/fields/{fieldId} - Update custom field definition operationId `updateCustomField` · tag Custom Fields · requires `X-API-Key` Update a custom merge field definition. All fields are optional — only provided fields are updated. If updating the `tag`, it must still be unique per account. Parameters: - `fieldId` (path, string, required) - Custom field definition ID Request body (`application/json`, required): Schema: `CreateCustomFieldRequest` - `name` (string, required, minLength 1, maxLength 100) - Display name. Letters, numbers, spaces, underscores, hyphens. - `tag` (string, required, minLength 1, maxLength 50) - Unique tag for merge fields. Letters, numbers, underscores only. - `description` (string, maxLength 500) - `category` (string, maxLength 50) - Category for organization (default: "Custom") Example: ```json { "name": "VIP Status", "description": "Updated description" } ``` Responses: - `200` - Updated custom field definition - `400` - Validation error or tag already exists - `404` - Custom field not found - `401` - Unauthorized ### DELETE /v1/contacts/fields/{fieldId} - Delete custom field definition operationId `deleteCustomField` · tag Custom Fields · requires `X-API-Key` Delete a custom merge field definition. If the field is in use by contacts, the request will fail with a `400` status unless `?force=true` is passed. When `force=true`, all per-contact values for this field are also deleted. Parameters: - `fieldId` (path, string, required) - Custom field definition ID - `force` (query, string) - Force delete even if field is in use by contacts Responses: - `200` - Custom field deleted - `400` - Field is in use — pass `?force=true` to delete anyway - `404` - Custom field not found - `401` - Unauthorized Example response: ```json { "message": "Custom field deleted", "deletedContactData": 42 } ``` ### GET /v1/contacts/{id}/fields - Get contact's custom field values operationId `getContactCustomFields` · tag Custom Fields · requires `X-API-Key` Get all custom field values for a specific contact. Returns the field definition metadata (tag, name) alongside each value. **Required permissions:** `contacts:read` and `custom-fields:read` Parameters: - `id` (path, string, required) - Contact ID Responses: - `200` - Custom field values for the contact - `404` - Contact not found - `401` - Unauthorized Example response: ```json { "data": [ { "fieldId": "665ghi789jkl012mno345pqr", "tag": "customer_status", "name": "Customer Status", "value": "Premium", "updatedAt": "2025-03-10T14:30:00Z" } ] } ``` ### PUT /v1/contacts/{id}/fields - Bulk set custom field values for a contact operationId `bulkSetContactCustomFields` · tag Custom Fields · requires `X-API-Key` Set multiple custom field values for a contact in a single request. Fields can be referenced by `fieldId` or `tag`. Existing values are overwritten. **Required permissions:** `contacts:write` and `custom-fields:write` Parameters: - `id` (path, string, required) - Contact ID Request body (`application/json`, required): - `fields` (array of object, required) - `fieldId` (string) - Custom field definition ID (use this or `tag`) - `tag` (string) - Custom field tag (use this or `fieldId`) - `value` (string, required) Example: ```json { "fields": [ { "tag": "customer_status", "value": "Gold" }, { "fieldId": "665ghi789jkl012mno345pqs", "value": "1250" } ] } ``` Responses: - `200` - Results for each field - `404` - Contact not found - `401` - Unauthorized ### PUT /v1/contacts/{id}/fields/{fieldId} - Set a custom field value for a contact operationId `setContactCustomField` · tag Custom Fields · requires `X-API-Key` Set or update a single custom field value for a contact. Creates the value if it doesn't exist, updates it if it does (upsert). **Required permissions:** `contacts:write` and `custom-fields:write` Parameters: - `id` (path, string, required) - Contact ID - `fieldId` (path, string, required) - Custom field definition ID Request body (`application/json`, required): - `value` (string, required) Example: ```json { "value": "Premium" } ``` Responses: - `200` - Custom field value set - `404` - Contact or custom field not found - `401` - Unauthorized ### DELETE /v1/contacts/{id}/fields/{fieldId} - Delete a custom field value for a contact operationId `deleteContactCustomField` · tag Custom Fields · requires `X-API-Key` Remove a custom field value from a contact. The field definition is not deleted — only the per-contact value is removed. **Required permissions:** `contacts:write` and `custom-fields:write` Parameters: - `id` (path, string, required) - Contact ID - `fieldId` (path, string, required) - Custom field definition ID Responses: - `200` - Custom field value deleted - `404` - Custom field value not found - `401` - Unauthorized Example response: ```json { "message": "Custom field value deleted" } ``` ## Groups Manage contact groups with full CRUD and membership operations. Groups organize contacts and can be linked to campaigns and message flows. Uses bidirectional sync to maintain consistency between Contact.groups and Group.contacts. **Base URL:** `https://api.sendrcs.eu/api/v1/groups` **Required permissions:** `groups:read` for read operations, `groups:write` for create/update/delete and membership management. ### GET /v1/groups - List groups operationId `listGroups` · tag Groups · requires `X-API-Key` List all contact groups for the authenticated user. Returns a lightweight summary sorted by newest first, including contact counts. Responses: - `200` - List of groups - `401` - Unauthorized Example response: ```json { "data": [ { "id": "60f1b2c3d4e5f6a7b8c9d0e1", "name": "VIP Customers", "description": "High-value customers", "contactCount": 150, "createdAt": "2025-01-15T10:00:00Z", "updatedAt": "2025-01-20T14:30:00Z" }, { "id": "60f1b2c3d4e5f6a7b8c9d0e2", "name": "Newsletter Subscribers", "description": null, "contactCount": 1200, "createdAt": "2025-01-10T08:00:00Z", "updatedAt": "2025-01-10T08:00:00Z" } ] } ``` ### POST /v1/groups - Create group operationId `createGroup` · tag Groups · requires `X-API-Key` Create a new contact group. Groups are used to organize contacts and can be linked to campaigns and message flows. Request body (`application/json`, required): Schema: `CreateGroupRequest` - `name` (string, required, minLength 1, maxLength 200) - Group name (required) - `description` (string, maxLength 1000) - Optional group description Example: ```json { "name": "VIP Customers", "description": "High-value customers" } ``` Responses: - `201` - Group created - `400` - Validation error Body: `ErrorResponse`. - `401` - Unauthorized ### GET /v1/groups/{id} - Get group operationId `getGroup` · tag Groups · requires `X-API-Key` Get a single group by ID. Optionally includes a paginated list of contacts in the group. **Include contacts:** Pass `?includeContacts=true` to get contacts in the group. Use `limit` and `offset` for pagination when including contacts. **Example requests:** ``` GET /api/v1/groups/60f1b2c3d4e5f6a7b8c9d0e1 GET /api/v1/groups/60f1b2c3d4e5f6a7b8c9d0e1?includeContacts=true&limit=50&offset=0 ``` Parameters: - `id` (path, string, required) - Group ID (MongoDB ObjectId) - `includeContacts` (query, string) - Include paginated contact list in response - `limit` (query, integer) - Max contacts to return when includeContacts=true - `offset` (query, integer) - Number of contacts to skip for pagination Responses: - `200` - Group details - `404` - Group not found - `401` - Unauthorized Example response: ```json { "data": { "id": "60f1b2c3d4e5f6a7b8c9d0e1", "name": "VIP Customers", "description": "High-value customers", "contactCount": 150, "createdAt": "2025-01-15T10:00:00Z", "updatedAt": "2025-01-20T14:30:00Z", "contacts": [ { "id": "665abc123def456ghi789jkl", "firstName": "Peter", "lastName": "Thomsen", "phone": "4512345678", "email": "peter@example.com", "disabled": false } ], "contactsReturned": 1, "contactsOffset": 0, "hasMore": true } } ``` ### PUT /v1/groups/{id} - Update group operationId `updateGroup` · tag Groups · requires `X-API-Key` Update a group's name and/or description. Does not modify group membership — use the contacts endpoints for that. Parameters: - `id` (path, string, required) - Group ID (MongoDB ObjectId) Request body (`application/json`, required): Schema: `UpdateGroupRequest` - `name` (string, minLength 1, maxLength 200) - New group name - `description` (string, maxLength 1000) - New group description Example: ```json { "name": "Premium Customers", "description": "Updated description" } ``` Responses: - `200` - Group updated - `400` - Validation error or no fields to update - `404` - Group not found - `401` - Unauthorized ### DELETE /v1/groups/{id} - Delete group operationId `deleteGroup` · tag Groups · requires `X-API-Key` Delete a contact group. This does NOT delete the contacts in the group — it only removes the group itself and clears the group reference from all contacts that belonged to it. Parameters: - `id` (path, string, required) - Group ID (MongoDB ObjectId) Responses: - `200` - Group deleted - `404` - Group not found - `401` - Unauthorized Example response: ```json { "message": "Group deleted", "id": "60f1b2c3d4e5f6a7b8c9d0e1", "name": "VIP Customers" } ``` ### POST /v1/groups/{id}/contacts - Add contacts to group operationId `addContactsToGroup` · tag Groups · requires `X-API-Key` Add contacts to a group by contact IDs. Uses bidirectional sync to maintain consistency between Contact.groups and Group.contacts. If the group is linked to campaigns or message flows, contacts are automatically synced to those entities as well. Duplicate contacts (already in the group) are silently ignored. Parameters: - `id` (path, string, required) - Group ID (MongoDB ObjectId) Request body (`application/json`, required): Schema: `GroupContactsRequest` - `contactIds` (array of string, required, maxItems 10000) - Array of contact IDs (MongoDB ObjectIds) to add or remove Example: ```json { "contactIds": [ "665abc123def456ghi789jkl", "665abc123def456ghi789jkm" ] } ``` Responses: - `200` - Contacts added Body: `GroupContactsResponse`. - `400` - Validation error - `404` - Group not found - `401` - Unauthorized Example response: ```json { "message": "2 contacts added to group", "contactsProcessed": 2, "newRelationships": 2, "linkedEntitiesSync": { "campaignsUpdated": 1, "flowsUpdated": 0 } } ``` ### DELETE /v1/groups/{id}/contacts - Remove contacts from group operationId `removeContactsFromGroup` · tag Groups · requires `X-API-Key` Remove contacts from a group by contact IDs. Uses bidirectional sync to maintain consistency. If the group is linked to campaigns or message flows, contacts are automatically removed from those entities as well. Contacts not in the group are silently ignored. The contacts themselves are NOT deleted. Parameters: - `id` (path, string, required) - Group ID (MongoDB ObjectId) Request body (`application/json`, required): Schema: `GroupContactsRequest` - `contactIds` (array of string, required, maxItems 10000) - Array of contact IDs (MongoDB ObjectIds) to add or remove Example: ```json { "contactIds": [ "665abc123def456ghi789jkl" ] } ``` Responses: - `200` - Contacts removed - `400` - Validation error - `404` - Group not found - `401` - Unauthorized Example response: ```json { "message": "1 contacts removed from group", "contactsProcessed": 1, "removedRelationships": 1, "linkedEntitiesSync": { "campaignsUpdated": 0, "flowsUpdated": 0 } } ``` ## Templates Manage message templates for SMS and RCS. Templates support categories, contexts (event/flow/general), default designation, and RCS-specific content types. **Base URL:** `https://api.sendrcs.eu/api/v1/templates` **Required permissions:** `templates:read` for read operations, `templates:write` for create/update/delete. ### GET /v1/templates - List templates operationId `listTemplates` · tag Templates · requires `X-API-Key` List message templates with page-based pagination and filtering. Supports filtering by type (SMS/RCS), RCS message subtype, template group, context, and name search. For RCS templates, each result includes a `parsedContent` field with the structured message content. **Example requests:** ``` GET /api/v1/templates?type=RCS&limit=20 GET /api/v1/templates?search=welcome&context=event GET /api/v1/templates?type=SMS&sort=createdAt&order=desc ``` Parameters: - `type` (query, string) - Filter by template type - `rcsMessageType` (query, string) - Filter by RCS message subtype (only applicable when type=RCS) - `templateGroupId` (query, string) - Filter by template group (folder). Pass a template group ID or `none` for ungrouped templates. Template groups are NOT contact groups. - `context` (query, string) - Filter by context - `search` (query, string) - Search by template name (case-insensitive) - `page` (query, integer) - Page number - `limit` (query, integer) - Results per page (1-100) - `sort` (query, string) - Sort field - `order` (query, string) - Sort order Responses: - `200` - Paginated list of templates Body: `TemplateListResponse`. - `400` - Validation error - `401` - Unauthorized Example response: ```json { "data": [ { "id": "665ghi789jkl012mno345pqr", "name": "Welcome Message", "content": "Welcome {{firstName}}! Thanks for signing up.", "type": "SMS", "rcsMessageType": null, "context": "general", "templateGroupId": null, "isDefault": false, "createdAt": "2025-01-15T10:00:00Z", "updatedAt": "2025-01-15T10:00:00Z" } ], "pagination": { "total": 25, "page": 1, "limit": 20, "pages": 2, "hasNext": true, "hasPrev": false } } ``` ### POST /v1/templates - Create template operationId `createTemplate` · tag Templates · requires `X-API-Key` Create a new message template. **For SMS templates:** Pass `content` as a plain text string. **For RCS templates:** Pass `content` as an object (same format as rcs_send content field). You must also provide `rcsMessageType`. The content is stored internally as a JSON string and returned with a `parsedContent` field containing the structured data. **Setting as default:** Pass `isDefault: true` to make this the default template for its type+context. Any existing default for the same type+context will be unset. Request body (`application/json`, required): Schema: `CreateTemplateRequest` - `name` (string, required, minLength 1, maxLength 200) - Template name (required) - `type` (string, required, one of `SMS`, `RCS`) - Template type (required) - `content` (string or object, required) - Template content. For SMS: a plain text string. For RCS: an object (same format as rcs_send content field). - `rcsMessageType` (string, one of `textBasic`, `text`, `richCard`, `carousel`, `image`, `video`, `audio`, `file`, `media`) - RCS message subtype (required when type=RCS) - `templateGroupId` (string) - Template group (folder) to place the template in. Omit or pass null for ungrouped. Template groups are NOT contact groups. - `context` (string, one of `event`, `flow`, `general`) - Template context (default "general") - `isDefault` (boolean) - Set as default template for this type+context (default false) Example - SMS template: ```json { "name": "Welcome SMS", "type": "SMS", "content": "Welcome {{firstName}}! Thanks for signing up.", "context": "general" } ``` Example - RCS rich card template: ```json { "name": "Product Card", "type": "RCS", "rcsMessageType": "richCard", "content": { "title": "New Product Available", "description": "Check out our latest product!", "media": { "url": "https://example.com/image.jpg", "height": "TALL" }, "cardActions": [ { "type": "url", "label": "View Product", "url": "https://example.com/product" } ] }, "templateGroupId": "665ghi789jkl012mno345abc" } ``` Responses: - `201` - Template created - `400` - Validation error (e.g. missing rcsMessageType for RCS, wrong content type) Body: `ErrorResponse`. - `401` - Unauthorized ### GET /v1/templates/{id} - Get template operationId `getTemplate` · tag Templates · requires `X-API-Key` Get a single template by ID. For RCS templates, includes `parsedContent` with the structured message content. Parameters: - `id` (path, string, required) - Template ID (MongoDB ObjectId) Responses: - `200` - Template details - `404` - Template not found - `401` - Unauthorized ### PUT /v1/templates/{id} - Update template operationId `updateTemplate` · tag Templates · requires `X-API-Key` Update an existing template. All fields are optional — only provided fields are updated. **RCS content handling:** If updating content for an RCS template, pass it as an object. If changing `rcsMessageType` without providing new content, the existing content is re-wrapped with the new message type. Parameters: - `id` (path, string, required) - Template ID (MongoDB ObjectId) Request body (`application/json`, required): Schema: `UpdateTemplateRequest` - `name` (string, minLength 1, maxLength 200) - `type` (string, one of `SMS`, `RCS`) - `content` (string or object) - New content. For RCS: object. For SMS: text string. - `rcsMessageType` (string, one of `textBasic`, `text`, `richCard`, `carousel`, `image`, `video`, `audio`, `file`, `media`) - `templateGroupId` (string) - Move the template to a template group (folder), or null to ungroup. Omit to leave unchanged. Template groups are NOT contact groups. - `context` (string, one of `event`, `flow`, `general`) - `isDefault` (boolean) Example: ```json { "name": "Updated Welcome Message", "content": "Hi {{firstName}}, welcome aboard!" } ``` Responses: - `200` - Template updated - `400` - Validation error - `404` - Template not found - `401` - Unauthorized ### DELETE /v1/templates/{id} - Delete template operationId `deleteTemplate` · tag Templates · requires `X-API-Key` Permanently delete a message template. Parameters: - `id` (path, string, required) - Template ID (MongoDB ObjectId) Responses: - `200` - Template deleted - `404` - Template not found - `401` - Unauthorized Example response: ```json { "message": "Template deleted", "id": "665ghi789jkl012mno345pqr", "name": "Welcome Message", "type": "SMS" } ``` ## Template Groups Manage template groups — folders for organizing message templates. Each template belongs to at most one template group (or is ungrouped). Deleting a template group never deletes its templates; they become ungrouped. **Template groups are NOT contact groups.** Contact groups (see the Groups tag) organize contacts; template groups organize message templates. All template-group fields are explicitly named (`templateGroupId`, `templateCount`, `ungroupedTemplateCount`) to avoid any confusion. **Base URL:** `https://api.sendrcs.eu/api/v1/templates/groups` **Required permissions:** `templates:read` … ### GET /v1/templates/groups - List template groups operationId `listTemplateGroups` · tag Template Groups · requires `X-API-Key` List all template groups (folders for message templates), sorted by name, each with its `templateCount`. Also returns `ungroupedTemplateCount` — the number of templates not in any group. Template groups are NOT contact groups. Responses: - `200` - List of template groups Body: `TemplateGroupListResponse`. - `401` - Unauthorized Example response: ```json { "data": [ { "id": "665abc123def456ghi789jkl", "name": "Campaigns DK", "templateCount": 5, "createdAt": "2025-01-15T10:00:00Z", "updatedAt": "2025-01-15T10:00:00Z" } ], "ungroupedTemplateCount": 12 } ``` ### POST /v1/templates/groups - Create template group operationId `createTemplateGroup` · tag Template Groups · requires `X-API-Key` Create a new template group (folder for message templates). Names must be unique per account — a duplicate name returns `400` with `error: duplicate_name`. Request body (`application/json`, required): Schema: `CreateTemplateGroupRequest` - `name` (string, required, minLength 1, maxLength 100) - Template group name (required, unique per account) Example: ```json { "name": "Campaigns DK" } ``` Responses: - `201` - Template group created - `400` - Validation error or duplicate name - `401` - Unauthorized ### POST /v1/templates/groups/assign - Assign templates to a template group operationId `assignTemplatesToTemplateGroup` · tag Template Groups · requires `X-API-Key` Move one or more templates into a template group, or remove them from any group by passing `templateGroupId: null`. Each template belongs to at most one template group. Template IDs that don't exist or belong to another account are silently skipped; `modifiedCount` reports how many templates were actually updated. Request body (`application/json`, required): Schema: `AssignTemplatesToTemplateGroupRequest` - `templateIds` (array of string, required) - MongoDB ObjectIds of the templates to move - `templateGroupId` (string, required) - Target template group ID, or null to remove the templates from any group Example - Move templates into a group: ```json { "templateIds": [ "665ghi789jkl012mno345pqr" ], "templateGroupId": "665abc123def456ghi789jkl" } ``` Example - Remove templates from their group: ```json { "templateIds": [ "665ghi789jkl012mno345pqr" ], "templateGroupId": null } ``` Responses: - `200` - Templates moved - `400` - Validation error or unknown template group - `401` - Unauthorized ### PUT /v1/templates/groups/{id} - Rename template group operationId `updateTemplateGroup` · tag Template Groups · requires `X-API-Key` Parameters: - `id` (path, string, required) - Template group ID (MongoDB ObjectId) Request body (`application/json`, required): Schema: `CreateTemplateGroupRequest` - `name` (string, required, minLength 1, maxLength 100) - Template group name (required, unique per account) Example: ```json { "name": "Campaigns DK 2025" } ``` Responses: - `200` - Template group renamed - `400` - Validation error or duplicate name - `404` - Template group not found ### DELETE /v1/templates/groups/{id} - Delete template group operationId `deleteTemplateGroup` · tag Template Groups · requires `X-API-Key` Delete a template group. **Templates inside the group are NOT deleted** — they become ungrouped. `ungroupedTemplateCount` in the response reports how many templates were affected. Parameters: - `id` (path, string, required) - Template group ID (MongoDB ObjectId) Responses: - `200` - Template group deleted - `404` - Template group not found --- # Part 3 - Webhook events SendRCS posts these events to the callback URL you configure. 6 event types. ## buttonClick Button click callback Sent to your webhook URL when a user clicks a button in an RCS message. Configure the webhook URL per button in your message's `suggestions` or `cardActions`. **Correlating with sent messages:** The `message.id` field in the payload matches the `messageRecordId` from the `/send` response. **Signature verification (optional):** Every request includes an `X-RCS-Signature: t=,v1=` header. To verify, compute `HMAC_SHA256(secret, t + "." + rawBody)` and compare to the `v1=` portion. Reject if `|now - t| > 300` seconds. See the **Webhooks** section overview for a full Node.js example. Verification is optional — you can safely ignore the header if you don't need it. Payload (`application/json`): - `event` (string) - `timestamp` (string (date-time)) - `interaction` (object) - `id` (string) - Unique interaction ID - `type` (string) - `buttonText` (string) - `postbackData` (string) - `clickedAt` (string (date-time)) - When the button was clicked - `message` (object) - `id` (string) - Message record ID. Matches `messageRecordId` from the /send response. - `campaignName` (string) - Campaign name from the original message, if set - `sentAt` (string (date-time)) - When the message was originally sent - `rcsData` (object) - `messageType` (string) - `brandId` (string) - `contact` (object) - `id` (string) - `phone` (string) - `name` (string) - `user` (object) - `id` (string) - `businessName` (string) - `metadata` (object) - `conversationId` (string) - `platform` (string) Example payload: ```json { "event": "rcs_button_clicked", "timestamp": "2024-01-15T10:30:00Z", "interaction": { "id": "507f1f77bcf86cd799439014", "type": "button_click", "buttonText": "Book Appointment", "postbackData": "book_appointment_service_123", "clickedAt": "2024-01-15T10:30:00Z" }, "message": { "id": "507f1f77bcf86cd799439011", "campaignName": "Summer Sale 2024", "sentAt": "2024-01-15T09:00:00Z", "rcsData": { "messageType": "richCard", "brandId": "your-rcs-brand-id" } }, "contact": { "id": "507f1f77bcf86cd799439012", "phone": "+4512345678", "name": "John Doe" }, "user": { "id": "507f1f77bcf86cd799439013", "businessName": "Your Business Name" }, "metadata": { "conversationId": "507f1f77bcf86cd799439015", "platform": "RCS" } } ``` ## deliveryStatus Delivery status callback Sent to your webhook URL when a message delivery status changes (delivered, read, failed). Configure status webhooks in [Settings > Webhooks](https://app.sendrcs.eu/settings/webhooks). **When `failed` fires:** for failures reported by the recipient's network (undeliverable, rejected, expired) and also for messages the platform could not send at all: country not supported or blocked, insufficient credits, disabled contact, or a rejection at submission. When an RCS message falls back to SMS you receive a `failed` `rcs_status` event for the RCS leg (error "... - SMS fallback used") followed by `sms_status` events for the SMS. The `error` text is the same as the `error` field in `GET /api/messages/log`. **Correlating with sent messages:** The `messageId` in the payload matches the `messageRecordId` field from the `/send` response. For `/send-batch`, messages are created asynchronously - use the batch job status endpoint to track individual messages. **Signature verification (optional):** Every request includes an `X-RCS-Signature: t=,v1=` header. To verify, compute `HMAC_SHA256(secret, t + "." + rawBody)` and compare to the `v1=` portion. Reject if `|now - t| > 300` seconds. See the **Webhooks** section overview for a full Node.js example. Verification is optional — you can safely ignore the header if you don't need it. Payload (`application/json`): - `event` (string, one of `rcs_status`, `sms_status`) - Event type (rcs_status for RCS messages, sms_status for SMS Fallback messages) - `messageId` (string) - Message record ID. Matches the `messageRecordId` from the /send response. - `status` (string, one of `sent`, `delivered`, `read`, `failed`) - Current delivery status - `phoneNumber` (string) - Recipient phone number - `contactId` (string) - Contact ID if the recipient is a known contact. Null when skipContactCreation was used and no contact exists. - `campaignName` (string) - Campaign name from the original message, if set - `timestamp` (string (date-time)) - `error` (string) - Error message when status is failed - `test` (boolean) - Whether this is a test webhook (true when triggered via test endpoint) Example payload: ```json { "event": "rcs_status", "messageId": "507f1f77bcf86cd799439011", "status": "delivered", "phoneNumber": "+4512345678", "contactId": "507f1f77bcf86cd799439012", "campaignName": "Summer Sale 2024", "timestamp": "2024-01-15T10:30:00Z", "error": null, "test": false } ``` ## rcsIncoming Incoming RCS message callback Sent to your webhook URL when a contact sends an RCS message to one of your RCS sender names — texts, images, videos, audio, files, contact cards, shared locations, and button/suggestion taps. Configure one webhook per sender name in [Settings > Webhooks](https://app.sendrcs.eu/settings/webhooks) under the **Incoming Webhooks** tab. **Message types:** `rcs.messageType` tells you what arrived: `text`, `image`, `video`, `audio`, `file`, `vcard`, `location`, or `button`. For media messages the `rcs.media` block is present; for shared locations `rcs.location` carries the coordinates. **Button taps:** Taps on suggested replies/buttons are delivered too, with `rcs.isButtonReply: true` plus `rcs.buttonText` and `rcs.postbackData`. If you also use per-button click webhooks you may receive both events for the same tap — filter on `rcs.isButtonReply` if you only want free-form messages here. **Media access:** `rcs.media.url` is a relative SendRCS API path that requires an authenticated session to download. Incoming media is retained for 48 hours after receipt — see `rcs.media.expiresAt`. **Signature verification (optional):** Every request includes an `X-RCS-Signature: t=,v1=` header. To verify, compute `HMAC_SHA256(secret, t + "." + rawBody)` and compare to the `v1=` portion. Reject if `|now - t| > 300` seconds. See the **Webhooks** section overview for a full Node.js example. Verification is optional — you can safely ignore the header if you don't need it. Payload (`application/json`): - `event` (string) - `timestamp` (string (date-time)) - `rcs` (object) - `id` (string) - Message record ID of the inbound message - `from` (string) - Sender phone number (the contact) - `to` (string) - Your RCS agent ID that received the message - `message` (string) - Text content, or a short description for media/location messages - `messageType` (string, one of `text`, `image`, `video`, `audio`, `file`, `vcard`, `location`, `button`) - What kind of message arrived - `isButtonReply` (boolean) - True when the message is a tap on a suggested reply/button - `buttonText` (string) - Button label, only present when isButtonReply is true - `postbackData` (string) - Button postback data, only present when isButtonReply is true - `media` (object) - Present for image/video/audio/file/vcard messages - `type` (string) - Media kind (matches messageType) - `name` (string) - Original file name, when available - `url` (string) - Relative SendRCS API path to download the media (requires an authenticated session) - `expiresAt` (string (date-time)) - When the media stops being available (48 hours after receipt) - `location` (object) - Present for shared locations - `latitude` (number) - `longitude` (number) - `label` (string) - `receivedAt` (string (date-time)) - `contact` (object) - `id` (string) - `name` (string) - `phone` (string) - `email` (string) - `sendername` (object) - `id` (string) - Sender name ID - `name` (string) - Sender name display name - `agentId` (string) - RCS agent ID of the sender name - `user` (object) - `id` (string) - `email` (string) - `metadata` (object) - `messageType` (string) - `direction` (string) - `platform` (string) - `provider` (string) - `conversationId` (string) - RCS conversation this message belongs to Example payload: ```json { "event": "rcs_incoming", "timestamp": "2024-01-15T10:30:00Z", "rcs": { "id": "507f1f77bcf86cd799439011", "from": "+4512345678", "to": "your-agent-id", "message": "Hi, do you have this in stock?", "messageType": "text", "isButtonReply": false, "receivedAt": "2024-01-15T10:30:00Z" }, "contact": { "id": "507f1f77bcf86cd799439012", "name": "John Doe", "phone": "+4512345678", "email": "john@example.com" }, "sendername": { "id": "507f1f77bcf86cd799439016", "name": "Your Brand", "agentId": "your-agent-id" }, "user": { "id": "507f1f77bcf86cd799439013", "email": "owner@business.com" }, "metadata": { "messageType": "RCS", "direction": "INBOUND", "platform": "RCS", "provider": "SendRCS", "conversationId": "507f1f77bcf86cd799439015" } } ``` ## smsIncoming Incoming SMS message callback Sent to your webhook URL when someone sends an SMS to one of your virtual numbers. Configure one webhook per virtual number in [Settings > Webhooks](https://app.sendrcs.eu/settings/webhooks) under the **Incoming Webhooks** tab. Virtual numbers can be subscribed in [Settings > Numbers](https://app.sendrcs.eu/settings/numbers). **Contacts:** If the sender is not yet in your contact list, a contact is created automatically and referenced in the `contact` block. **Signature verification (optional):** Every request includes an `X-RCS-Signature: t=,v1=` header. To verify, compute `HMAC_SHA256(secret, t + "." + rawBody)` and compare to the `v1=` portion. Reject if `|now - t| > 300` seconds. See the **Webhooks** section overview for a full Node.js example. Verification is optional — you can safely ignore the header if you don't need it. Payload (`application/json`): - `event` (string) - `timestamp` (string (date-time)) - `sms` (object) - `id` (string) - Message record ID of the inbound message - `from` (string) - Sender phone number (the contact) - `to` (string) - Your virtual number that received the message - `message` (string) - SMS text content - `encoding` (string) - SMS encoding (GSM-7 or UCS-2) - `segments` (integer) - Number of SMS segments - `receivedAt` (string (date-time)) - `contact` (object) - `id` (string) - `name` (string) - `phone` (string) - `email` (string) - `virtualNumber` (object) - `id` (string) - Virtual number ID - `phoneNumber` (string) - The virtual number in E.164 format - `subscriptionId` (string) - Your subscription ID for this virtual number - `user` (object) - `id` (string) - `email` (string) - `metadata` (object) - `messageType` (string) - `direction` (string) - `platform` (string) Example payload: ```json { "event": "sms_incoming", "timestamp": "2024-01-15T10:30:00Z", "sms": { "id": "507f1f77bcf86cd799439011", "from": "+4512345678", "to": "+4587654321", "message": "Hi, I would like to book an appointment", "encoding": "GSM-7", "segments": 1, "receivedAt": "2024-01-15T10:30:00Z" }, "contact": { "id": "507f1f77bcf86cd799439012", "name": "John Doe", "phone": "+4512345678", "email": "john@example.com" }, "virtualNumber": { "id": "507f1f77bcf86cd799439017", "phoneNumber": "+4587654321", "subscriptionId": "507f1f77bcf86cd799439018" }, "user": { "id": "507f1f77bcf86cd799439013", "email": "owner@business.com" }, "metadata": { "messageType": "SMS", "direction": "INBOUND", "platform": "SMS" } } ``` ## formSubmission Form submission callback Sent to your webhook URL every time a visitor submits one of your forms — **before** the double opt-in is confirmed. Configure one webhook per form in [Settings > Webhooks](https://app.sendrcs.eu/settings/webhooks) under the **Form Submission Webhooks** tab. The same webhook also receives the `form_optin_confirmed` event when the visitor later confirms. **Opt-in status:** `optIn.status` is always `PENDING` at submit time — the contact is not activated (and, for existing contacts, not updated) until the visitor confirms via the opt-in link. `contact.optInStatus` reports the contact's current stored status. `optIn.confirmationSent` is `false` when the form's "submit once" throttle suppressed a repeat opt-in message. **Submitted data:** The `data` block contains exactly what the visitor submitted, including custom fields with their labels. For existing contacts these values are NOT yet written to the contact — that happens on confirmation. **Signature verification (optional):** Every request includes an `X-RCS-Signature: t=,v1=` header. To verify, compute `HMAC_SHA256(secret, t + "." + rawBody)` and compare to the `v1=` portion. Reject if `|now - t| > 300` seconds. See the **Webhooks** section overview for a full Node.js example. Verification is optional — you can safely ignore the header if you don't need it. Payload (`application/json`): - `event` (string) - `timestamp` (string (date-time)) - `form` (object) - `id` (string) - Form ID - `name` (string) - Internal form name - `title` (string) - Public form title - `slug` (string) - Public form ID used in the form URL - `submission` (object) - `id` (string) - Submission record ID - `action` (string, one of `created`, `updated`) - Whether a new contact was created or the phone number matched an existing contact - `submittedAt` (string (date-time)) - `ip` (string) - Visitor IP address - `optIn` (object) - `status` (string, one of `PENDING`) - Always PENDING at submit time — the contact is not activated until the visitor confirms - `confirmationSent` (boolean) - False when the form's "submit once" throttle suppressed a repeat opt-in message - `confirmedAt` (string (date-time)) - Always null for this event - `contact` (object) - `id` (string) - `phone` (string) - `optInStatus` (string, one of `PENDING`, `CONFIRMED`) - The contact's current stored opt-in status (CONFIRMED when an already-confirmed contact re-submits) - `data` (object) - Exactly what the visitor submitted. For existing contacts these values are not written to the contact until confirmation. - `phone` (string) - `firstName` (string) - `lastName` (string) - `email` (string) - `customFields` (array of object) - `id` (string) - Custom field ID - `label` (string) - Field label as shown on the form - `value` (string) - Submitted value - `user` (object) - `id` (string) - `test` (boolean) - Whether this is a test webhook (true when triggered via test endpoint) Example payload: ```json { "event": "form_submission", "timestamp": "2024-01-15T10:30:00Z", "form": { "id": "507f1f77bcf86cd799439021", "name": "Newsletter signup", "title": "Join our newsletter", "slug": "a1b2c3d4e5f6a7b8c9d0e1f2" }, "submission": { "id": "507f1f77bcf86cd799439022", "action": "created", "submittedAt": "2024-01-15T10:30:00Z", "ip": "203.0.113.10" }, "optIn": { "status": "PENDING", "confirmationSent": true, "confirmedAt": null }, "contact": { "id": "507f1f77bcf86cd799439012", "phone": "+4512345678", "optInStatus": "PENDING" }, "data": { "phone": "+4512345678", "firstName": "John", "lastName": "Doe", "email": "john@example.com", "customFields": [ { "id": "507f1f77bcf86cd799439033", "label": "Company", "value": "Acme Inc" } ] }, "user": { "id": "507f1f77bcf86cd799439013" } } ``` ## formOptInConfirmed Form opt-in confirmed callback Sent to the same form webhook when the visitor taps the confirmation link in the double opt-in message — the final opt-in. Fires exactly once per confirmation; repeat taps on the link send nothing. **Opt-in status:** `optIn.status` is always `CONFIRMED` for this event. At this point the contact has been activated, and (when `optIn.dataApplied` is `true`) the submitted data and group memberships have been written to the contact. **Signature verification (optional):** Every request includes an `X-RCS-Signature: t=,v1=` header. To verify, compute `HMAC_SHA256(secret, t + "." + rawBody)` and compare to the `v1=` portion. Reject if `|now - t| > 300` seconds. See the **Webhooks** section overview for a full Node.js example. Verification is optional — you can safely ignore the header if you don't need it. Payload (`application/json`): - `event` (string) - `timestamp` (string (date-time)) - `form` (object) - `id` (string) - Form ID - `name` (string) - Internal form name - `title` (string) - Public form title - `slug` (string) - Public form ID used in the form URL - `optIn` (object) - `status` (string, one of `CONFIRMED`) - Always CONFIRMED for this event - `confirmedAt` (string (date-time)) - When the visitor tapped the confirmation link - `submittedAt` (string (date-time)) - When the original form submission happened - `dataApplied` (boolean) - Whether the submitted data and group memberships were written to the contact ("Update contact" setting, or a new contact) - `contact` (object) - `id` (string) - `phone` (string) - `firstName` (string) - `lastName` (string) - `email` (string) - `optInStatus` (string, one of `CONFIRMED`) - `data` (object) - The originally submitted values, including custom fields with their labels - `phone` (string) - `firstName` (string) - `lastName` (string) - `email` (string) - `customFields` (array of object) - `id` (string) - Custom field ID - `label` (string) - Field label as shown on the form - `value` (string) - Submitted value - `user` (object) - `id` (string) Example payload: ```json { "event": "form_optin_confirmed", "timestamp": "2024-01-15T10:35:00Z", "form": { "id": "507f1f77bcf86cd799439021", "name": "Newsletter signup", "title": "Join our newsletter", "slug": "a1b2c3d4e5f6a7b8c9d0e1f2" }, "optIn": { "status": "CONFIRMED", "confirmedAt": "2024-01-15T10:35:00Z", "submittedAt": "2024-01-15T10:30:00Z", "dataApplied": true }, "contact": { "id": "507f1f77bcf86cd799439012", "phone": "+4512345678", "firstName": "John", "lastName": "Doe", "email": "john@example.com", "optInStatus": "CONFIRMED" }, "data": { "phone": "+4512345678", "firstName": "John", "lastName": "Doe", "email": "john@example.com", "customFields": [ { "id": "507f1f77bcf86cd799439033", "label": "Company", "value": "Acme Inc" } ] }, "user": { "id": "507f1f77bcf86cd799439013" } } ``` --- # Part 4 - Schemas 67 named schemas referenced above, expanded here. ## SendMessageRequest - `phoneNumber` (string, required) - Recipient phone number (E.164 format) - `messageType` (string, required, one of `text`, `textBasic`, `richCard`, `carousel`, `media`, `file`, `image`, `video`, `audio`) - Type of RCS message - `content` (MessageContent, required) - Message content varies by messageType: - **text**: `text` string + optional `suggestions` (max 11 chips) - **textBasic**: `text` string only (no suggestions, max 160 characters) - **richCard**: At least `title` or `description` required, optional `media` (omitted if no fileUrl), `cardActions` (max 4), optional `suggestions` (max 11) - **carousel**: `cardContents` array (max 10 cards, each requiring at least `title` or `description`, optional `media`) + optional `suggestions` - **media**: `url` + optional `caption` (sent as rich card if caption present), or `media` object + optional `suggestions` - **file**: `url` or `media` object with file URL (PDFs, documents, etc.) - **image**: `url` or `media` object with image URL + optional `caption` - **video**: `url` or `media` object with video URL + optional `caption` - **audio**: `url` or `media` object with audio URL **Merge fields:** text, card titles/descriptions and button/chip URLs support `{{merge_field}}` placeholders (contact/company fields, custom tags, and `{{unsubscribe_link}}`), replaced per recipient at send time. See "Merge Fields & Unsubscribe Link" in the API overview. - `url` (string (uri), maxLength 2000) - Direct URL to media file (for media/image/video/audio/file). Alternative to using the media object. - `caption` (string, maxLength 2000) - Caption text for media messages. When present, the message is sent as a rich card with the caption as description. - `text` (string, maxLength 3072) - Text content (for text messages) - `title` (string, maxLength 200) - Card title (for richCard). At least title or description is required. - `description` (string, maxLength 2000) - Card description (for richCard). At least title or description is required. - `cardOrientation` (string, one of `VERTICAL`, `HORIZONTAL`, default `"VERTICAL"`) - Card layout orientation (for richCard) - `imageAlignment` (string, one of `LEFT`, `RIGHT`, default `"RIGHT"`) - Image alignment for horizontal cards (for richCard) - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - Card action buttons (max 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `cardContents` (array of CardContent, maxItems 10) - Carousel cards (min 2, max 10) - `title` (string, maxLength 200) - Card title. At least title or description is required. - `description` (string, maxLength 2000) - Card description. At least title or description is required. - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestions` (array of Suggestion, maxItems 11) - Chip suggestions (max 11) - `reply` (object) - `text` (string, maxLength 25) - `postbackData` (string, maxLength 2048) - `webhookUrl` (string (uri)) - `action` (CardAction) - Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `sendernameId` (string, required) - MongoDB ObjectId of approved RCS sender - `contactId` (string) - Optional existing contact ID - `skipContactCreation` (boolean, default `false`) - Skip creating a contact record (not allowed for conversation-based senders) - `skipDisabled` (boolean, default `true`) - Skip sending to disabled contacts - `campaignName` (string, maxLength 100) - Campaign name for tracking - `scheduleAt` (string) - ISO 8601 date-time for scheduling - `timeZone` (string) - IANA timezone for scheduling - `smsFallback` (SmsFallback) - SMS fallback when RCS is unavailable - `message` (string, required, maxLength 1530) - SMS fallback text. Supports the same merge fields as the RCS body (contact, company and custom fields, `{{today}}`, `{{unsubscribe_link}}`), substituted per recipient when the fallback is sent. `{{unsubscribe_link}}` becomes a unique per-recipient opt-out URL when the recipient is a contact. - `sendernameId` (string, required) - SMS sender ID ## SendBatchRequest - `phoneNumbers` (array of string, required, maxItems 10000) - Array of recipient phone numbers - `messageType` (string, required, one of `text`, `textBasic`, `richCard`, `carousel`, `media`, `file`, `image`, `video`, `audio`) - `content` (MessageContent, required) - Message content varies by messageType: - **text**: `text` string + optional `suggestions` (max 11 chips) - **textBasic**: `text` string only (no suggestions, max 160 characters) - **richCard**: At least `title` or `description` required, optional `media` (omitted if no fileUrl), `cardActions` (max 4), optional `suggestions` (max 11) - **carousel**: `cardContents` array (max 10 cards, each requiring at least `title` or `description`, optional `media`) + optional `suggestions` - **media**: `url` + optional `caption` (sent as rich card if caption present), or `media` object + optional `suggestions` - **file**: `url` or `media` object with file URL (PDFs, documents, etc.) - **image**: `url` or `media` object with image URL + optional `caption` - **video**: `url` or `media` object with video URL + optional `caption` - **audio**: `url` or `media` object with audio URL **Merge fields:** text, card titles/descriptions and button/chip URLs support `{{merge_field}}` placeholders (contact/company fields, custom tags, and `{{unsubscribe_link}}`), replaced per recipient at send time. See "Merge Fields & Unsubscribe Link" in the API overview. - `url` (string (uri), maxLength 2000) - Direct URL to media file (for media/image/video/audio/file). Alternative to using the media object. - `caption` (string, maxLength 2000) - Caption text for media messages. When present, the message is sent as a rich card with the caption as description. - `text` (string, maxLength 3072) - Text content (for text messages) - `title` (string, maxLength 200) - Card title (for richCard). At least title or description is required. - `description` (string, maxLength 2000) - Card description (for richCard). At least title or description is required. - `cardOrientation` (string, one of `VERTICAL`, `HORIZONTAL`, default `"VERTICAL"`) - Card layout orientation (for richCard) - `imageAlignment` (string, one of `LEFT`, `RIGHT`, default `"RIGHT"`) - Image alignment for horizontal cards (for richCard) - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - Card action buttons (max 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `cardContents` (array of CardContent, maxItems 10) - Carousel cards (min 2, max 10) - `title` (string, maxLength 200) - Card title. At least title or description is required. - `description` (string, maxLength 2000) - Card description. At least title or description is required. - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestions` (array of Suggestion, maxItems 11) - Chip suggestions (max 11) - `reply` (object) - `text` (string, maxLength 25) - `postbackData` (string, maxLength 2048) - `webhookUrl` (string (uri)) - `action` (CardAction) - Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `sendernameId` (string, required) - `skipContactCreation` (boolean, default `false`) - `skipDisabled` (boolean, default `true`) - `campaignName` (string, maxLength 100) - `scheduleAt` (string) - `timeZone` (string) - `smsFallback` (SmsFallback) - SMS fallback when RCS is unavailable - `message` (string, required, maxLength 1530) - SMS fallback text. Supports the same merge fields as the RCS body (contact, company and custom fields, `{{today}}`, `{{unsubscribe_link}}`), substituted per recipient when the fallback is sent. `{{unsubscribe_link}}` becomes a unique per-recipient opt-out URL when the recipient is a contact. - `sendernameId` (string, required) - SMS sender ID - `batchSize` (integer, default `100`, minimum 1, maximum 1000) - Number of messages per batch - `delayMs` (integer, default `50`, minimum 0, maximum 10000) - Delay between batches in milliseconds ## SendSmsRequest - `phoneNumber` (string, required) - Recipient phone number (E.164 format) - `message` (string, required, maxLength 1530) - SMS message text (max 1530 characters / 10 segments). Supports the full merge field set: contact fields (`{{contact_first_name}}`, `{{contact_last_name}}`, `{{contact_phone}}`, `{{contact_email}}`), company fields (`{{company_name}}`, `{{company_phone}}`, `{{company_website}}`, ...), `{{today}}`, custom field tags (`{{your_tag}}`), and `{{unsubscribe_link}}` - all replaced before segments are calculated, so billing reflects the resolved length. Contact and custom fields resolve from the recipient's contact record (auto-created unless `skipContactCreation: true`); unknown or unresolvable placeholders are replaced with an empty string. `{{unsubscribe_link}}` is replaced with a unique per-recipient opt-out URL and requires a contact - it is left as-is when `skipContactCreation: true` is used for a number that is not already a contact. - `sendernameId` (string, required) - ID or name of an approved SMS sender - `campaignName` (string, maxLength 100) - Campaign name for tracking - `skipContactCreation` (boolean, default `false`) - Skip creating a contact record (message still logged with phone number) - `skipDisabled` (boolean, default `true`) - Skip sending to disabled contacts - `scheduleAt` (string) - ISO 8601 date-time for scheduling - `timeZone` (string) - IANA timezone for scheduling ## SendSmsResponse - `success` (boolean) - `messageId` (string) - Queue job ID (immediate) or message record ID (scheduled) - `scheduled` (boolean) - Whether the message was scheduled for later - `queued` (boolean) - Whether the message was queued for immediate sending - `jobId` (string) - Scheduled job ID (only when scheduled) - `smsSegments` (integer) - Number of SMS segments the message will use - `smsEncoding` (string, one of `GSM-7`, `Unicode`) - Character encoding used - `creditsEstimated` (integer) - Estimated credits (equals smsSegments, only for immediate sends) - `scheduleTime` (string (date-time)) - Scheduled send time in UTC (only when scheduled) - `localScheduleTime` (string) - Scheduled send time in local timezone (only when scheduled) - `effectiveTimezone` (string) - Timezone used for scheduling (only when scheduled) - `timezoneSource` (string, one of `request`, `user`, `default`) - Where the timezone was resolved from (only when scheduled) ## MessageContent Message content varies by messageType: - **text**: `text` string + optional `suggestions` (max 11 chips) - **textBasic**: `text` string only (no suggestions, max 160 characters) - **richCard**: At least `title` or `description` required, optional `media` (omitted if no fileUrl), `cardActions` (max 4), optional `suggestions` (max 11) - **carousel**: `cardContents` array (max 10 cards, each requiring at least `title` or `description`, optional `media`) + optional `suggestions` - **media**: `url` … - `url` (string (uri), maxLength 2000) - Direct URL to media file (for media/image/video/audio/file). Alternative to using the media object. - `caption` (string, maxLength 2000) - Caption text for media messages. When present, the message is sent as a rich card with the caption as description. - `text` (string, maxLength 3072) - Text content (for text messages) - `title` (string, maxLength 200) - Card title (for richCard). At least title or description is required. - `description` (string, maxLength 2000) - Card description (for richCard). At least title or description is required. - `cardOrientation` (string, one of `VERTICAL`, `HORIZONTAL`, default `"VERTICAL"`) - Card layout orientation (for richCard) - `imageAlignment` (string, one of `LEFT`, `RIGHT`, default `"RIGHT"`) - Image alignment for horizontal cards (for richCard) - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - Card action buttons (max 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `cardContents` (array of CardContent, maxItems 10) - Carousel cards (min 2, max 10) - `title` (string, maxLength 200) - Card title. At least title or description is required. - `description` (string, maxLength 2000) - Card description. At least title or description is required. - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestions` (array of Suggestion, maxItems 11) - Chip suggestions (max 11) - `reply` (object) - `text` (string, maxLength 25) - `postbackData` (string, maxLength 2048) - `webhookUrl` (string (uri)) - `action` (CardAction) - Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. ## Media - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source ## CardAction Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. ## CardContent Single card in a carousel (VERTICAL orientation only). At least title or description is required. Media is optional and omitted from payload if no fileUrl is set. - `title` (string, maxLength 200) - Card title. At least title or description is required. - `description` (string, maxLength 2000) - Card description. At least title or description is required. - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. ## Suggestion Chip suggestion (reply button or action). Action chips use the CardAction shape, so an openUrlInWebview chip supports openUrlInWebviewAction.viewMode (FULL, TALL, or HALF) exactly like an openUrlInWebview card action - viewMode is not card-only. Exceptions: postback and addToCalendar are card-only (use a reply chip with postbackData, and createCalendarEvent with createCalendarEventAction, respectively); the unsubscribe stop-keyword type is chip-only. - `reply` (object) - `text` (string, maxLength 25) - `postbackData` (string, maxLength 2048) - `webhookUrl` (string (uri)) - `action` (CardAction) - Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. ## SmsFallback SMS fallback when RCS is unavailable - `message` (string, required, maxLength 1530) - SMS fallback text. Supports the same merge fields as the RCS body (contact, company and custom fields, `{{today}}`, `{{unsubscribe_link}}`), substituted per recipient when the fallback is sent. `{{unsubscribe_link}}` becomes a unique per-recipient opt-out URL when the recipient is a contact. - `sendernameId` (string, required) - SMS sender ID ## SendMessageResponse - `success` (boolean) - `true` when the message was handed to the network (RCS) or queued for sending (SMS fallback). This is an acceptance result, not a delivery result: use status webhooks or `GET /api/messages/log` for the final delivery status. - `messageId` (string) - Network message ID for RCS sends. Null when the SMS fallback was used (the SMS is queued). - `messageRecordId` (string) - Internal message record ID. Use this to correlate with status webhook and button click webhook events and to look the message up in `GET /api/messages/log?messageId=`. When the SMS fallback was used this is the ID of the RCS record the fallback belongs to. - `method` (string, one of `RCS`, `SMS_FALLBACK`) - Channel actually used. `SMS_FALLBACK` when the SMS fallback was queued instead of RCS. - `scheduled` (boolean) - `queued` (boolean) - `true` when the message (SMS fallback) is queued and will be sent asynchronously. - `creditsUsed` (number) - Credits charged for an RCS send. For a queued SMS fallback this equals `estimatedCredits`; the actual charge happens when the SMS is sent. - `estimatedCredits` (number) - Credits charged (RCS) or expected to be charged (queued SMS fallback). `0` when nothing will be charged. - `fallbackUsed` (boolean) - `true` when the SMS fallback was sent instead of RCS. - `fallbackWillBeUsed` (boolean) - `fallbackReason` (string) - Why the fallback (or no fallback) was used, e.g. `COUNTRY_NOT_SUPPORTED`, `COUNTRY_BLOCKED`, `PROVIDER_ERROR`, `NO_FALLBACK_COUNTRY_NOT_SUPPORTED`, `NO_FALLBACK_COUNTRY_BLOCKED`, `NO_FALLBACK_PROVIDER_ERROR`, or `SMS_COUNTRY_NOT_SUPPORTED` when neither RCS nor SMS is available for the destination (then `success` is `false` and nothing is sent or charged). - `hasInteractiveButtons` (boolean) - `error` (string) - Error details when `success` is `false`. - `errorType` (string) - Error category when `success` is `false` (e.g. `INVALID_PHONE_NUMBER`, `VALIDATION_ERROR`). - `countrySupported` (boolean) - `false` when RCS is not available for the destination country (not in the price list, or disabled in your account's country settings). The message is then either sent as SMS fallback (`fallbackUsed: true`) or fails (`success: false`). - `countryError` (string) - Explanation when `countrySupported` is `false`. When SMS is not available for the destination either, the request fails synchronously (`success: false`, `fallbackReason: SMS_COUNTRY_NOT_SUPPORTED`) and this explains both. - `conversation` (ConversationInfo) - `conversationId` (string) - `chargedMessage` (boolean) - `freeMessage` (boolean) - `billingMode` (string, one of `per_message`, `auto`) - Billing mode used for this message (per_message senders always charge per message, auto uses conversation windows) - `status` (string, one of `NEW`, `ACTIVE`, `PER_MESSAGE`) - PER_MESSAGE when sender uses per-message billing (no conversation window applies) - `remainingTime` (string) - Remaining conversation window time. Null for per-message billing senders. - `messageCount` (object) - `total` (integer) - `outbound` (integer) - `inbound` (integer) ## BatchFiltering Contact filtering summary for a batch request - `totalOriginal` (integer) - `totalEnabled` (integer) - `totalDisabled` (integer) - `totalInvalid` (integer) - `totalTestPhones` (integer) - `skipDisabled` (boolean) - `disabledContacts` (array of string) - Disabled phone numbers (first 50) - `invalidPhoneNumbers` (array of string) - Invalid phone numbers (first 50) - `message` (string) - `testPhoneMessage` (string) ## SendBatchResponse Returned with HTTP 202 when the batch is queued for immediate background processing - `success` (boolean) - `scheduled` (boolean) - `queued` (boolean) - `totalQueued` (integer) - `totalPhoneNumbers` (integer) - `estimatedDurationSeconds` (number) - `estimatedCompletionTime` (string (date-time)) - `message` (string) - `firstJobId` (string) - First queued job ID (batch progress is tracked via /queue/status) - `totalJobs` (integer) - `hasInteractiveButtons` (boolean) - `warnings` (array of string) - `filtering` (BatchFiltering) - Contact filtering summary for a batch request - `totalOriginal` (integer) - `totalEnabled` (integer) - `totalDisabled` (integer) - `totalInvalid` (integer) - `totalTestPhones` (integer) - `skipDisabled` (boolean) - `disabledContacts` (array of string) - Disabled phone numbers (first 50) - `invalidPhoneNumbers` (array of string) - Invalid phone numbers (first 50) - `message` (string) - `testPhoneMessage` (string) - `tracking` (object) - `info` (string) - `statusEndpoint` (string) - `pollingRecommendation` (string) ## SendBatchScheduledResponse Returned with HTTP 200 when scheduleAt is provided. Use jobId with /batch/{jobId}/status and DELETE /batch/{jobId}. - `success` (boolean) - `scheduled` (boolean) - `jobId` (string) - `totalRecipients` (integer) - `scheduleTime` (string (date-time)) - `localScheduleTime` (string) - `effectiveTimezone` (string) - `timezoneSource` (string) - `smsFallbackEnabled` (boolean) - `hasInteractiveButtons` (boolean) - `warnings` (array of string) - `filtering` (BatchFiltering) - Contact filtering summary for a batch request - `totalOriginal` (integer) - `totalEnabled` (integer) - `totalDisabled` (integer) - `totalInvalid` (integer) - `totalTestPhones` (integer) - `skipDisabled` (boolean) - `disabledContacts` (array of string) - Disabled phone numbers (first 50) - `invalidPhoneNumbers` (array of string) - Invalid phone numbers (first 50) - `message` (string) - `testPhoneMessage` (string) ## ConversationInfo - `conversationId` (string) - `chargedMessage` (boolean) - `freeMessage` (boolean) - `billingMode` (string, one of `per_message`, `auto`) - Billing mode used for this message (per_message senders always charge per message, auto uses conversation windows) - `status` (string, one of `NEW`, `ACTIVE`, `PER_MESSAGE`) - PER_MESSAGE when sender uses per-message billing (no conversation window applies) - `remainingTime` (string) - Remaining conversation window time. Null for per-message billing senders. - `messageCount` (object) - `total` (integer) - `outbound` (integer) - `inbound` (integer) ## ConversationListItem - `conversationId` (string) - `phoneNumber` (string) - `contact` (object) - `_id` (string) - `name` (string) - `phone` (string) - `email` (string) - `status` (string, one of `ACTIVE`, `EXPIRED`) - ACTIVE while the 24-hour window is open, EXPIRED once it has passed - `isActive` (boolean) - `messageCount` (object) - `total` (integer) - `outbound` (integer) - `inbound` (integer) - `creditsCharged` (number) - `freeMessagesUsed` (integer) - `initiatedBy` (string, one of `BUSINESS`, `USER`) - Who opened the current window. USER means the contact messaged first. - `sessionStarted` (string (date-time)) - `sessionExpires` (string (date-time)) - `lastMessage` (string (date-time)) - `remainingTime` (integer) - Milliseconds left in the window. 0 when expired. ## ConversationStatus - `success` (boolean) - `phoneNumber` (string) - `hasActiveConversation` (boolean) - Whether there is an active 24-hour session window - `conversation` (object) - Conversation details (null when no active conversation) - `id` (string) - Conversation ID - `senderId` (string) - RCS sender ID used for this conversation - `conversationType` (string, one of `MARKETING`, `AUTHENTICATION`, `SERVICE`, `UTILITY`, `USER_INITIATED`) - `pricingModel` (string, one of `PER_MESSAGE`, `CONVERSATION_BASED`) - `startedAt` (string (date-time)) - `expiresAt` (string (date-time)) - `remainingTime` (string) - Human-readable remaining time (e.g. "23h 57m") - `messageCount` (integer) - Total messages in this conversation - `creditsCharged` (number) - Credits charged for this conversation - `isCharged` (boolean) - Whether conversation billing has been applied ## TimezonesResponse - `success` (boolean) - `user` (object) - `configuredTimezone` (string) - `currentLocalTime` (string) - `currentUtcTime` (string) - `timezoneOptions` (array of object) - `timezone` (string) - `localTime` (string) - `offset` (string) - `isCurrent` (boolean) ## ValidateScheduleResponse - `success` (boolean) - `valid` (boolean) - `input` (object) - `scheduleAt` (string) - `providedTimezone` (string) - `wasAbsoluteTime` (boolean) - `parsed` (object) - `scheduleTime` (string) - `localScheduleTime` (string) - `effectiveTimezone` (string) - `timezoneSource` (string) - `isInFuture` (boolean) ## ValidateMessageRequest - `messageType` (string) - `content` (MessageContent) - Message content varies by messageType: - **text**: `text` string + optional `suggestions` (max 11 chips) - **textBasic**: `text` string only (no suggestions, max 160 characters) - **richCard**: At least `title` or `description` required, optional `media` (omitted if no fileUrl), `cardActions` (max 4), optional `suggestions` (max 11) - **carousel**: `cardContents` array (max 10 cards, each requiring at least `title` or `description`, optional `media`) + optional `suggestions` - **media**: `url` + optional `caption` (sent as rich card if caption present), or `media` object + optional `suggestions` - **file**: `url` or `media` object with file URL (PDFs, documents, etc.) - **image**: `url` or `media` object with image URL + optional `caption` - **video**: `url` or `media` object with video URL + optional `caption` - **audio**: `url` or `media` object with audio URL **Merge fields:** text, card titles/descriptions and button/chip URLs support `{{merge_field}}` placeholders (contact/company fields, custom tags, and `{{unsubscribe_link}}`), replaced per recipient at send time. See "Merge Fields & Unsubscribe Link" in the API overview. - `url` (string (uri), maxLength 2000) - Direct URL to media file (for media/image/video/audio/file). Alternative to using the media object. - `caption` (string, maxLength 2000) - Caption text for media messages. When present, the message is sent as a rich card with the caption as description. - `text` (string, maxLength 3072) - Text content (for text messages) - `title` (string, maxLength 200) - Card title (for richCard). At least title or description is required. - `description` (string, maxLength 2000) - Card description (for richCard). At least title or description is required. - `cardOrientation` (string, one of `VERTICAL`, `HORIZONTAL`, default `"VERTICAL"`) - Card layout orientation (for richCard) - `imageAlignment` (string, one of `LEFT`, `RIGHT`, default `"RIGHT"`) - Image alignment for horizontal cards (for richCard) - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - Card action buttons (max 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `cardContents` (array of CardContent, maxItems 10) - Carousel cards (min 2, max 10) - `title` (string, maxLength 200) - Card title. At least title or description is required. - `description` (string, maxLength 2000) - Card description. At least title or description is required. - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestions` (array of Suggestion, maxItems 11) - Chip suggestions (max 11) - `reply` (object) - `text` (string, maxLength 25) - `postbackData` (string, maxLength 2048) - `webhookUrl` (string (uri)) - `action` (CardAction) - Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. ## ValidationResult - `valid` (boolean) - Overall result (message and webhook validation combined) - `messageValidation` (object) - `valid` (boolean) - `errors` (array of string) - `warnings` (array of string) - `webhookValidation` (object) - `valid` (boolean) - `errors` (array of string) - `hasInteractiveButtons` (boolean) - `suggestionLimits` (object) - `total` (integer) - `textMaxLength` (integer) ## SandboxRequest The body of `POST /send` (or `/send-batch`). Only `messageType` and `content` are used; every other field is accepted so you can post the exact body you will send later, and is reported back under `sandbox.ignored_fields`. - `messageType` (string, required, one of `text`, `textBasic`, `richCard`, `carousel`, `media`, `file`, `image`, `video`, `audio`) - `content` (MessageContent, required) - Message content varies by messageType: - **text**: `text` string + optional `suggestions` (max 11 chips) - **textBasic**: `text` string only (no suggestions, max 160 characters) - **richCard**: At least `title` or `description` required, optional `media` (omitted if no fileUrl), `cardActions` (max 4), optional `suggestions` (max 11) - **carousel**: `cardContents` array (max 10 cards, each requiring at least `title` or `description`, optional `media`) + optional `suggestions` - **media**: `url` + optional `caption` (sent as rich card if caption present), or `media` object + optional `suggestions` - **file**: `url` or `media` object with file URL (PDFs, documents, etc.) - **image**: `url` or `media` object with image URL + optional `caption` - **video**: `url` or `media` object with video URL + optional `caption` - **audio**: `url` or `media` object with audio URL **Merge fields:** text, card titles/descriptions and button/chip URLs support `{{merge_field}}` placeholders (contact/company fields, custom tags, and `{{unsubscribe_link}}`), replaced per recipient at send time. See "Merge Fields & Unsubscribe Link" in the API overview. - `url` (string (uri), maxLength 2000) - Direct URL to media file (for media/image/video/audio/file). Alternative to using the media object. - `caption` (string, maxLength 2000) - Caption text for media messages. When present, the message is sent as a rich card with the caption as description. - `text` (string, maxLength 3072) - Text content (for text messages) - `title` (string, maxLength 200) - Card title (for richCard). At least title or description is required. - `description` (string, maxLength 2000) - Card description (for richCard). At least title or description is required. - `cardOrientation` (string, one of `VERTICAL`, `HORIZONTAL`, default `"VERTICAL"`) - Card layout orientation (for richCard) - `imageAlignment` (string, one of `LEFT`, `RIGHT`, default `"RIGHT"`) - Image alignment for horizontal cards (for richCard) - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - Card action buttons (max 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `cardContents` (array of CardContent, maxItems 10) - Carousel cards (min 2, max 10) - `title` (string, maxLength 200) - Card title. At least title or description is required. - `description` (string, maxLength 2000) - Card description. At least title or description is required. - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestions` (array of Suggestion, maxItems 11) - Chip suggestions (max 11) - `reply` (object) - `text` (string, maxLength 25) - `postbackData` (string, maxLength 2048) - `webhookUrl` (string (uri)) - `action` (CardAction) - Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `smsFallback` (object) - Only `message` is used (shown under the phone); `sendernameId` is ignored. - `message` (string, maxLength 1530) - `sendernameId` (string) - `phoneNumber` (string) - Accepted and ignored - `phoneNumbers` (array of string) - Accepted and ignored - `sendernameId` (string) - Accepted and ignored - `contactId` (string) - Accepted and ignored - `campaignName` (string) - Accepted and ignored - `scheduleAt` (string) - Accepted and ignored - `timeZone` (string) - Accepted and ignored - `skipDisabled` (boolean) - Accepted and ignored - `skipContactCreation` (boolean) - Accepted and ignored - `batchSize` (integer) - Accepted and ignored - `delayMs` (integer) - Accepted and ignored ## SandboxMediaReport One media slot found in the content and what the preview page renders for it. - `path` (string) - `original` (string) - The URL you sent (only in POST responses) - `allow_listed` (boolean) - True when the URL is one of the sample media files and is rendered as is - `rendered` (string) - The URL shown on the preview page; `null` when a thumbnail was removed - `original_host` (string) - Host of a foreign URL (only in GET /sandbox/preview/{slug}) ## SandboxInfo - `nothing_sent` (boolean) - `ignored_fields` (array of string) - `unknown_fields` (array of string) - Top-level fields that are neither sandbox nor send fields (usually typos) - `media` (array of SandboxMediaReport) - `path` (string) - `original` (string) - The URL you sent (only in POST responses) - `allow_listed` (boolean) - True when the URL is one of the sample media files and is rendered as is - `rendered` (string) - The URL shown on the preview page; `null` when a thumbnail was removed - `original_host` (string) - Host of a foreign URL (only in GET /sandbox/preview/{slug}) - `next_step` (string) - `samples_url` (string (uri)) - `docs_url` (string (uri)) - `signup_url` (string (uri)) - `note` (string) ## SandboxValidateResponse - `success` (boolean) - `valid` (boolean) - `errors` (array of string) - `warnings` (array of string) - `messageType` (string) - `normalizedContent` (MessageContent) - Message content varies by messageType: - **text**: `text` string + optional `suggestions` (max 11 chips) - **textBasic**: `text` string only (no suggestions, max 160 characters) - **richCard**: At least `title` or `description` required, optional `media` (omitted if no fileUrl), `cardActions` (max 4), optional `suggestions` (max 11) - **carousel**: `cardContents` array (max 10 cards, each requiring at least `title` or `description`, optional `media`) + optional `suggestions` - **media**: `url` + optional `caption` (sent as rich card if caption present), or `media` object + optional `suggestions` - **file**: `url` or `media` object with file URL (PDFs, documents, etc.) - **image**: `url` or `media` object with image URL + optional `caption` - **video**: `url` or `media` object with video URL + optional `caption` - **audio**: `url` or `media` object with audio URL **Merge fields:** text, card titles/descriptions and button/chip URLs support `{{merge_field}}` placeholders (contact/company fields, custom tags, and `{{unsubscribe_link}}`), replaced per recipient at send time. See "Merge Fields & Unsubscribe Link" in the API overview. - `url` (string (uri), maxLength 2000) - Direct URL to media file (for media/image/video/audio/file). Alternative to using the media object. - `caption` (string, maxLength 2000) - Caption text for media messages. When present, the message is sent as a rich card with the caption as description. - `text` (string, maxLength 3072) - Text content (for text messages) - `title` (string, maxLength 200) - Card title (for richCard). At least title or description is required. - `description` (string, maxLength 2000) - Card description (for richCard). At least title or description is required. - `cardOrientation` (string, one of `VERTICAL`, `HORIZONTAL`, default `"VERTICAL"`) - Card layout orientation (for richCard) - `imageAlignment` (string, one of `LEFT`, `RIGHT`, default `"RIGHT"`) - Image alignment for horizontal cards (for richCard) - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - Card action buttons (max 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `cardContents` (array of CardContent, maxItems 10) - Carousel cards (min 2, max 10) - `title` (string, maxLength 200) - Card title. At least title or description is required. - `description` (string, maxLength 2000) - Card description. At least title or description is required. - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestions` (array of Suggestion, maxItems 11) - Chip suggestions (max 11) - `reply` (object) - `text` (string, maxLength 25) - `postbackData` (string, maxLength 2048) - `webhookUrl` (string (uri)) - `action` (CardAction) - Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestionLimits` (object) - `total` (integer) - `textMaxLength` (integer) - `limits` (object) - Every content limit the validator enforces (same as `GET /limits`) - `hasChipSuggestions` (boolean) - `hasCardActions` (boolean) - `buttonAnalysis` (object) - `cardActions` (array of object) - `chipSuggestions` (array of object) - `recommendations` (array of string) - `sandbox` (SandboxInfo) - `nothing_sent` (boolean) - `ignored_fields` (array of string) - `unknown_fields` (array of string) - Top-level fields that are neither sandbox nor send fields (usually typos) - `media` (array of SandboxMediaReport) - `path` (string) - `original` (string) - The URL you sent (only in POST responses) - `allow_listed` (boolean) - True when the URL is one of the sample media files and is rendered as is - `rendered` (string) - The URL shown on the preview page; `null` when a thumbnail was removed - `original_host` (string) - Host of a foreign URL (only in GET /sandbox/preview/{slug}) - `next_step` (string) - `samples_url` (string (uri)) - `docs_url` (string (uri)) - `signup_url` (string (uri)) - `note` (string) ## SandboxPreviewResponse - `success` (boolean) - `valid` (boolean) - `errors` (array of string) - `warnings` (array of string) - `messageType` (string) - `normalizedContent` (MessageContent) - Message content varies by messageType: - **text**: `text` string + optional `suggestions` (max 11 chips) - **textBasic**: `text` string only (no suggestions, max 160 characters) - **richCard**: At least `title` or `description` required, optional `media` (omitted if no fileUrl), `cardActions` (max 4), optional `suggestions` (max 11) - **carousel**: `cardContents` array (max 10 cards, each requiring at least `title` or `description`, optional `media`) + optional `suggestions` - **media**: `url` + optional `caption` (sent as rich card if caption present), or `media` object + optional `suggestions` - **file**: `url` or `media` object with file URL (PDFs, documents, etc.) - **image**: `url` or `media` object with image URL + optional `caption` - **video**: `url` or `media` object with video URL + optional `caption` - **audio**: `url` or `media` object with audio URL **Merge fields:** text, card titles/descriptions and button/chip URLs support `{{merge_field}}` placeholders (contact/company fields, custom tags, and `{{unsubscribe_link}}`), replaced per recipient at send time. See "Merge Fields & Unsubscribe Link" in the API overview. - `url` (string (uri), maxLength 2000) - Direct URL to media file (for media/image/video/audio/file). Alternative to using the media object. - `caption` (string, maxLength 2000) - Caption text for media messages. When present, the message is sent as a rich card with the caption as description. - `text` (string, maxLength 3072) - Text content (for text messages) - `title` (string, maxLength 200) - Card title (for richCard). At least title or description is required. - `description` (string, maxLength 2000) - Card description (for richCard). At least title or description is required. - `cardOrientation` (string, one of `VERTICAL`, `HORIZONTAL`, default `"VERTICAL"`) - Card layout orientation (for richCard) - `imageAlignment` (string, one of `LEFT`, `RIGHT`, default `"RIGHT"`) - Image alignment for horizontal cards (for richCard) - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - Card action buttons (max 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `cardContents` (array of CardContent, maxItems 10) - Carousel cards (min 2, max 10) - `title` (string, maxLength 200) - Card title. At least title or description is required. - `description` (string, maxLength 2000) - Card description. At least title or description is required. - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestions` (array of Suggestion, maxItems 11) - Chip suggestions (max 11) - `reply` (object) - `text` (string, maxLength 25) - `postbackData` (string, maxLength 2048) - `webhookUrl` (string (uri)) - `action` (CardAction) - Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestionLimits` (object) - `total` (integer) - `textMaxLength` (integer) - `limits` (object) - Every content limit the validator enforces (same as `GET /limits`) - `hasChipSuggestions` (boolean) - `hasCardActions` (boolean) - `buttonAnalysis` (object) - `cardActions` (array of object) - `chipSuggestions` (array of object) - `recommendations` (array of string) - `sandbox` (SandboxInfo) - `nothing_sent` (boolean) - `ignored_fields` (array of string) - `unknown_fields` (array of string) - Top-level fields that are neither sandbox nor send fields (usually typos) - `media` (array of SandboxMediaReport) - `path` (string) - `original` (string) - The URL you sent (only in POST responses) - `allow_listed` (boolean) - True when the URL is one of the sample media files and is rendered as is - `rendered` (string) - The URL shown on the preview page; `null` when a thumbnail was removed - `original_host` (string) - Host of a foreign URL (only in GET /sandbox/preview/{slug}) - `next_step` (string) - `samples_url` (string (uri)) - `docs_url` (string (uri)) - `signup_url` (string (uri)) - `note` (string) - `preview` (object) - `preview_id` (string) - Prefixed `sbx_`; cannot be used to send - `share_url` (string (uri)) - `expires_at` (string (date-time)) - `summary` (string) - `deduplicated` (boolean) - True when identical input already had a preview (its expiry is kept) - `sendable` (boolean) ## SandboxValidationFailedResponse Same body as a failed `POST /send`, plus the `sandbox` block. - `success` (boolean) - `message` (string) - `errors` (array of string) - `warnings` (array of string) - `buttonAnalysis` (object) - `sandbox` (SandboxInfo) - `nothing_sent` (boolean) - `ignored_fields` (array of string) - `unknown_fields` (array of string) - Top-level fields that are neither sandbox nor send fields (usually typos) - `media` (array of SandboxMediaReport) - `path` (string) - `original` (string) - The URL you sent (only in POST responses) - `allow_listed` (boolean) - True when the URL is one of the sample media files and is rendered as is - `rendered` (string) - The URL shown on the preview page; `null` when a thumbnail was removed - `original_host` (string) - Host of a foreign URL (only in GET /sandbox/preview/{slug}) - `next_step` (string) - `samples_url` (string (uri)) - `docs_url` (string (uri)) - `signup_url` (string (uri)) - `note` (string) ## SandboxRateLimited - `success` (boolean) - `code` (string) - `message` (string) - `retry_after_seconds` (integer) ## SandboxSamplesResponse - `success` (boolean) - `base_url` (string (uri)) - `placeholder_url` (string (uri)) - What non-allow-listed images are replaced with on the preview page - `samples` (array of object) - `id` (string) - `url` (string (uri)) - `contentType` (string) - `kind` (string, one of `image`, `video`, `file`) - `label` (string) - `usage` (object) - Where to put a sample URL for each message type - `policy` (string) ## SandboxSharedPreview - `preview_id` (string) - `sandbox` (boolean) - `message_type` (string) - `content` (MessageContent) - Message content varies by messageType: - **text**: `text` string + optional `suggestions` (max 11 chips) - **textBasic**: `text` string only (no suggestions, max 160 characters) - **richCard**: At least `title` or `description` required, optional `media` (omitted if no fileUrl), `cardActions` (max 4), optional `suggestions` (max 11) - **carousel**: `cardContents` array (max 10 cards, each requiring at least `title` or `description`, optional `media`) + optional `suggestions` - **media**: `url` + optional `caption` (sent as rich card if caption present), or `media` object + optional `suggestions` - **file**: `url` or `media` object with file URL (PDFs, documents, etc.) - **image**: `url` or `media` object with image URL + optional `caption` - **video**: `url` or `media` object with video URL + optional `caption` - **audio**: `url` or `media` object with audio URL **Merge fields:** text, card titles/descriptions and button/chip URLs support `{{merge_field}}` placeholders (contact/company fields, custom tags, and `{{unsubscribe_link}}`), replaced per recipient at send time. See "Merge Fields & Unsubscribe Link" in the API overview. - `url` (string (uri), maxLength 2000) - Direct URL to media file (for media/image/video/audio/file). Alternative to using the media object. - `caption` (string, maxLength 2000) - Caption text for media messages. When present, the message is sent as a rich card with the caption as description. - `text` (string, maxLength 3072) - Text content (for text messages) - `title` (string, maxLength 200) - Card title (for richCard). At least title or description is required. - `description` (string, maxLength 2000) - Card description (for richCard). At least title or description is required. - `cardOrientation` (string, one of `VERTICAL`, `HORIZONTAL`, default `"VERTICAL"`) - Card layout orientation (for richCard) - `imageAlignment` (string, one of `LEFT`, `RIGHT`, default `"RIGHT"`) - Image alignment for horizontal cards (for richCard) - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - Card action buttons (max 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `latitude` (number) - `longitude` (number) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `cardContents` (array of CardContent, maxItems 10) - Carousel cards (min 2, max 10) - `title` (string, maxLength 200) - Card title. At least title or description is required. - `description` (string, maxLength 2000) - Card description. At least title or description is required. - `media` (Media) - `height` (string, one of `SHORT`, `MEDIUM`, `TALL`) - Media height - `contentInfo` (object) - `fileUrl` (string (uri), maxLength 2000) - URL to media file (HTTPS required, max 2000 chars) - `contentType` (string, one of `image/jpeg`, `image/jpg`, `image/png`, `image/gif`, `video/h263`, `video/mp4`, `video/mpeg`, `video/webm`) - MIME type of the media file - `thumbnailUrl` (string (uri)) - Placeholder image URL shown while media loads - `forceRefresh` (boolean, default `false`) - Whether to force refresh the media from source - `cardActions` (array of CardAction, maxItems 4) - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `suggestions` (array of Suggestion, maxItems 11) - Chip suggestions (max 11) - `reply` (object) - `text` (string, maxLength 25) - `postbackData` (string, maxLength 2048) - `webhookUrl` (string (uri)) - `action` (CardAction) - Button/chip action. This schema is shared between card buttons (cardActions) and action chips (suggestions), but not every type is valid on both surfaces. Card-only types: postback (on chips, use a reply with postbackData instead) and addToCalendar (with calendarAction). Chip-only types: createCalendarEvent (with createCalendarEventAction) and unsubscribe. All other types are valid on both. - `text` (string, required, maxLength 25) - Button text - `type` (string, required, one of `postback`, `openUrl`, `openUrlInWebview`, `dial`, `addToCalendar`, `createCalendarEvent`, `viewLocation`, `shareLocation`, `unsubscribeLink`, `unsubscribe`) - postback and addToCalendar are valid on card buttons only; createCalendarEvent and unsubscribe are valid on chips only. All other types are valid on both. - `postbackData` (string, maxLength 2048) - Data sent to webhook on click - `webhookUrl` (string (uri)) - Webhook URL for postback actions - `openUrlAction` (object) - `url` (string (uri)) - `openUrlInWebviewAction` (object) - Opens URL in messaging app webview (not external browser) - `url` (string (uri)) - URL to open in the webview - `description` (string) - Optional accessibility description - `viewMode` (string, one of `FULL`, `TALL`, `HALF`, default `"FULL"`) - Size of the webview window - `dialAction` (object) - `phoneNumber` (string) - `calendarAction` (object) - Calendar action for card buttons (type: addToCalendar). Card-only - chips use createCalendarEventAction with type createCalendarEvent instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `createCalendarEventAction` (object) - Calendar action for chips (type: createCalendarEvent). Chip-only - card buttons use calendarAction with type addToCalendar instead. - `startTime` (string (date-time)) - `endTime` (string (date-time)) - `title` (string) - `description` (string) - `viewLocationAction` (object) - `latLong` (object) - `label` (string) - `shareLocationAction` (object) - Prompts user to share their current location (no properties required) - `unsubscribeAction` (object) - Unsubscribe action — sends a stop keyword as a reply to trigger the unsubscribe flow. Chip-only (not available as a card action). The stopKeyword is auto-corrected at preview time to match the active trigger configuration for the sendername. - `stopKeyword` (string, default `"stop"`) - The keyword sent as a reply when the recipient taps the chip. Auto-corrected by the server to match the active trigger's configured keyword. - `sms_fallback` (object) - `message` (string) - `summary` (string) - `warnings` (array of string) - `media` (array of SandboxMediaReport) - `path` (string) - `original` (string) - The URL you sent (only in POST responses) - `allow_listed` (boolean) - True when the URL is one of the sample media files and is rendered as is - `rendered` (string) - The URL shown on the preview page; `null` when a thumbnail was removed - `original_host` (string) - Host of a foreign URL (only in GET /sandbox/preview/{slug}) - `created_at` (string (date-time)) - `expires_at` (string (date-time)) - `expires_in_hours` (integer) - `agent` (object) - `name` (string) ## ButtonGuidelines - `maxCardActions` (integer) - `maxChipSuggestions` (integer) - `maxButtonTextLength` (integer) - `maxTextLength` (integer) - `maxPostbackDataLength` (integer) - `maxPayloadSizeBytes` (integer) - Maximum JSON payload size (250 KB) - `maxMediaUrlLength` (integer) - `maxMediaFileSizeBytes` (integer) - Maximum media file size (100 MB) - `supportedActionTypes` (array of string) ## BatchJobStatus - `jobId` (string) - `status` (string, one of `pending`, `processing`, `completed`, `failed`, `cancelled`) - `progress` (object) - `total` (integer) - `processed` (integer) - `successful` (integer) - `failed` (integer) - `createdAt` (string (date-time)) - `completedAt` (string (date-time)) ## WebhookPayload Payload sent to your webhook URL when a button is clicked - `event` (string) - `timestamp` (string (date-time)) - `interaction` (object) - `id` (string) - Unique interaction ID - `type` (string) - `buttonText` (string) - `postbackData` (string) - `clickedAt` (string (date-time)) - When the button was clicked - `message` (object) - `id` (string) - Message record ID. Matches `messageRecordId` from the /send response. - `campaignName` (string) - Campaign name from the original message, if set - `sentAt` (string (date-time)) - When the message was originally sent - `rcsData` (object) - `messageType` (string) - `brandId` (string) - `contact` (object) - `id` (string) - `phone` (string) - `name` (string) - `user` (object) - `id` (string) - `businessName` (string) - `metadata` (object) - `conversationId` (string) - `platform` (string) ## StatusWebhookPayload Payload sent to your status webhook URL on delivery status changes - `event` (string, one of `rcs_status`, `sms_status`) - Event type (rcs_status for RCS messages, sms_status for SMS Fallback messages) - `messageId` (string) - Message record ID. Matches the `messageRecordId` from the /send response. - `status` (string, one of `sent`, `delivered`, `read`, `failed`) - Current delivery status - `phoneNumber` (string) - Recipient phone number - `contactId` (string) - Contact ID if the recipient is a known contact. Null when skipContactCreation was used and no contact exists. - `campaignName` (string) - Campaign name from the original message, if set - `timestamp` (string (date-time)) - `error` (string) - Error message when status is failed - `test` (boolean) - Whether this is a test webhook (true when triggered via test endpoint) ## RcsIncomingWebhookPayload Payload sent to your incoming RCS webhook URL when a contact messages one of your RCS sender names - `event` (string) - `timestamp` (string (date-time)) - `rcs` (object) - `id` (string) - Message record ID of the inbound message - `from` (string) - Sender phone number (the contact) - `to` (string) - Your RCS agent ID that received the message - `message` (string) - Text content, or a short description for media/location messages - `messageType` (string, one of `text`, `image`, `video`, `audio`, `file`, `vcard`, `location`, `button`) - What kind of message arrived - `isButtonReply` (boolean) - True when the message is a tap on a suggested reply/button - `buttonText` (string) - Button label, only present when isButtonReply is true - `postbackData` (string) - Button postback data, only present when isButtonReply is true - `media` (object) - Present for image/video/audio/file/vcard messages - `type` (string) - Media kind (matches messageType) - `name` (string) - Original file name, when available - `url` (string) - Relative SendRCS API path to download the media (requires an authenticated session) - `expiresAt` (string (date-time)) - When the media stops being available (48 hours after receipt) - `location` (object) - Present for shared locations - `latitude` (number) - `longitude` (number) - `label` (string) - `receivedAt` (string (date-time)) - `contact` (object) - `id` (string) - `name` (string) - `phone` (string) - `email` (string) - `sendername` (object) - `id` (string) - Sender name ID - `name` (string) - Sender name display name - `agentId` (string) - RCS agent ID of the sender name - `user` (object) - `id` (string) - `email` (string) - `metadata` (object) - `messageType` (string) - `direction` (string) - `platform` (string) - `provider` (string) - `conversationId` (string) - RCS conversation this message belongs to ## SmsIncomingWebhookPayload Payload sent to your incoming SMS webhook URL when someone texts one of your virtual numbers - `event` (string) - `timestamp` (string (date-time)) - `sms` (object) - `id` (string) - Message record ID of the inbound message - `from` (string) - Sender phone number (the contact) - `to` (string) - Your virtual number that received the message - `message` (string) - SMS text content - `encoding` (string) - SMS encoding (GSM-7 or UCS-2) - `segments` (integer) - Number of SMS segments - `receivedAt` (string (date-time)) - `contact` (object) - `id` (string) - `name` (string) - `phone` (string) - `email` (string) - `virtualNumber` (object) - `id` (string) - Virtual number ID - `phoneNumber` (string) - The virtual number in E.164 format - `subscriptionId` (string) - Your subscription ID for this virtual number - `user` (object) - `id` (string) - `email` (string) - `metadata` (object) - `messageType` (string) - `direction` (string) - `platform` (string) ## FormSubmissionWebhookPayload Payload sent to your form webhook URL when a visitor submits the form (before opt-in confirmation) - `event` (string) - `timestamp` (string (date-time)) - `form` (object) - `id` (string) - Form ID - `name` (string) - Internal form name - `title` (string) - Public form title - `slug` (string) - Public form ID used in the form URL - `submission` (object) - `id` (string) - Submission record ID - `action` (string, one of `created`, `updated`) - Whether a new contact was created or the phone number matched an existing contact - `submittedAt` (string (date-time)) - `ip` (string) - Visitor IP address - `optIn` (object) - `status` (string, one of `PENDING`) - Always PENDING at submit time — the contact is not activated until the visitor confirms - `confirmationSent` (boolean) - False when the form's "submit once" throttle suppressed a repeat opt-in message - `confirmedAt` (string (date-time)) - Always null for this event - `contact` (object) - `id` (string) - `phone` (string) - `optInStatus` (string, one of `PENDING`, `CONFIRMED`) - The contact's current stored opt-in status (CONFIRMED when an already-confirmed contact re-submits) - `data` (object) - Exactly what the visitor submitted. For existing contacts these values are not written to the contact until confirmation. - `phone` (string) - `firstName` (string) - `lastName` (string) - `email` (string) - `customFields` (array of object) - `id` (string) - Custom field ID - `label` (string) - Field label as shown on the form - `value` (string) - Submitted value - `user` (object) - `id` (string) - `test` (boolean) - Whether this is a test webhook (true when triggered via test endpoint) ## FormOptInConfirmedWebhookPayload Payload sent to your form webhook URL when the visitor confirms the double opt-in (final opt-in) - `event` (string) - `timestamp` (string (date-time)) - `form` (object) - `id` (string) - Form ID - `name` (string) - Internal form name - `title` (string) - Public form title - `slug` (string) - Public form ID used in the form URL - `optIn` (object) - `status` (string, one of `CONFIRMED`) - Always CONFIRMED for this event - `confirmedAt` (string (date-time)) - When the visitor tapped the confirmation link - `submittedAt` (string (date-time)) - When the original form submission happened - `dataApplied` (boolean) - Whether the submitted data and group memberships were written to the contact ("Update contact" setting, or a new contact) - `contact` (object) - `id` (string) - `phone` (string) - `firstName` (string) - `lastName` (string) - `email` (string) - `optInStatus` (string, one of `CONFIRMED`) - `data` (object) - The originally submitted values, including custom fields with their labels - `phone` (string) - `firstName` (string) - `lastName` (string) - `email` (string) - `customFields` (array of object) - `id` (string) - Custom field ID - `label` (string) - Field label as shown on the form - `value` (string) - Submitted value - `user` (object) - `id` (string) ## MessageLogResponse - `messages` (array of MessageLogEntry) - `id` (string) - Message record ID - `type` (string, one of `SMS`, `RCS`, `MMS`) - Message type - `status` (string, one of `SCHEDULED`, `SENDING`, `SENT`, `DELIVERED`, `READ`, `RECEIVED`, `FAILED`, `BLOCKED`, `REJECTED`) - Current delivery status - `direction` (string, one of `OUTBOUND`, `INBOUND`) - Message direction - `source` (string, one of `EVENT_REMINDER`, `MESSAGE_FLOW`, `DIRECT_MESSAGE`, `API_MESSAGE`, `API_SMS`, `RCS_INBOX_REPLY`, `SMS_INBOX_REPLY`) - How the message was created (API_SMS for messages sent via /send-sms) - `phoneNumber` (string) - Recipient phone number - `content` (string) - Message text content - `campaignName` (string) - Campaign name if set - `sendernameId` (string) - ID of the sender name used for this message - `smsFallback` (object) - SMS fallback details if fallback was used - `creditsCost` (number) - Credits charged for this message - `createdAt` (string (date-time)) - When the message record was created - `sentAt` (string (date-time)) - When the message was sent - `deliveredAt` (string (date-time)) - When the message was delivered - `readAt` (string (date-time)) - When the message was read - `scheduledAt` (string (date-time)) - Scheduled send time (for scheduled messages) - `failedAt` (string (date-time)) - When the message failed - `error` (string) - Failure reason when `status` is FAILED, BLOCKED or REJECTED (for example "Country not supported", or a delivery failure reported by the recipient's network). Null for messages that did not fail. The same text is sent in the `error` field of status webhooks. - `pagination` (object) - `nextCursor` (string) - Base64 cursor for the next page. Null when there are no more results. - `hasNext` (boolean) - Whether more results are available - `limit` (integer) - The limit that was applied - `count` (integer) - Number of messages in this response ## MessageLogEntry - `id` (string) - Message record ID - `type` (string, one of `SMS`, `RCS`, `MMS`) - Message type - `status` (string, one of `SCHEDULED`, `SENDING`, `SENT`, `DELIVERED`, `READ`, `RECEIVED`, `FAILED`, `BLOCKED`, `REJECTED`) - Current delivery status - `direction` (string, one of `OUTBOUND`, `INBOUND`) - Message direction - `source` (string, one of `EVENT_REMINDER`, `MESSAGE_FLOW`, `DIRECT_MESSAGE`, `API_MESSAGE`, `API_SMS`, `RCS_INBOX_REPLY`, `SMS_INBOX_REPLY`) - How the message was created (API_SMS for messages sent via /send-sms) - `phoneNumber` (string) - Recipient phone number - `content` (string) - Message text content - `campaignName` (string) - Campaign name if set - `sendernameId` (string) - ID of the sender name used for this message - `smsFallback` (object) - SMS fallback details if fallback was used - `creditsCost` (number) - Credits charged for this message - `createdAt` (string (date-time)) - When the message record was created - `sentAt` (string (date-time)) - When the message was sent - `deliveredAt` (string (date-time)) - When the message was delivered - `readAt` (string (date-time)) - When the message was read - `scheduledAt` (string (date-time)) - Scheduled send time (for scheduled messages) - `failedAt` (string (date-time)) - When the message failed - `error` (string) - Failure reason when `status` is FAILED, BLOCKED or REJECTED (for example "Country not supported", or a delivery failure reported by the recipient's network). Null for messages that did not fail. The same text is sent in the `error` field of status webhooks. ## MessageContentResponse - `message` (MessageContentDetail) - Full reusable content of a message. The shape of `content` depends on `type`: a structured object for RCS, a plain string for SMS/MMS. - `id` (string) - Message record ID - `type` (string, one of `SMS`, `RCS`, `MMS`) - Message type - `status` (string, one of `SCHEDULED`, `SENDING`, `SENT`, `DELIVERED`, `READ`, `RECEIVED`, `FAILED`, `BLOCKED`, `REJECTED`) - Current delivery status - `direction` (string, one of `OUTBOUND`, `INBOUND`) - Message direction - `source` (string, one of `EVENT_REMINDER`, `MESSAGE_FLOW`, `DIRECT_MESSAGE`, `API_MESSAGE`, `API_SMS`, `RCS_INBOX_REPLY`, `SMS_INBOX_REPLY`) - How the message was created - `phoneNumber` (string) - Recipient phone number - `messageType` (string, one of `text`, `textBasic`, `richCard`, `carousel`, `media`, `file`, `image`, `video`, `audio`) - RCS message type. Null for SMS/MMS. Pass this to the RCS preview/send endpoints to recreate the message. - `content` (MessageContent or string) - Reusable message content. For RCS this is the structured content object (matching the RCS send `content` field). For SMS/MMS this is the message text string. - `text` (string) - Flat text representation — the SMS body, or the RCS fallback/display text. - `error` (string) - Failure reason when the message failed (same text as in the message log and status webhooks). Null otherwise. - `smsFallback` (object) - SMS fallback configuration set on the original RCS message (null if none). RCS only. - `message` (string) - SMS fallback message text - `sendernameId` (string) - Sender name used for the fallback SMS - `smsSegments` (integer) - SMS segment count. SMS/MMS only. - `smsEncoding` (string, one of `GSM-7`, `Unicode`) - SMS encoding. SMS/MMS only. - `campaignName` (string) - Campaign name if set - `sendernameId` (string) - ID of the sender name used for this message - `createdAt` (string (date-time)) - When the message record was created - `sentAt` (string (date-time)) - When the message was sent ## MessageContentDetail Full reusable content of a message. The shape of `content` depends on `type`: a structured object for RCS, a plain string for SMS/MMS. - `id` (string) - Message record ID - `type` (string, one of `SMS`, `RCS`, `MMS`) - Message type - `status` (string, one of `SCHEDULED`, `SENDING`, `SENT`, `DELIVERED`, `READ`, `RECEIVED`, `FAILED`, `BLOCKED`, `REJECTED`) - Current delivery status - `direction` (string, one of `OUTBOUND`, `INBOUND`) - Message direction - `source` (string, one of `EVENT_REMINDER`, `MESSAGE_FLOW`, `DIRECT_MESSAGE`, `API_MESSAGE`, `API_SMS`, `RCS_INBOX_REPLY`, `SMS_INBOX_REPLY`) - How the message was created - `phoneNumber` (string) - Recipient phone number - `messageType` (string, one of `text`, `textBasic`, `richCard`, `carousel`, `media`, `file`, `image`, `video`, `audio`) - RCS message type. Null for SMS/MMS. Pass this to the RCS preview/send endpoints to recreate the message. - `content` (MessageContent or string) - Reusable message content. For RCS this is the structured content object (matching the RCS send `content` field). For SMS/MMS this is the message text string. - `text` (string) - Flat text representation — the SMS body, or the RCS fallback/display text. - `error` (string) - Failure reason when the message failed (same text as in the message log and status webhooks). Null otherwise. - `smsFallback` (object) - SMS fallback configuration set on the original RCS message (null if none). RCS only. - `message` (string) - SMS fallback message text - `sendernameId` (string) - Sender name used for the fallback SMS - `smsSegments` (integer) - SMS segment count. SMS/MMS only. - `smsEncoding` (string, one of `GSM-7`, `Unicode`) - SMS encoding. SMS/MMS only. - `campaignName` (string) - Campaign name if set - `sendernameId` (string) - ID of the sender name used for this message - `createdAt` (string (date-time)) - When the message record was created - `sentAt` (string (date-time)) - When the message was sent ## Sendername - `id` (string) - Use this as sendernameId when sending - `name` (string) - `type` (string, one of `SMS`, `RCS`) - `status` (string, one of `DRAFT`, `PENDING`, `APPROVED`, `REJECTED`) - `isDefault` (boolean) - `isTest` (boolean) - `supportsConversationBilling` (boolean) - `rejectionReason` (string) - Present only when status is REJECTED - `testPhones` (array of string) - Present only for approved test senders ## ErrorResponse - `message` (string) - `error` (string) - `errors` (array of object) - `msg` (string) - `param` (string) - `location` (string) ## Contact - `id` (string) - Contact ID - `firstName` (string) - First name (max 100 characters) - `lastName` (string) - Last name (max 100 characters) - `phone` (string) - Phone number with country code, stored as digits only (no `+`/`00` prefix). Unique per account. - `email` (string) - Email address - `disabled` (boolean) - Whether the contact is soft-deleted (disabled contacts cannot receive messages) - `groups` (array of object) - Groups the contact belongs to - `id` (string) - `name` (string) - `createdAt` (string (date-time)) - `customFields` (array of ContactCustomFieldValue) - Only present when `?include=customFields` is requested - `fieldId` (string) - Custom field definition ID - `tag` (string) - Custom field tag (for use in templates as `{{tag}}`) - `name` (string) - Custom field display name - `value` (string) - The field value for this contact - `updatedAt` (string (date-time)) - When this value was last updated ## ContactCustomFieldValue - `fieldId` (string) - Custom field definition ID - `tag` (string) - Custom field tag (for use in templates as `{{tag}}`) - `name` (string) - Custom field display name - `value` (string) - The field value for this contact - `updatedAt` (string (date-time)) - When this value was last updated ## CustomFieldDefinition - `id` (string) - Custom field definition ID - `name` (string) - Display name (1-100 chars) - `tag` (string) - Tag for use in templates as `{{tag}}`. Alphanumeric + underscores only (1-50 chars). - `description` (string) - Optional description - `category` (string) - Category for organization (default "Custom") - `createdAt` (string (date-time)) ## CreateContactRequest - `firstName` (string, maxLength 100) - `lastName` (string, maxLength 100) - `phone` (string, required) - Phone number with country code (6-20 digits, optional + or 00 prefix - stored without the prefix). Must be unique per account. - `email` (string (email)) - `groups` (array of string) - Array of group IDs to assign the contact to - `customFields` (array of object) - Custom field values to set on creation - `fieldId` (string) - Custom field definition ID - `value` (string) ## UpdateContactRequest - `firstName` (string, maxLength 100) - `lastName` (string, maxLength 100) - `phone` (string) - `email` (string (email)) - `groups` (array of string) - Replace the contact's group memberships with this list - `customFields` (array of object) - `fieldId` (string) - `value` (string) - `disabled` (boolean) - Set to `true` to soft-delete, `false` to re-enable ## CreateCustomFieldRequest - `name` (string, required, minLength 1, maxLength 100) - Display name. Letters, numbers, spaces, underscores, hyphens. - `tag` (string, required, minLength 1, maxLength 50) - Unique tag for merge fields. Letters, numbers, underscores only. - `description` (string, maxLength 500) - `category` (string, maxLength 50) - Category for organization (default: "Custom") ## BulkContactRequest - `contacts` (array of object, required, maxItems 1000) - `phone` (string, required) - `firstName` (string, maxLength 100) - `lastName` (string, maxLength 100) - `email` (string (email)) - `customFields` (array of object) - `fieldId` (string) - Custom field definition ID (use this or `tag`) - `tag` (string) - Custom field tag (use this or `fieldId`) - `value` (string) - `upsert` (boolean, default `false`) - When `true`, existing contacts (matched by phone) are updated. When `false`, duplicates are skipped. - `groups` (array of string) - Group IDs to assign all contacts to ## BulkContactResponse - `created` (integer) - Number of contacts created - `updated` (integer) - Number of contacts updated (only in upsert mode) - `failed` (integer) - Number of contacts that failed - `errors` (array of object) - `phone` (string) - `error` (string) ## ContactListResponse - `data` (array of Contact) - `id` (string) - Contact ID - `firstName` (string) - First name (max 100 characters) - `lastName` (string) - Last name (max 100 characters) - `phone` (string) - Phone number with country code, stored as digits only (no `+`/`00` prefix). Unique per account. - `email` (string) - Email address - `disabled` (boolean) - Whether the contact is soft-deleted (disabled contacts cannot receive messages) - `groups` (array of object) - Groups the contact belongs to - `id` (string) - `name` (string) - `createdAt` (string (date-time)) - `customFields` (array of ContactCustomFieldValue) - Only present when `?include=customFields` is requested - `fieldId` (string) - Custom field definition ID - `tag` (string) - Custom field tag (for use in templates as `{{tag}}`) - `name` (string) - Custom field display name - `value` (string) - The field value for this contact - `updatedAt` (string (date-time)) - When this value was last updated - `pagination` (object) - `nextCursor` (string) - Base64 cursor for the next page. Null when there are no more results. - `hasNext` (boolean) - Whether more results are available - `limit` (integer) - The limit that was applied - `count` (integer) - Number of contacts in this response ## Group - `id` (string) - Group ID (MongoDB ObjectId) - `name` (string) - Group name - `description` (string) - Group description - `contactCount` (integer) - Number of contacts in the group - `createdAt` (string (date-time)) - `updatedAt` (string (date-time)) ## GroupContact - `id` (string) - `firstName` (string) - `lastName` (string) - `phone` (string) - `email` (string) - `disabled` (boolean) ## CreateGroupRequest - `name` (string, required, minLength 1, maxLength 200) - Group name (required) - `description` (string, maxLength 1000) - Optional group description ## UpdateGroupRequest - `name` (string, minLength 1, maxLength 200) - New group name - `description` (string, maxLength 1000) - New group description ## GroupContactsRequest - `contactIds` (array of string, required, maxItems 10000) - Array of contact IDs (MongoDB ObjectIds) to add or remove ## GroupContactsResponse - `message` (string) - `contactsProcessed` (integer) - Total number of contact IDs provided - `newRelationships` (integer) - Number of new contacts actually added (excludes duplicates) - `linkedEntitiesSync` (object) - Sync results for linked campaigns/flows - `campaignsUpdated` (integer) - `flowsUpdated` (integer) ## TemplateObject - `id` (string) - Template ID (MongoDB ObjectId) - `name` (string) - Template name - `content` (string) - Template content (plain text for SMS, JSON string for RCS) - `type` (string, one of `SMS`, `RCS`) - Template type - `rcsMessageType` (string, one of `textBasic`, `text`, `richCard`, `carousel`, `image`, `video`, `audio`, `file`, `media`) - RCS message subtype (null for SMS) - `context` (string, one of `event`, `flow`, `general`) - Template context - `templateGroupId` (string) - ID of the template group (folder) this template belongs to, or null if ungrouped. Template groups are NOT contact groups. - `isDefault` (boolean) - Whether this is the default template for its type+context - `parsedContent` (object) - Parsed RCS content (only present for RCS templates) - `messageType` (string) - `content` (object) - `createdAt` (string (date-time)) - `updatedAt` (string (date-time)) ## CreateTemplateRequest - `name` (string, required, minLength 1, maxLength 200) - Template name (required) - `type` (string, required, one of `SMS`, `RCS`) - Template type (required) - `content` (string or object, required) - Template content. For SMS: a plain text string. For RCS: an object (same format as rcs_send content field). - `rcsMessageType` (string, one of `textBasic`, `text`, `richCard`, `carousel`, `image`, `video`, `audio`, `file`, `media`) - RCS message subtype (required when type=RCS) - `templateGroupId` (string) - Template group (folder) to place the template in. Omit or pass null for ungrouped. Template groups are NOT contact groups. - `context` (string, one of `event`, `flow`, `general`) - Template context (default "general") - `isDefault` (boolean) - Set as default template for this type+context (default false) ## UpdateTemplateRequest - `name` (string, minLength 1, maxLength 200) - `type` (string, one of `SMS`, `RCS`) - `content` (string or object) - New content. For RCS: object. For SMS: text string. - `rcsMessageType` (string, one of `textBasic`, `text`, `richCard`, `carousel`, `image`, `video`, `audio`, `file`, `media`) - `templateGroupId` (string) - Move the template to a template group (folder), or null to ungroup. Omit to leave unchanged. Template groups are NOT contact groups. - `context` (string, one of `event`, `flow`, `general`) - `isDefault` (boolean) ## TemplateListResponse - `data` (array of TemplateObject) - `id` (string) - Template ID (MongoDB ObjectId) - `name` (string) - Template name - `content` (string) - Template content (plain text for SMS, JSON string for RCS) - `type` (string, one of `SMS`, `RCS`) - Template type - `rcsMessageType` (string, one of `textBasic`, `text`, `richCard`, `carousel`, `image`, `video`, `audio`, `file`, `media`) - RCS message subtype (null for SMS) - `context` (string, one of `event`, `flow`, `general`) - Template context - `templateGroupId` (string) - ID of the template group (folder) this template belongs to, or null if ungrouped. Template groups are NOT contact groups. - `isDefault` (boolean) - Whether this is the default template for its type+context - `parsedContent` (object) - Parsed RCS content (only present for RCS templates) - `messageType` (string) - `content` (object) - `createdAt` (string (date-time)) - `updatedAt` (string (date-time)) - `pagination` (object) - `total` (integer) - Total number of templates matching the filter - `page` (integer) - Current page number - `limit` (integer) - Results per page - `pages` (integer) - Total number of pages - `hasNext` (boolean) - Whether there is a next page - `hasPrev` (boolean) - Whether there is a previous page ## TemplateGroupObject A template group — a folder for organizing message templates. NOT a contact group. - `id` (string) - Template group ID (MongoDB ObjectId) - `name` (string) - Template group name (unique per account) - `templateCount` (integer) - Number of templates in this group - `createdAt` (string (date-time)) - `updatedAt` (string (date-time)) ## TemplateGroupListResponse - `data` (array of TemplateGroupObject) - `id` (string) - Template group ID (MongoDB ObjectId) - `name` (string) - Template group name (unique per account) - `templateCount` (integer) - Number of templates in this group - `createdAt` (string (date-time)) - `updatedAt` (string (date-time)) - `ungroupedTemplateCount` (integer) - Number of templates that are not in any template group ## CreateTemplateGroupRequest - `name` (string, required, minLength 1, maxLength 100) - Template group name (required, unique per account) ## AssignTemplatesToTemplateGroupRequest - `templateIds` (array of string, required) - MongoDB ObjectIds of the templates to move - `templateGroupId` (string, required) - Target template group ID, or null to remove the templates from any group --- # Part 5 - Assistant setup notes Copy-paste instructions published for individual AI tools. They repeat the API basics above in the shape each tool expects. ## ai-instructions/claude.md Source: https://docs.sendrcs.eu/ai-instructions/claude.md ### SendRCS API Instructions for Claude Copy this into your Claude Project's custom instructions or system prompt. --- #### SendRCS API You have access to the SendRCS API for sending interactive mobile messages via RCS (Rich Communication Services). ##### Base URL ``` https://api.sendrcs.eu/api/rcs ``` ##### Authentication Include in all requests: ``` Authorization: Bearer ``` or ``` X-API-Key: ``` ##### Try it without an account (Sandbox) You can validate and preview any message body before you have an API key. The sandbox runs the same validator as `POST /send`, returns the same error shape, and gives you a phone-mockup preview link. Nothing is sent, no credits are used, and no auth header is needed. - `POST https://api.sendrcs.eu/api/rcs/sandbox/validate` - validation only - `POST https://api.sendrcs.eu/api/rcs/sandbox/preview` - validation plus a `share_url` (expires after 24 hours) - `GET https://api.sendrcs.eu/api/rcs/sandbox/samples` - sample images, a video and a PDF you may reference Send the exact body you would send to `/send` or `/send-batch`. Recipient and sender fields (`phoneNumber`, `sendernameId`, `scheduleAt`, ...) are accepted and listed under `sandbox.ignored_fields`. Only media from `/sandbox/samples` is rendered; other `https` URLs still pass validation but show as a placeholder in the preview. ```bash curl -X POST https://api.sendrcs.eu/api/rcs/sandbox/preview \ -H "Content-Type: application/json" \ -d '{ "messageType": "richCard", "content": { "title": "Your order is on its way", "description": "Track the parcel or talk to us.", "media": { "height": "TALL", "contentInfo": { "fileUrl": "https://sendrcs.eu/images/rcsexamples/shoes-1.jpg", "contentType": "image/jpeg" } }, "cardActions": [ { "type": "openUrl", "text": "Track order", "openUrlAction": { "url": "https://example.com/track" } }, { "type": "dial", "text": "Call us", "dialAction": { "phoneNumber": "+4512345678" } } ] } }' ``` Read `valid`, `errors`, `warnings`, `preview.share_url` and `preview.expires_at`. Show the developer the `share_url`: it renders the message on a phone mockup (Android and iOS). Rate limit: 20 requests per minute and 100 per day per IP. Sandbox preview ids cannot be used to send. When the developer is ready, they create a free account at https://app.sendrcs.eu/auth/signup and you call `/send` with their API key. ##### Message Types 1. **text** - Text with up to 11 chip suggestions (reply buttons) 2. **textBasic** - Plain text only (no interactive elements) 3. **richCard** - Card with image, title, description, up to 4 action buttons + 11 chips 4. **carousel** - Multiple rich cards (horizontal scroll, max 10 cards) 5. **media** - Standalone image/video with optional suggestions 6. **file** - Send files (PDF, documents, etc.) 7. **image** - Send images (JPG, PNG, GIF, etc.) 8. **video** - Send videos (MP4, etc.) 9. **audio** - Send audio files (MP3, etc.) ##### Send Single Message ```bash POST /api/rcs/send Content-Type: application/json { "phoneNumber": "+4512345678", "messageType": "text", "sendernameId": "", "content": { "text": "Hello! How can we help?", "suggestions": [ { "reply": { "text": "Book Now", "postbackData": "book_appointment", "webhookUrl": "https://yourapp.com/webhook" } }, { "action": { "text": "Call Us", "type": "dial", "dialAction": { "phoneNumber": "+4512345678" } } } ] } } ``` ##### Send Rich Card ```json { "phoneNumber": "+4512345678", "messageType": "richCard", "sendernameId": "", "content": { "title": "Special Offer", "description": "50% off today only!", "media": { "height": "MEDIUM", "contentInfo": { "fileUrl": "https://example.com/image.jpg" } }, "cardActions": [ { "text": "Shop Now", "type": "openUrl", "openUrlAction": { "url": "https://shop.example.com" } }, { "text": "Call Us", "type": "dial", "dialAction": { "phoneNumber": "+4512345678" } } ] } } ``` ##### Send Batch (up to 10,000) ```json POST /api/rcs/send-batch { "phoneNumbers": ["+4512345678", "+4587654321"], "messageType": "text", "sendernameId": "", "content": { "text": "Batch message" }, "campaignName": "February Campaign", "skipDisabled": true } ``` ##### Schedule Message Add to any request: ```json { "scheduleAt": "2024-02-15T14:00:00", "timeZone": "Europe/Copenhagen" } ``` ##### Cancel a Scheduled Message ``` DELETE https://api.sendrcs.eu/api/messages/ ``` Note the base path: this endpoint sits on `/api`, not `/api/rcs`. Cancels one scheduled message before it is sent, RCS or SMS alike. Pass the `messageId` from the scheduled send response, or an `id` from `GET https://api.sendrcs.eu/api/messages/log?status=SCHEDULED`. Only messages still in `SCHEDULED` status can be cancelled; anything already sending or sent returns `404`. Cancelling one recipient of a scheduled batch drops only that message and leaves the rest of the batch on schedule - to cancel a whole batch call `DELETE /api/rcs/batch/` instead. Requires the `messages:write` permission. ```json { "success": true, "message": "Scheduled message cancelled successfully" } ``` ##### SMS Fallback Add to any request for automatic SMS when RCS unavailable: ```json { "smsFallback": { "message": "Fallback SMS text (max 1530 chars)", "sendernameId": "" } } ``` ##### Send File/Image/Video/Audio ```json { "phoneNumber": "+4512345678", "messageType": "file", "sendernameId": "", "content": { "media": { "contentInfo": { "fileUrl": "https://example.com/document.pdf" } } } } ``` Use `messageType`: `file`, `image`, `video`, or `audio` depending on content. ##### Send Standalone SMS ```json POST /api/rcs/send-sms { "phoneNumber": "+4512345678", "message": "Hi {{contact_first_name}}! Your order from {{company_name}} is ready.", "sendernameId": "", "campaignName": "Order Ready" } ``` Plain SMS with no RCS involved. Max 1530 chars, billed per segment. Supports the full merge field set (see Merge Fields below) including `{{unsubscribe_link}}` - placeholders are replaced per recipient before the segment count is calculated. Supports `scheduleAt`/`timeZone` like `/send`. ##### Key Options - `skipContactCreation: true` - Don't create contact records - `skipDisabled: true` - Skip disabled contacts (default) - `campaignName: "string"` - Track campaigns ##### Limits - Chip suggestions: 11 max (any message type, any mix of reply + action chips) - Card actions: 4 max per card - Carousel cards: 10 max - Text: 3072 chars - Button text: 25 chars - Batch: 10,000 recipients ##### Card Action Types (cardActions - buttons ON the card, max 4) - `openUrl` - Open URL in browser - `openUrlInWebview` - Open URL in in-app webview (`openUrlInWebviewAction.viewMode`: FULL, TALL, or HALF) - `dial` - Open phone dialer - `postback` - Send postback data to webhook (card-only; on chips use a reply chip instead) - `addToCalendar` - Add to calendar via `calendarAction: { title, startTime, endTime, description }` (card-only; chips use `createCalendarEvent`) - `viewLocation` - Open maps (`viewLocationAction: { latLong: { latitude, longitude }, label }`) - `shareLocation` - Ask the user to share their location - `unsubscribeLink` - Unsubscribe button; URL must be exactly `{{unsubscribe_link}}`. REQUIRED for marketing messages. All button and chip types support optional `postbackData` + `webhookUrl` - when both are set, a webhook fires on tap regardless of action type. ##### Chip Suggestion Types **Reply** (sends postback to webhook): ```json { "reply": { "text": "Yes", "postbackData": "confirm", "webhookUrl": "https://..." } } ``` **Action** (native device action): ```json { "action": { "text": "Call", "type": "dial", "dialAction": { "phoneNumber": "+45..." } } } ``` Action types: - `dial` - Open phone dialer - `openUrl` - Open URL in browser - `openUrlInWebview` - Open URL in in-app webview (viewMode: FULL, TALL, or HALF) - `createCalendarEvent` - Add to calendar via `createCalendarEventAction: { title, startTime, endTime, description }` (chip-only naming; card buttons use `addToCalendar`) - `viewLocation` - Open maps - `shareLocation` - Share user's location - `unsubscribeLink` - Open per-recipient unsubscribe URL (`{{unsubscribe_link}}`) - `unsubscribe` - Send stop keyword as reply to trigger the unsubscribe flow (chip-only, `unsubscribeAction: { stopKeyword: "STOP" }`) Note: `postback` is NOT a valid chip type - use a reply chip with `postbackData` instead. ##### Merge Fields (Personalization) Use `{{field_name}}` placeholders in text, card titles/descriptions, and button/chip URLs. They are replaced per recipient at send time (the recipient must be an existing contact, or pass `contactId`): - Contact: `{{contact_first_name}}`, `{{contact_last_name}}`, `{{contact_phone}}`, `{{contact_email}}` - Company: `{{company_name}}`, `{{company_email}}`, `{{company_phone}}`, `{{company_website}}` - Custom fields: any `{{custom_tag}}` defined on the account - Date: `{{today}}` - Unsubscribe: `{{unsubscribe_link}}` - unique per-recipient opt-out URL. REQUIRED for marketing messages (use it in an `unsubscribeLink` button or chip). Note: the `smsFallback.message` text supports the same merge fields as the RCS body (contact, company, custom fields, `{{today}}`, `{{unsubscribe_link}}`), substituted per recipient when the fallback is sent. ##### Response ```json { "success": true, "messageId": "...", "creditsUsed": 1, "conversation": { "status": "ACTIVE", "freeMessage": true } } ``` ##### Error Handling Check for: - `success: false` in response - `errors` array for validation errors - HTTP 400 for bad requests - HTTP 401 for auth issues ## ai-instructions/chatgpt.md Source: https://docs.sendrcs.eu/ai-instructions/chatgpt.md ### SendRCS API Instructions for ChatGPT Copy this into your ChatGPT Custom Instructions or GPT configuration. --- #### SendRCS API Reference I can help you send RCS (Rich Communication Services) messages using the SendRCS API. **Base URL:** `https://api.sendrcs.eu/api/rcs` **Auth:** Bearer token or X-API-Key header ##### Quick Reference **Message Types:** - `text` - Text + up to 11 reply buttons (chips) - `textBasic` - Plain text only (no interactive elements) - `richCard` - Image card with up to 4 buttons + 11 chips - `carousel` - Multiple cards (max 10) - `media` - Image/video with optional suggestions - `file` - Send files (PDF, documents) - `image` - Send images - `video` - Send videos - `audio` - Send audio files **Key Endpoints:** - `POST /send` - Single message - `POST /send-batch` - Up to 10,000 recipients - `GET /conversations/:phone/status` - Check conversation - `POST /sandbox/preview` - Validate + preview without an API key (nothing is sent) - `DELETE /api/messages/{id}` - Cancel a scheduled message (on `/api`, not `/api/rcs`) ##### Try It Without an Account (Sandbox) You can validate and preview any message body before you have an API key. The sandbox runs the same validator as `POST /send`, returns the same error shape, and gives you a phone-mockup preview link. Nothing is sent, no credits are used, and no auth header is needed. - `POST https://api.sendrcs.eu/api/rcs/sandbox/validate` - validation only - `POST https://api.sendrcs.eu/api/rcs/sandbox/preview` - validation plus a `share_url` (expires after 24 hours) - `GET https://api.sendrcs.eu/api/rcs/sandbox/samples` - sample images, a video and a PDF you may reference Send the exact body you would send to `/send`. Recipient and sender fields (`phoneNumber`, `sendernameId`, `scheduleAt`, ...) are accepted and listed under `sandbox.ignored_fields`. Only media from `/sandbox/samples` is rendered; other `https` URLs still pass validation but show as a placeholder in the preview. ```json POST https://api.sendrcs.eu/api/rcs/sandbox/preview { "messageType": "richCard", "content": { "title": "Your order is on its way", "description": "Track the parcel or talk to us.", "media": { "height": "TALL", "contentInfo": { "fileUrl": "https://sendrcs.eu/images/rcsexamples/shoes-1.jpg", "contentType": "image/jpeg" } }, "cardActions": [ { "type": "openUrl", "text": "Track order", "openUrlAction": { "url": "https://example.com/track" } }, { "type": "dial", "text": "Call us", "dialAction": { "phoneNumber": "+4512345678" } } ] } } ``` Read `valid`, `errors`, `warnings`, `preview.share_url` and `preview.expires_at`. Show the developer the `share_url` (phone mockup, Android and iOS). Rate limit: 20 requests per minute and 100 per day per IP. Sandbox preview ids cannot be used to send. When ready, create a free account at https://app.sendrcs.eu/auth/signup and call `/send` with an API key. ##### Send Text Message ```json POST /api/rcs/send { "phoneNumber": "+4512345678", "messageType": "text", "sendernameId": "SENDER_ID", "content": { "text": "Hello! How can we help?", "suggestions": [ { "reply": { "text": "Book Now", "postbackData": "book", "webhookUrl": "https://yoursite.com/webhook" } }, { "action": { "text": "Call Us", "type": "dial", "dialAction": { "phoneNumber": "+4512345678" } } } ] } } ``` ##### Send Rich Card ```json { "phoneNumber": "+4512345678", "messageType": "richCard", "sendernameId": "SENDER_ID", "content": { "title": "Special Offer", "description": "Limited time deal!", "media": { "height": "MEDIUM", "contentInfo": { "fileUrl": "https://example.com/image.jpg" } }, "cardActions": [ { "text": "Shop", "type": "openUrl", "openUrlAction": { "url": "https://shop.com" } } ] } } ``` ##### Batch Send ```json POST /api/rcs/send-batch { "phoneNumbers": ["+4512345678", "+4587654321"], "messageType": "text", "sendernameId": "SENDER_ID", "content": { "text": "Batch message" }, "campaignName": "Campaign Name" } ``` ##### Schedule Add to request: `"scheduleAt": "2024-02-15T14:00:00", "timeZone": "Europe/Copenhagen"` ##### Cancel a Scheduled Message `DELETE https://api.sendrcs.eu/api/messages/{id}` - note this one sits on `/api`, not `/api/rcs`. Works for a scheduled RCS or SMS message. Use the `messageId` from the scheduled send response, or an `id` from `GET https://api.sendrcs.eu/api/messages/log?status=SCHEDULED`. Only `SCHEDULED` messages can be cancelled; already sent returns `404`. One recipient of a scheduled batch can be cancelled on its own - the rest of the batch still goes out. To cancel a whole batch: `DELETE /api/rcs/batch/{jobId}`. Requires `messages:write`. ##### SMS Fallback Add: `"smsFallback": { "message": "SMS text", "sendernameId": "SMS_SENDER_ID" }` ##### Send File/Image/Video/Audio ```json { "phoneNumber": "+4512345678", "messageType": "file", "sendernameId": "SENDER_ID", "content": { "media": { "contentInfo": { "fileUrl": "https://example.com/document.pdf" } } } } ``` Use `messageType`: `file`, `image`, `video`, or `audio` depending on content. ##### Send Standalone SMS ```json POST /api/rcs/send-sms { "phoneNumber": "+4512345678", "message": "Hi {{contact_first_name}}! Your order from {{company_name}} is ready.", "sendernameId": "SMS_SENDER_ID" } ``` Plain SMS, no RCS. Max 1530 chars, billed per segment. Full merge field support (see Merge Fields) - replaced per recipient before segments are calculated. Supports `scheduleAt`/`timeZone`. ##### Limits - 11 chip suggestions (any message type) - 4 card actions per card, 10 carousel cards - 3072 char text, 25 char button text - 10,000 batch recipients ##### Card Button Types (cardActions, max 4) - `openUrl` - Open URL - `openUrlInWebview` - In-app webview (`viewMode`: FULL/TALL/HALF) - `dial` - Open dialer - `postback` - Send postback data to webhook (card-only) - `addToCalendar` - Add to calendar via `calendarAction` (card-only) - `viewLocation` / `shareLocation` - Maps - `unsubscribeLink` - Unsubscribe URL, must be `{{unsubscribe_link}}` (REQUIRED for marketing) ##### Chip Types (suggestions, max 11) - `reply` - Text reply with postbackData to webhook (use instead of postback on chips) - `dial` - Open dialer - `openUrl` - Open URL - `openUrlInWebview` - In-app webview (`viewMode`: FULL/TALL/HALF) - `createCalendarEvent` - Add to calendar via `createCalendarEventAction` (chips only; cards use `addToCalendar`) - `viewLocation` / `shareLocation` - Maps - `unsubscribeLink` - Per-recipient unsubscribe URL (`{{unsubscribe_link}}`) - `unsubscribe` - Sends stop keyword as reply (chip-only, `unsubscribeAction: { stopKeyword: "STOP" }`) All button/chip types support optional `postbackData` + `webhookUrl` for click tracking. ##### Merge Fields Use `{{field_name}}` in text, card titles/descriptions, and button/chip URLs - replaced per recipient at send time (recipient must be an existing contact, or pass `contactId`): - `{{contact_first_name}}`, `{{contact_last_name}}`, `{{contact_phone}}`, `{{contact_email}}` - `{{company_name}}`, `{{company_email}}`, `{{company_phone}}`, `{{company_website}}` - Any `{{custom_tag}}` custom field, plus `{{today}}` - `{{unsubscribe_link}}` - per-recipient opt-out URL, REQUIRED for marketing messages Note: `smsFallback.message` supports the same merge fields as the RCS body (contact, company, custom fields, `{{today}}`, `{{unsubscribe_link}}`), substituted per recipient when the fallback is sent. ##### Options - `skipContactCreation: true` - No contact record - `skipDisabled: true` - Skip disabled contacts ## ai-instructions/cursor.md Source: https://docs.sendrcs.eu/ai-instructions/cursor.md ### SendRCS API Instructions for Cursor Add this to your `.cursor/rules` or project instructions. --- #### SendRCS API When working with this codebase, use these patterns for SendRCS messaging. ##### API Configuration ```typescript const SENDRCS_API_BASE = 'https://api.sendrcs.eu/api/rcs'; const headers = { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, // or 'X-API-Key': apiKey }; ``` ##### Try It Without an Account (Sandbox) You can validate and preview any message body before you have an API key. The sandbox runs the same validator as `POST /send`, returns the same error shape, and gives you a phone-mockup preview link. Nothing is sent, no credits are used, and no auth header is needed. - `POST ${SENDRCS_API_BASE}/sandbox/validate` - validation only - `POST ${SENDRCS_API_BASE}/sandbox/preview` - validation plus a `share_url` (expires after 24 hours) - `GET ${SENDRCS_API_BASE}/sandbox/samples` - sample images, a video and a PDF you may reference Send the exact body you would send to `/send` or `/send-batch`. Recipient and sender fields (`phoneNumber`, `sendernameId`, `scheduleAt`, ...) are accepted and listed under `sandbox.ignored_fields`. Only media from `/sandbox/samples` is rendered; other `https` URLs still pass validation but show as a placeholder in the preview. ```typescript interface SandboxPreviewResult { success: boolean; valid: boolean; errors: string[]; warnings: string[]; preview?: { preview_id: string; share_url: string; expires_at: string; sendable: false }; sandbox: { nothing_sent: true; ignored_fields: string[]; unknown_fields: string[] }; } // No auth header: works before the developer has an account. async function sandboxPreview(body: SendMessageRequest): Promise { const response = await fetch(`${SENDRCS_API_BASE}/sandbox/preview`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }); return response.json(); // 400 carries the same body as a failed /send } const result = await sandboxPreview({ messageType: 'richCard', content: { title: 'Your order is on its way', description: 'Track the parcel or talk to us.', media: { height: 'TALL', contentInfo: { fileUrl: 'https://sendrcs.eu/images/rcsexamples/shoes-1.jpg', contentType: 'image/jpeg' } }, cardActions: [ { type: 'openUrl', text: 'Track order', openUrlAction: { url: 'https://example.com/track' } }, { type: 'dial', text: 'Call us', dialAction: { phoneNumber: '+4512345678' } }, ], }, } as SendMessageRequest); console.log(result.valid, result.errors, result.preview?.share_url); ``` Rate limit: 20 requests per minute and 100 per day per IP. Sandbox preview ids cannot be used to send. When ready, create a free account at https://app.sendrcs.eu/auth/signup and call `/send` with an API key. ##### Send Single Message ```typescript async function sendRcsMessage(phoneNumber: string, text: string, sendernameId: string) { const response = await fetch(`${SENDRCS_API_BASE}/send`, { method: 'POST', headers, body: JSON.stringify({ phoneNumber, messageType: 'text', sendernameId, content: { text } }) }); return response.json(); } ``` ##### Send Rich Card ```typescript interface RichCardContent { title: string; description: string; media: { height: 'SHORT' | 'MEDIUM' | 'TALL'; contentInfo: { fileUrl: string }; }; cardActions: Array<{ text: string; type: 'openUrl' | 'dial' | 'postback'; openUrlAction?: { url: string }; dialAction?: { phoneNumber: string }; postbackData?: string; webhookUrl?: string; }>; } async function sendRichCard(phoneNumber: string, content: RichCardContent, sendernameId: string) { return fetch(`${SENDRCS_API_BASE}/send`, { method: 'POST', headers, body: JSON.stringify({ phoneNumber, messageType: 'richCard', sendernameId, content }) }).then(r => r.json()); } ``` ##### Send Batch ```typescript async function sendBatch( phoneNumbers: string[], content: { text: string }, sendernameId: string, options?: { campaignName?: string; skipDisabled?: boolean; skipContactCreation?: boolean; smsFallback?: { message: string; sendernameId: string }; } ) { return fetch(`${SENDRCS_API_BASE}/send-batch`, { method: 'POST', headers, body: JSON.stringify({ phoneNumbers, messageType: 'text', sendernameId, content, ...options }) }).then(r => r.json()); } ``` ##### Schedule Message ```typescript // Add to any send request: { scheduleAt: '2024-02-15T14:00:00', timeZone: 'Europe/Copenhagen' } ``` ##### Cancel a Scheduled Message ```typescript // Scheduled messages (RCS or SMS) can be cancelled until they are sent. // Note the base: this endpoint is on /api, not /api/rcs. const SENDRCS_API_ROOT = 'https://api.sendrcs.eu/api'; // id = messageId from the scheduled send response, or an id from // GET ${SENDRCS_API_ROOT}/messages/log?status=SCHEDULED async function cancelScheduledMessage(id: string) { return fetch(`${SENDRCS_API_ROOT}/messages/${id}`, { method: 'DELETE', headers }).then(r => r.json()); // { success: true, message: '...' } } ``` Only messages still in `SCHEDULED` status can be cancelled; anything already sending or sent returns `404`. Cancelling one recipient of a scheduled batch leaves the rest of the batch on schedule - to cancel a whole batch call `DELETE ${SENDRCS_API_BASE}/batch/${jobId}` instead. Requires the `messages:write` permission. ##### Send Standalone SMS ```typescript // Plain SMS (no RCS). Max 1530 chars, billed per segment. // message supports the full merge field set ({{contact_first_name}}, {{company_name}}, // {{custom_tag}}, {{today}}, {{unsubscribe_link}}) - replaced per recipient before // segments are calculated. Also supports scheduleAt/timeZone. async function sendSms(phoneNumber: string, message: string, sendernameId: string) { return fetch(`${SENDRCS_API_BASE}/send-sms`, { method: 'POST', headers, body: JSON.stringify({ phoneNumber, message, sendernameId }) }).then(r => r.json()); } ``` ##### Message Types ```typescript type MessageType = 'text' | 'textBasic' | 'richCard' | 'carousel' | 'media' | 'file' | 'image' | 'video' | 'audio'; // Text content (max 11 chip suggestions) interface TextContent { text: string; suggestions?: Suggestion[]; } // Rich card content interface RichCardContent { title: string; description: string; media: Media; cardActions: CardAction[]; // max 4 suggestions?: Suggestion[]; // max 11 } // Carousel content interface CarouselContent { cardContents: RichCardContent[]; // max 10 suggestions?: Suggestion[]; } // File/Image/Video/Audio content interface MediaFileContent { media: { contentInfo: { fileUrl: string }; }; } // Suggestion (chip button) interface Suggestion { reply?: { text: string; postbackData: string; webhookUrl: string }; action?: CardAction; // NOT all types are chip-valid: postback and addToCalendar are card-only. // Chips use createCalendarEvent (createCalendarEventAction) for calendar, // a reply chip instead of postback, and support the chip-only unsubscribe type. } // Card action button / action chip interface CardAction { text: string; // max 25 chars // Card buttons: postback | openUrl | openUrlInWebview | dial | addToCalendar | viewLocation | shareLocation | unsubscribeLink // Chips: openUrl | openUrlInWebview | dial | createCalendarEvent | viewLocation | shareLocation | unsubscribeLink | unsubscribe type: 'postback' | 'openUrl' | 'openUrlInWebview' | 'dial' | 'addToCalendar' | 'createCalendarEvent' | 'viewLocation' | 'shareLocation' | 'unsubscribeLink' | 'unsubscribe'; postbackData?: string; // optional on all types; with webhookUrl set, a webhook fires on tap webhookUrl?: string; openUrlAction?: { url: string }; // openUrl + unsubscribeLink (URL must be exactly {{unsubscribe_link}}) openUrlInWebviewAction?: { url: string; viewMode?: 'FULL' | 'TALL' | 'HALF' }; dialAction?: { phoneNumber: string }; calendarAction?: { title: string; startTime: string; endTime: string; description?: string }; // addToCalendar (card-only) createCalendarEventAction?: { title: string; startTime: string; endTime: string; description?: string }; // createCalendarEvent (chip-only) viewLocationAction?: { latLong: { latitude: number; longitude: number }; label?: string }; unsubscribeAction?: { stopKeyword: string }; // unsubscribe (chip-only) } ``` ##### Response Types ```typescript interface SendResponse { success: boolean; messageId: string; creditsUsed: number; fallbackUsed: boolean; conversation?: { conversationId: string; status: 'NEW' | 'ACTIVE'; freeMessage: boolean; }; } interface BatchResponse { success: boolean; jobId: string; totalOriginal: number; totalEnabled: number; totalDisabled: number; estimatedCredits: number; } ``` ##### Error Handling ```typescript interface ApiError { message: string; error?: string; errors?: Array<{ msg: string; param: string }>; } // Always check success const result = await sendRcsMessage(...); if (!result.success) { console.error('Send failed:', result.message || result.errors); } ``` ##### Send File/Image/Video/Audio ```typescript async function sendFile(phoneNumber: string, fileUrl: string, sendernameId: string, type: 'file' | 'image' | 'video' | 'audio' = 'file') { return fetch(`${SENDRCS_API_BASE}/send`, { method: 'POST', headers, body: JSON.stringify({ phoneNumber, messageType: type, sendernameId, content: { media: { contentInfo: { fileUrl } } } }) }).then(r => r.json()); } ``` ##### Limits Reference ```typescript const LIMITS = { MAX_CHIP_SUGGESTIONS: 11, // any message type, any mix of reply + action chips MAX_CARD_ACTIONS: 4, MAX_CAROUSEL_CARDS: 10, MAX_TEXT_LENGTH: 3072, MAX_BUTTON_TEXT: 25, MAX_BATCH_SIZE: 10000, MAX_SMS_FALLBACK: 1530, }; // Action types for chip suggestions (postback and addToCalendar are card-only) type ChipActionType = 'dial' | 'openUrl' | 'openUrlInWebview' | 'createCalendarEvent' | 'viewLocation' | 'shareLocation' | 'unsubscribeLink' | 'unsubscribe'; ``` ##### Merge Fields (Personalization) Use `{{field_name}}` placeholders in text, card titles/descriptions, and button/chip URLs - replaced per recipient at send time (recipient must be an existing contact, or pass `contactId`): - Contact: `{{contact_first_name}}`, `{{contact_last_name}}`, `{{contact_phone}}`, `{{contact_email}}` - Company: `{{company_name}}`, `{{company_email}}`, `{{company_phone}}`, `{{company_website}}` - Custom fields: any `{{custom_tag}}` defined on the account, plus `{{today}}` - Unsubscribe: `{{unsubscribe_link}}` - unique per-recipient opt-out URL. REQUIRED for marketing messages (use it in an `unsubscribeLink` button or chip). Note: `smsFallback.message` supports the same merge fields as the RCS body (contact, company, custom fields, `{{today}}`, `{{unsubscribe_link}}`), substituted per recipient when the fallback is sent.