# Bulk create SMS subscribers

Start or continue an async bulk import of pre-verified SMS subscribers.

Source: https://statusdashboard.com/docs/api/subscriptions/sms/subscribers/bulk-create

`POST /app/subscribers/sms/subscribers/bulk`

Starts or continues an **async** bulk import. Each request returns immediately with a `jobId`; its `subscribers` are queued for background processing right away (not held until the last chunk). Poll [Get bulk import job](/docs/api/subscriptions/sms/subscribers/bulk-get) until `status` is `completed` or `failed`.

While a job is active (`uploading` or `processing`), the organization is locked for SMS: single [create](/docs/api/subscriptions/sms/subscribers/create), a second bulk SMS import, and status dashboard SMS sign-ups are temporarily blocked. (Email bulk imports use a separate lock.)

Set `uploadComplete: true` on the **last** request so the job can finish: that closes uploads, sets `totalRows`, and allows `status` to become `completed` once all batches are processed. If you never send it, any rows already queued may still be imported, but the job will not complete successfully — after about **30 minutes** without progress it is marked `failed` and the lock is released. Partial results are not rolled back.

**Limits:**

* Up to **500** subscribers per request (send multiple requests to upload a large file)
* Up to **50,000** subscribers per job (entire import operation)

There is no admin-managed (`locked`) flag for SMS bulk rows — that concept applies only to email.

***

## Request
See [API Basics](/docs/api) for required headers.

### Body
| Field                        | Type    | Required | Description                                                                                                                                     |
| ---------------------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `subscribers`                | array   | Yes      | 1–500 items for this request                                                                                                                    |
| `subscribers[].phone`        | string  | Yes      | E.164 phone number (e.g. `+14155552671`)                                                                                                        |
| `subscribers[].dashboardId`  | uuid    | Yes      | Target dashboard                                                                                                                                |
| `subscribers[].componentIds` | uuid\[] | Yes      | At least one component on that dashboard                                                                                                        |
| `jobId`                      | uuid    | No       | Omit on the first request; include on subsequent chunk requests for the same import                                                             |
| `uploadComplete`             | boolean | No       | Set to `true` on the final chunk when all rows have been sent. Required for the job to complete; omit or `false` while more chunks will follow. |

***

## Sample request — start import
```bash
curl -X POST https://api.statusdashboard.com/app/subscribers/sms/subscribers/bulk \
  -H "Authorization: Bearer bcf847abf5c6:def456" \
  -H "Content-Type: application/json" \
  -d '{
    "subscribers": [
      {
        "phone": "+14155552671",
        "dashboardId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "componentIds": ["b2c3d4e5-f6a7-8901-bcde-f12345678901"]
      }
    ],
    "uploadComplete": true
  }'
```

## Sample response
**Status: `202 Accepted`**

```json
{
  "jobId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "status": "processing",
  "uploadedRows": 1,
  "totalRows": 1,
  "uploadComplete": true
}
```

Before `uploadComplete` is set, `status` is `uploading` and `totalRows` is `null`.

***

## Chunked upload
For imports larger than 500 rows, send multiple requests with the same `jobId`:

1. **First request** — omit `jobId`; receive `jobId` in the response
2. **Middle requests** — include `jobId`; omit `uploadComplete` or set `false`
3. **Final request** — include `jobId` and `"uploadComplete": true`

Each request may contain 1–500 `subscribers`. The job rejects uploads that would exceed 50,000 total rows.

***

## Row classification (after processing)
Each row is classified asynchronously. Aggregated counts and samples are available on the [job status](/docs/api/subscriptions/sms/subscribers/bulk-get) endpoint:

| Result          | Meaning                                                                     |
| --------------- | --------------------------------------------------------------------------- |
| `created`       | New pre-verified subscription                                               |
| `skipped`       | Already verified on that dashboard                                          |
| `quotaExceeded` | Org-wide unique phone quota exhausted (first subscription for a new number) |
| `errors`        | Invalid phone, dashboard, component, or channel not enabled                 |

Sample arrays (`created`, `skipped`, `errors`, `quotaExceeded`) are capped at 100 entries per job.

***

## Error responses
| Status | When                                                                              |
| ------ | --------------------------------------------------------------------------------- |
| `400`  | Invalid body, more than 500 rows in one request, or job would exceed 50,000 rows  |
| `403`  | Requires admin or subscriber role, or feature disabled                            |
| `404`  | `jobId` not found or does not belong to this organization                         |
| `409`  | Another bulk import is already in progress, or job is no longer accepting uploads |
