REST API reference
For scripts and your own software. Agents use the same operations through the MCP server.
Authentication
Every call takes your key in a header:
curl https://api.dmwire.ai/v1/usage \
-H "X-API-Key: $DMWIRE_API_KEY"{
"plan": "free",
"status": "active",
"day": "2026-10-17",
"sends": 0,
"sends_cap": 50,
"new_contacts": 0,
"new_contacts_cap": 10,
"accounts_total": 0,
"accounts_connected": 0,
"accounts_cap": 1
}Authorization: Bearer works the same way:
curl https://api.dmwire.ai/v1/usage \
-H "Authorization: Bearer $DMWIRE_API_KEY"{
"plan": "free",
"status": "active",
"day": "2026-10-17",
"sends": 0,
"sends_cap": 50,
"new_contacts": 0,
"new_contacts_cap": 10,
"accounts_total": 0,
"accounts_connected": 0,
"accounts_cap": 1
}A wrong key:
curl https://api.dmwire.ai/v1/usage \
-H "X-API-Key: dmw_live_this_key_does_not_exist"{
"success": false,
"error_code": 401,
"error": "Invalid API key"
}Errors
Refusals carry a machine readable error_code and a human error message. Daily limit refusals (HTTP 429) carry retry_after_s. The full list of send refusals is on Limits and account safety.
curl https://api.dmwire.ai/v1/messages/does-not-exist \
-H "X-API-Key: $DMWIRE_API_KEY"{
"error_code": "message_not_found",
"error": "Message not found"
}Sending a message
POST /messages answers 202 with the message id and status queued. Then follow it with GET /messages/{id} or the message.sent, message.delivered, message.read and message.failed webhooks. Run before any account was connected, it is refused like this:
curl \
-X POST https://api.dmwire.ai/v1/messages \
-H "X-API-Key: $DMWIRE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"to": {"phone": "+31612345678"}, "text": "Hi Ana, your order is ready for pickup."}'{
"error_code": "no_connected_account",
"error": "No connected WhatsApp or Telegram account"
}Reading past messages
GET /messages lists the most recent messages on your accounts, sent and received, newest first. It is REST only for now: the MCP server has no tool for it, agents use wait_for_messages for new messages instead. A fresh account has none yet:
curl "https://api.dmwire.ai/v1/messages?limit=5" \
-H "X-API-Key: $DMWIRE_API_KEY"{
"messages": [],
"next_cursor": null
}All operations
The OpenAPI document itself is at https://api.dmwire.ai/v1/docs/openapi.yaml, ready for any OpenAPI tool.
Generated from the live OpenAPI document at api.dmwire.ai/v1/docs/openapi.yaml (version 1.0.0, read 2026-10-17). Base URL https://api.dmwire.ai/v1. Send your key in the X-API-Key header or as Authorization: Bearer.
Messages
GET/messages
List Recent Messages · list_messages, REST only (no MCP tool yet)
The most recent messages on your accounts, sent and received, newest first (up to 100 per call, 50 by default). Filter by direction or account_id. When next_cursor is not null, pass it as before to read the next page. For waiting on NEW messages use wait_for_messages.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
limit | query | integer (default 50, max 100) | no | |
direction | query | string | no | |
account_id | query | string | no | |
before | query | string | no | The next_cursor of the previous page |
Responses
200The messages, newest first400Invalid limit, direction or cursor
POST/messages
Send a Message · MCP tool send_message
Queues a text message (optionally with one media URL) from a connected account. Address the recipient by to.phone (international format) or to.contact_id. account_id may be omitted when exactly one account is connected. On WhatsApp a new phone number creates the contact; on Telegram only an existing contact can be messaged. Answers 202 with the message id; read get_message for the delivery status. Refusals carry error_code: no_connected_account (409), ambiguous_account (400), account_not_found (404), contact_not_found (404), telegram_requires_existing_contact (422), contact_channel_mismatch (422), contact_do_not_disturb (422), contact_private (422), account_suspended (403), loop_guard (429), opener_burst (429), daily_send_cap (429), new_contact_cap (429). A 429 carries retry_after_s.
Request body (JSON)
| Field | Type | Required | What it is |
|---|---|---|---|
account_id | string | no | The account to send from (see list_accounts). Optional when one account is connected. |
to | object | yes | |
to.phone | string | no | |
to.contact_id | string | no | |
text | string | yes | |
media_url | string | no | An https URL of an image, video, audio file or document to attach. |
Responses
202Queued for delivery400Invalid request403Account suspended409No connected account422The contact cannot be messaged429A daily cap, the loop guard or the burst guard was hit
GET/messages/wait
Wait for New Messages · MCP tool wait_for_messages
Long poll for incoming messages. Returns as soon as at least one message newer than cursor arrives (up to 50 per call), or an empty list when timeout_s passes. Always pass the returned cursor into the next call; without a cursor the wait starts from now. At most 2 waits may be open at once per account (429 wait_concurrency).
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
cursor | query | string | no | The opaque cursor returned by the previous call. |
account_id | query | string | no | Only messages received on this account. |
timeout_s | query | integer (default 25, max 55) | no |
Responses
200New messages (possibly none) and the cursor for the next call400Invalid cursor or timeout429Too many concurrent waits
GET/messages/{id}
Get a Message · MCP tool get_message
Reads one message (sent or received) with its current status. Use it after send_message to see whether the message was sent, delivered, read or failed.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
id | path | string | yes |
Responses
200The message404No such message on this account
Accounts
GET/accounts
List Connected Accounts · MCP tool list_accounts
Lists the WhatsApp Web and Telegram accounts connected to your account. status is one of connected, connecting, disconnected or restricted. limits shows today's sends and new contacts against the caps; ramp_day is how far the account is into its warm-up ramp (null when the account has no ramp).
Responses
200The connected accounts404This API is not enabled for the account
POST/channels/telegram/connect
Start a connection session · MCP tool connect_telegram
Begins a connection session for an account phone number. In code mode the connection service sends a one-time login code to the account and reports code_required; in qr mode it returns a login token and QR URL to display. Poll the status endpoint until the channel reports connected.
Request body (JSON)
| Field | Type | Required | What it is |
|---|---|---|---|
phone_number | string | yes | The account phone number to connect, in E.164 format. |
mode | code | qr | no | Login method. 'code' sends a one-time login code to the account; 'qr' returns a login token and QR URL to display for scanning. |
force_new | boolean | no | When true, discards any existing session and starts fresh. |
Responses
200Session started.400Invalid or missing request fields.403This channel is not enabled for the account, or the plan lacks a channels tier.409This account is already connected elsewhere.
POST/channels/telegram/connect/{phoneNumber}/verify-code
Submit the login code · MCP tool submit_telegram_code
Submits the one-time login code for a pending session. On success the status becomes connected; when the account has two-factor enabled the status becomes password_required and the password must be submitted next.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
phoneNumber | path | string | yes | The account phone number being connected, in E.164 format. |
Request body (JSON)
| Field | Type | Required | What it is |
|---|---|---|---|
code | string | yes | The one-time login code received by the account. |
Responses
200Code accepted.400Missing or invalid login code.403This channel is not enabled for the account.
POST/channels/telegram/connect/{phoneNumber}/verify-password
Submit the two-factor password · MCP tool submit_telegram_password
Submits the two-factor password for a session that reported password_required. On success the status becomes connected and the channel is live.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
phoneNumber | path | string | yes | The account phone number being connected, in E.164 format. |
Request body (JSON)
| Field | Type | Required | What it is |
|---|---|---|---|
password | string | yes | The account's two-factor password. |
Responses
200Password accepted.400Missing or invalid password.403This channel is not enabled for the account.
GET/channels/telegram/connect/{phoneNumber}/status
Get live connection status · MCP tool get_telegram_status
Returns the live connection status for a session, polled from the connection service. Poll this until the status is connected or an error/disconnected status. Reports not_initialized when no session exists.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
phoneNumber | path | string | yes | The account phone number, in E.164 format. |
Responses
200Current connection status.404No connection record exists for this phone number.
DELETE/channels/telegram/{phoneNumber}
Disconnect the channel · MCP tool disconnect_telegram
Tears down the connection session and removes the account record. Idempotent: the record is removed even when the connection service is unreachable, so repeated calls succeed.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
phoneNumber | path | string | yes | The account phone number to disconnect, in E.164 format. |
Responses
200Channel removed.404No connection record exists for this phone number.
POST/channels/whatsapp-web/connections
Start a WhatsApp Web pairing session · MCP tool connect_whatsapp
Begins pairing a WhatsApp number by QR code. Creates or resets the connection and returns a session whose QR code you then fetch and have the end user scan. Poll the status endpoint until the connection reports connected.
Request body (JSON)
| Field | Type | Required | What it is |
|---|---|---|---|
phone_number | string | yes | The WhatsApp number to connect, in E.164 format (for example +15551230000). |
link_method | qr | pairing_code | no | How the phone is linked. 'qr' (default) shows a QR code to scan; 'pairing_code' returns an 8-character code to type on the phone under Linked devices, Link with phone number instead. |
force_new | boolean | no | Discard any existing session for this number and start a fresh pairing. |
import_contacts | boolean | no | Import the device's existing contacts on first connection. |
pause_ai_for_imported_contacts | boolean | no | When importing contacts, keep automated replies paused for them. |
import_existing_chats | boolean | no | Import existing chat history on first connection. Requires import_contacts to be true. |
mark_imported_contacts_as_private | boolean | no | When importing contacts, create them as private (no messages stored or sent for them). Incompatible with import_existing_chats. |
Responses
200Pairing session started.400Missing or invalid request fields.401Authentication required or invalid.403The account's plan does not include channel connections.404The referenced account could not be found.409This number is already connected.503The connection service is temporarily at capacity.
GET/channels/whatsapp-web/connections/{phoneNumber}/qr
Get the pairing QR code · MCP tool get_whatsapp_qr
Returns the current QR code for a pairing session so it can be rendered for the end user to scan. The code is short-lived; poll this endpoint every 10 to 15 seconds until the status reports connected, and restart the connection if the code expires.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
phoneNumber | path | string | yes | The connected number in E.164 format. |
Responses
200QR code returned.400This number cannot be paired with WhatsApp Web.401Authentication required or invalid.404The connection was not found, or the QR code is not ready yet.410The QR code expired. Restart the connection to generate a new one.503The QR service is temporarily unavailable.
GET/channels/whatsapp-web/connections/{phoneNumber}/status
Get the connection status · MCP tool get_whatsapp_status
Returns the live connection status for a pairing session. The terminal success state is connected (the service may also report open). Treat disconnected and not_initialized as terminal failure states. Poll this endpoint after the end user scans the QR code to confirm the connection.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
phoneNumber | path | string | yes | The connected number in E.164 format. |
Responses
200Connection status returned.401Authentication required or invalid.404The connection was not found.502The connection service could not be reached.
DELETE/channels/whatsapp-web/connections/{phoneNumber}
Disconnect a WhatsApp Web connection · MCP tool disconnect_whatsapp
Disconnects the pairing session and removes the connection record. The operation is idempotent and always succeeds locally even if the connection service is unreachable.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
phoneNumber | path | string | yes | The connected number in E.164 format. |
Responses
200Disconnected. status is "removed" when the number was deleted from the account.401Authentication required or invalid.404The connection was not found.
Usage
GET/usage
Get Today's Usage · MCP tool get_usage
Today's sends and new contacts (UTC day) against the plan caps.
Responses
200Today's usage
Webhooks
GET/webhooks
List Webhook Subscriptions · MCP tool list_webhooks
Returns every webhook subscription configured on the account, including the events each one listens to. The returned id (or the exact name) can be used as the webhookId path parameter in the other webhook endpoints.
Responses
200The account's webhook subscriptions401Missing or invalid credentials403Webhooks are not enabled for this account
POST/webhooks
Create Webhook Subscription · MCP tool create_webhook
Creates a new webhook subscription. The URL must use HTTPS and be publicly reachable. The subscribed_to array selects which event types are delivered to the URL, use GET /webhooks/events to discover the valid values. Optionally pass a name so the subscription can be addressed by a stable identifier later.
Request body (JSON)
| Field | Type | Required | What it is |
|---|---|---|---|
url | string | yes | HTTPS URL that will receive event payloads via POST. |
subscribed_to | array | yes | Event types to subscribe to. Must contain at least one valid event name. |
name | string | no | Optional display name. Also usable as the webhookId path parameter. Defaults to a timestamped name. |
retries_enabled | boolean | no | Retry failed deliveries (1m, 5m, 30m, 2h). Off by default. See the Retries section for the duplicate-delivery caveat. |
enabled | boolean | no | Account on/off switch for the subscription. Defaults to true (on). Pass false to create it switched off. |
generate_signing_secret | boolean | no | When true, an HMAC signing secret is minted for the new subscription (turning signing on) and returned once as a top-level signing_secret field in the response. Store it then, it is never returned in full again by create/list/update; only GET /webhooks/{webhookId}/signing-secret exposes it afterwards. You can also turn signing on later via POST /webhooks/{webhookId}/signing-secret. |
Responses
201Subscription created400Validation error (missing url, empty or invalid subscribed_to, or a URL that is not allowed)403Webhooks are not enabled for this account
GET/webhooks/events
List Subscribable Event Types · MCP tool list_webhook_events
Returns the full list of event types that webhook subscriptions can listen to. Use these exact strings in the subscribed_to field when creating or updating a subscription. The events are (message.received, message.sent, message.delivered, message.read, message.failed, account.connected, account.disconnected, account.restricted, account.limit_reached), plus a descriptions map; subscribing to an event outside the account's set returns 400.
Responses
200The valid event types401Missing or invalid credentials403Webhooks are not enabled for this account
PUT/webhooks/{webhookId}
Update Webhook Subscription · MCP tool update_webhook
Updates an existing webhook subscription. Provide at least one of url, subscribed_to or name; omitted fields keep their current values. When the URL changes, delivery for the new URL is automatically re-enabled so a previously failing endpoint gets a fresh start.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
webhookId | path | string | yes | The subscription id (from the id field) or its exact name. |
Request body (JSON)
| Field | Type | Required | What it is |
|---|---|---|---|
url | string | no | New HTTPS delivery URL. |
subscribed_to | array | no | Replacement event list. Must contain at least one valid event name. |
name | string | no | New display name. |
retries_enabled | boolean | no | Turn delivery retries on or off for this subscription. |
enabled | boolean | no | Account on/off switch. Set false to switch the subscription off (suppress delivery), true to switch it back on. Signing is managed separately via the signing-secret routes, not here. |
Responses
200Subscription updated403Webhooks are not enabled for this account404No subscription with that id or name exists on this account
DELETE/webhooks/{webhookId}
Delete Webhook Subscription · MCP tool delete_webhook
Removes a webhook subscription so its URL stops receiving event payloads. Delivery-health counters for the URL are reset, so re-adding the same URL later starts with a clean record.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
webhookId | path | string | yes | The subscription id (from the id field) or its exact name. |
Responses
200Subscription deleted403Webhooks are not enabled for this account404No subscription with that id or name exists on this account
POST/webhooks/{webhookId}/test
Send Test Payload · MCP tool test_webhook
Sends a sample payload to the subscription's URL so you can verify your receiver end-to-end. Optionally pass an event to control which event type the sample payload simulates. Test deliveries never affect the subscription's health counters. The response reports whether the delivery succeeded; a failed test returns delivered:false together with the failure details instead of an error status.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
webhookId | path | string | yes | The subscription id (from the id field) or its exact name. |
Request body (JSON)
| Field | Type | Required | What it is |
|---|---|---|---|
event | string | no | Event type to simulate. Must be one of the values from GET /webhooks/events. Defaults to a delivery event. |
Responses
200Test attempted, check the delivered flag for the outcome403Webhooks are not enabled for this account404No subscription with that id or name exists on this account
GET/webhooks/{webhookId}/health
Get Webhook Delivery Health · MCP tool get_webhook_health
Returns the delivery-health record for the subscription's URL: how many deliveries have succeeded and failed, whether delivery is currently paused after repeated failures, and the details of the most recent failure. Returns health:null when no deliveries have been attempted yet.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
webhookId | path | string | yes | The subscription id (from the id field) or its exact name. |
Responses
200The delivery-health record for the subscription's URL403Webhooks are not enabled for this account404No subscription with that id or name exists on this account
POST/webhooks/{webhookId}/reenable
Re-enable Webhook Delivery · MCP tool reenable_webhook
Resumes delivery for a webhook whose URL was paused automatically after repeated failures. Resets the paused flag and failure counters. No delivery attempt is made, use the test endpoint afterwards to confirm the receiver is healthy again.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
webhookId | path | string | yes | The subscription id (from the id field) or its exact name. |
Responses
200Delivery re-enabled403Webhooks are not enabled for this account404No subscription with that id or name exists on this account
GET/webhooks/{webhookId}/signing-secret
Read Webhook Signing Secret · MCP tool get_webhook_signing_secret
Returns the subscription's HMAC signing secret so you can configure signature verification on your receiver. The secret is symmetric, we hold the same value your endpoint needs, so it is readable rather than write-once. When signing is off, signing_enabled is false and signing_secret is null. Signed deliveries carry an `X-Webhook-Signature` header (`v1=<hex>`), an HMAC-SHA256 of `<X-Webhook-Timestamp>.<raw request body>` keyed with this secret. Verify against the raw body, reject stale timestamps, and compare in constant time.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
webhookId | path | string | yes | The subscription id (from the id field) or its exact name. |
Responses
200The subscription's signing state403Webhooks are not enabled for this account404No subscription with that id or name exists on this account
POST/webhooks/{webhookId}/signing-secret
Generate or Rotate Webhook Signing Secret · MCP tool rotate_webhook_signing_secret
Mints a new signing secret for the subscription, or replaces the existing one. Creating a secret is how signing is turned on. The new secret is returned in the response. Rotation is a hard cutover, not a grace period: the very next delivery is signed with the new secret only. When rotating a live endpoint, accept both the old and new secret briefly so deliveries in flight during the change still verify.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
webhookId | path | string | yes | The subscription id (from the id field) or its exact name. |
Responses
200Signing secret generated or rotated403Webhooks are not enabled for this account404No subscription with that id or name exists on this account
DELETE/webhooks/{webhookId}/signing-secret
Turn Off Webhook Signing · MCP tool turn_off_webhook_signing
Removes the subscription's signing secret, turning signing off. Subsequent deliveries carry no signature headers, exactly as the webhook behaved before signing was enabled.
Parameters
| Name | In | Type | Required | What it is |
|---|---|---|---|---|
webhookId | path | string | yes | The subscription id (from the id field) or its exact name. |
Responses
200Signing turned off403Webhooks are not enabled for this account404No subscription with that id or name exists on this account
Last checked against the live service on 2026-10-17.