Create nRF9x Devices in Bulk
POST/v1/organizations/:organization_slug/projects/:project_slug/long-range/devices/bulk
Send this request to https://api.nrfcloud.com, not the default Memfault API host.
Create multiple Devices for a given Project in one asynchronous request by posting a CSV file.
Each row must contain exactly one credential for the device: an onboarding token, an attestation token, or a device certificate. nRF Cloud verifies tokens before creating devices. Certificates are registered under the device ID provided in the row.
A header row is required, and columns may appear in any order. For each data row, provide a value in exactly one of the following columns: onboardingToken, attestationToken, or certificate. Leave any other credential columns blank. A request may contain at most 1000 data rows.
| Column | Required? | Description |
|---|---|---|
deviceId | Yes | The device ID to onboard. A token must attest to this ID; if it attests to a different ID, only that row fails. A certificate is registered under this ID. |
onboardingToken | Conditional | For nRF93M1 devices. A signed JWT attesting the device and the public key nRF Cloud verifies its JWTs with. |
attestationToken | Conditional | For nRF9151 and nRF9161 devices. The attestation token the modem returns from the AT%ATTESTTOKEN command. |
certificate | Conditional | For nRF9160 devices. The device's X.509 certificate in PEM format. Enclose the multiline value in double quotes and include a newline after -----END CERTIFICATE-----, before the closing quote. |
Return Value
A 202 Accepted response is returned after the CSV is parsed and request-level validation succeeds. Device creation has not started at that point.
Follow the upload to completion
The response contains a Location header with the URL for tracking the bulk operation. The response body's url field contains the same value.
Follow the URL in the Location header by sending GET requests until status is no longer IN_PROGRESS. Always use the returned URL; its structure is not part of this contract and may change.
The link answers with:
| Field | Type | Description |
|---|---|---|
id | String | The ID of the bulk operation. |
status | String | IN_PROGRESS, SUCCEEDED, or FAILED. SUCCEEDED means every row onboarded; FAILED means at least one did not. |
requestedAt | DateTime | When the upload was accepted. |
completedAt | DateTime | When every row had been attempted. Absent while status is IN_PROGRESS. |
uploadedDataUrl | String | URL for downloading the CSV exactly as submitted. |
errorSummary | Object | Present only on FAILED. Carries errorCount and an errors array of { row, deviceId, error }, where row matches the zero-based line number in the uploaded CSV. The header is row 0 and data rows start at 1. |
Failure details live in errorSummary, which lists only the rows that failed. Rows absent from it were onboarded, so recovering usually means fixing the listed rows and resubmitting a CSV containing only them.
{
"id": "01EZZJVDQJPWT7V4FWNVDHNMM5",
"status": "FAILED",
"requestedAt": "2026-08-19T12:00:00Z",
"completedAt": "2026-08-19T12:00:04Z",
"uploadedDataUrl": "/v1/organizations/acme/projects/demo/bulk-ops-requests/01EZZJVDQJPWT7V4FWNVDHNMM5/uploaded-data",
"errorSummary": {
"errorCount": 1,
"errors": [
{
"row": 1,
"deviceId": "cafe0002-0000-0000-0000-000000000002",
"error": "Can not verify token"
}
]
}
}
Request
Responses
- 202
- 400
Accepted
Response Headers
Link to follow for the result of this upload.
Bad Request