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, renders each row against the selected template, and stores valid rows as pending bulk items. Execute second. FutureSMS creates SMS orders only for rows that are still valid at send time.

This split is intentional: it lets you find file-format problems before messages are sent, and it keeps row-level failures visible after execution.

Endpoints

MethodPathPurpose
POST/api/v1/sms/bulk-jobsUpload and itemize a CSV file.
POST/api/v1/sms/bulk-jobs/{job_id}/executionsExecute accepted rows.
GET/api/v1/sms/bulk-jobs/{job_id}Read job-level counts and status.
GET/api/v1/sms/bulk-jobs/{job_id}/itemsRead row-level outcomes.

Authentication: Bearer access token.

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 with recipient rows.

Upload and execution are separate. Store the returned bulk_job_id after upload, then use it to execute the job or inspect row-level results.

Request fields

File Format

The upload file must be a UTF-8 CSV file with a .csv filename. UTF-8 with BOM is accepted.

phone_number is required. var is optional and is used only when the selected template needs a variable.

Bulk upload file
phone_number,var
+639171230001,Alice
+639171230002,Bob

Only these columns are supported:

ColumnRequiredRules
phone_numberYesE.164 phone number, for example +639171234567.
varDepends on templateOptional template value. For {{var}} templates, max 24 characters.

Extra columns are allowed only when they are empty. If an unsupported column has a value, that row is rejected with an error like unsupported columns present: note.

Template Rules

The upload request chooses one template with the multipart form field template_id. The CSV only supplies phone_number and optional var; it cannot choose a different template per row.

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.

Upload Request

POST /api/v1/sms/bulk-jobs uses multipart/form-data.

FieldTypeRequiredMeaning
template_idstringYesApproved template logical ID, such as TPL-3644.
sender_idstringYesAuthorized sender logical ID, such as SID-3425, or AUTO when FutureSMS has enabled auto routing for your merchant.
filefileYesUTF-8 CSV file.
Form fields
{
  "template_id": "TPL-3644",
  "sender_id": "AUTO",
  "file": "bulk-upload.csv"
}

Execute Request

POST /api/v1/sms/bulk-jobs/{job_id}/executions starts execution for an items_ready job.

Execution does not just replay upload validation. Each pending row goes through send-time checks again, including account state, approved template, sender or auto-routing availability, destination policy, order creation, and dispatch registration.

Rows that pass become filled and receive an order_id. Rows that fail become rejected with an error_message.

Item Filters

GET /api/v1/sms/bulk-jobs/{job_id}/items supports row-level filters.

Query parameterTypeExample
bulk_job_item_idstringBKI-5519
row_nointeger1
order_idstringSDPS26032510301200003MC
bulk_item_statusenumfilled
phone_numberstring+63917105134
error_messagestringInvalid destination number
created_at_fromdate-time2026-03-25T10:30:00Z
created_at_todate-time2026-03-25T10:30:00Z
updated_at_fromdate-time2026-03-25T10:30:00Z
updated_at_todate-time2026-03-25T10:30:00Z
pageinteger1
limitinteger500
sort_byenumrow_no
sort_orderenumasc

Response fields

Upload Counts

After the file is accepted, FutureSMS starts itemization. The upload response waits for itemization to finish when it completes within the configured wait window.

During itemization FutureSMS:

  1. Reads rows in CSV order.
  2. Trims header names and cell values.
  3. Validates phone_number.
  4. Applies the selected template rules to var.
  5. Validates rendered message length.
  6. Stores valid rows as pending bulk items.
  7. Records rejected rows with a row-level error message.
CountMeaning
total_rowsNumber of data rows in the CSV.
valid_rowsRows accepted as pending bulk items.
invalid_rowsRows rejected during upload itemization.

If no valid rows remain, the job becomes failed and cannot be executed.

Job Status

StatusMeaning
items_readyUpload itemization finished and at least one row can be executed.
executedExecution was accepted and row processing has completed.
failedUpload itemization or job processing failed before execution.

ordered_rows is the number of bulk items that produced orders. It is populated after execution, not after upload.

Item Status

StatusMeaning
pendingRow was accepted during upload and is waiting for execution.
filledRow produced an SMS order.
rejectedRow failed upload validation or send-time checks.

Common rejected reasons include missing phone_number, unsupported non-empty columns, invalid phone number, missing var for a variable template, var provided for a fixed template, message content too long, unavailable destination, unavailable auto routing when sender_id is AUTO, or insufficient account state.

Behavior

Upload counts describe file itemization only. They do not mean messages have been sent.

Do not treat valid_rows as sent messages. Orders are created only after the execution step accepts and processes eligible rows.

Execution creates SMS orders only for rows that pass send-time checks. Query the job and item endpoints to inspect final row outcomes.

Status codes

StatusWhereMeaning
201UploadBulk job was created and itemization result is returned.
202ExecuteExecution command was accepted.
200QueryJob or item rows were returned.
401AllMissing, invalid, or expired access token.
403AllAuthenticated caller is not allowed to use this job, sender, template, or auto-routing setup.
404Job routesBulk job was not found or is not visible to the caller.
409ExecuteJob is not executable, has no valid rows, or has already moved from items_ready.
422AllBody, path, query, file, or row validation failed.

Examples

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
}
Execute response
{
  "message": "Accepted",
  "details": "Bulk execution command accepted",
  "data": {
    "bulk_job_id": "BKJ-9001"
  },
  "meta": null
}
Job response after execution
{
  "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": "executed",
    "started_at": "2026-03-25T10:40:58Z",
    "completed_at": "2026-03-25T10:41:05Z",
    "created_at": "2026-03-25T10:40:58Z"
  },
  "meta": null
}
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": "Destination is not available for this account",
      "created_at": "2026-03-25T10:41:04Z",
      "updated_at": "2026-03-25T10:42:33Z"
    }
  ],
  "meta": {
    "pagination": {
      "page": 1,
      "limit": 50,
      "total": 2
    }
  }
}