ChinqIT VerifyChinqIT Verify

Broadcasts (Bulk SMS API)

Send the same SMS to many recipients in a single API call.

Send SMS messages to multiple recipients in a single API call.

Note: The dashboard feature is called Broadcasts (Diffusions / البث). The underlying API keeps the legacy bulk path for backwards compatibility — bulk.completed webhook events and /api/v2/bulk/* endpoints are unchanged.

Authentication and base URL are documented in the API Reference.

Composer (dashboard)

The dashboard's broadcast composer uses a focused three-step flow:

  • 1 · Recipients — upload an .xlsx or .xls spreadsheet or paste phone numbers. These are the two primary ways to start. A saved audience is available as a secondary shortcut. The recipients card shows the valid, invalid, and duplicate counts and a preview of the first rows (phone, the spreadsheet's other columns, and whether each number will be sent) before you continue.
  • 2 · Message — choose the Sender ID (each API key sends under its sender ID; a key without one uses the platform default), then write one common message, which is used by default for every sendable recipient. If the spreadsheet includes a message column, open Spreadsheet messages (advanced) to opt into its read-only per-row messages. Personalization tokens can use other spreadsheet columns. A live phone preview, headed by the chosen sender ID, updates as you edit.
  • 3 · Review & send — confirm the recipients and counts, the message and preview, estimated cost, current balance, balance after send, and timing. Sending now is the default; scheduling is a secondary option under More options.

On steps 1 and 2 a cost summary sits beside the form on wide screens, and below it on phones: recipients, parts per message, price per part, cost, and the balance after the send. Parts per message shows a dash until there is a message to count, and price per part a dash while pricing is unavailable. If your balance does not cover the send, the summary shows the shortfall and a Top up your balance link.

Saved audiences are stored per account (up to 50,000 recipients each) and can be reused across broadcasts. The public API is unchanged — see the API reference for /api/v2/bulk/*.

Templates & personalization (dashboard)

The composer can start from a saved template (⚡ Start from a template). Templates are per-account: create them from a message you composed, or write one in the template manager, and organize them by category (reminder, announcement, promotion, other).

On the Templates page, each card shows the template's category, its text with the {variables} highlighted, and when it was saved. Select Use on a card to open the composer with that template filled in. Use the card's menu (⋯) to edit or delete it; deleting asks for confirmation first. The new and edit forms show a live phone preview of the message; the character and SMS-part count is shown above the message box.

Messages can include personalization tokens: any {word} in the message is replaced with the value of the matching column in your recipient file. For example, a first_name column lets you send "Hello {first_name}, your appointment is confirmed". Common tokens such as {first_name}, {amount}, or {link} can be inserted with one click. When a recipient row is missing a value, the token is left as-is in the sent message and the composer warns you before sending.

Scheduling (dashboard)

Scheduled broadcasts are a secondary dashboard option under More options — the public API always sends immediately.

  1. In the composer, open More options, switch When from Send now to Schedule, and pick a date and time (between 1 minute and 30 days ahead).
  2. The broadcast is pre-charged at schedule time — same cost and balance check as an immediate send — and enters the scheduled state.
  3. At the chosen time it sends automatically; the live results screen shows it firing and completing.
  4. Until it fires you can cancel it from the live results screen. Cancelling refunds the full pre-charge and marks the broadcast failed (reason cancelled) — see Billing.

Live results & history

Once a broadcast starts, its detail page becomes a live results screen:

  • Sent rate — the share of valid recipients whose message was written to the operator (e.g. 97.4%). "Sent" means accepted by the operator, not that the recipient's handset confirmed receipt (SMPP delivery reports are not aggregated to broadcasts).
  • Recipient grid — one dot per valid recipient: dots fill in as messages go out, failed dots are red and stay in place as the broadcast runs, and queued dots are faint. Above 2,500 recipients each dot stands for a fixed group of recipients, and the legend says how many.
  • Counters — Sent / Queued / Failed / Invalid, color-coded and updated by 2-second polling while the broadcast runs.
  • Sends per minute — the current send rate, with a trend line of recent status polls. A finished broadcast shows its average. Both are derived client-side while the page is open.
  • What we sent — a preview of the message content, captured when the broadcast was created.
  • Duration — elapsed time while running, total time once finished.
  • Recipients — each recipient's phone, status, parts, cost, and error, filterable by status.
  • Download CSV — a phone,status,error report of every recipient, matching what the screen shows at download time.

The header names the sender ID the broadcast uses. If a broadcast finishes while its page is open, a notification confirms how many recipients it was sent to.

The history page ("Your broadcasts") shows four summary cards — broadcasts, messages sent, delivery rate, spent — scoped to the active filters and search, followed by a table of named broadcasts (name, template type, audience size, progress, cost, status, created date). Progress shows the messages sent out of the valid recipients, with the percentage; a scheduled broadcast shows its scheduled time there instead. The audience is the full uploaded list, with its invalid numbers counted underneath. The delivery rate covers completed broadcasts only, so one still sending does not lower it. Search matches the name or batch ID across all your broadcasts, not only the page on screen. Click a broadcast to open its live results.

Admin monitoring (dashboard)

The support team has a Broadcasts page in the admin that mirrors the client history metrics:

  • Four summary cards — broadcasts, messages sent, delivery rate, spent — scoped to the active filters (status and period) and the client search, and always consistent with the table.
  • Period filter — today, last 7 days, last 30 days, this month — applied to both the table and the cards (bounds computed in the server's timezone).
  • Broadcasts table — name (or batch ID), client, audience, progress, cost, status, created date. Progress shows the messages sent out of the valid recipients, with the percentage; a scheduled broadcast shows its scheduled fire time there. Search by name, batch ID, or client.

The admin screen cannot open a broadcast's detail: live-results screens stay with the owning client account.

Overview

The Bulk SMS API allows you to send SMS messages to multiple recipients efficiently. Each recipient can have a custom message, or you can send the same message to all recipients.

Send Bulk Messages

POST /api/v2/bulk/send

Send SMS messages to multiple recipients in a single request.

Request Body

ParameterTypeRequiredDescription
recipientsarrayYesArray of recipient objects (max 50,000)
recipients[].phonestringYesPhone number in international format
recipients[].messagestringYesMessage text for this recipient

Example Request

{
  "recipients": [
    {
      "phone": "+22238089336",
      "message": "Hello John, your order is ready!"
    },
    {
      "phone": "+22238089337",
      "message": "Hello Sarah, your order is ready!"
    }
  ]
}

Success Response (202 Accepted)

{
  "success": true,
  "message": "Bulk batch created successfully",
  "batchId": "550e8400-e29b-41d4-a716-446655440000",
  "totalValid": 2,
  "totalInvalid": 0,
  "cost": 0.04,
  "totalParts": 2,
  "pricePerMessage": 0.02,
  "status": "processing"
}

Error Responses

400 Bad Request - Validation Error

{
  "success": false,
  "message": "Validation failed",
  "errors": [
    {
      "path": ["recipients"],
      "message": "Array must contain at least 1 element(s)"
    }
  ]
}

402 Payment Required - Insufficient Balance

{
  "success": false,
  "message": "Insufficient balance",
  "details": {
    "requiredAmount": 0.04,
    "validRecipients": 2,
    "invalidRecipients": 0
  }
}

409 Conflict - Broadcast Not Created

Rare. Nothing was sent or charged; send it again.

{
  "success": false,
  "message": "This broadcast could not be created; nothing was sent or charged. Send it again."
}

400 Bad Request - No Valid Recipients

{
  "success": false,
  "message": "No valid recipients found",
  "invalidRecipients": [
    {
      "phone": "invalid",
      "reason": "Invalid phone number format"
    }
  ]
}

Get Batch Status

GET /api/v2/bulk/status/

Retrieve the status of a bulk send batch.

URL Parameters

ParameterTypeRequiredDescription
batchIdstringYesThe batch ID returned from the send request

Example Response

{
  "success": true,
  "batch": {
    "batchId": "550e8400-e29b-41d4-a716-446655440000",
    "status": "processing",
    "totalMessages": 100,
    "validMessages": 98,
    "invalidCount": 2,
    "successfulCount": 45,
    "failedCount": 5,
    "totalCost": 1.96,
    "totalParts": 100,
    "progress": 51,
    "createdAt": "2024-01-15T10:30:00.000Z",
    "updatedAt": "2024-01-15T10:31:30.000Z",
    "messages": [
      {
        "messageId": "msg-123",
        "phone": "+22238089336",
        "status": "sent",
        "cost": 0.02,
        "parts": 1
      },
      {
        "messageId": "msg-124",
        "phone": "+22238089337",
        "status": "failed",
        "error": "Invalid destination address"
      }
    ]
  }
}

Status Values

StatusDescription
pendingBatch created, awaiting processing
scheduledBatch created with a future scheduledAt; sends automatically at that time
processingMessages are being queued and sent
completedAll messages sent successfully
partialSome messages succeeded, some failed
failedAll messages failed

A failed batch may carry a failureReason: cancelled, insufficient_balance when your balance could not pay for it, or creation_abandoned when it could not be created (in both cases nothing was sent or charged).

Message Status Values

StatusDescription
pendingMessage awaiting processing
queuedMessage queued for sending
sentMessage sent successfully
failedMessage failed to send

Rate Limits

  • Maximum 50,000 recipients per batch for messages of up to one SMS part (~160 characters ASCII / ~70 characters UCS-2)
  • Standard per-second API rate limits do not apply to bulk endpoints (see API Reference)
  • A key can send 2 broadcasts per minute. More return 429 with retryAfter (seconds).
  • When the gateway is already receiving several large broadcasts, it returns 503 with a Retry-After header (seconds). Nothing was created or charged; send the same request again after that delay.
  • The request body must arrive within 5 seconds plus 2 seconds per MB (45 seconds for 20 MB). A slower upload is answered 408; nothing was created or charged.
  • Messages are queued and processed asynchronously

The request body is also capped at 20 MB, so multi-part or max-length messages reduce the effective batch size. As a rule of thumb, the body must satisfy recipients × (encoded message bytes + ~40 bytes of per-recipient JSON overhead) ≤ 20 MB, i.e. roughly recipients ≈ (20 MB − overhead) / (encoded message length + ~40 bytes):

  • 50,000 recipients with 160-character single-part ASCII messages fit comfortably (~10 MB)
  • Max-length messages (up to 2,000 encoded bytes) fit only ~10,000 recipients

Phone Number Format

See API Reference (SMS / Mauritania) for accepted phone number formats.

Billing

You are charged for every message we submit to the operator. The operator bills us on reception, not on acceptance, so a message it receives and then refuses is still billed.

  • Sent — the operator accepted the submission. Charged.
  • Rejected — the operator refused the submission, for example because the number is not a valid destination. Charged, because the operator billed us for it.
  • Failed — we never submitted the message: our gateway could not reach the operator, or your balance was insufficient. Not charged, and any charge already taken is refunded.

Cost is per message, based on its length and encoding: a longer body, or non-GSM-7 text such as Arabic or emoji, splits into more parts, and a multi-part SMS is charged per part. Funds for the whole batch are deducted upfront when the batch is created.

Messages addressed to a number the operator rejected as invalid within the last hour are not resubmitted, and are not charged.

Code Examples

JavaScript/Node.js

const response = await fetch('https://sms.chinqit.com/api/v2/bulk/send', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'your_api_key_here'
  },
  body: JSON.stringify({
    recipients: [
      { phone: '+22238089336', message: 'Hello!' },
      { phone: '+22238089337', message: 'Hi there!' }
    ]
  })
});

const result = await response.json();
console.log(result.batchId);

Python

import requests

response = requests.post(
    'https://sms.chinqit.com/api/v2/bulk/send',
    headers={
        'Content-Type': 'application/json',
        'X-API-Key': 'your_api_key_here'
    },
    json={
        'recipients': [
            {'phone': '+22238089336', 'message': 'Hello!'},
            {'phone': '+22238089337', 'message': 'Hi there!'}
        ]
    }
)

result = response.json()
print(result['batchId'])

PHP

<?php
$ch = curl_init('https://sms.chinqit.com/api/v2/bulk/send');

$data = [
    'recipients' => [
        ['phone' => '+22238089336', 'message' => 'Hello!'],
        ['phone' => '+22238089337', 'message' => 'Hi there!']
    ]
];

curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Content-Type: application/json',
    'X-API-Key: your_api_key_here'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
echo $result['batchId'];

cURL

curl -X POST https://sms.chinqit.com/api/v2/bulk/send \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_api_key_here" \
  -d '{
    "recipients": [
      {"phone": "+22238089336", "message": "Hello!"},
      {"phone": "+22238089337", "message": "Hi there!"}
    ]
  }'

Polling for Status

After sending a batch, poll the status endpoint every 2-5 seconds until the batch completes:

const checkStatus = async (batchId) => {
  const response = await fetch(`https://sms.chinqit.com/api/v2/bulk/status/${batchId}`, {
    headers: { 'X-API-Key': 'your_api_key_here' }
  });
  
  const result = await response.json();
  
  if (['completed', 'failed', 'partial'].includes(result.batch.status)) {
    console.log('Batch complete!', result.batch);
    return result.batch;
  }
  
  // Continue polling
  console.log(`Progress: ${result.batch.progress}%`);
  setTimeout(() => checkStatus(batchId), 2000);
};

Error Codes

CodeDescription
NO_CREDITInsufficient balance
TOP_UP_REQUIREDNew accounts send OTPs only until their first top-up.
BILLING_FAILEDBilling transaction failed
INVALID_DESTINATIONInvalid phone number format
QUEUE_ERRORFailed to queue message

Best Practices

  1. Validate phone numbers before sending to avoid unnecessary costs
  2. Poll for status to track message delivery
  3. Handle partial failures - some messages may succeed while others fail
  4. Batch size - Keep batches under 10,000 for optimal performance
  5. Retry logic - Failed messages are automatically retried up to 4 times

On this page