GuidesAPI Reference
Log In
API Reference

Create a bulk customer job

Create a bulk customer import job.

Use this endpoint to import (create/update) many customers from an NDJSON payload. The API creates a job, returns a job_id (UUID), and (for upload mode) provides a presigned upload_url where you upload the NDJSON file.

Basic workflow (upload mode)

  1. POST this endpoint to create a job.
  2. Upload NDJSON to upload_url.
  3. POST the start action to queue processing.
  4. Poll status / download results via the Bulk Jobs API.

Inline mode

Pass ingest_mode=1 with rows in inline_payload. The job queues automatically—no upload or start call required.

Store binding

Bulk jobs are bound to the store specified in the X-Active-Store header during job creation. This header is required for all bulk job operations. The store context is used for:

  • Permission checks during job creation and execution
  • Stamping created/updated records with the appropriate store identifier

Important: When starting an upload-mode job, you must use the same X-Active-Store value that was provided during job creation. If a different store is specified, the start request will be rejected with a 400 error (job_store_mismatch).

For status checks, cancellation, and results retrieval, any store with appropriate permissions may be used.

NDJSON rows (upsert semantics)

Each non-empty NDJSON line is a customer payload plus bulk helper fields.

  • If customer_id is omitted, the row is treated as a create.
  • If customer_id is provided, the row is treated as an update.
  • Each row MUST include _client_row_id, a required client-supplied identifier used to correlate input rows to results; this value is echoed back on every results NDJSON row.
  • Delete rows (_action='delete') are not supported for this endpoint.

Bulk customer rows reuse the same fields and validation rules as the Published Customer Create and Update APIs. Create rows (without customer_id) support the full Published Customer create field set; update rows (with customer_id) support the full Published Customer update field set. See the Published Customer API reference for the complete field list; anything accepted there is also accepted in bulk.

To clear a field on update, set it explicitly to null or "". Omitting a field leaves it unchanged.

Example NDJSON payload

{"_client_row_id": "CUST-1", "customer_number": "BULK-10005", "first_name": "John", "last_name": "Doe", "email": "[email protected]"}
{"_client_row_id": "CUST-2", "customer_id": 104172, "customer_number": "BULK-10005", "email": "[email protected]"}

Results file format

When processing completes, download the results NDJSON from results_url. Each line is a JSON object with the following fields:

FieldDescription
_client_row_idRequired client-supplied identifier, echoed from input. Use to correlate results to input rows.
statusRow-level status code (see Status Codes table below).
action_takenDatabase action code (see Action Taken Codes table below).
<entity_id>The entity's primary key (e.g., customer_id). Present on success rows and on error rows where the conflicting record can be identified (for example, when a business-rule failure references an existing record).
errorsArray of {field, code, message} objects. Present on error rows (see Error Codes table below).

Status codes

The status field uses these integer codes:

CodeLabelDescription
0SUCCESSRow processed successfully
1ERRORRow failed validation or processing
2CANCELLEDRow processing was cancelled

Action taken codes

The action_taken field uses these integer codes:

CodeLabelDescription
0INSERTEDNew record created
1UPDATEDExisting record updated
2DELETEDExisting record deleted
3NONENo database change (typically due to error)

Error codes

The code field in error objects uses these integer codes:

CodeLabelDescription
0REQUIREDRequired field missing
1INVALIDInvalid value or format
2BUSINESS_RULEBusiness logic constraint violated
3UNIQUEUniqueness constraint violated
4INVALID_SCHEMASchema or structure error
5PERMISSIONPermission denied
6NOT_FOUNDReferenced entity not found

Example result rows

{"_client_row_id": "ROW-1", "status": 0, "action_taken": 0, "customer_id": 104200}
{"_client_row_id": "ROW-2", "status": 1, "action_taken": 3, "errors": [{"field": "email", "code": 1, "message": "Enter a valid email address."}]}

Optional: callback authentication

If you provide callback_url, the system will POST a completion callback when the job reaches a terminal state. To authenticate callbacks, provide callback_secret in the create request. When set, callbacks include HMAC signature headers:

  • X-ERC-Callback-Timestamp: Unix epoch seconds
  • X-ERC-Callback-Signature: v1=<hex> where the hex value is an HMAC-SHA256 over <timestamp>.<canonical_body>

Receivers should validate timestamp freshness and verify the signature before trusting the payload. Signature verification is performed against a canonical JSON form; do not compute the HMAC over the raw HTTP body. Use the following algorithm:

timestamp = request.headers['X-ERC-Callback-Timestamp']
signature = request.headers['X-ERC-Callback-Signature']  # 'v1=<hex>'
payload = JSON.parse(request.body)
canonical_body = JSON.stringify(payload, sort_keys=true, separators=(',', ':'))
expected_hex = HMAC_SHA256_HEX(secret, timestamp + '.' + canonical_body)
expected = 'v1=' + expected_hex
compare(expected, signature)  # timing-safe compare

Error responses

  • 400 invalid_schema: Request body failed validation.
  • 401 authentication_required: Missing or invalid authentication.
  • 403 permission_denied: API key lacks required bulk job permission.
  • 429 job_limit_reached: Tenant has reached maximum active bulk jobs.
Recent Requests
Log in to see full request history
TimeStatusUser Agent
Retrieving recent requests…
LoadingLoading…
Body Params

Job-level request serializer for customer bulk jobs.

Validation of inline payload items is deferred to async processing,
consistent with upload mode. This avoids duplicating validation logic
and ensures all row-level errors are reported uniformly via the bulk
job results API.

string
length between 1 and 64

Optional caller-supplied idempotency key for job deduplication. When provided, must be a non-empty string containing only alphanumeric characters, hyphens, and underscores (pattern: ^[a-zA-Z0-9_-]+$). Requests with the same client_request_id will reuse the same job instead of creating duplicates. If omitted, a new random job ID is generated for each request.

integer
enum
Defaults to 2

Payload ingestion strategy. Use 2 (Upload) for large payloads uploaded via presigned S3 URL. Use 1 (Inline) for small payloads (max 500 rows) included directly in the request body.

  • 1 - Inline
  • 2 - Upload
Allowed:
uri

Optional HTTPS callback URL for job completion notifications.

string

Optional secret used to sign webhook callbacks. When provided, bulk job callbacks include HMAC signature headers so the receiver can authenticate the sender. This value is never returned by the API.

string

Inline payload. JSON object with an 'items' array containing up to 500 row objects. Only used when ingest_mode=1 (Inline); ignored for ingest_mode=2 (Upload).

Headers
integer
required

The id of the active store to be used for this API invocation

string
enum
Defaults to application/json

Generated from available request content types

Allowed:
Responses

Language
Credentials
Bearer
JWT
URL
LoadingLoading…
Response
Click Try It! to start a request and see the response here! Or choose an example:
application/json