Bulk Messaging
Overview
Bulk Messaging is a two-step workflow for sending many SMS rows with the same approved template and sender selection.
Upload first. FutureSMS parses the CSV and stores accepted rows as bulk items. Execute second. FutureSMS creates SMS orders only when the execution command is accepted and the rows pass send-time checks.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /api/v1/sms/bulk-jobs | Upload bulk SMS rows. |
POST | /api/v1/sms/bulk-jobs/{job_id}/executions | Execute a bulk SMS job. |
GET | /api/v1/sms/bulk-jobs/{job_id} | Get bulk SMS job status. |
GET | /api/v1/sms/bulk-jobs/{job_id}/items | List bulk SMS job items. |
Authentication: Bearer access token.
POST /api/v1/sms/bulk-jobs
Before You Begin
You need an authenticated merchant API user, an approved template, an authorized sender or enabled auto routing, and a UTF-8 CSV file.
The upload request chooses one template with the multipart form field template_id. The CSV file contains recipient rows; it does not select a different template per row.
phone_number,var
+639171234567,Alice
+639181234568,Bob
CSV file rules:
| Column | Required | Rules |
|---|---|---|
phone_number | Yes | E.164 phone number, for example +639171234567. |
var | Depends on template | Optional template value. For {{var}} templates, max 24 characters. |
Extra CSV columns are allowed only when they are empty. If an unsupported column has a value, that row is rejected with an error such as unsupported columns present: note.
Template rules:
| Template content | CSV var behavior |
|---|---|
Welcome to FutureSMS, {{var}} | var is required and replaces {{var}}. |
{{free}} | var is required and becomes the full message body. |
Service notice from FutureSMS | var must be empty. |
Rendered content must fit in one SMS: GSM-7 content can use up to 160 units, and Unicode content can use up to 70 characters.
Request fields
Content type: multipart/form-data.
| Field | Type | Required | Rules |
|---|---|---|---|
template_id | string | Yes | Approved template logical ID. |
sender_id | string | Yes | Authorized sender logical ID, or AUTO when auto routing is enabled. |
file | file | Yes | UTF-8 CSV file. |
{
"template_id": "TPL-3644",
"sender_id": "SID-3425",
"file": "bulk-upload.csv"
}
Response fields
| Field | Meaning |
|---|---|
bulk_job_id | FutureSMS bulk job identifier. Store it for execution and lookup. |
total_rows | Number of data rows in the uploaded CSV. |
valid_rows | Rows accepted as bulk items during upload. |
invalid_rows | Rows rejected during upload. |
bulk_job_status | Job status: items_ready, executed, or failed. |
Behavior
201 Created means the upload request created a bulk job and returned the upload result. It does not mean messages have been sent.
valid_rows as sent messages. Orders are created only after the execution endpoint accepts and processes eligible rows.During upload, FutureSMS reads rows in CSV order, trims header names and cell values, validates phone_number, applies the selected template rule to var, validates the rendered SMS length, stores valid rows as bulk items, and records rejected rows with row-level errors.
The upload response does not include ordered_rows; query the job endpoint after execution to read that counter.
Status codes
| Status | Meaning |
|---|---|
201 | Bulk job was created and the upload result was returned. |
401 | Missing, invalid, or expired access token. |
403 | Authenticated caller is not allowed to use the selected merchant context, template, sender, or auto-routing setup. |
422 | Multipart body, file, or field validation failed. |
Example
{
"message": "Created",
"details": null,
"data": {
"bulk_job_id": "BKJ-9001",
"total_rows": 2,
"valid_rows": 2,
"invalid_rows": 0,
"bulk_job_status": "items_ready"
},
"meta": null
}
POST /api/v1/sms/bulk-jobs/{job_id}/executions
Before You Begin
Use a bulk_job_id returned by the upload endpoint. The request has no body.
Bulk endpoints do not define an order-level dlr_address field. Bulk SMS delivery report callbacks use the merchant callback address configured for the account.
Request fields
Path parameters:
| Name | Required | Example |
|---|---|---|
job_id | Yes | BKJ-5151 |
There is no request body.
Response fields
| Field | Meaning |
|---|---|
data.bulk_job_id | Bulk job identifier accepted for execution. |
Behavior
202 Accepted means the execution command was accepted. It does not return final row outcomes, and it does not return ordered_rows.
After calling this endpoint, use GET /api/v1/sms/bulk-jobs/{job_id} for job-level counters and GET /api/v1/sms/bulk-jobs/{job_id}/items for row-level outcomes.
Status codes
| Status | Meaning |
|---|---|
202 | Execution command was accepted. |
401 | Missing, invalid, or expired access token. |
403 | Authenticated caller is not allowed to execute this job. |
404 | Bulk job was not found or is not visible to the caller. |
409 | Job is not executable, has no valid rows, or has already moved from its executable state. |
422 | Path parameter validation failed. |
Example
{
"message": "Accepted",
"data": {
"bulk_job_id": "BKJ-9001"
},
"details": null,
"meta": null
}
GET /api/v1/sms/bulk-jobs/{job_id}
Before You Begin
Use the bulk_job_id returned by the upload endpoint. This endpoint is for job-level counters and timestamps, not row-level outcomes.
Request fields
Path parameters:
| Name | Required | Example |
|---|---|---|
job_id | Yes | BKJ-3875 |
Response fields
| Field | Meaning |
|---|---|
bulk_job_id | FutureSMS bulk job identifier. |
merchant_id | Merchant logical ID, when returned. |
user_id | API user logical ID, when returned. |
template_id | Template logical ID, when returned. |
sender_id | Sender logical ID, when returned. |
total_rows | Number of rows in the uploaded CSV. |
valid_rows | Rows accepted during upload. |
invalid_rows | Rows rejected during upload. |
ordered_rows | Rows that produced SMS orders after execution. |
bulk_job_status | Job status: items_ready, executed, or failed. |
started_at | Job start time, when available. |
completed_at | Job completion time, when available. |
created_at | UTC creation time. |
Behavior
Use this endpoint to read the current job-level state. After execution, this is where clients read ordered_rows.
Status codes
| Status | Meaning |
|---|---|
200 | Job was found and returned. |
401 | Missing, invalid, or expired access token. |
403 | Authenticated caller is not allowed to view this job. |
404 | Bulk job was not found or is not visible to the caller. |
422 | Path parameter validation failed. |
Example
{
"message": "OK",
"details": null,
"data": {
"bulk_job_id": "BKJ-9001",
"total_rows": 2,
"valid_rows": 2,
"invalid_rows": 0,
"ordered_rows": 2,
"bulk_job_status": "items_ready",
"started_at": "2026-03-25T10:40:58Z",
"completed_at": "2026-03-25T10:41:05Z",
"created_at": "2026-03-25T10:40:58Z"
},
"meta": null
}
GET /api/v1/sms/bulk-jobs/{job_id}/items
Before You Begin
Use the bulk_job_id returned by the upload endpoint. This endpoint returns row-level outcomes and supports pagination.
Request fields
Path parameters:
| Name | Required | Example |
|---|---|---|
job_id | Yes | BKJ-4468 |
Query parameters:
| Field | Type | Required | Rules |
|---|---|---|---|
bulk_job_item_id | string or null | No | Max 32 chars. |
row_no | integer or null | No | CSV row number. |
order_id | string or null | No | Max 32 chars. |
bulk_item_status | enum or null | No | pending, filled, or rejected. |
phone_number | string or null | No | Max 32 chars. |
error_message | string or null | No | Max 255 chars. |
created_at_from | date-time or null | No | Lower created-at bound. |
created_at_to | date-time or null | No | Upper created-at bound. |
updated_at_from | date-time or null | No | Lower updated-at bound. |
updated_at_to | date-time or null | No | Upper updated-at bound. |
page | integer | No | Default 1, minimum 1. |
limit | integer | No | Default 10, minimum 1, maximum 500. |
sort_by | enum | No | Default row_no. |
sort_order | enum | No | asc or desc; default asc. |
Allowed sort_by values: bulk_job_item_id, row_no, order_id, bulk_item_status, phone_number, error_message, created_at, updated_at.
Response fields
Each item in data contains:
| Field | Meaning |
|---|---|
bulk_job_item_id | FutureSMS bulk job item identifier. |
row_no | CSV row number. |
order_id | SMS order identifier when the row produced an order; otherwise null. |
bulk_item_status | Row status: pending, filled, or rejected. |
phone_number | Destination phone number. |
error_message | Row-level rejection or processing error, when available. |
created_at | UTC creation time. |
updated_at | UTC update time. |
meta.pagination contains page, limit, and total.
Behavior
Use this endpoint to inspect individual rows after upload or execution. filled rows have an order_id; rejected rows have an error_message when an error is available.
Status codes
| Status | Meaning |
|---|---|
200 | Item rows were returned. |
401 | Missing, invalid, or expired access token. |
403 | Authenticated caller is not allowed to view this job. |
404 | Bulk job was not found or is not visible to the caller. |
422 | Path or query parameter validation failed. |
Example
{
"message": "OK",
"details": null,
"data": [
{
"bulk_job_item_id": "BKI-12001",
"row_no": 1,
"order_id": "SDPB260325104102A1B2C3E",
"bulk_item_status": "filled",
"phone_number": "+639171230001",
"error_message": null,
"created_at": "2026-03-25T10:41:02Z",
"updated_at": "2026-03-25T10:41:02Z"
},
{
"bulk_job_item_id": "BKI-12002",
"row_no": 2,
"order_id": null,
"bulk_item_status": "rejected",
"phone_number": "+639171230002",
"error_message": "Phone number is blacklisted for this merchant",
"created_at": "2026-03-25T10:41:04Z",
"updated_at": "2026-03-25T10:42:33Z"
}
],
"meta": {
"pagination": {
"page": 1,
"limit": 10,
"total": 2
}
}
}