1. Create your account
Sign up for free. The account starts as pending and a person reviews it before enabling real sending; sandbox mode lets you integrate and test the API without spending balance and without any message going out.
Integrate SMS, WhatsApp Business API and Telegram sending with a single JSON REST API: X-Api-Key authentication, native idempotency, status webhooks signed with HMAC-SHA256, configurable rate limit, clear errors and a sandbox mode for integrating without spending a euro.
Authentication, endpoints and copy-ready curl examples, step by step.
POST /messages: your first SMS in five minutes, with idempotency.
POST /messages/bulk: up to 5,000 SMS in a single call, with safe retries.
Notifications and contracts with evidential value, from the same token.
The same message to hundreds or thousands, without programming: lists, a file or manual paste.
Integrate and test the whole flow without spending and without anything reaching anyone.
No mandatory SDK, no IP whitelist, no talking to sales to enable channels: one HTTPS request with JSON and your message is queued.
Sign up for free. The account starts as pending and a person reviews it before enabling real sending; sandbox mode lets you integrate and test the API without spending balance and without any message going out.
In the dashboard you will find your api_key (format sk_live_…) and your api_secret. They are sent in the X-Api-Key and X-Api-Secret headers of each request, always over HTTPS. The secret is shown only once, when it is generated. If you are coming from another platform, they are the same two fields you already have configured: only the values change.
Call POST /api/v1/messages with the channel, the recipient in E.164 format and the text. The sender goes in from and is optional: if you do not set it, the platform's default sender is used. The response returns the message created, its cost in euros and its status.
curl -X POST https://app.smsverifica.com/api/v1/messages \
-H "X-Api-Key: sk_live_tu_api_key" \
-H "X-Api-Secret: tu_api_secret" \
-H "Idempotency-Key: pedido-1234-confirmacion" \
-H "Content-Type: application/json" \
-d '{
"channel": "sms",
"to": "+34612345678",
"from": "MITIENDA",
"body": "Your order 1234 has been shipped. You will receive it tomorrow before 14:00."
}'
{
"data": {
"id": 98231,
"channel": "sms",
"direction": "out",
"to": "+34612345678",
"from": "MITIENDA",
"status": "queued",
"cost_eur": "0.0390",
"queued_at": "2026-07-08T10:15:02+02:00",
"created_at": "2026-07-08T10:15:02+02:00"
}
}
The recipient is normalised to E.164 and the country prefix is detected automatically to apply the correct rate (see the SMS rates by country). The cost is deducted from your balance transactionally when queued and, if the message fails permanently, it is refunded automatically.
The same endpoint, the same JSON and the same webhooks for all three channels: switching from SMS to WhatsApp or Telegram means changing the value of one field. That is a WhatsApp Business API and a Telegram API that do not force you to learn three different integrations.
Send templates approved by Meta with variables and state the conversation category for exact per-message pricing (WhatsApp prices by category). Free text within the 24-hour service window is also supported.
curl -X POST https://app.smsverifica.com/api/v1/messages \
-H "X-Api-Key: sk_live_tu_api_key" \
-H "X-Api-Secret: tu_api_secret" \
-H "Idempotency-Key: entrega-5566-aviso" \
-H "Content-Type: application/json" \
-d '{
"channel": "whatsapp",
"to": "+34612345678",
"template_name": "aviso_entrega",
"template_vars": { "1": "Maria", "2": "Thursday from 9:00 to 14:00" },
"category": "utility"
}'
Connect your own bot and send Telegram notifications at €0.010 per message. Our Telegram webhook automatically captures the chat_id when the customer starts a conversation with your bot; from then on, you send using their phone number as the reference.
curl -X POST https://app.smsverifica.com/api/v1/messages \
-H "X-Api-Key: sk_live_tu_api_key" \
-H "X-Api-Secret: tu_api_secret" \
-H "Idempotency-Key: visita-889-recepcion" \
-H "Content-Type: application/json" \
-d '{
"channel": "telegram",
"to": "+34612345678",
"body": "Your visitor has just arrived at reception. Automatic notice from QRACCESO."
}'
curl https://app.smsverifica.com/api/v1/account/balance \
-H "X-Api-Key: sk_live_tu_api_key" \
-H "X-Api-Secret: tu_api_secret"
# {"data":{"balance_eur":"842.3100"}}
This is rarely seen on the Spanish market: a real campaigns API. With POST /api/v1/campaigns you launch a bulk send to a whole contact list, with date scheduling and a prior balance check; the platform calculates the cost, queues the messages and updates the counters of sent, delivered, failed and read in real time.
curl -X POST https://app.smsverifica.com/api/v1/campaigns \
-H "X-Api-Key: sk_live_tu_api_key" \
-H "X-Api-Secret: tu_api_secret" \
-H "Content-Type: application/json" \
-d '{
"channel": "sms",
"name": "October MOT reminder",
"contact_list_id": 42,
"body": "Your MOT expires this month. Book an appointment at your usual garage.",
"scheduled_at": "2026-10-01T09:00:00+02:00"
}'
# {"data":{"id":57,"status":"scheduled","total":1840,"cost_estimate":"71.7600", ...}}
If you prefer to start with the dashboard before integrating, the user manual goes through every screen with screenshots.
Certified messaging —a file of chained evidence and a verifiable PDF— is included in the same REST API, with no separate modules or additional contracts: not every provider offers it through the API, and anyone who integrated someone else's and was left without it can migrate here with two endpoints. The certification surcharge is €0.25 per message, the same on all three channels, on top of the channel price.
curl -X POST https://app.smsverifica.com/api/v1/certified \
-H "X-Api-Key: sk_live_tu_api_key" \
-H "X-Api-Secret: tu_api_secret" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: notif-2026-0001" \
-d '{
"channel": "sms",
"to": "+34612345678",
"subject": "Payment demand",
"content": "Full text that will be certified in the evidence file."
}'
# {"data":{"id":31,"status":"queued","content_hash":"5f1c…","portal_url":"…/c/…", ...}}
# The evidence PDF, whenever you need it: GET /api/v1/certified/31/certificate
Available today only to Spain (+34) and as a non-qualified electronic registered delivery service (art. 3.36 of the eIDAS Regulation), with a qualified time stamp from Camerfirma on every file — the limits and what each thing proves, on the certified messaging page.
| Method and path | What it does |
|---|---|
POST /messages | Sends an SMS, WhatsApp or Telegram message. Accepts Idempotency-Key. |
GET /messages/{id} | Retrieves a message: status, cost, timestamps for its whole life cycle. |
GET /messages | Lists your messages with pagination and filters. |
POST /campaigns | Creates and confirms a bulk campaign over a contact list, with optional scheduling. |
GET /campaigns/{id} | Campaign statistics: sent, delivered, failed, read and cost. |
POST /verify/start | Sends an OTP verification code by SMS, WhatsApp or Telegram. |
POST /verify/check | Checks the OTP code (a wrong code is 200 with status: invalid). |
POST /certified | Sends a certified communication: evidence file + recipient portal. |
GET /certified/{id} | Status of the certified file: opens, reading, signature. |
GET /certified/{id}/certificate | Downloads the evidence PDF (issued on request). |
GET /account/balance | Balance in euros, in real time. |
GET /pricing/sms | SMS rates by country prefix, always up to date. |
GET /senders | Your senders and their status in the CNMC Alias Registry: whether they would go out with your brand, with the backup sender or be rejected when sending to Spain. |
GET /senders/check | The same for a specific send (from and to), before sending it. |
A network timeout at the worst moment and your customer receives the same SMS twice... and you pay for it twice. With SMSverifica that cannot happen.
Add the Idempotency-Key header to your POST /messages with a unique identifier for your operation (for example, pedido-1234-confirmacion). If you repeat the request — because your job retried it, because there was a timeout, because your process restarted — the API does not create a second message or charge twice: it returns the original response with the header X-Idempotency-Replayed: true.
This is native sending idempotency, built into the API itself, and it is a real differentiator: none of the Spanish SMS providers documents it in its API, and neither do the big international messaging platforms on their sending endpoint. If you build serious systems — queues, retries with backoff, workers — you know exactly why it matters.
The check, documentation in hand and with the date consulted, is in the comparisons with Twilio, LabsMobile, Esendex, Instasent and BulkGate (in Spanish).
# We repeat the same POST with the same Idempotency-Key
curl -i -X POST https://app.smsverifica.com/api/v1/messages \
-H "Idempotency-Key: pedido-1234-confirmacion" \
...
HTTP/2 201
X-Idempotency-Replayed: true
# Same JSON, same message id: zero duplicates, zero double charges
Receive on your server every status transition — message.sent, message.delivered, message.failed, message.read, message.received and campaign.completed — with a cryptographic signature that lets you verify the notice is authentic. Of the Spanish providers whose documentation we reviewed in July 2026, none signed their delivery notifications; we sign all of them.
X-Signature: sha256=7f83b1657ff1fc53b92dc18148a1d6...
Content-Type: application/json
{
"event": "message.delivered",
"delivery_id": 4521,
"timestamp": "2026-07-08T10:15:09+02:00",
"data": {
"id": 98231,
"channel": "sms",
"to": "+34612345678",
"status": "delivered",
"delivered_at": "2026-07-08T10:15:08+02:00"
}
}
// You define the secret per webhook in your dashboard
$cuerpo = file_get_contents('php://input');
$esperada = 'sha256=' . hash_hmac('sha256', $cuerpo, $secreto);
if (hash_equals($esperada, $_SERVER['HTTP_X_SIGNATURE'] ?? '')) {
// Authentic payload: process the event
}
Every delivery carries the header X-Signature: sha256=<hmac> calculated over the exact body with your secret. Verifying it takes three lines of code in any language.
If your endpoint does not respond 2xx, we retry up to 5 times with increasing waits (1 min, 5 min, 15 min, 1 h, 4 h). At-least-once semantics: use delivery_id to deduplicate.
Every attempt is recorded in your dashboard with the response code and date. Was your server down? Resend the delivery manually with one click, without opening tickets.
A documented simulation environment, at no cost: integrate and test without spending balance and without any real message going out.
With sandbox mode enabled, your API requests behave exactly as in production — the same endpoints, the same responses, the same signed webhooks — but no message goes out to the network and the cost is 0.0000 of balance. The simulator goes through the message's whole life cycle: queued → sent → delivered (or failed/read), firing your webhooks at each transition so you can test your integration end to end.
You also have test numbers (magic numbers) that force each final status — delivered, failed, read — so you can check how your application reacts to each scenario, including the automatic refund of a failed send. All documented in your dashboard, with no card and no expiry of the test account.
How it works, without embellishment: signing up creates the account in pending status and in sandbox mode; in sandbox no message goes out and no balance is spent. A person reviews the sign-up before enabling real sending. Any questions, write to us.
Idempotency-Key and replayed responsesAll errors share the same format — {"error":{"code":"<slug>","message":"..."}} — with a stable code you can program against and a human-readable message. Validation errors include the details per field.
| HTTP | Code | When it happens |
|---|---|---|
| 401 | unauthorized | Invalid API key or secret. |
| 402 | insufficient_balance | Insufficient balance: the message or campaign is not queued. There are never half-done sends. |
| 403 | account_not_active | Account pending approval or suspended. |
| 404 | not_found | The resource does not exist or belongs to another account (multi-tenant isolation). |
| 422 | validation_failed | Invalid data; includes details with the errors per field. |
| 422 | wa_outside_window | WhatsApp free text outside the 24 h service window: use a template. |
| 422 | telegram_no_chat_id | The contact has not yet started a conversation with your Telegram bot. |
| 422 | no_provider | There is no provider configured for that channel on your account. |
| 422 | no_pricing | Destination prefix without a rate: we never send blindly, or for free by mistake. |
| 429 | rate_limited | Your requests-per-minute limit has been exceeded. |
Each account has its own limit — 60 requests per minute by default, which can be raised according to your real volume —, so other customers' traffic never affects yours. For bulk sending you do not need to hammer the API: POST /campaigns dispatches thousands of messages with a single request and with controlled throttling towards the operators.
If your balance does not cover the send you receive an immediate 402 insufficient_balance and nothing is queued. The balance deduction is transactional, with an automatic refund if the message fails permanently, and you can enable auto top-up with a threshold so that the balance never holds you back. See the balance model with no expiry.
The delivery quality of your SMS depends on the reputation of the routes. That is why we do not let just anyone in.
Every sign-up at SMSverifica is reviewed by a person before the account is activated. That anti-spam filter keeps our routes and senders clean, and aims to make sure your legitimate traffic —your OTPs, your delivery notices, your campaigns— does not share a route with junk. Sandbox mode does not wait for that review: it opens on its own when you verify your email, and the human review then unlocks real sending and payments.
Isolation between accounts is total: your messages, contacts, campaigns and credentials live under your account_id and are invisible to any other account. Channel credentials are stored encrypted in the database and you can rotate your api_secret from the dashboard at any time.
Yes. It is a standard REST API with JSON over HTTPS: any language that can make an HTTP request can send SMS, WhatsApp and Telegram messages. The curl examples on this page carry over as they are to PHP (cURL/Guzzle), Python (requests), Node.js (fetch/axios), Java, C# or Go.
No, and neither does sandbox mode: it simulates the message's whole life cycle (queued, sent, delivered, failed, read) at zero cost and without any real message going out. Real sending is enabled when a person reviews and approves your account.
In two ways: by querying GET /api/v1/messages/{id} or, better, by receiving status webhooks on your server (message.sent, message.delivered, message.failed, message.read, message.received and campaign.completed). Each webhook is signed with HMAC-SHA256 in the X-Signature header and retried automatically with increasing waits if your endpoint does not respond.
The API responds 402 with the code insufficient_balance and the message is not queued: there are never half-done sends or billing surprises. You can enable auto top-up with a threshold so the balance replenishes itself.
Each account has its own rate limit, 60 requests per minute by default, which can be raised on request according to your volume. If you exceed it you receive a standard 429 (rate_limited). For bulk sending use POST /campaigns: a single request for thousands of recipients.
If you include the Idempotency-Key header, the second request does not generate a duplicate send or charge: the API returns the original response with the header X-Idempotency-Replayed: true. It is native API idempotency, ideal for safe retries after a timeout.
A free account with no card, sandbox mode with no card, sign-up reviewed by hand, a balance that never expires and support in Spanish from people who have read the same documentation as you. Building user verification? See also the OTP verification API by SMS.