SMS API της PILAS
Στείλε SMS από το site, το e-shop ή την εφαρμογή σου με ένα απλό REST αίτημα. Κωδικοί επιβεβαίωσης, υπενθυμίσεις ραντεβού, ειδοποιήσεις παραγγελιών — με τους δικούς σου αποστολείς και χρέωση από το υπόλοιπο SMS του λογαριασμού σου.
curl -X POST https://www.pilas.gr/api/sms/v1/send \
-H "Authorization: Bearer pk_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"to": "6912345678", "text": "Ο κωδικός σας είναι 4821"}'<?php
$ch = curl_init('https://www.pilas.gr/api/sms/v1/send');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('PILAS_SMS_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'to' => '6912345678',
'text' => 'Ο κωδικός σας είναι 4821',
]),
]);
$res = json_decode(curl_exec($ch), true);
echo $res['messages'][0]['status']; // sentconst res = await fetch('https://www.pilas.gr/api/sms/v1/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PILAS_SMS_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ to: '6912345678', text: 'Ο κωδικός σας είναι 4821' }),
});
const data = await res.json();
console.log(data.messages[0].status); // sentimport os, requests
r = requests.post(
'https://www.pilas.gr/api/sms/v1/send',
headers={'Authorization': f"Bearer {os.environ['PILAS_SMS_KEY']}"},
json={'to': '6912345678', 'text': 'Ο κωδικός σας είναι 4821'},
timeout=20,
)
print(r.json()['messages'][0]['status']) # sentΕισαγωγή
Το API δέχεται και επιστρέφει JSON πάνω από HTTPS. Όλα τα endpoints βρίσκονται κάτω από τη διεύθυνση:
https://www.pilas.gr/api/sms/v1Πώς ξεκινάς
- Κάνε λογαριασμό και ζήτα πρόσβαση από τη σελίδα «API».
- Μετά την έγκριση, φτιάξε ένα κλειδί Sandbox για δοκιμές και ένα Live για τις πραγματικές αποστολές.
- Αγόρασε πακέτο SMS — τα SMS δεν λήγουν ποτέ.
Ταυτοποίηση
Κάθε κλήση στέλνει το κλειδί σου στην κεφαλίδα Authorization:
Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxΕναλλακτικά δέχεται και την κεφαλίδα X-API-Key. Τα κλειδιά pk_live_… στέλνουν πραγματικά μηνύματα, τα pk_test_… δουλεύουν σε sandbox.
Αποστολή
POST /send — στέλνει ένα μήνυμα σε έναν ή περισσότερους παραλήπτες.
| Πεδίο | Τύπος | Περιγραφή |
|---|---|---|
to | string | array | Κινητό ή λίστα κινητών. Δεκτά: 69XXXXXXXX, 3069XXXXXXXX, +3069…, διεθνή. Διπλότυπα αφαιρούνται. |
text | string | Το μήνυμα, έως 1.000 χαρακτήρες. Στέλνεται σε κεφαλαία χωρίς τόνους (GSM-7). |
messages | array | Αντί για to/text: [{to, text, reference}] — διαφορετικό κείμενο ανά παραλήπτη. |
sender | string | Εγκεκριμένος αποστολέας σου. Αν λείπει, χρησιμοποιείται ο προεπιλεγμένος. |
schedule_at | string | int | Προγραμματισμός: ISO 8601 ή unix timestamp, από 1 λεπτό έως 180 ημέρες μπροστά. |
reference | string | Δικό σου αναγνωριστικό (π.χ. αριθμός παραγγελίας), έως 80 χαρακτήρες. |
curl -X POST https://www.pilas.gr/api/sms/v1/send \
-H "Authorization: Bearer pk_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"to": "6912345678", "text": "Ο κωδικός σας είναι 4821"}'<?php
$ch = curl_init('https://www.pilas.gr/api/sms/v1/send');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('PILAS_SMS_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'to' => '6912345678',
'text' => 'Ο κωδικός σας είναι 4821',
]),
]);
$res = json_decode(curl_exec($ch), true);
echo $res['messages'][0]['status']; // sentconst res = await fetch('https://www.pilas.gr/api/sms/v1/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.PILAS_SMS_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ to: '6912345678', text: 'Ο κωδικός σας είναι 4821' }),
});
const data = await res.json();
console.log(data.messages[0].status); // sentimport os, requests
r = requests.post(
'https://www.pilas.gr/api/sms/v1/send',
headers={'Authorization': f"Bearer {os.environ['PILAS_SMS_KEY']}"},
json={'to': '6912345678', 'text': 'Ο κωδικός σας είναι 4821'},
timeout=20,
)
print(r.json()['messages'][0]['status']) # sentΑπάντηση
Για έως 10 παραλήπτες η απάντηση έρχεται με την τελική κατάσταση αποστολής. Για περισσότερους, τα μηνύματα επιστρέφονται ως queued και στέλνονται αμέσως μετά — η κατάσταση τους φαίνεται με GET /messages ή μέσω webhook.
{
"ok": true,
"batch_id": "bat_t3k9qx2f8a1c04e7b9",
"mode": "live",
"count": 1,
"segments": 1,
"credits_charged": 1,
"summary": { "sent": 1 },
"messages": [
{
"id": "msg_t3k9qx5d2e7a90c1f3",
"batch_id": "bat_t3k9qx2f8a1c04e7b9",
"to": "306912345678",
"sender": "MYBRAND",
"text": "Ο ΚΩΔΙΚΟΣ ΣΑΣ ΕΙΝΑΙ 4821",
"segments": 1,
"status": "sent",
"error": null,
"reference": null,
"mode": "live",
"credits_charged": 1,
"created_at": "2026-11-02T10:15:04+02:00",
"sent_at": "2026-11-02T10:15:05+02:00",
"delivered_at": null
}
],
"balance": 4210
}Πολλοί παραλήπτες
Το to δέχεται πίνακα έως 1.000 αριθμών με το ίδιο κείμενο. Για διαφορετικό κείμενο ανά παραλήπτη χρησιμοποίησε το messages:
{
"sender": "MYBRAND",
"messages": [
{ "to": "6912345678", "text": "Η παραγγελία 1042 στάλθηκε", "reference": "order-1042" },
{ "to": "6987654321", "text": "Η παραγγελία 1043 στάλθηκε", "reference": "order-1043" }
]
}Αν έστω ένας αριθμός ή ένα κείμενο δεν είναι έγκυρο, η κλήση απορρίπτεται ολόκληρη με 422 και τη λίστα invalid — δεν στέλνεται τίποτα και δεν χρεώνεσαι.
Προγραμματισμός
Πρόσθεσε schedule_at για αποστολή αργότερα (από 1 λεπτό έως 180 ημέρες). Χωρίς ζώνη ώρας, η ώρα θεωρείται ώρα Ελλάδας.
{
"to": ["6912345678", "6987654321"],
"text": "Υπενθύμιση: το ραντεβού σας είναι αύριο στις 10:00",
"schedule_at": "2026-11-05T18:00:00+02:00"
}Η απάντηση έρχεται με 202 και τα μηνύματα σε κατάσταση scheduled. Τα SMS δεσμεύονται τη στιγμή του προγραμματισμού. Ακύρωση:
curl -X POST https://www.pilas.gr/api/sms/v1/messages/msg_t3k9qx5d2e7a90c1f3/cancel \
-H "Authorization: Bearer pk_live_xxxxxxxx"
# ή όλη την αποστολή
curl -X POST https://www.pilas.gr/api/sms/v1/batches/bat_t3k9qx2f8a1c04e7b9/cancel \
-H "Authorization: Bearer pk_live_xxxxxxxx"Κατάσταση & ιστορικό
GET /messages/{id} επιστρέφει ένα μήνυμα. GET /messages επιστρέφει το ιστορικό, με φίλτρα status, batch_id, reference, to, mode, from, until και σελιδοποίηση page / per_page (έως 100).
curl "https://www.pilas.gr/api/sms/v1/messages?status=failed&from=2026-11-01&per_page=50" \
-H "Authorization: Bearer pk_live_xxxxxxxx"Τιμές κατάστασης
| status | Σημασία |
|---|---|
queued | Σε ουρά για αποστολή |
scheduled | Προγραμματισμένο — μπορεί να ακυρωθεί |
sending | Αποστέλλεται τώρα |
sent | Παραδόθηκε στο δίκτυο κινητής |
delivered | Επιβεβαιώθηκε η παράδοση στη συσκευή |
failed | Απέτυχε (δες error) — αν δεν στάλθηκε, τα SMS επιστρέφονται |
rejected | Δεν στάλθηκε: ο παραλήπτης έχει ζητήσει διακοπή (STOP) — χωρίς χρέωση |
canceled | Ακυρώθηκε — τα SMS επιστράφηκαν |
Όλα τα endpoints
| Μέθοδος | Endpoint | Τι κάνει |
|---|---|---|
| POST | /send | Αποστολή σε έναν ή πολλούς παραλήπτες (άμεσα ή προγραμματισμένα) |
| GET | /messages/{id} | Κατάσταση ενός μηνύματος |
| GET | /messages | Ιστορικό με φίλτρα και σελιδοποίηση |
| GET | /batches/{batch_id} | Σύνοψη και μηνύματα μιας αποστολής |
| POST | /messages/{id}/cancel | Ακύρωση προγραμματισμένου μηνύματος (επιστροφή SMS) |
| POST | /batches/{batch_id}/cancel | Ακύρωση όλων των προγραμματισμένων μιας αποστολής |
| GET | /balance | Υπόλοιπο SMS, όρια και κατανάλωση ημέρας |
| GET | /senders | Οι εγκεκριμένοι αποστολείς σου |
| POST | /webhook/test | Δοκιμαστικό webhook προς το URL σου |
Υπόλοιπο & αποστολείς
GET /balance επιστρέφει τα διαθέσιμα SMS, τα όρια και τα μηνύματα της ημέρας. GET /senders επιστρέφει τους εγκεκριμένους αποστολείς σου και ποιος είναι ο προεπιλεγμένος.
{
"ok": true,
"mode": "live",
"credits": 4210,
"unit": "SMS",
"limits": { "per_minute": 60, "per_day": 2000, "used_today": 148, "max_recipients_per_request": 1000 }
}Webhooks
Όρισε ένα https:// URL στη σελίδα «API» του λογαριασμού σου. Κάθε φορά που αλλάζει η κατάσταση ενός μηνύματος σου στέλνουμε POST με event message.sent, message.delivered, message.failed, message.rejected ή message.canceled:
{
"event": "message.delivered",
"created": 1762071310,
"data": {
"id": "msg_t3k9qx5d2e7a90c1f3",
"batch_id": "bat_t3k9qx2f8a1c04e7b9",
"to": "306912345678",
"status": "delivered",
"reference": "order-1042",
"delivered_at": "2026-11-02T10:15:09+02:00"
}
}Απάντησε με οποιοδήποτε 2xx μέσα σε 6 δευτερόλεπτα. Αν αποτύχει, ξαναδοκιμάζουμε έως 6 φορές (μετά από 1′, 5′, 30′, 2 ώρες και 6 ώρες).
Έλεγχος υπογραφής
Κάθε webhook έχει κεφαλίδα X-Pilas-Signature: t=<unix>,v1=<hex>, όπου v1 = HMAC-SHA256(μυστικό, "t.σώμα"). Έλεγξέ την πριν εμπιστευτείς το αίτημα:
<?php
$secret = getenv('PILAS_WEBHOOK_SECRET'); // whsec_…
$body = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_PILAS_SIGNATURE'] ?? '';
preg_match('/t=(\d+),v1=([a-f0-9]+)/', $header, $m);
$expected = hash_hmac('sha256', $m[1] . '.' . $body, $secret);
if (!$m || !hash_equals($expected, $m[2]) || abs(time() - (int)$m[1]) > 300) {
http_response_code(400); exit;
}
$event = json_decode($body, true);
// … ενημέρωσε τη βάση σου με $event['data']['status']
http_response_code(200);import crypto from 'node:crypto';
// Express: app.post('/pilas-webhook', express.raw({ type: 'application/json' }), handler)
function handler(req, res) {
const [, t, v1] = /t=(\d+),v1=([a-f0-9]+)/.exec(req.get('X-Pilas-Signature') || '') || [];
const expected = crypto.createHmac('sha256', process.env.PILAS_WEBHOOK_SECRET)
.update(`${t}.${req.body}`).digest('hex');
const ok = v1 && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1))
&& Math.abs(Date.now() / 1000 - Number(t)) < 300;
if (!ok) return res.sendStatus(400);
const event = JSON.parse(req.body);
// … ενημέρωσε τη βάση σου με event.data.status
res.sendStatus(200);
}Sandbox
Με κλειδί pk_test_… το API συμπεριφέρεται ακριβώς όπως στην παραγωγή — επικύρωση, απαντήσεις, webhooks — αλλά κανένα SMS δεν φεύγει και τίποτα δεν χρεώνεται. Τα μηνύματα γίνονται αμέσως delivered.
Για να δοκιμάσεις αποτυχία, στείλε σε αριθμό που τελειώνει σε 0000: το μήνυμα γίνεται failed με error simulated_failure.
Όρια & idempotency
- 60 κλήσεις ανά λεπτό και 2.000 μηνύματα την ημέρα ανά λογαριασμό (αυξάνονται κατόπιν αιτήματος).
- Έως 1.000 παραλήπτες ανά κλήση, κείμενο έως 1.000 χαρακτήρες.
- Όταν ξεπεραστεί όριο, η απάντηση είναι
429με κεφαλίδαRetry-After.
Για να μη σταλεί ποτέ κάτι δύο φορές (π.χ. σε επανάληψη μετά από timeout), στείλε την κεφαλίδα Idempotency-Key με μοναδική τιμή ανά αποστολή. Για 24 ώρες, η ίδια τιμή επιστρέφει την αρχική απάντηση χωρίς νέα αποστολή ή χρέωση.
curl -X POST https://www.pilas.gr/api/sms/v1/send \
-H "Authorization: Bearer pk_live_xxxxxxxx" \
-H "Idempotency-Key: order-1042-shipped" \
-H "Content-Type: application/json" \
-d '{"to": "6912345678", "text": "Η παραγγελία σας στάλθηκε"}'Σφάλματα
Κάθε σφάλμα επιστρέφει "ok": false με σταθερό code για τον κώδικά σου και ελληνικό message για τους χρήστες σου:
{
"ok": false,
"error": {
"code": "insufficient_credits",
"message": "Δεν υπάρχουν αρκετά SMS. Χρειάζονται 120, υπόλοιπο 80.",
"credits_needed": 120,
"balance": 80
}
}| HTTP | code | Πότε |
|---|---|---|
| 400 | invalid_json | Το σώμα δεν είναι έγκυρο JSON |
| 401 | missing_api_key / invalid_api_key | Λείπει ή δεν ισχύει το κλειδί |
| 402 | insufficient_credits | Δεν φτάνει το υπόλοιπο SMS — δεν στάλθηκε τίποτα |
| 403 | api_access_inactive / account_inactive | Η πρόσβαση είναι σε αναστολή ή ο λογαριασμός ανενεργός |
| 404 | not_found | Άγνωστο id ή endpoint |
| 405 | method_not_allowed | Λάθος μέθοδος HTTP |
| 409 | not_cancelable / idempotency_in_progress | Δεν ακυρώνεται πια / η ίδια κλήση εκτελείται ήδη |
| 422 | invalid_recipients, sender_not_approved, invalid_schedule, content_not_allowed … | Λάθος στα δεδομένα — δεν στάλθηκε τίποτα |
| 429 | rate_limited / daily_limit_exceeded | Ξεπεράστηκε όριο — δες Retry-After |
| 503 | service_unavailable | Προσωρινή αδυναμία — ξαναδοκίμασε αργότερα |
Έτοιμος να ξεκινήσεις;
Ζήτα πρόσβαση και δοκίμασε δωρεάν με κλειδί sandbox.
