Home API Reference

API Reference

Detailed documentation of the public API endpoints and usage.
Temitayo Olakunle
By Temitayo Olakunle
• 1 article

Convert Product Documentation

Convert API Send WhatsApp & SMS messages, manage contacts, and receive delivery events over a simple REST API. All requests are authenticated with a Bearer API key and return JSON. An in-app version of these docs is available at /docs in the dashboard. - Base URL: https://<your-domain>/api/v1 - Auth: Authorization: Bearer <api_key> - Content type: application/json Authentication Create a key under Account → API keys (/api_keys). The full key (rk_live_…) is shown once at creation — store it securely; only a hashed digest is kept server-side. Authorization: Bearer rk_live_xxxxxxxxxxxxxxxxxxxxxxxx Requests without a valid, active key return 401 Unauthorized. Send a message POST /api/v1/messages — send a single transactional message (OTP, alert, notification). | Field | Type | Description | |--------------|--------|-------------| | to | string | Recipient phone, international format e.g. +2348012345678. Required. | | message | string | Text to send. Required. | | channel | string | whatsapp, sms, or smart (default smart). | | first_name | string | Optional; stored on the contact, usable as {{first_name}}. | | last_name | string | Optional. | | session | string | Optional WhatsApp session id (defaults to your connected number). | WhatsApp only Delivers strictly over WhatsApp. Errors if no number is connected or the recipient isn't reachable on WhatsApp. curl -X POST https://<your-domain>/api/v1/messages \ -H "Authorization: Bearer rk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "to": "+2348012345678", "message": "Your code is 123456", "channel": "whatsapp" }' SMS only Always sends over SMS via the configured provider, using your approved Sender ID. curl -X POST https://<your-domain>/api/v1/messages \ -H "Authorization: Bearer rk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "to": "+2348012345678", "message": "Your code is 123456", "channel": "sms" }' Smart routing (default) Sends over WhatsApp when a number is connected and the recipient is on WhatsApp; otherwise automatically falls back to SMS. curl -X POST https://<your-domain>/api/v1/messages \ -H "Authorization: Bearer rk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "to": "+2348012345678", "message": "Hi {{first_name}}, your order shipped!", "channel": "smart", "first_name": "Ada" }' Response — 201 Created { "success": true, "channel": "whatsapp", "status": "sent", "message_id": "wamid.XXXX", "cost": 5.0, "to": "+2348012345678" } Contacts Add a single contact POST /api/v1/contacts curl -X POST https://<your-domain>/api/v1/contacts \ -H "Authorization: Bearer rk_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "phone_number": "+2348012345678", "first_name": "Ada", "last_name": "Obi", "email": "[[email protected]](mailto:[email protected])" }' - Update: PATCH /api/v1/contacts/:id - Delete: DELETE /api/v1/contacts/:id Bulk upload For large lists, import a CSV or Excel (.xlsx) file from **Contacts → a list → Add contacts**. Map your columns to fields (phone, first/last name, email, custom fields); Convert imports them in the background and checks each number against WhatsApp. To bulk-load programmatically, loop the single-contact endpoint: const contacts = [ { phone_number: "+2348010000001", first_name: "Ada" }, { phone_number: "+2348010000002", first_name: "Uche" } ]; for (const c of contacts) { await fetch("https://<your-domain>/api/v1/contacts", { method: "POST", headers: { "Authorization": "Bearer rk_live_xxx", "Content-Type": "application/json" }, body: JSON.stringify(c) }); } Custom fields Attach your own attributes via custom_fields. They become merge tags (e.g. {{order_id}}) in messages. { "phone_number": "+2348012345678", "first_name": "Ada", "custom_fields": { "order_id": "ORD-1042", "tier": "gold" } } Campaigns - GET /api/v1/campaigns — list recent campaigns - GET /api/v1/campaigns/:id — one campaign with its contact list { "id": 12, "name": "October promo", "status": "sent", "routing_strategy": "smart_fallback", "sent_count": 940, "failed_count": 6, "actual_cost": 4700.0, "contact_list": { "id": 3, "name": "Customers" } } Routing strategies | Strategy | Behaviour | |------------------|-----------| | whatsapp_only | WhatsApp only; contacts not on WhatsApp are skipped. | | sms_only | SMS to everyone via your Sender ID. | | whatsapp_first | Try WhatsApp; fall back to SMS for the rest. | | smart_fallback | WhatsApp for registered contacts, SMS for everyone else (recommended). | Webhooks Add an endpoint under Webhooks (/webhook_endpoints) and choose which events to receive. Convert sends a POST with a JSON body for each event. Leave all events unchecked to receive every event. Events | Event | Fires when… | |----------------------|-------------| | message.sent | A single message was accepted by the provider. | | message.delivered | A message was delivered to the recipient's device. | | message.failed | A message could not be delivered. | | campaign.completed | A campaign finished sending to its whole list. | | contact.created | A new contact was added (UI, import, or API). | Payload { "event": "message.delivered", "created_at": "2026-06-29T12:00:00Z", "data": { "message_id": 8841, "to": "+2348012345678", "channel": "whatsapp", "status": "delivered", "provider_message_id": "wamid.XXXX", "cost": 5.0 } } Verifying signatures Each request carries X-Convert-Signature: sha256=<hmac> — an HMAC-SHA256 of the raw request body using the endpoint's signing secret (shown on the endpoint page). expected = "sha256=" + OpenSSL::HMAC.hexdigest( "SHA256", ENV["CONVERT_WEBHOOK_SECRET"], request.raw_post ) verified = ActiveSupport::SecurityUtils.secure_compare( expected, request.headers["X-Convert-Signature"].to_s ) Respond with any 2xx to acknowledge. Non-2xx responses (or timeouts) are retried with backoff (up to 5 attempts). Errors Failed requests return a JSON body with error and a stable error_code: { "success": false, "error": "Insufficient wallet balance to send this message.", "error_code": "insufficient_balance" } | HTTP | error_code | Meaning | |------|-----------------------------|---------| | 401 | unauthorized | Missing, invalid, or revoked API key. | | 402 | insufficient_balance | Wallet balance too low to send. | | 422 | invalid_channel | channel must be whatsapp, sms, or smart. | | 422 | invalid_phone | The to number isn't a valid phone number. | | 422 | missing_message | No message body provided. | | 422 | whatsapp_not_connected | WhatsApp requested but no number is connected. | | 422 | recipient_not_on_whatsapp | Recipient not reachable on WhatsApp (use sms/smart). | | 502 | send_failed / sms_failed| The provider rejected or failed to send. |

Last updated on Sep 05, 2026