Returning.AIDevelopers
v1

API reference / Bulk Operations

.md

Bulk update users from CSV

Upload a CSV that changes many traders' profiles, roles, balances or custom fields in one background job.

Last updated 26 Sep 2026API v1

Method
POST
Path
https://api.returning.ai/v1/users/bulk-update
Permission
bulkUpdateUser
Retries
No Idempotency-Key; list jobs before retrying

When to use this

  • A nightly feed from your platform adds coins or XP, or writes custom field values, for many traders at once.
  • You fill in broker customer IDs for traders who were created without one.
  • Support needs to correct balances or roles for a list of traders, with a result for every row.

Authentication#

Header
Authorization: Bearer <API_KEY>
Permission
bulkUpdateUserShown in the dashboard as “Bulk Update User”

Uploading needs bulkUpdateUser. Reading jobs back needs getBulkUpdate, shown in the dashboard as Get Bulk Update History, so give your key both. Every row is limited to traders in the key's community. Keep the key on your server.

Behaviour#

The upload only queues a job. Jobs for one community run one at a time, oldest first. A job moves from In Queue to In Progress, then ends as Completed or Failed.

  • Failed usually means the file was rejected as a whole, for example an unknown column, a number column without its _op column, or a missing identifier column. The API does not return the reason, so check the file against the rules below.
  • Completed can still include failed rows. Get bulk update job details lists the rows, a page at a time, with status, and failed rows carry errors, each with a code such as USER_NOT_AVAILABLE. Read details only once the job is Completed.
  • XP and coin changes are saved to each trader's history, and XP changes can also update the trader's level and XP-based roles. New roles and badges can send the trader a role or badge email if your community has those emails switched on.

Request#

Headers#

Authorization#stringREQUIRED
Community API key with bulkUpdateUser.

RuleBearer <API_KEY>

Body#

Send multipart/form-data with the file in field data. The first line must be a header row, and columns are separated by commas. You can also send the rows as JSON, {"data": [{"Email": "trader@example.com", "Total Coins": "500", "Total Coins_op": "add"}]}, with the same column names.

CSV
Email,Total Coins,Total Coins_op,KYC status
trader@example.com,500,add,verified
  • Rows are matched by Email, not case-sensitive. Only the first row for each trader is applied; repeats fail.
  • Columns, spelled exactly like this: Email, First Name, Last Name, Phone Number, Country, Language, Role, Tag, Badges, Total XP, Total Coins, Penalty(warned/suspended/banned), and each custom field by its display name. Any other column fails the job.
  • Total XP, Total Coins and numerical custom fields need a matching _op column, such as Total Coins_op, with add, subtract or overwrite. Fill the operator cell on every row, even where the value is empty.
  • An empty cell is skipped, so it never clears a value.
  • Role replaces the trader's roles, apart from @all, with the comma-separated list in the cell. Add a Role_op column with add or subtract to change only the listed roles. Tag and Badges replace the current list.
  • To set a broker customer ID, add its column by display name. It fills an empty identifier but cannot change one that is already set.
  • There is no row limit; the file can be up to 30 MB.
data#fileREQUIRED
The CSV file, with a header row and one row per trader. The form field must be named data.

Rule.csv, max 30 MB

Eg"users.csv"

log_name#stringOPTIONAL
A name for this job. Make it unique; it is how you find the job in List bulk update jobs.

Eg"Coins top-up 2026-09-26"

identifier#stringOPTIONAL
Omit to match rows by the Email column. To match by broker customer ID instead, send the key of your community's active identifier field and include a column with that name.

RuleYour active identifier field's key

Eg"customerid"

currency_cap#stringOPTIONAL
false applies coin additions in full. Otherwise, if your community has a coin earning cap, add amounts in Total Coins are limited by it.

Ruletrue or false

Eg"false"

Send the body as multipart/form-data.

curl --request POST \
  --url https://api.returning.ai/v1/users/bulk-update \
  --header 'Authorization: Bearer <API_KEY>' \
  --form 'data=@users.csv' \
  --form 'log_name=Coins top-up 2026-09-26'

Response#

A 200 returns only a confirmation that the job is queued. It has no job ID, so use a unique log_name and look the job up with List bulk update jobs.

status#stringALWAYS
Result of the request.

Rulesuccess

message#stringALWAYS
Human-readable summary. Do not branch on it.

Eg"Bulk update user process is queued"

data#objectALWAYS
A short confirmation.

Eg{ ... }

message#stringALWAYS
Human-readable confirmation that the job is queued.

Eg"Bulk update user process is queued. You can check in Api Log ( community settings )"

{
  "status": "success",
  "message": "Bulk update user process is queued",
  "data": {
    "message": "Bulk update user process is queued. You can check in Api Log ( community settings )"
  }
}

Errors#

Errors from the API key check, such as 401 and 403, carry the code in meta.code. Other errors from this endpoint have no code, so branch on the HTTP status. Problems inside the file never cause an error here; they show up in the job's status and row details.

Fix the request03

400Fix the request
No file arrived, the file is not a CSV, or JSON data is not a non-empty array. Send the file as multipart form field data, with a .csv name or a text/csv type.
401AUTHENTICATION_REQUIREDFix the request
The key is missing, invalid or expired. Send Authorization: Bearer <API_KEY> with a current key.
403API_KEY_PERMISSION_DENIEDFix the request
The key is valid but lacks bulkUpdateUser. Add the permission in Settings > Integration > API Keys.

Fix the data01

404COMMUNITY_NOT_FOUNDFix the data
The key's community no longer exists. Use a key from an active community.

Retry with backoff02

500Retry with backoff
A JSON 500 means the file was not saved and no job was created; retry with backoff. A 500 error page without JSON means the file is over 30 MB or was sent under a form field other than data; fix the upload instead.
500AUTHENTICATION_FAILEDRetry with backoff
The key could not be checked. Retry the same request with backoff.
{
  "status": "fail",
  "message": "Uploaded file must be a CSV"
}

Next step#

List bulk update jobsGET/v1/users/bulk-updateFind your job by matching name to your log_name, and save its _id.