Developer reference
AppRinger WA API
Send WhatsApp messages, manage templates, and embed the AR Inbox UI inside your CRM. All endpoints live under
https://wa.appringer.co.in/api/v1
and authenticate with a Sanctum API key (issued from Settings → Integration → API keys).
https://wa.appringer.co.in/api/v1
- Authorization: Bearer <api-key>
- X-API-Key: <api-key>
Getting started
- Sign in to AR Inbox and open Settings → Integration → API keys.
- Create a key with the abilities you need (e.g.
messages:send,templates:read). The plaintext token is shown once — copy it immediately. - Make a sanity call to GET /me to confirm the key + abilities.
- Send your first message with POST /messages/template.
Authentication
Every request must carry an API key in either of these headers (use whichever your HTTP client prefers — they're equivalent):
Authorization: Bearer wabt_xxxxxxxxxxxxxxxxxxxxxxxx
X-API-Key: wabt_xxxxxxxxxxxxxxxxxxxxxxxx
Each key is scoped to a single merchant and a fixed set of abilities (Sanctum scopes). Calling an endpoint with a key that's missing the required ability returns 403 forbidden. Keys can be revoked at any time from the integration panel; revocation is immediate.
Response envelope
Every /api/v1/* response is wrapped in a consistent envelope. Branch on code (numeric, mirrors the HTTP status) or the HTTP status itself — both will always agree.
{
"code": 200,
"data": { … },
"message": "OK"
}
{
"code": 422,
"data": null,
"error": "validation_failed",
"message": "The body field is required.",
"errors": {
"body": ["The body field is required."]
}
}
On errors, data is always null and a top-level error slug identifies the failure class. Field-level validation errors are surfaced as a top-level errors map (Laravel-conventional shape). Subscription-blocked responses additionally include a pay_now URL.
Error codes
The error slug returned in the envelope identifies the failure cleanly without parsing the prose message. Common values across endpoints:
| HTTP | Error slug | When |
|---|---|---|
| 400 | bad_request | Malformed request (e.g. invalid JSON body). |
| 401 | unauthenticated | Missing, malformed, or revoked API key. |
| 403 | forbidden | Key is valid but missing the required ability for this endpoint. |
| 404 | not_found | The referenced resource (template id, etc.) does not exist on this merchant. |
| 409 | conflict | The mutation conflicts with current state (e.g. duplicate template name+language). |
| 422 | validation_failed | Request body / query failed validation. Includes errors map. |
| 422 | invalid_variables | Template variables don't match the template definition. |
| 402 | subscription_blocked | Merchant's annual platform fee is unpaid. Response includes pay_now. |
| 429 | too_many_requests | Rate limit exceeded; retry with backoff. |
| 500 | server_error | Unhandled exception inside AR Inbox. Logged with a correlation id; safe to retry. |
A handful of domain-specific errors (SessionWindowClosedException, WabaNotConnectedException, InsufficientWalletBalanceException) surface their class basename as error rather than a slug — these are documented inline per endpoint below.
Outbound Webhooks
Configure real-time webhooks under Settings → Developer & Webhook Settings to receive instant HTTP POST callbacks when message statuses update or inbound button replies occur.
Supported Event Types
| Event | Description |
|---|---|
| msg_sent | Fired when an outbound message is queued and dispatched to Meta. |
| msg_delivered | Fired when Meta confirms message delivery to the recipient's phone. |
| msg_read | Fired when the recipient opens and reads the WhatsApp message. |
| msg_failed | Fired when message delivery fails due to invalid recipient or Meta error. |
| msg_skipped | Fired when a message is skipped because the recipient's WhatsApp availability probability score falls below the configured threshold. |
| button_reply | Fired when an inbound quick-reply or button response is received. |
HMAC Signature Verification
When a secret is configured, every HTTP POST request contains the X-AppRinger-Signature header computed via HMAC SHA-256 over the raw JSON payload:
$signature = hash_hmac('sha256', $rawBody, $webhookSecret);
Sample Payloads by Event
{
"event": "msg_skipped",
"msg_id": "MSG_CUSTOM_001",
"camp_id": "CAMP_AUGUST_01",
"client_reference": "order-1234",
"wa_message_id": null,
"status": "skipped_low_probability",
"recipient": "+919876543210",
"template_name": "welcome_alert",
"wa_probability_score": {
"phone": "+919876543210",
"delivery_probability": 15.4,
"read_probability": 8.2,
"risk_level": "high",
"data_points": 142
},
"timestamp": "2026-08-01T08:50:00+00:00"
}
{
"event": "msg_sent",
"msg_id": "MSG_CUSTOM_001",
"camp_id": "CAMP_AUGUST_01",
"client_reference": "order-1234",
"wa_message_id": "wamid.HBgMOTE4MTQzOTU2NDM4...",
"status": "sent",
"recipient": "+919876543210",
"timestamp": "2026-08-01T08:50:00+00:00"
}
{
"event": "msg_delivered",
"msg_id": "MSG_CUSTOM_001",
"camp_id": "CAMP_AUGUST_01",
"client_reference": "order-1234",
"wa_message_id": "wamid.HBgMOTE4MTQzOTU2NDM4...",
"status": "delivered",
"recipient": "+919876543210",
"timestamp": "2026-08-01T08:50:02+00:00"
}
{
"event": "msg_read",
"msg_id": "MSG_CUSTOM_001",
"camp_id": "CAMP_AUGUST_01",
"client_reference": "order-1234",
"wa_message_id": "wamid.HBgMOTE4MTQzOTU2NDM4...",
"status": "read",
"recipient": "+919876543210",
"timestamp": "2026-08-01T08:51:15+00:00"
}
{
"event": "msg_failed",
"msg_id": "MSG_CUSTOM_001",
"camp_id": "CAMP_AUGUST_01",
"client_reference": "order-1234",
"wa_message_id": "wamid.HBgMOTE4MTQzOTU2NDM4...",
"status": "failed",
"recipient": "+919876543210",
"error_code": "131026",
"error_title": "Undeliverable",
"error_message": "Unable to deliver message to recipient number",
"timestamp": "2026-08-01T08:50:05+00:00"
}
{
"event": "button_reply",
"wa_message_id": "wamid.HBgMOTE4MTQzOTU2NDM5...",
"from": "+919876543210",
"recipient": "+919717146603",
"body": "Confirm Order",
"button_reply": {
"title": "Confirm Order",
"id": "BTN_CONFIRM_123",
"payload": "BTN_CONFIRM_123"
},
"timestamp": "2026-08-01T08:52:00+00:00"
}
Sanity-check the API key
Returns the owning user, their merchant account and the abilities granted to the token. Hit this first after issuing a key to confirm credentials are wired up correctly.
curl -X GET 'https://wa.appringer.co.in/api/v1/me' \
-H 'X-API-Key: YOUR_API_KEY'
Response · 200
{
"code": 200,
"data": {
"user": {
"id": 1,
"name": "Riya Singh",
"email": "riya@acme.test",
"role": "merchant_admin"
},
"merchant": {
"id": 1,
"name": "Acme Pvt Ltd",
"slug": "acme"
},
"token": {
"name": "crm-prod",
"abilities": [
"messages:send",
"templates:read"
]
}
},
"message": "OK"
}
Errors
| HTTP | Error slug | When |
|---|---|---|
| 401 | unauthenticated | Missing, malformed, or revoked API key. |
Check the prepaid balance
Returns the current wallet balance, the configured low-balance threshold, and the per-message rates this merchant pays. Useful for surfacing a top-up nag inside your own dashboard before a send fails with InsufficientWalletBalanceException.
curl -X GET 'https://wa.appringer.co.in/api/v1/wallet/balance' \
-H 'X-API-Key: YOUR_API_KEY'
Response · 200
{
"code": 200,
"data": {
"merchant": {
"id": 1,
"slug": "acme"
},
"currency": "INR",
"balance": "1000.00",
"low_balance_threshold": "500.00",
"is_low_balance": false,
"rates": {
"session": "0.50",
"utility": "0.60",
"marketing": "1.20",
"authentication": "0.40",
"text": "0.50",
"template": "0.60"
}
},
"message": "OK"
}
Errors
| HTTP | Error slug | When |
|---|---|---|
| 401 | unauthenticated | Missing or invalid API key. |
| 403 | forbidden | Key is missing the wallet:read ability. |
| 402 | subscription_blocked | Annual platform fee unpaid. Response includes pay_now URL. |
Send a session (free-form) text message
Sends a plaintext WhatsApp message. Only allowed inside the 24-hour customer service window (i.e. the recipient must have messaged you in the last 24 hours). Use POST /messages/template to re-engage outside the window.
Request body
| Field | Type | Description |
|---|---|---|
|
to
required
|
string
|
E.164 recipient phone, no leading +. Required unless conversation_id is supplied. 5–32 chars. |
|
body
required
|
string
|
Message text. 1–4096 chars. |
|
conversation_id
optional
|
integer
|
AR Inbox conversation id. When set, the recipient + phone are inferred from the conversation and to can be omitted. |
|
phone_number_id
optional
|
integer
|
AR Inbox phone-number row id. Send from a specific number when the merchant has multiple connected numbers. Defaults to the merchant's default number. |
|
client_reference
optional
|
string
|
Your idempotency key / order id. Echoed back on the response and on every status webhook. ≤64 chars. |
|
msg_id
optional
|
string
|
Your custom message ID. Echoed back on status responses, status webhooks, and queryable via GET /messages/status/{msgId}. ≤128 chars. |
|
camp_id
optional
|
string
|
Campaign identifier for tracking campaign performance across webhooks. ≤128 chars. |
curl -X POST 'https://wa.appringer.co.in/api/v1/messages/text' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"to": "919876543210",
"body": "Hi, your order #1234 has shipped.",
"client_reference": "order-1234"
}'
Response · 202
{
"code": 202,
"data": {
"id": 42,
"status": "queued",
"client_reference": "order-1234",
"wa_message_id": null,
"billed_amount": "0.5000",
"currency": "INR"
},
"message": "Accepted"
}
status starts at queued and progresses to sent / delivered / read as Meta acknowledges. Occasionally Meta acks within the same request and you'll see sent already in this response. billed_amount is a decimal string (4 d.p.); wa_message_id is filled once Meta returns it (poll id if you need it synchronously).
Errors
| HTTP | Error slug | When |
|---|---|---|
| 401 | unauthenticated | Missing or invalid API key. |
| 403 | forbidden | Key is missing the messages:send ability. |
| 422 | validation_failed | Required field missing or malformed. Response includes errors: { field: [messages] }. |
| 422 | SessionWindowClosedException | Recipient has not messaged you in the last 24h. Send a template instead. |
| 402 | InsufficientWalletBalanceException | Wallet balance is below the cost of this send. Top up at the URL in pay_now. |
| 409 | WabaNotConnectedException | Merchant has no connected WhatsApp number. Complete the registration wizard in the AR Inbox dashboard. |
| 402 | subscription_blocked | Annual platform fee unpaid. |
Send an approved template message
Send one of your approved templates. Use this for any first-touch / re-engagement send outside the 24-hour session window — order confirmations, shipping updates, marketing nudges, OTPs. Pass either template_id (preferred — copy it from WhatsApp settings → Templates) or the template_name+language pair.
Authentication (OTP) Templates:
For authentication templates with a Copy Code or One-Tap Autofill button, you must supply the OTP code twice in the API request: once for the message body and once for the button. You can do this easily using the simplified variables and button_variables fields, or via raw components.
Example using variables:
{
"to": "919876543210",
"template_name": "otp_verification",
"language": "en_US",
"variables": {"1": "123456"},
"button_variables": ["123456"]
}
Request body
| Field | Type | Description |
|---|---|---|
|
to
required
|
string
|
E.164 recipient phone, no leading +. Required unless conversation_id is supplied. |
|
template_id
conditional
|
integer
|
AR Inbox template id. Required unless template_name + language are supplied. Copy it from the API id chip next to each template in the dashboard. |
|
template_name
conditional
|
string
|
Template name as registered with Meta. Required when template_id is omitted. |
|
language
conditional
|
string
|
BCP-47 language tag (e.g. en_US). Required when template_id is omitted. |
|
variables
optional
|
object
|
Positional map for {{1}}, {{2}}… in the body. We build Meta's components for you. Keys can be "1", "2"… or named (when your template defines named params). |
|
header_variables
optional
|
array
|
Values for header placeholders, in order. |
|
button_variables
optional
|
array
|
Values for URL-button or OTP/Authentication button (Copy Code / One-Tap) placeholders, in order. |
|
components
optional
|
array
|
Raw Meta-shape components array. Power-user escape hatch — wins over variables if both are sent. Use this when a header carries a media asset. |
|
conversation_id
optional
|
integer
|
AR Inbox conversation id. Optional alternative to to. |
|
phone_number_id
optional
|
integer
|
Send from a specific number. Defaults to the merchant's default. |
|
client_reference
optional
|
string
|
Your idempotency key / order id. Echoed back on the response and on every status webhook. |
|
msg_id
optional
|
string
|
Your custom message ID. Echoed back on status responses, status webhooks, and queryable via GET /messages/status/{msgId}. ≤128 chars. |
|
camp_id
optional
|
string
|
Campaign identifier for tracking campaign performance across webhooks. ≤128 chars. |
curl -X POST 'https://wa.appringer.co.in/api/v1/messages/template' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"to": "919876543210",
"template_id": 42,
"variables": {
"1": "Riya",
"2": "A-12"
},
"client_reference": "order-1234"
}'
Response · 202
{
"code": 202,
"data": {
"id": 43,
"status": "sent",
"client_reference": "order-1234",
"wa_message_id": "wamid.HBgMOTE4MTQzOTU2NDM4FQIAERgSMUQ3...",
"billed_amount": "0.6000",
"currency": "INR",
"template_name": "order_confirmation"
},
"message": "Accepted"
}
Identical envelope to /messages/text plus a template_name field for traceability. status is usually queued; if Meta acks before this response returns you may see sent directly. wa_message_id is filled once Meta returns the WAMID.
Errors
| HTTP | Error slug | When |
|---|---|---|
| 401 | unauthenticated | Missing or invalid API key. |
| 403 | forbidden | Key missing messages:send. |
| 404 | not_found | Template id / name+language doesn't match any template on this merchant. Run GET /templates to list what's available. |
| 422 | validation_failed | Required field missing or malformed. |
| 422 | invalid_variables | The variables map doesn't match what the template definition expects. |
| 402 | InsufficientWalletBalanceException | Wallet too low for the per-template rate. |
| 409 | WabaNotConnectedException | No connected WhatsApp number on this merchant. |
| 402 | subscription_blocked | Annual platform fee unpaid. |
Send a template message in bulk
Send an approved template to multiple recipients in one request. Useful for broadcasts and large campaigns.
Request body
| Field | Type | Description |
|---|---|---|
|
template_id
optional
|
integer
|
AR Inbox template id. |
|
template_name
optional
|
string
|
Template name as registered with Meta. |
|
language
optional
|
string
|
BCP-47 language tag. |
|
phone_number_id
optional
|
integer
|
Send from a specific number. Defaults to the merchant's default. |
|
recipients
required
|
array
|
List of recipient objects (up to 10000). |
|
recipients[].to
required
|
string
|
E.164 recipient phone. |
|
recipients[].variables
optional
|
object
|
Variables for this recipient. |
curl -X POST 'https://wa.appringer.co.in/api/v1/messages/bulk-template' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"template_id": 42,
"recipients": [
{
"to": "919876543210",
"variables": { "1": "Riya" },
"client_reference": "order-1"
}
]
}'
Response · 202
{
"message": "Bulk message queued successfully.",
"queued_count": 1
}
Errors
| HTTP | Error slug | When |
|---|---|---|
| 401 | unauthenticated | Missing or invalid API key. |
| 403 | forbidden | Key missing messages:send ability. |
| 422 | validation_failed | Required field missing or malformed. |
Check delivery status by msg_id
Query the current delivery status of a message using your custom msg_id, Meta's wa_message_id, or client_reference. Returns delivery timestamps and error details if the send failed.
curl -X GET 'https://wa.appringer.co.in/api/v1/messages/status/MSG_12345' \
-H 'X-API-Key: YOUR_API_KEY'
Response · 200
{
"code": 200,
"data": {
"id": 42,
"msg_id": "MSG_12345",
"camp_id": "CAMP_6789",
"client_reference": "order-1234",
"wa_message_id": "wamid.HBgMOTE4MTQzOTU2NDM4...",
"recipient": "+919876543210",
"status": "delivered",
"sent_at": "2026-08-01T08:50:00+00:00",
"delivered_at": "2026-08-01T08:50:02+00:00",
"read_at": null,
"failed_at": null,
"error_code": null,
"error_title": null,
"error_message": null,
"billed_amount": "0.5000",
"currency": "INR"
},
"message": "OK"
}
Errors
| HTTP | Error slug | When |
|---|---|---|
| 401 | unauthenticated | Missing or invalid API key. |
| 403 | forbidden | Key missing messages:send ability. |
| 404 | not_found | Message with the given msg_id not found. |
List your WhatsApp senders
Discovery endpoint for the phone_number_id you can pass to POST /messages/text and POST /messages/template when routing through a specific sender on a multi-line merchant. Omit phone_number_id on the send endpoints and we'll use the row flagged is_default: true here.
Query parameters
| Field | Type | Description |
|---|---|---|
|
status
optional
|
string
|
Filter by phone state. One of connected (default), pending, disconnected, or all. Only connected rows are valid as phone_number_id on the send endpoints; ?status=all is for auditing why a number disappeared. |
curl -X GET 'https://wa.appringer.co.in/api/v1/phone-numbers' \
-H 'X-API-Key: YOUR_API_KEY'
Response · 200
{
"code": 200,
"data": {
"data": [
{
"id": 7,
"display_phone_number": "+91 80 5555 5555",
"verified_name": "Acme Pvt Ltd",
"status": "connected",
"is_default": true,
"quality_rating": "GREEN"
},
{
"id": 12,
"display_phone_number": "+91 80 5555 7777",
"verified_name": "Acme Support",
"status": "connected",
"is_default": false,
"quality_rating": "GREEN"
}
],
"meta": {
"count": 2,
"status": "connected"
}
},
"message": "OK"
}
Rows are sorted with the default-flagged number first, then by id — same order as the picker on the dashboard. id is the value to pass as phone_number_id on the send endpoints; display_phone_number is for showing in your own UI.
Errors
| HTTP | Error slug | When |
|---|---|---|
| 401 | unauthenticated | Missing or invalid API key. |
| 403 | forbidden | Key is missing the messages:send ability. |
List your templates
Returns the catalog of templates registered on the merchant. By default a slim summary per row; pass ?detailed=true to also receive the full Meta-shape components + example blobs.
Tip: the API id chip next to each template in WhatsApp settings → Templates is the same id returned here — no need to hit this endpoint just to look up an id.
Query parameters
| Field | Type | Description |
|---|---|---|
|
category
optional
|
string
|
Filter by UTILITY, MARKETING, or AUTHENTICATION. |
|
status
optional
|
string
|
Filter by pending, approved, rejected, paused, disabled, or deleted. |
|
per_page
optional
|
integer
|
Page size, 1–200. Default 50. |
|
detailed
optional
|
boolean
|
Include full components + example blobs. Default false. |
curl -X GET 'https://wa.appringer.co.in/api/v1/templates?status=approved&category=UTILITY' \
-H 'X-API-Key: YOUR_API_KEY'
Response · 200
{
"code": 200,
"data": {
"data": [
{
"id": 3,
"name": "new_lead_collected",
"language": "en",
"category": "UTILITY",
"status": "approved",
"meta_template_id": "867792625617937",
"approved_at": "2026-05-20T08:34:00+00:00",
"body_preview": "🛎 New Lead Collected\nCustomer: *{{1}}*…",
"variables_count": 4,
"buttons_count": 0,
"has_header": false,
"has_footer": false
}
],
"meta": {
"page": 1,
"per_page": 50,
"total": 1,
"total_pages": 1
}
},
"message": "OK"
}
Errors
| HTTP | Error slug | When |
|---|---|---|
| 401 | unauthenticated | Missing or invalid API key. |
| 403 | forbidden | Key missing templates:read. |
| 422 | validation_failed | Unknown category or status value, or per_page out of range. |
Register a new template for Meta review
Submits a draft template to Meta for review. Lands locally in pending status; Meta typically returns an approval within minutes and the row auto-flips to approved via webhook. Once approved, send it with POST /messages/template.
Request body
| Field | Type | Description |
|---|---|---|
|
name
required
|
string
|
Snake_case template id, max 128 chars. Must match /^[a-z0-9_]+$/i. Must be unique per language. |
|
language
required
|
string
|
BCP-47 language tag, e.g. en_US, hi, mr. |
|
category
required
|
string
|
One of UTILITY (transactional), MARKETING (promotional), AUTHENTICATION (OTP). |
|
components
required
|
array
|
Meta-shape components array. At minimum a BODY component with text. Use {{1}}, {{2}}… for placeholders. |
|
example
optional
|
object
|
Sample values Meta uses while reviewing. Required for templates with placeholders, e.g. { "body_text": [["Riya", "A-12"]] }. |
curl -X POST 'https://wa.appringer.co.in/api/v1/templates' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"name": "order_confirmation",
"language": "en_US",
"category": "UTILITY",
"components": [
{ "type": "BODY", "text": "Hi {{1}}, your order {{2}} is confirmed." }
],
"example": { "body_text": [["Riya", "A-12"]] }
}'
Response · 201
{
"code": 201,
"data": {
"id": 5,
"name": "order_confirmation",
"language": "en_US",
"category": "UTILITY",
"status": "pending",
"meta_template_id": null,
"approved_at": null,
"components": [
{
"type": "BODY",
"text": "Hi {{1}}, your order {{2}} is confirmed."
}
],
"example": {
"body_text": [
[
"Riya",
"A-12"
]
]
}
},
"message": "Created"
}
Errors
| HTTP | Error slug | When |
|---|---|---|
| 401 | unauthenticated | Missing or invalid API key. |
| 403 | forbidden | Key missing templates:write. |
| 422 | validation_failed | Malformed components, missing example for a placeholder template, etc. |
| 409 | conflict | A template with the same name+language already exists on this merchant. |
Delete a template
Deletes a template. Note that this only removes the template from AR Inbox; you must still delete it in the Meta Business Manager.
curl -X DELETE 'https://wa.appringer.co.in/api/v1/templates/42' \
-H 'X-API-Key: YOUR_API_KEY'
Response · 200
{
"message": "Template deleted successfully."
}
Errors
| HTTP | Error slug | When |
|---|---|---|
| 401 | unauthenticated | Missing or invalid API key. |
| 403 | forbidden | Key missing templates:write. |
| 404 | not_found | Template not found for this merchant. |
Mint a JWT for embedded SSO (BETA)
BETA / Testing Feature: Server-to-server mint of a short-lived JWT for Iframe SSO embedding inside partner CRMs. Use the returned embed_url as an iframe src to drop the AR Inbox UI inside your CRM with the targeted user already signed in. With auto_provision: true, the user is created on-the-fly if they don't exist yet.
Request body
| Field | Type | Description |
|---|---|---|
|
email
optional
|
string
|
Target user's email. Required unless user_id is supplied. |
|
user_id
optional
|
integer
|
AR Inbox user id. Required unless email is supplied. |
|
ttl_seconds
optional
|
integer
|
Token lifetime, 60–86400 (24h). Default 300 (5 min). |
|
redirect_to
optional
|
string
|
Path inside AR Inbox to land on. Must start with /. Defaults to /inbox. |
|
auto_provision
optional
|
boolean
|
Create the user if not found. Requires email + name + role. |
|
role
optional
|
string
|
When auto-provisioning: agent or manager. |
|
name
optional
|
string
|
When auto-provisioning: display name, max 120 chars. |
|
features
optional
|
array
|
When auto-provisioning: subset of ["inbox", "tasks"] to grant. |
curl -X POST 'https://wa.appringer.co.in/api/v1/iframe-tokens' \
-H 'X-API-Key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"email": "agent@acme.com",
"ttl_seconds": 300,
"redirect_to": "/inbox",
"auto_provision": true,
"role": "agent",
"features": ["inbox", "tasks"]
}'
Response · 200
{
"code": 200,
"data": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOjIsImV4cCI6MTczMjAwMDAwMH0.signature",
"expires_at": "2026-05-20T14:00:00+00:00",
"embed_url": "https://app.example.com/embed?token=eyJ0eXAi...",
"ttl_seconds": 300,
"user": {
"id": 2,
"email": "agent@acme.com",
"name": "Agent Two",
"role": "agent"
},
"provisioned": false
},
"message": "OK"
}
Errors
| HTTP | Error slug | When |
|---|---|---|
| 401 | unauthenticated | Missing or invalid API key. |
| 403 | forbidden | Key missing iframe:mint or not merchant-scoped. |
| 404 | not_found | User not found. Pass auto_provision: true + name + role to create them on the fly. |
| 422 | provision_failed | Auto-provision validation failed. Response includes errors map. |
| 422 | user_inactive | Target user is disabled. |
| 422 | merchant_missing | API key's merchant row is missing (rare; usually a deleted merchant). |