GuidesAPI Reference
Log In
API Reference

Create a bulk customer loyalty program job

Create a bulk customer loyalty program job.

Use this endpoint to upsert customer loyalty program memberships from NDJSON rows. The API creates a job, returns a job_id (UUID), and (for upload mode) provides an 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 (loyalty membership upsert semantics)

Each non-empty NDJSON line is a loyalty membership payload plus bulk helper fields. Rows are upserts keyed by customer_id + loyalty_program_id.

  • If a matching loyalty membership exists, the row updates it.
  • If no matching loyalty membership exists, the row creates it.
  • 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 loyalty rows reuse the same fields and validation rules as the Published Customer Loyalty Program create and update APIs. Each membership row supports the same field set documented there (such as loyalty_status, membership_number, point_balance, and reward_balance), plus bulk helper fields like _client_row_id. See the Published Customer Loyalty Program API reference for the complete field list; anything accepted there is also accepted in bulk.

Example NDJSON payload

{"_client_row_id": "L-1", "customer_id": 104172, "loyalty_program_id": 1, "loyalty_status": 10}
{"_client_row_id": "L-2", "customer_id": 104173, "loyalty_program_id": 1, "point_balance": "100.00"}

Optional: balance fields

The loyalty membership payload supports optional balance fields: adjust_point_balance, point_balance, and reward_balance. If you provide multiple balance fields, adjust_point_balance takes precedence over point_balance.

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_loyalty_program_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 loyalty bulk jobs.

Extends the shared bulk job contract with loyalty-specific
validation, including HTTPS-only callback_url. Inline payloads
are validated at the NDJSON layer rather than here.

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