Portal
Messaging Workflows

Bulk Messaging

Prepare, upload, execute, and inspect bulk SMS jobs.

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

MethodPathPurpose
POST/api/v1/sms/bulk-jobsUpload bulk SMS rows.
POST/api/v1/sms/bulk-jobs/{job_id}/executionsExecute 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}/itemsList 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.

Download CSV starter template

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.

CSV starter file
phone_number,var
+639171234567,Alice
+639181234568,Bob

CSV file rules:

ColumnRequiredRules
phone_numberYesE.164 phone number, for example +639171234567.
varDepends on templateOptional 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 contentCSV 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 FutureSMSvar 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.

FieldTypeRequiredRules
template_idstringYesApproved template logical ID.
sender_idstringYesAuthorized sender logical ID, or AUTO when auto routing is enabled.
filefileYesUTF-8 CSV file.
Form fields
{
  "template_id": "TPL-3644",
  "sender_id": "SID-3425",
  "file": "bulk-upload.csv"
}

Response fields

FieldMeaning
bulk_job_idFutureSMS bulk job identifier. Store it for execution and lookup.
total_rowsNumber of data rows in the uploaded CSV.
valid_rowsRows accepted as bulk items during upload.
invalid_rowsRows rejected during upload.
bulk_job_statusJob 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.

Do not treat 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

StatusMeaning
201Bulk job was created and the upload result was returned.
401Missing, invalid, or expired access token.
403Authenticated caller is not allowed to use the selected merchant context, template, sender, or auto-routing setup.
422Multipart body, file, or field validation failed.

Example

Upload response
{
  "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:

NameRequiredExample
job_idYesBKJ-5151

There is no request body.

Response fields

FieldMeaning
data.bulk_job_idBulk 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

StatusMeaning
202Execution command was accepted.
401Missing, invalid, or expired access token.
403Authenticated caller is not allowed to execute this job.
404Bulk job was not found or is not visible to the caller.
409Job is not executable, has no valid rows, or has already moved from its executable state.
422Path parameter validation failed.

Example

Execute response
{
  "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:

NameRequiredExample
job_idYesBKJ-3875

Response fields

FieldMeaning
bulk_job_idFutureSMS bulk job identifier.
merchant_idMerchant logical ID, when returned.
user_idAPI user logical ID, when returned.
template_idTemplate logical ID, when returned.
sender_idSender logical ID, when returned.
total_rowsNumber of rows in the uploaded CSV.
valid_rowsRows accepted during upload.
invalid_rowsRows rejected during upload.
ordered_rowsRows that produced SMS orders after execution.
bulk_job_statusJob status: items_ready, executed, or failed.
started_atJob start time, when available.
completed_atJob completion time, when available.
created_atUTC creation time.

Behavior

Use this endpoint to read the current job-level state. After execution, this is where clients read ordered_rows.

Status codes

StatusMeaning
200Job was found and returned.
401Missing, invalid, or expired access token.
403Authenticated caller is not allowed to view this job.
404Bulk job was not found or is not visible to the caller.
422Path parameter validation failed.

Example

Job response
{
  "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:

NameRequiredExample
job_idYesBKJ-4468

Query parameters:

FieldTypeRequiredRules
bulk_job_item_idstring or nullNoMax 32 chars.
row_nointeger or nullNoCSV row number.
order_idstring or nullNoMax 32 chars.
bulk_item_statusenum or nullNopending, filled, or rejected.
phone_numberstring or nullNoMax 32 chars.
error_messagestring or nullNoMax 255 chars.
created_at_fromdate-time or nullNoLower created-at bound.
created_at_todate-time or nullNoUpper created-at bound.
updated_at_fromdate-time or nullNoLower updated-at bound.
updated_at_todate-time or nullNoUpper updated-at bound.
pageintegerNoDefault 1, minimum 1.
limitintegerNoDefault 10, minimum 1, maximum 500.
sort_byenumNoDefault row_no.
sort_orderenumNoasc 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:

FieldMeaning
bulk_job_item_idFutureSMS bulk job item identifier.
row_noCSV row number.
order_idSMS order identifier when the row produced an order; otherwise null.
bulk_item_statusRow status: pending, filled, or rejected.
phone_numberDestination phone number.
error_messageRow-level rejection or processing error, when available.
created_atUTC creation time.
updated_atUTC 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

StatusMeaning
200Item rows were returned.
401Missing, invalid, or expired access token.
403Authenticated caller is not allowed to view this job.
404Bulk job was not found or is not visible to the caller.
422Path or query parameter validation failed.

Example

Item response
{
  "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
    }
  }
}