Guide & Integration
Download Guide (.md)Pak Sender sends SMS, voice calls and emails to your customers — OTP codes, invoices, daily payment reports, thank-you messages, loyalty points, offers and new-stock alerts. Everything can be done by hand on the website or automatically from your own software through the API.
| Feature | Manual (website page) | Automatic (API) |
|---|---|---|
| SMS / voice OTP / voice call | Send Message | POST /messages/send-sms, /messages/send-voice-otp, /messages/send-voice-call |
| One SMS to many numbers | Bulk Campaigns | POST /messages/send-bulk-sms |
| Email campaigns | Email Marketing | — |
| Invoice / receipt / statement | Invoice via Email / SMS | POST /invoices |
| Many people, own amounts | Bulk Invoices | POST /invoices/bulk |
| Daily payments → report | Daily Entries & Reports | POST /entries, /entries/report |
| Repeat daily / weekly / monthly | Invoice form → Schedule / repeat | POST /invoice-schedules |
| Thank-you, points, order updates | Automations (+ Send test) | POST /events |
| Sales, offers, new stock | Offers & Broadcasts | POST /broadcasts |
| Customer list & tags | Customer Contacts | POST /contacts |
Account setup (once)
"smtp": ["a@shop.com","b@shop.com"] (or select several on the website). Each email goes through the account with the most sends left today, full accounts are skipped and a failed send is retried on the next one.Who receives what?
We only send to numbers / emails that you give us. This table shows where the recipient comes from in each feature:
| You use… | It goes to… |
|---|---|
| Send Message / Bulk Campaign | The number(s) you type or upload |
Invoice (web or POST /invoices) | The customer_phone (SMS) or customer_email (email) on that invoice |
| Bulk invoices | Each row / document's own phone or email — rows with the same person are merged into one document |
| Daily entries report | The phone / email saved with that customer's entries (customer_ref) |
| Recurring invoice | The customer on the schedule (or the one your source_url returns each time) |
| Automation, audience event_customer | The customer sent with the event |
| Automation / broadcast, audience all | All your Customer Contacts |
| Automation / broadcast, audience tag | Only contacts with that tag (vip, wholesale, lahore…) |
sms_opt_out / email_opt_out) are skipped automatically by automations and broadcasts. Only message people who expect to hear from you.Manual: SMS, voice & email
Send Message — choose SMS, Voice OTP or Voice Call, enter the number(s) and text, see the cost preview, press Send. Urdu is supported (Unicode, 70 characters per part). Bulk Campaigns — upload a CSV of numbers. Email Marketing — contacts, lists, templates and campaigns with open / click tracking.
Manual: invoices & receipts
Templates live in Invoice Templates. You can also import any Email Marketing template and add placeholders such as {{customer_name}}, {{items_table}}, {{total}}.
Manual: bulk invoices / reports
Bulk Invoices → choose SMS or email and the document type → paste rows:
Ali Khan, 03001234567, 2026-09-28, Payment received, 70 Ali Khan, 03001234567, 2026-09-29, Payment received, 80 Sara, 03002222222, 2026-09-29, Voucher #12, 1500
Ali gets one statement with both dates (total 150), Sara gets her own. The result of every person is shown after sending.
Manual: daily entries & reports
Use this when a customer has many small payments / vouchers during the day or month.
Manual: recurring invoices
On the invoice form choose Schedule / repeat: one time on a date, every day, week, month or N days; first send date & time (Pakistan time); stop on a date or after N invoices. Manage them in Recurring Invoices — send now, pause, resume, cancel.
Manual: automations, offers & contacts
Automations — "when EVENT happens → send". Start from an example (thank-you after order, loyalty points, order shipped, review request after 1 day, sale for everyone, new stock for a tag), adjust the text and press Send test to try it on your own number. Events normally come from your software (next section).
Offers & Broadcasts — write the offer, choose all contacts or a tag, send now or at a date & time. Customer Contacts — add / import customers and tags; contacts are also created automatically from every event.
Automatic: API basics
- Base URL:
https://paksender.com/api/v1 - Header:
Authorization: Bearer YOUR_API_KEY(orX-API-Key) andContent-Type: application/json - Every response:
{"status":"success","message":"…","data":{…}}or{"status":"error","message":"…","error_code":"…"} - Common error codes:
AUTH_ERROR(401),VALIDATION_ERROR(422),INSUFFICIENT_BALANCE(402),KYC_REQUIRED/FEATURE_DISABLED(403),NOT_FOUND(404) - Send your own reference (
external_ref,external_id) — it comes back in responses and webhooks, and makes retries safe.
A tiny PHP helper used by the recipes below:
<?php
const API_BASE = 'https://paksender.com/api/v1';
const API_KEY = 'YOUR_API_KEY';
function api_call(string $method, string $path, array $body = []): array
{
$ch = curl_init(API_BASE . $path);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . API_KEY, 'Content-Type: application/json', 'Accept: application/json'],
CURLOPT_POSTFIELDS => $body ? json_encode($body) : null,
]);
$res = json_decode(curl_exec($ch), true) ?: ['status' => 'error', 'message' => curl_error($ch)];
curl_close($ch);
return $res;
}
Recipe: OTP by SMS or voice
$code = random_int(100000, 999999); // store it with an expiry on your side
api_call('POST', '/messages/send-sms', [
'receiver_number' => '+923001234567',
'message' => "Your login code is $code. Valid for 5 minutes.",
]);
// voice call that reads the code: POST /messages/send-voice-otp {"receiver_number":"+923001234567","message":"482913"}
Recipe: one SMS to many numbers (bulk SMS)
Website: Bulk Campaigns → name, text, paste numbers (one per line) or upload a CSV → Send. API: up to 10,000 numbers per request — same text, or a personal text per number:
// same text for everyone
$r = api_call('POST', '/messages/send-bulk-sms', [
'campaign_name' => 'Eid greetings',
'receivers' => ['+923001111111', '03002222222', '+923003333333'], // or "0300…,0301…"
'message' => 'Eid Mubarak from Your Shop!',
]);
$campaignId = $r['data']['campaign']['id'];
// a different text per number (bills, balances, results…)
api_call('POST', '/messages/send-bulk-sms', ['messages' => [
['to' => '+923001111111', 'message' => 'Ali, your bill is Rs 1,200'],
['to' => '+923002222222', 'message' => 'Sara, your bill is Rs 950'],
]]);
// progress
api_call('GET', '/messages/bulk/' . $campaignId); // status, sent, failed, errors
Campaigns are sent in the background in batches of 50 (usually within a couple of minutes). The balance is checked up front; each SMS is charged when it is sent. Need names, tags and unsubscribe handling? Use Offers & Broadcasts instead.
Recipe: invoice or receipt when an order is paid
api_call('POST', '/invoices', [
'channel' => 'email', // or 'sms' + customer_phone
'doc_type' => 'receipt', // invoice | receipt | statement
'smtp' => 'billing@yourshop.com', // your SMTP username
'customer_name' => $order->name,
'customer_email' => $order->email,
'items' => array_map(fn($i) => ['product' => $i->name, 'qty' => $i->qty, 'price' => $i->price], $order->items),
'discount' => $order->discount,
'external_ref' => 'ORDER-' . $order->id,
'callback_url' => 'https://yourshop.com/webhooks/invoices', // optional
]);
Many customers at once (e.g. today's receipts): POST /invoices/bulk with "documents": [ {…}, {…} ] — up to 100 per call.
Recipe: monthly billing (fees, rent, internet, membership)
Create the schedule once when the customer subscribes — we send every month until the end date:
$r = api_call('POST', '/invoice-schedules', [
'channel' => 'sms',
'customer_name' => 'Ali Khan',
'customer_phone' => '+923001234567',
'items' => [['product' => 'Monthly fee', 'price' => 3500]],
'frequency' => 'monthly', // once | daily | weekly | monthly | custom (+ interval_days)
'start_at' => '2026-10-01 10:00', // Pakistan time
'end_at' => '2027-06-30 23:59', // or 'max_runs' => 9
'external_ref' => 'STUDENT-88',
]);
$scheduleId = $r['data']['schedule']['id']; // save it
// later: PATCH /invoice-schedules/{id} {"status":"paused"} | {"status":"active"} | {"status":"cancelled"}
Amount changes every month? Add source_url — we fetch the current customer / lines from your system right before each send (see Webhooks & live data).
Recipe: daily report from your POS / ledger
Push every payment, voucher or sale as it happens, then let a schedule send one statement per customer every night:
// 1) whenever a payment / voucher is recorded in your software
api_call('POST', '/entries', ['entries' => [[
'customer_ref' => 'CUST-12', // your customer ID
'customer_name' => 'Ali Traders',
'customer_phone' => '+923001234567',
'date' => date('Y-m-d'),
'description' => 'Payment received',
'amount' => 70,
'external_id' => 'PAY-9001', // same id again = ignored (safe retry)
]]]);
// 2) once per customer: automatic report every day at 9 PM until year end
api_call('POST', '/invoice-schedules', [
'channel' => 'sms', 'use_entries' => true, 'customer_ref' => 'CUST-12',
'customer_name' => 'Ali Traders', 'customer_phone' => '+923001234567',
'frequency' => 'daily', 'start_at' => '2026-09-29 21:00', 'end_at' => '2026-12-31 23:59',
]);
// …or send whenever you want: POST /entries/report {"channel":"sms","customer_ref":"CUST-12"} / {"channel":"sms","all":true}
The customer receives, for example: "Ali Traders: Statement (27 Sep – 29 Sep) · 27 Sep Payment = PKR 70.00 · 28 Sep Payment = PKR 80.00 · Total: PKR 150.00". Reported entries are never sent twice.
Recipe: thank-you + loyalty points
1) Create the automation once (website → Automations, or POST /automations) with event order.completed and text like "Thank you {{first_name}}! Order #{{order_id}}. You earned {{points}} points." 2) From your software, on every order:
api_call('POST', '/events', [
'event' => 'order.completed',
'customer' => ['external_id' => 'C-12', 'name' => 'Ali Khan', 'phone' => '+923001234567', 'tags' => ['vip']],
'data' => ['order_id' => '1042', 'amount' => '2,500', 'points' => 25, 'total_points' => 340],
]);
Use any event names you like (points.earned, order.shipped, birthday, payment.due…) — every active automation with that name runs. Several automations can share one event (e.g. SMS now + review request after 1 day).
Recipe: sale, offer or new stock
api_call('POST', '/broadcasts', [
'name' => 'New stock — winter jackets',
'channel' => 'sms', // sms | email | both
'audience' => 'tag', 'audience_tag' => 'vip', // or 'audience' => 'all'
'sms_body' => '{{business_name}}: {{product}} now in stock! {{link}}',
'data' => ['product' => 'Winter jackets', 'link' => 'https://yourshop.com/jackets'],
'send_at' => '2026-11-01 10:00', // empty = now
]);
Keep your contact list in sync with POST /contacts (up to 1000 per call, matched by external_id / phone / email; tags are merged).
Broadcast to any list of numbers
No contacts saved? Send straight to a list — on the website choose Send to → These numbers / emails and paste one per line, or via API:
api_call('POST', '/broadcasts', [
'name' => 'Eid offer',
'channel' => 'sms',
'audience' => 'numbers',
'recipients' => ['+923001111111', '03002222222', ['phone' => '+923003333333', 'name' => 'Sara', 'data' => ['code' => 'SARA20']]],
'sms_body' => 'Eid Mubarak {{first_name}}! Use code {{code}} for 20% off.',
'data' => ['code' => 'EID20'], // default for everyone, per-recipient "data" overrides
'save_tag' => 'eid-2026', // optional: keep them as contacts with this tag
]);
WooCommerce / WordPress example
Add to your theme's functions.php (or a small plugin) together with the helper above:
add_action('woocommerce_order_status_completed', function ($order_id) {
$o = wc_get_order($order_id);
api_call('POST', '/events', [
'event' => 'order.completed',
'customer' => [
'external_id' => 'WC-' . $o->get_customer_id(),
'name' => $o->get_formatted_billing_full_name(),
'phone' => $o->get_billing_phone(),
'email' => $o->get_billing_email(),
],
'data' => ['order_id' => $o->get_order_number(), 'amount' => $o->get_total(), 'points' => floor($o->get_total() / 100)],
]);
});
Webhooks & live data
Webhook (we → you): pass callback_url on an invoice or schedule; after every send we POST invoice.sent / invoice.failed with the full invoice and an HMAC signature. Your secret is on the Recurring Invoices page.
$raw = file_get_contents('php://input');
if (!hash_equals(hash_hmac('sha256', $raw, 'YOUR_WEBHOOK_SECRET'), $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '')) {
http_response_code(401); exit;
}
$event = json_decode($raw, true); // $event['invoice']['external_ref'], ['status'], ['total'] …
http_response_code(200);
Live data (we ← you): give a schedule a source_url. Right before each send we GET it and use what you return:
// https://yourapp.com/sms/invoice-data?schedule_id=…&external_ref=STUDENT-88
echo json_encode([
'customer_name' => 'Ali Khan',
'items' => [['product' => 'October fee', 'price' => 3500], ['product' => 'Late fine', 'price' => 200]],
'due_date' => '2026-10-10',
'skip' => false, // true = nothing to bill this time (no message, no charge)
]);
How it works inside
- Instant: single invoices, events with a few recipients and API SMS are sent within the same request.
- Queued: broadcasts, big automations, delayed messages, recurring invoices and daily reports are processed every minute by our scheduler.
- Never twice: each message / schedule run is claimed once; entries are marked when reported; repeated
external_ids are ignored. - History: All Invoices, Recurring Invoices, Message History and Message Activity show every message with its status and error.
Charges & limits
| SMS | Per part — 160 characters (70 for Urdu); long texts use several parts |
|---|---|
| Per email, through your own SMTP (your provider's daily limits apply) | |
| Invoices, reports, automations, broadcasts | Same as the SMS / email they send — no extra fee |
| Skipped messages | Free (no entries, opted-out contact, no phone / email) |
| Limits per call | Bulk SMS 10,000 numbers · bulk invoices 100 · entries 500 · contacts 1000 · bulk web page 200 recipients |
Troubleshooting
- KYC_REQUIRED / SMS page locked
- Your KYC is not approved yet — email still works.
- "SMTP not found or not active"
- Add / activate an SMTP in Email Marketing → SMTP Settings and pass its username as
smtp. - INSUFFICIENT_BALANCE
- Top up; scheduled items retry on their next run.
- Event returns
automations: 0 - No active automation uses that exact event name — check spelling and that it is switched on.
- Report "nothing sent"
- That customer has no new entries since the last report (correct behaviour — nothing is charged).
- CONTENT_BLOCKED / "Message blocked"
- The text contains prohibited content (gambling / betting promotion, scams, blocked links). Nothing was sent or charged — change the text.
- A message shows "Skipped"
- The contact unsubscribed, or has no phone / email for that channel.