Skip to main content

SMS Sending API

The SMS sending API lets you send messages under your own sender name directly from your system. You can send a single message, such as an order notification or a code, or a batch of up to 1000 messages in one request. Pricing and billing are the same as for sending from the panel.

API version

This guide describes API v2 (/api/v2/sms). The previous API v1 (/api/v1/sending/{key}) still works. See Migrating from API v1 for the differences.

Before you start​

  • The SMS Sending service is active on your account in the panel.
  • You have at least one approved sender (status active). Sender names (up to 12 characters) are registered in the panel under Senders.
  • You have funds: a prepaid balance or free postpaid limit.

API key​

You will find the key in the panel: SMS Sending → API access. Send it in the header of every request:

Authorization: Bearer YOUR_KEY

The base URL is https://panel.dpay.pl/api/v2/sms. Request bodies are JSON objects (Content-Type: application/json).

The key is a password

Anyone who knows the key can send SMS at your cost. Use it only server-side. Never put it in a mobile app or in website code.

Endpoints​

POST/api/v2/sms/messagesSend one message.Full contract in the API Reference GET/api/v2/sms/messages/{id}Message status.Full contract in the API Reference POST/api/v2/sms/batchesSend a batch of up to 1000 messages.Full contract in the API Reference GET/api/v2/sms/batches/{id}Batch status and the result of every message.Full contract in the API Reference GET/api/v2/sms/sendersYour account's senders and their status.Full contract in the API Reference

Single message​

curl -X POST "https://panel.dpay.pl/api/v2/sms/messages" \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1234-shipped" \
-d '{"msisdn":"600100200","sender":"SKLEP","message":"Twoje zamówienie zostało wysłane."}'
FieldRequiredDescription
msisdnyesRecipient number, see Recipient number.
senderyesAn approved sender name of your account.
messageyesText, up to 900 bytes, see Text and segments.

A 202 Accepted response means we accepted the message to the queue and reserved its cost. It does not mean delivery - the message is handed over to the operator in the background.

{
"batch_id": null,
"id": "9b2f4c1e-6f0a-4d7e-9a41-3c8f2d7b5e10",
"msisdn": "600100200",
"sender": "SKLEP",
"status": "queued",
"segments": 1,
"cost_net": "0.081300",
"created_at": "2026-10-14T12:00:00+02:00",
"finished_at": null
}

Keep the id - GET /api/v2/sms/messages/{id} tells you how the hand-over ended. The same URL is in the Location response header.

Bulk sending​

A batch holds up to 1000 messages in one request. The batch-level sender is the default sender, and every message can set its own.

curl -X POST "https://panel.dpay.pl/api/v2/sms/batches" \
-H "Authorization: Bearer YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: campaign-2026-10" \
-d '{
"name": "October campaign",
"sender": "SKLEP",
"messages": [
{"msisdn": "600100200", "message": "Wiadomość 1"},
{"msisdn": "+48600100201", "message": "Wiadomość 2 od innego nadawcy", "sender": "INNY"}
]
}'

A batch is accepted as a whole or not at all.

  • If even one message is invalid, you get 422 and nothing is accepted. The error.fields object points at every invalid message, e.g. messages.1.msisdn is the number of the second message (counting from zero).
  • If your funds do not cover the whole batch, you get 402.

An accepted batch returns 202 with status pending and a price quote in estimate. Messages are created in the background, usually within a few seconds - that is when the cost is reserved.

{
"id": "3d6a0f5e-2b7c-4e19-8f3a-1c9d5e7b2a40",
"name": "October campaign",
"status": "pending",
"failure_reason": null,
"messages_count": 2,
"counts": {"queued": 0, "sending": 0, "sent": 0, "failed": 0, "unknown": 0},
"segments": 0,
"cost_net": null,
"created_at": "2026-10-14T12:00:00+02:00",
"updated_at": "2026-10-14T12:00:00+02:00",
"messages": [],
"estimate": {"segments": 2, "cost_net": "0.162600"}
}

Batches sent through the API also appear in the panel under Import, and their messages under History and reports.

Batch progress and results​

curl "https://panel.dpay.pl/api/v2/sms/batches/3d6a0f5e-2b7c-4e19-8f3a-1c9d5e7b2a40" \
-H "Authorization: Bearer YOUR_KEY"

The response contains message counts per status and the result of every message in request order (index):

{
"id": "3d6a0f5e-2b7c-4e19-8f3a-1c9d5e7b2a40",
"status": "completed_with_errors",
"counts": {"queued": 0, "sending": 0, "sent": 1, "failed": 1, "unknown": 0},
"segments": 2,
"cost_net": "0.081300",
"messages": [
{"index": 0, "id": "5a1e9c2d-7b3f-4a8e-b6d4-0f2c9e1a7b35", "msisdn": "600100200", "sender": "SKLEP", "status": "sent", "segments": 1, "cost_net": "0.081300"},
{"index": 1, "id": "c7d2b8e4-1f6a-4c3d-9e5b-8a0f2d4c6e19", "msisdn": "600100201", "sender": "INNY", "status": "failed", "segments": 1, "cost_net": "0.000000"}
]
}
Batch statusMeaning
pendingAccepted, waiting to be processed. The messages list is still empty.
processingMessages created, some are still queued or being handed over.
completedAll messages handed over to the operator (sent).
completed_with_errorsFinished, but some messages are failed or unknown.
rejectedRejected while processing. Nothing was sent or charged - the reason is in failure_reason.

A batch can still be rejected after acceptance if the account changes in the meantime:

failure_reasonWhat happenedWhat to do
insufficient_fundsAnother sending used up the funds between acceptance and processing.Top up and send the batch again.
sender_inactiveA sender used in the batch is no longer active.Check your senders in the panel.
account_unavailableSMS sending is no longer available on the account.Check the service status in the panel.
invalid_batch, processing_failedThe batch could not be processed.Send it again; if it repeats, contact support.

You can send a rejected batch again, also with the same Idempotency-Key.

Message statuses​

StatusMeaning
queuedAccepted, waiting in the queue.
sendingBeing handed over to the operator.
sentThe operator accepted the message. This is not a delivery receipt from the recipient's phone.
failedThe operator rejected the message. It is not charged (cost_net = 0.000000).
unknownThe hand-over result could not be confirmed. The amount stays reserved until it is clarified - contact support if in doubt.

Idempotency​

Networks fail: a request may arrive while its response is lost. To make sure a retry does not send the SMS twice, add an Idempotency-Key header with your own unique key (1-128 printable ASCII characters, case-sensitive), e.g. your order number.

  • A retry with the same key and the same body returns the original result instead of sending again. The response then has the Idempotent-Replayed: true header.
  • The same key with a different body is a 409 idempotency_conflict error.
  • A batch key covers the whole request body, including name.
  • A request rejected by validation, missing funds or an inactive account does not use up the key. The key of a batch rejected while processing (rejected) can be used again, too.

Recipient number​

msisdn is a Polish number in one of the formats: 600100200, +48600100200, 0048600100200 or 48600100200. Spaces and hyphens are ignored. Responses always return the number as 9 digits.

Text and segments​

The text can be up to 900 bytes in UTF-8. It is sent exactly as received - we do not strip spaces or line breaks.

A long message is split into segments, and the charge is the number of segments times your account's rate:

Encoding1 segmentWhen split, per segment
GSM-7 (Latin letters without Polish characters, digits, basic symbols)160 characters153 characters
Unicode70 characters67 characters
Polish characters

A single character outside the GSM-7 alphabet, e.g. ą, ę or ł, switches the whole message to Unicode. A 100-character message fits in one segment without Polish characters, but takes two with them.

The number of segments and the price of every message are returned in segments and cost_net (net amount in PLN). They are calculated by the same calculator as sending from the panel.

Errors​

Every error has the same shape: a stable code to handle in your code and a message for humans.

{
"error": {
"code": "validation_failed",
"message": "Żądanie zawiera niepoprawne dane.",
"fields": {
"messages.1.msisdn": ["Numer odbiorcy to dziewięć cyfr, opcjonalnie z prefiksem +48."]
}
}
}
HTTPcodeMeaning
400invalid_jsonThe request body is not a JSON object.
401unauthenticatedMissing or invalid key.
402insufficient_fundsNo balance or free limit (for a batch: for the whole batch).
403account_unavailableSMS sending is not active on the account.
404not_foundNo such message or batch on your account.
409idempotency_conflictIdempotency-Key already used for a different body.
422validation_failedInvalid fields - details in fields. Also covers an inactive sender.
429rate_limitedRequest limit exceeded. Retry after the time in the Retry-After header.

Limits​

  • 120 requests per minute per API key. To send many messages, use batches instead of single requests in a loop.
  • 1000 messages per batch and 900 bytes per message.
  • A burst of failed authentication attempts from one address blocks further requests from that address for the rest of the minute (429).

Migrating from API v1​

API v1 still works, but new integrations should use v2.

API v1API v2
Keyin the URL: /api/v1/sending/{key}in the Authorization: Bearer header
Message fieldsnumber, override, messagemsisdn, sender, message
Bulk sending/{key}/mass, a JSON list, message by message/batches, a JSON object, the whole batch processed in the background
Errors200 with error|... for each list itemHTTP codes and error.code, invalid items pointed at in fields
Message statusnoneGET /messages/{id}, GET /batches/{id}
Retriesno duplicate protectionIdempotency-Key

The API key is the same - you do not need a new one.