Download OpenAPI specification:Download
The RCS API enables you to send rich, interactive messages using Google's RCS for Business platform. Send text messages, rich cards with images, carousels, and interactive suggestions to engage users with modern messaging experiences.
/rcs/v1/messages endpoint with the message content, recipient phone number, and agent ID.Suggestions attach interactive buttons to a message or card. Each suggestion is either a reply (sends text back to your webhook) or an action (opens a URL, dials a number, views a location, or creates a calendar event).
open_url_action opens a web page when the user taps the button. By default the link opens in the device's browser. You can instead open it inside an in-app webview, which keeps the user in the messaging app.
| Field | Required | Description |
|---|---|---|
url |
yes | The URL to open. |
application |
no | BROWSER (device browser) or WEBVIEW (in-app webview). Defaults to the device browser when omitted. |
webview_view_mode |
no | Webview size: FULL, HALF, or TALL. Only honored when application is WEBVIEW. |
description |
no | Accessibility description of the action. |
Open in the device browser (default):
{
"action": {
"text": "Open website",
"postback_data": "open_site",
"open_url_action": {
"url": "https://www.spirius.com"
}
}
}
Open in an in-app webview:
{
"action": {
"text": "Open in app",
"postback_data": "open_webview",
"open_url_action": {
"url": "https://www.spirius.com",
"application": "WEBVIEW",
"webview_view_mode": "HALF",
"description": "Open spirius.com in a half-screen webview"
}
}
}
The API is lenient: unknown fields in a request are dropped rather than rejected. Use the exact snake_case field names above — a misspelled or unsupported field is silently ignored (and logged server-side), so the option simply has no effect. Webview support and view modes ultimately depend on the recipient's RCS client.
200 OK: Successful GET requests (e.g., health checks).202 Accepted: Message accepted for delivery (async processing).400 Bad Request: Invalid request format, missing required fields, or validation errors.401 Unauthorized: Missing or invalid API key.403 Forbidden: Valid credentials but insufficient permissions, or invalid webhook signature.429 Too Many Requests: Rate limit exceeded (default: 100 requests/minute per client).500 Internal Server Error: Unexpected server error.501 Not Implemented: Requested feature not yet implemented (e.g., conversations agent).503 Service Unavailable: Service temporarily unavailable.The API uses dual-header authentication with API key and client ID:
X-API-Key: your_api_key_here
X-Client-ID: your_client_id
Content-Type: application/json
Example:
POST /rcs/v1/messages HTTP/1.1
Host: api.example.com
X-API-Key: rcs_live_abc123xyz
X-Client-ID: acme_corp
Content-Type: application/json
X-Client-ID429 Too Many RequestsPhone numbers must be in E.164 format:
+[country_code][number]+46701234567 (Sweden), +12025551234 (USA)+ and country codeThe API sends RCS Events to your configured webhook URL when users interact with your messages or when message status changes.
Inbound (User Messages):
rcs.in.text: User sent a text messagercs.in.file: User sent a file (image, video, document)rcs.in.suggested.reply: User clicked a reply suggestion buttonrcs.in.suggested.action: User clicked an action suggestion (URL, dial, location)Outbound (Message Status):
rcs.out.delivered: Message successfully delivered to devicercs.out.read: User opened/read the messagercs.out.failed: Message failed to sendSubscription Events:
rcs.sub.subscribed: User subscribed via Google Messages UIrcs.sub.unsubscribed: User unsubscribed (includes STOP words)Test Events (Dry Run):
test.rcs.out.delivered: Simulated delivery event for testingFor complete webhook documentation including event payload structure, all field descriptions, example payloads, retry behavior, and integration guide, see the RCS Events section in the API reference.
The API supports multiple RBM agents for different use cases:
Specify the agent using the agent_id field in your request.
Liveness check endpoint that verifies the API process is running.
This endpoint is used by Kubernetes liveness probes to determine if the pod should be restarted. It returns 200 as long as the process is alive.
Returns: HealthResponse: Status information indicating the API is alive
{- "status": "string",
- "message": "string"
}Readiness check endpoint that verifies the API can handle requests.
This endpoint is used by Kubernetes readiness probes to determine if the pod should receive traffic. It checks critical dependencies:
Returns 200 only when all dependencies are available.
Returns: HealthResponse: Status information indicating the API is ready JSONResponse: 503 response if dependencies are not available
{- "status": "string",
- "message": "string"
}The RCS API sends RCS Events to your configured webhook URL when users interact with your RCS messages or when message delivery status changes.
| Category | Event Type | Description | Additional Info |
|---|---|---|---|
| Inbound | rcs.in.text | User sent a text message | Freeform text from user |
| Inbound | rcs.in.file | User sent a file (image, video, document) | Contains file URI and metadata |
| Inbound | rcs.in.suggested.reply | User clicked a reply suggestion button | Quick reply in conversation |
| Inbound | rcs.in.suggested.action | User clicked an action suggestion | Opens URL, dials phone, shares location, etc. |
| Outbound | rcs.out.delivered | Message successfully delivered to user's device | Reliable delivery confirmation |
| Outbound | rcs.out.read | User opened and read the message | Reliable read confirmation |
| Outbound | rcs.out.failed | Message failed to send | When delivery fails due to error or network issue |
| Subscription | rcs.sub.subscribed | User subscribed via Google Messages UI or other channels | User explicitly consents |
| Subscription | rcs.sub.unsubscribed | User unsubscribed via Google Messages UI, stop words, or other channels | Must honor immediately |
| Test (Dry Run) | test.rcs.out.delivered | Simulated delivery event for testing | One event per dry run message |
rcs.sub.unsubscribed events. Your webhook may receive both the text message (rcs.in.text) and the unsubscribe event (rcs.sub.unsubscribed). Handle these idempotently.When sending messages with dry_run: true, no actual RCS message is sent. Instead, the API simulates the complete message flow and generates a test event to your webhook:
test.rcs.out.delivered is sent (simulating successful delivery)test.rcs.out. prefix| Setting | Value | Notes |
|---|---|---|
| HTTP Method | POST | All webhook events are sent via POST |
| Content-Type | application/json | Event payload in JSON format |
| Authentication | HMAC-SHA256 signature (always included) | Signature sent in X-Webhook-Signature header - validation optional but recommended |
| Configuration | Per-agent webhooks | Use Webhook Management API to configure |
| Required Response | HTTP 200-299 | Any 2xx status code acknowledges receipt |
| Timeout | 10 seconds | Endpoint must respond within 10 seconds |
| Field | Type | Description | Always Present |
|---|---|---|---|
| event_type | string | Type of event (see Event Types table above) | All events |
| agent_id | string | RBM agent identifier (e.g., 'spirius_notifications_agent') | All events |
| send_time | string | Event timestamp in ISO 8601 format | All events |
| event_id | string | Unique identifier for this specific event occurrence (format: evt_*) |
All events - use for deduplication |
| message_id | string | Message identifier linking events to their message | For message-related events |
| client_message_id | string | Optional client-provided ID from original send request | Only if provided by client |
| sender_phone_number | string | User's phone number in E.164 format | When available |
| text | string | Plain text content of message | For text messages |
| content_message | object | Structured message content (suggestion responses) | For button clicks |
| user_file | object | File information (payload + thumbnail) | For file uploads |
| Scenario | Behavior |
|---|---|
| Successful Delivery (2xx response) | No retries - event acknowledged |
| Failed Delivery (non-2xx, timeout, connection error) | Automatic retry with exponential backoff |
| Retry Schedule | Exponential backoff over several minutes |
| Maximum Attempts | Multiple attempts before dead-letter queue |
| Duplicate Detection | Same event may be delivered multiple times - use event_id to detect duplicates |
HMAC Signature:
X-Webhook-Signature header/regenerate-secret endpointSecurity Best Practices:
Python Signature Verification Example:
import hmac
import hashlib
from fastapi import FastAPI, Request, HTTPException, status
def verify_webhook_signature(payload_bytes: bytes, signature: str, secret: str) -> bool:
'''
Verify HMAC-SHA256 signature from X-Webhook-Signature header.
Args:
payload_bytes: Raw request body bytes
signature: Value from X-Webhook-Signature header
secret: Your webhook secret from webhook creation/regeneration
Returns:
True if signature is valid, False otherwise
'''
expected_signature = hmac.new(
secret.encode('utf-8'),
payload_bytes,
hashlib.sha256
).hexdigest()
# Use compare_digest to prevent timing attacks
return hmac.compare_digest(expected_signature, signature)
# Example usage in FastAPI
app = FastAPI()
@app.post('/webhook')
async def webhook(request: Request):
signature = request.headers.get('X-Webhook-Signature')
payload_bytes = await request.body()
# Validate signature (optional but recommended)
if signature and not verify_webhook_signature(payload_bytes, signature, WEBHOOK_SECRET):
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='Invalid signature')
# Process event
event = await request.json()
# ... your business logic here ...
return {'status': 'received'}
When users unsubscribe, you may receive both an unsubscribe event AND a text message with a keyword (STOP, BAJA, parar, etc.). Ensure your system handles these idempotently to avoid duplicate processing.
User Text Message
{
"event_type": "rcs.in.text",
"agent_id": "spirius_notifications_agent",
"send_time": "2025-10-12T15:15:30.995131Z",
"event_id": "evt_a1b2c3d4e5f678901234",
"sender_phone_number": "+1234567890",
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"text": "Hello, I have a question"
}
File Upload (User Sent Image/Document)
{
"event_type": "rcs.in.file",
"agent_id": "spirius_notifications_agent",
"send_time": "2025-11-04T17:41:04.790512Z",
"event_id": "evt_9f8e7d6c5b4a32109876",
"sender_phone_number": "+34693363516",
"message_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"user_file": {
"payload": {
"mime_type": "image/heic",
"file_size_bytes": 910755,
"file_name": "IMG_1644.heic",
"file_uri": "https://rcs-copper-eu.googleapis.com/blob/..."
},
"thumbnail": {
"mime_type": "image/jpeg",
"file_size_bytes": 8249,
"file_uri": "https://rcs-copper-eu.googleapis.com/blob/..."
}
}
}
Reply Suggestion (User Clicked Quick Reply)
{
"event_type": "rcs.in.suggested.reply",
"agent_id": "spirius_notifications_agent",
"send_time": "2025-10-12T15:18:00.000000Z",
"event_id": "evt_fedcba9876543210abcd",
"sender_phone_number": "+1234567890",
"message_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"content_message": {
"suggestion_response": {
"postback_data": "action_confirm",
"text": "Confirm",
"type": "REPLY"
}
}
}
Action Button (User Clicked Link/URL)
{
"event_type": "rcs.in.suggested.action",
"agent_id": "spirius_notifications_tggofuud_agent",
"send_time": "2025-11-04T17:49:06.726341Z",
"event_id": "evt_123456789abcdef01234",
"sender_phone_number": "+34693363516",
"message_id": "8ca1552a-b8a8-4068-bbf4-1046760224f4",
"content_message": {
"suggestion_response": {
"postback_data": "book_nature_event",
"text": "Book now",
"type": "ACTION"
}
}
}
Message Delivered Event
{
"event_type": "rcs.out.delivered",
"agent_id": "spirius_notifications_agent",
"send_time": "2025-10-12T15:16:00.123456Z",
"sender_phone_number": "+1234567890",
"event_id": "evt_d1e2f3a4b5c6d7e8f9a0",
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"client_message_id": "client_msg_001",
"create_time": "2025-10-12T15:16:00.123456Z"
}
Message Read Event
{
"event_type": "rcs.out.read",
"agent_id": "spirius_notifications_agent",
"send_time": "2025-10-12T15:17:30.456789Z",
"sender_phone_number": "+1234567890",
"event_id": "evt_e1f2a3b4c5d6e7f8a9b0",
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"client_message_id": "client_msg_001",
"create_time": "2025-10-12T15:17:30.456789Z"
}
Message Failed Event
{
"event_type": "rcs.out.failed",
"agent_id": "spirius_notifications_agent",
"send_time": "2025-10-12T15:18:00.000000Z",
"event_id": "evt_f1a2b3c4d5e6f7a8b9c0",
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"client_message_id": "client_msg_001",
"create_time": "2025-10-12T15:18:00.000000Z"
}
User Subscribed Event (Google Messages UI)
{
"event_type": "rcs.sub.subscribed",
"agent_id": "spirius_notifications_agent",
"send_time": "2025-10-12T15:21:00.000000Z",
"event_id": "evt_1a2b3c4d5e6f7a8b9c0d",
"create_time": "2025-10-12T15:21:00.000000Z"
}
User Unsubscribed Event (Google Messages UI)
{
"event_type": "rcs.sub.unsubscribed",
"agent_id": "spirius_notifications_agent",
"send_time": "2025-10-12T15:20:00.000000Z",
"event_id": "evt_2b3c4d5e6f7a8b9c0d1e",
"create_time": "2025-10-12T15:20:00.000000Z"
}
Test Delivered Event (Dry Run Mode)
{
"event_type": "test.rcs.out.delivered",
"agent_id": "spirius_notifications_agent",
"send_time": "2025-10-12T15:24:00.000000Z",
"event_id": "evt_3c4d5e6f7a8b9c0d1e2f",
"message_id": "9b2d5c8e-1234-4abc-9def-0123456789ab",
"client_message_id": "client_msg_test_001",
"create_time": "2025-10-12T15:24:00.000000Z"
}
| agent_id required | string (Agent Id) Agent identifier (e.g., 'spirius_notifications_agent') | ||||||
| send_time required | string (Send Time) Event send time (RFC3339) | ||||||
Sender Phone Number (string) or Sender Phone Number (null) (Sender Phone Number) User's phone number in E.164 format | |||||||
Any of string (Sender Phone Number) User's phone number in E.164 format | |||||||
Message Id (string) or Message Id (null) (Message Id) Message identifier for tracking | |||||||
Any of string (Message Id) Message identifier for tracking | |||||||
Client Message Id (string) or Client Message Id (null) (Client Message Id) Optional client-provided message ID from original request | |||||||
Any of string (Client Message Id) Optional client-provided message ID from original request | |||||||
Text (string) or Text (null) (Text) Message text content | |||||||
Any of string (Text) Message text content | |||||||
ContentMessage (object) or null Structured message content | |||||||
Any of
| |||||||
UserFile (object) or null File uploaded by user (images, videos, documents) | |||||||
Any of
| |||||||
Event Type (string) or Event Type (null) (Event Type) Event type (message.delivered, message.read, user.typing, etc.) - added outside model | |||||||
Any of string (Event Type) Event type (message.delivered, message.read, user.typing, etc.) - added outside model | |||||||
Event Id (string) or Event Id (null) (Event Id) Unique Spirius event identifier for deduplication (format: evt_*) - present for all events | |||||||
Any of string (Event Id) Unique Spirius event identifier for deduplication (format: evt_*) - present for all events | |||||||
Create Time (string) or Create Time (null) (Create Time) Event creation time (RFC3339) | |||||||
Any of string (Create Time) Event creation time (RFC3339) | |||||||
| property name* additional property | any | ||||||
{- "agent_id": "spirius_notifications_agent",
- "event_id": "evt_a1b2c3d4e5f678901234",
- "event_type": "rcs.in.text",
- "message_id": "550e8400-e29b-41d4-a716-446655440000",
- "send_time": "2025-10-12T15:15:30.995131Z",
- "sender_phone_number": "+1234567890",
- "text": "Hello, I have a question"
}nullSend a rich RCS message asynchronously using a specific RBM agent.
This endpoint requires X-API-Key header authentication. It validates the request, publishes the message to a queue for async processing, and returns immediately. The actual RBM API call happens asynchronously in the worker service.
Async Processing:
Message Tracking:
message_id in the response is your tracking ID for this messageclient_message_id to correlate with your own system| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
| recipient required | string (Recipient) The user's phone number in E.164 format. | ||||||||
| agent_id required | string (Agent Id) The RBM agent identifier. | ||||||||
required | object (rcs_api__models__rbm_message_models__ContentMessage) | ||||||||
| |||||||||
Message Traffic Type (string) or Message Traffic Type (null) (Message Traffic Type) Traffic type to categorize the message. Defaults to agent's use case if not specified. | |||||||||
Any of string (Message Traffic Type) Enum: "AUTHENTICATION" "TRANSACTION" "PROMOTION" "SERVICEREQUEST" "ACKNOWLEDGEMENT" "MESSAGE_TRAFFIC_TYPE_UNSPECIFIED" Traffic type to categorize the message. Defaults to agent's use case if not specified. | |||||||||
Client Message Id (string) or Client Message Id (null) (Client Message Id) Optional client-provided message ID for tracking in your system. Will be included in webhook events. | |||||||||
Any of <= 256 characters string (Client Message Id) <= 256 characters Optional client-provided message ID for tracking in your system. Will be included in webhook events. | |||||||||
| dry_run | boolean (Dry Run) Default: false When true, simulates message sending without calling RBM API. Generates | ||||||||
{- "recipient": "string",
- "agent_id": "string",
- "message": {
- "text": "string",
- "content_info": {
- "force_refresh": false,
}, - "rich_card": {
- "standalone_card": {
- "card_orientation": "HORIZONTAL",
- "thumbnail_image_alignment": "LEFT",
- "card_content": {
- "title": "string",
- "description": "string",
- "media": {
- "height": "SHORT",
- "content_info": {
- "force_refresh": false,
}
}, - "suggestions": [
- {
- "reply": {
- "text": "string",
- "postback_data": "string"
}, - "action": {
- "text": "string",
- "postback_data": "string",
- "dial_action": {
- "phone_number": "string"
}, - "open_url_action": {
- "application": "BROWSER",
- "webview_view_mode": "FULL",
- "description": "string"
}, - "view_location_action": {
- "lat_long": {
- "latitude": null,
- "longitude": null
}, - "label": "string"
}, - "create_calendar_event_action": {
- "start_time": "string",
- "end_time": "string",
- "title": "string",
- "description": "string"
}
}
}
]
}
}, - "carousel_card": {
- "card_width": "SMALL",
- "card_contents": [
- {
- "title": "string",
- "description": "string",
- "media": {
- "height": "SHORT",
- "content_info": {
- "force_refresh": false,
}
}, - "suggestions": [
- {
- "reply": {
- "text": "string",
- "postback_data": "string"
}, - "action": {
- "text": "string",
- "postback_data": "string",
- "dial_action": {
- "phone_number": null
}, - "open_url_action": {
- "url": null,
- "application": null,
- "webview_view_mode": null,
- "description": null
}, - "view_location_action": {
- "lat_long": null,
- "label": null
}, - "create_calendar_event_action": {
- "start_time": null,
- "end_time": null,
- "title": null,
- "description": null
}
}
}
]
}, - {
- "title": "string",
- "description": "string",
- "media": {
- "height": "SHORT",
- "content_info": {
- "force_refresh": false,
}
}, - "suggestions": [
- {
- "reply": {
- "text": "string",
- "postback_data": "string"
}, - "action": {
- "text": "string",
- "postback_data": "string",
- "dial_action": {
- "phone_number": null
}, - "open_url_action": {
- "url": null,
- "application": null,
- "webview_view_mode": null,
- "description": null
}, - "view_location_action": {
- "lat_long": null,
- "label": null
}, - "create_calendar_event_action": {
- "start_time": null,
- "end_time": null,
- "title": null,
- "description": null
}
}
}
]
}
]
}
}, - "suggestions": [
- {
- "reply": {
- "text": "string",
- "postback_data": "string"
}, - "action": {
- "text": "string",
- "postback_data": "string",
- "dial_action": {
- "phone_number": "string"
}, - "open_url_action": {
- "application": "BROWSER",
- "webview_view_mode": "FULL",
- "description": "string"
}, - "view_location_action": {
- "lat_long": {
- "latitude": 0,
- "longitude": 0
}, - "label": "string"
}, - "create_calendar_event_action": {
- "start_time": "string",
- "end_time": "string",
- "title": "string",
- "description": "string"
}
}
}
]
}, - "message_traffic_type": "AUTHENTICATION",
- "client_message_id": "string",
- "dry_run": false
}{- "message_id": "string",
- "client_message_id": "string"
}List all testers for a specific agent.
This endpoint retrieves all test devices registered for the specified agent. Only testers associated with agents assigned to the authenticated client are accessible.
Query Parameters:
Returns:
| agent_id required | string (Agent Id) Agent ID to list testers for |
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
{- "testers": [
- {
- "name": "string",
- "phone_number": "string",
- "agent_id": "string",
- "invite_status": "string",
- "label": "string"
}
], - "total_count": 0
}Add a tester to an agent.
This endpoint registers a test device for the specified agent. The device owner will receive an invitation to become a tester.
Request Body:
Returns:
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
| phone_number required | string (Phone Number) ^\+[1-9]\d{1,14}$ Phone number in E.164 format (e.g., +1234567890) |
| agent_id required | string (Agent Id) Agent ID to add the tester to |
Label (string) or Label (null) (Label) Optional label for this tester (e.g., 'John iOS') | |
Any of <= 25 characters string (Label) <= 25 characters Optional label for this tester (e.g., 'John iOS') | |
{- "phone_number": "string",
- "agent_id": "string",
- "label": "string"
}{- "name": "string",
- "phone_number": "string",
- "agent_id": "string",
- "invite_status": "string",
- "label": "string"
}Get a specific tester by phone number.
Query Parameters:
Returns:
| phone_number required | string (Phone Number) |
| agent_id required | string (Agent Id) Agent ID |
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
{- "name": "string",
- "phone_number": "string",
- "agent_id": "string",
- "invite_status": "string",
- "label": "string"
}Update a tester's Spirius-specific fields.
This endpoint updates only the label field. All RBM fields are synced automatically from Google's API to ensure consistency.
Query Parameters:
Request Body:
Returns:
| phone_number required | string (Phone Number) |
| agent_id required | string (Agent Id) Agent ID |
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
Label (string) or Label (null) (Label) Updated label for this tester (e.g., 'John iOS') | |
Any of <= 25 characters string (Label) <= 25 characters Updated label for this tester (e.g., 'John iOS') | |
{- "label": "string"
}{- "name": "string",
- "phone_number": "string",
- "agent_id": "string",
- "invite_status": "string",
- "label": "string"
}Remove a tester from an agent.
This endpoint removes a test device registration from the specified agent.
Query Parameters:
Returns:
| phone_number required | string (Phone Number) |
| agent_id required | string (Agent Id) Agent ID |
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
{- "detail": [
- {
- "loc": [
- "string"
], - "msg": "string",
- "type": "string",
- "input": null,
- "ctx": { }
}
]
}Check if a phone number supports RCS messaging and retrieve supported features.
**Caching Strategy:**
- Results are cached globally (shared across all clients)
- RCS-supported devices: 7 day cache TTL
- Non-RCS devices: 24 hour cache TTL
- Use `force_refresh=true` to bypass cache
**Use Cases:**
- Pre-send capability check for campaign planning
- Testing specific devices during development
- Integration workflows that route based on capability
**Note:** When sending messages via `/messages`, capability checking happens
automatically in the background - you don't need to call this endpoint explicitly
for every message send.
| phone required | string (Phone) Examples: +1234567890 Phone number in E.164 format (e.g., +1234567890) |
| agent_id required | string (Agent Id) Examples: agent_id=notifications RBM agent ID to use for capability check |
| force_refresh | boolean (Force Refresh) Default: false Force fresh capability check, bypassing cache. Useful for testing. |
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
{- "phone_number": "+1234567890",
- "rcs_supported": true,
- "features": [
- "RICHCARD",
- "ACTION_CREATE_CALENDAR_EVENT",
- "ACTION_DIAL",
- "ACTION_OPEN_URL",
- "ACTION_SHARE_LOCATION",
- "ACTION_VIEW_LOCATION"
], - "checked_at": "2025-10-13T10:30:00Z",
- "cached": true
}Register a new webhook to receive RCS events for a specific agent.
**Security:**
- Webhook secret is automatically generated server-side (64-char hex)
- Secret is returned in response - store it securely for HMAC verification
- HTTPS required in production environments
**Event Filtering:**
- Use `enabled_events` to subscribe to specific event types
- Default: `["*"]` receives all events (broadcast pattern)
- Multiple webhooks per agent supported - events broadcast to ALL matching webhooks
**Available Event Types:**
- `"*"` - All events (wildcard)
Inbound (user messages):
- `"rcs.in.text"` - User sent a text message
- `"rcs.in.file"` - User sent a file (image, video, document)
- `"rcs.in.suggested.reply"` - User clicked a reply suggestion button
- `"rcs.in.suggested.action"` - User clicked an action suggestion (URL, dial, location)
Outbound (message status):
- `"rcs.out.delivered"` - Message delivered to user's device
- `"rcs.out.read"` - User opened and read the message
- `"rcs.out.failed"` - Message failed to send
Subscription:
- `"rcs.sub.subscribed"` - User subscribed via Google Messages
- `"rcs.sub.unsubscribed"` - User unsubscribed (includes STOP words)
**Example Filters:**
- `["*"]` - Receive all events
- `["rcs.out.delivered", "rcs.out.failed"]` - Only delivery status
- `["rcs.sub.unsubscribed"]` - Only unsubscribe events
- `["rcs.in.text", "rcs.in.suggested.reply"]` - Only inbound messages
- `["rcs.out.delivered", "rcs.out.read"]` - Message engagement tracking
**Event Delivery:**
- Events are signed with HMAC-SHA256 using the webhook secret
- Set `is_enabled: false` to create in disabled state (optional, defaults to true)
- Add `description` for organizational purposes (optional)
**Constraints:**
- Maximum 5 webhooks per agent
- Cannot create duplicate (agent_id + webhook_url) combination
- Returns 409 Conflict if same URL already registered for agent
- Returns 400 Bad Request if webhook limit exceeded
**Verification:**
- Use `POST /v1/webhooks/{id}/test` to verify webhook is working
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
| agent_id required | string (Agent Id) [ 1 .. 100 ] characters Agent identifier for this webhook |
| webhook_url required | string <uri> (Webhook Url) [ 1 .. 2083 ] characters Webhook URL to receive events |
| is_enabled | boolean (Is Enabled) Default: true Whether webhook is enabled |
| enabled_events | Array of strings (Enabled Events) Default: ["*"] Event types to receive. Use ['*'] for all events, or specify individual event types |
Description (string) or Description (null) (Description) Optional description for organizational purposes | |
Any of <= 255 characters string (Description) <= 255 characters Optional description for organizational purposes | |
{- "agent_id": "string",
- "is_enabled": true,
- "enabled_events": [
- "*"
], - "description": "string"
}{- "id": "550e8400-e29b-41d4-a716-446655440000",
- "agent_id": "spirius-notifications",
- "webhook_secret": "whsec_a1b2c3d4e5f6...",
- "is_enabled": true,
- "enabled_events": [
- "*"
], - "description": "Production webhook for all events",
- "created_at": "2024-01-15T10:30:00Z",
- "updated_at": "2024-01-15T10:30:00Z"
}List all webhook configurations for a specific agent.
**Multiple Webhooks Pattern:**
- An agent can have multiple webhook endpoints
- Events are broadcast to ALL enabled webhooks that match the event filter
- Use `enabled_events` to control which webhooks receive which events
**Use Cases:**
- Separate endpoints for different services (analytics, CRM, compliance)
- Testing webhooks alongside production webhooks
- Third-party integrations (e.g., Segment, Customer.io)
| agent_id required | string (Agent Id) |
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
[- {
- "id": "550e8400-e29b-41d4-a716-446655440000",
- "agent_id": "spirius-notifications",
- "webhook_secret": "whsec_a1b2c3...",
- "is_enabled": true,
- "enabled_events": [
- "*"
], - "description": "Production webhook - all events",
- "created_at": "2024-01-15T10:30:00Z",
- "updated_at": "2024-01-15T10:30:00Z"
}, - {
- "id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
- "agent_id": "spirius-notifications",
- "webhook_secret": "whsec_x9y8z7...",
- "is_enabled": true,
- "enabled_events": [
- "message.delivered",
- "message.failed"
], - "description": "Analytics - delivery events only",
- "created_at": "2024-01-16T14:20:00Z",
- "updated_at": "2024-01-16T14:20:00Z"
}
]Retrieve a specific webhook configuration by ID.
**Note:** Webhooks are agent-owned. Any authenticated client can view any webhook.
| webhook_id required | string <uuid> (Webhook Id) Webhook ID |
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
{- "id": "550e8400-e29b-41d4-a716-446655440000",
- "agent_id": "spirius-notifications",
- "webhook_secret": "whsec_a1b2c3...",
- "is_enabled": true,
- "created_at": "2024-01-15T10:30:00Z",
- "updated_at": "2024-01-15T10:30:00Z"
}Update webhook configuration (partial update).
**Updatable fields:**
- `webhook_url`: Change webhook endpoint URL
- `is_enabled`: Enable or disable webhook temporarily
- `enabled_events`: Change event subscription
- `description`: Update organizational description
**Available Event Types for `enabled_events`:**
- `"*"` - All events (wildcard)
- `"message.sent"` - User sent a message or clicked a button
- `"message.delivered"` - Message delivered to device
- `"message.read"` - User read the message
- `"message.failed"` - Message failed to send
- `"user.typing"` - User is typing
- `"user.subscribed"` - User subscribed via Google Messages
- `"user.unsubscribed"` - User unsubscribed via Google Messages
- `"user.opted_in"` - User opted in via API/other channels
- `"user.opted_out"` - User opted out via API/other channels
**Immutable fields:**
- `agent_id`: Cannot be changed (delete and recreate instead)
- `webhook_secret`: Cannot be updated (use regenerate-secret endpoint)
**All fields are optional** - only provide fields you want to update.
| webhook_id required | string <uuid> (Webhook Id) Webhook ID |
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
Webhook Url (string) or Webhook Url (null) (Webhook Url) New webhook URL | |
Any of [ 1 .. 2083 ] characters string <uri> (Webhook Url) [ 1 .. 2083 ] characters New webhook URL | |
Is Enabled (boolean) or Is Enabled (null) (Is Enabled) Whether webhook is enabled | |
Any of boolean (Is Enabled) Whether webhook is enabled | |
Array of Enabled Events (strings) or Enabled Events (null) (Enabled Events) Event types to receive | |
Any of Array string | |
Description (string) or Description (null) (Description) Webhook description | |
Any of <= 255 characters string (Description) <= 255 characters Webhook description | |
{- "is_enabled": true,
- "enabled_events": [
- "string"
], - "description": "string"
}{- "id": "550e8400-e29b-41d4-a716-446655440000",
- "agent_id": "spirius-notifications",
- "webhook_secret": "whsec_a1b2c3...",
- "is_enabled": false,
- "created_at": "2024-01-15T10:30:00Z",
- "updated_at": "2024-01-15T16:45:00Z"
}Permanently delete a webhook configuration.
**⚠️ Warning:**
- Deletion is permanent and cannot be undone
- All event delivery to this webhook will stop immediately
- Consider disabling instead (`is_enabled: false`) for temporary pause
**Alternative:** Use `PATCH /v1/webhooks/{id}` with `is_enabled: false`
to temporarily disable without losing configuration.
| webhook_id required | string <uuid> (Webhook Id) Webhook ID |
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
{- "detail": "Webhook 550e8400-e29b-41d4-a716-446655440000 not found"
}Generate a new webhook secret (secret rotation).
**Use cases:**
- Secret compromised or exposed
- Periodic security policy (rotate every 90 days)
- Initial deployment mistake
**⚠️ Important:**
- Old secret is immediately invalidated
- Update your webhook verification code before events arrive
- Test webhook after regeneration to confirm new secret works
**Process:**
1. Call this endpoint to get new secret
2. Update your webhook handler to verify with new secret
3. Test webhook with `POST /v1/webhooks/{id}/test`
| webhook_id required | string <uuid> (Webhook Id) Webhook ID |
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
{- "id": "550e8400-e29b-41d4-a716-446655440000",
- "webhook_secret": "whsec_x7y8z9...",
- "regenerated_at": "2024-01-20T09:15:00Z"
}Enable a webhook to resume receiving events.
| webhook_id required | string <uuid> (Webhook Id) Webhook ID |
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "agent_id": "string",
- "webhook_url": "string",
- "webhook_secret": "string",
- "is_enabled": true,
- "enabled_events": [
- "string"
], - "description": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}Disable a webhook to temporarily stop receiving events without deleting the configuration.
| webhook_id required | string <uuid> (Webhook Id) Webhook ID |
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
{- "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
- "agent_id": "string",
- "webhook_url": "string",
- "webhook_secret": "string",
- "is_enabled": true,
- "enabled_events": [
- "string"
], - "description": "string",
- "created_at": "2019-08-24T14:15:22Z",
- "updated_at": "2019-08-24T14:15:22Z"
}Send a test event to the webhook URL to verify it's working correctly.
**What it does:**
- Sends a test event with `event_type: "webhook.test"`
- Measures response time
- Validates webhook endpoint is reachable
**Use cases:**
- Verify webhook after initial setup
- Test after URL change
- Confirm webhook after secret regeneration
- Troubleshoot delivery issues
**Test event format:**
```json
{
"event_type": "webhook.test",
"timestamp": "2024-01-15T10:30:00Z",
"webhook_id": "550e8400-e29b-41d4-a716-446655440000",
"agent_id": "spirius-notifications",
"message": "This is a test event from Spirius RCS API"
}
```
**Success criteria:**
- Webhook returns 200, 201, 202, or 204
- Response received within 10 seconds
| webhook_id required | string <uuid> (Webhook Id) Webhook ID |
| x-api-key required | string (X-Api-Key) API key for authentication |
| x-client-id required | string (X-Client-Id) Client ID for identification |
{- "success": true,
- "status_code": 200,
- "response_time_ms": 145,
- "test_event_sent": {
- "event_type": "webhook.test",
- "timestamp": "2024-01-15T10:30:00Z"
}
}