Returning.AIDevelopers
v1

API reference / Bulk Operations

.md

Get bulk update job details

Read the row-by-row result of a finished bulk update job, including which rows failed and why.

Last updated 26 Sep 2026API v1

Method
GET
Path
https://api.returning.ai/v1/users/bulk-update/{id}/details
Permission
getBulkUpdate
Retries
Read-only; exact retries are safe

When to use this

  • After a CSV upload, find the traders whose rows failed and the reason for each.
  • Build a correction file that holds only the failed rows, fixed, for a new upload.
  • Reconcile a nightly feed by matching each row's result back to your own records.

Authentication#

Header
Authorization: Bearer <API_KEY>
Permission
getBulkUpdateShown in the dashboard as “Get Bulk Update History”

This endpoint needs getBulkUpdate, shown in the dashboard as Get Bulk Update History. Use a Community API key and keep it on your server. The key decides the community, so you can only read jobs from that community.

Behaviour#

Row results exist only once a job is Completed. Until then, and for a Failed job, this endpoint reads back the file you uploaded: the rows keep your original column names, every row is success with no errors, and successUsers counts every row. Check Get bulk update job status first, and read details only when it returns Completed.

Rows are not guaranteed to be in file order, so match them to your records by email or identifier. successUsers and failedUsers cover the whole job; data holds one page. Keep reading while hasNextPage is true. A page past the last one returns an empty data list.

Request#

Path parameters#

id#stringREQUIRED
The job's _id from List bulk update jobs.

Rule24 hex characters

Eg"66f000000000000000000701"

Query parameters#

page#integerOPTIONAL
Page of rows, starting at 1.

RuleWhole number, 1 or more; default 1

Eg1

limit#integerOPTIONAL
Rows per page.

RuleDefault 10; max 100

Eg100

Headers#

Authorization#stringREQUIRED
Community API key with getBulkUpdate.

RuleBearer <API_KEY>

curl --request GET \
  --url 'https://api.returning.ai/v1/users/bulk-update/66f000000000000000000701/details?page=1&limit=100' \
  --header 'Authorization: Bearer <API_KEY>'

Response#

Each row is one line of your file, with status and errors added. The cells come back as strings, keyed by the lower-case column name. The standard columns use short keys: Email is email, First Name is firstname, Last Name is lastname, Phone Number is phone, Role is roles, Tag is tags, Total XP is xp and Total Coins is coin. Operator and custom field columns are lower-cased, such as total coins_op and kyc status.

A failed row lists its reasons in errors. Branch on code:

  • USER_NOT_AVAILABLE: no trader in your community matches the row's email or identifier.
  • USER_NOT_COMMUNITY_MEMBER: the account exists but is not a member of your community.
  • REQUIRED_VALUE: the Email cell, or the identifier cell, is empty.
  • DUPLICATE_VALUE: an earlier row in the file has the same email or identifier. Only the first row is applied.
  • IDENTIFIER_CONFLICT: the trader already has a different broker customer ID, or another trader has this one.
  • IDENTIFIER_LOOKUP_UNAVAILABLE: lookup by identifier wasn't available. Upload the row again later.
  • INVALID_OPERATOR: an _op cell is not add, subtract or overwrite.
  • INVALID_VALUE: a cell failed its check, for example an unknown role, tag or badge, text in a number column, a badly formatted date, or an unknown country or language.
  • ROW_OUTCOME_UNCERTAIN: the coin change couldn't be checked against your community's rule that traders must visit before earning coins, so the row was not applied. Upload it again later.
  • BULK_ROW_FAILED: any other failure, for example a trader who hasn't visited your community yet when that rule is on.

Before you upload a failed row again, read the trader back: an error while saving can leave part of a row applied. An ID that matches no job in your community returns 200 with zero counts and no data list, not 404.

status#stringALWAYS
Result of the request.

Rulesuccess

message#stringALWAYS
Human-readable summary. Do not branch on it.
data#objectALWAYS
Row counts for the whole job, paging, and this page of rows.
successUsers#integerALWAYS
Rows in the whole job with no errors.
failedUsers#integerALWAYS
Rows in the whole job with at least one error.
pagination#objectALWAYS
Paging totals for the job's rows.
total#integer
Rows in the job. 0 when the ID matches no job in your community.
currentPage#integer
The page you asked for.
totalPages#integer
Pages at this limit.
hasNextPage#boolean
true when a later page has rows.
hasPrevPage#boolean
true when page is above 1.
data#object[]
This page of rows. Each row repeats its uploaded cells as strings, keyed by lower-case column name. Left out when the ID matches no job.
email#string
The row's Email cell. Other cells follow under their own keys, such as coin for Total Coins.
status#stringALWAYS
failed when the row has errors; otherwise success, which means no error was recorded for the row.

Rulesuccess or failed

errors#object[]ALWAYS
Why the row failed, up to 10 entries. Empty for a success row.
field#string
The standard column the error is about, such as email, total_coins or role. null for custom field and identifier columns, and when no one column is at fault.
code#string
The reason, as a stable code. See the list under Response.

RuleUSER_NOT_AVAILABLE, USER_NOT_COMMUNITY_MEMBER, REQUIRED_VALUE, DUPLICATE_VALUE, IDENTIFIER_CONFLICT, IDENTIFIER_LOOKUP_UNAVAILABLE, INVALID_OPERATOR, INVALID_VALUE, ROW_OUTCOME_UNCERTAIN, BULK_ROW_FAILED

message#string
A fixed description of the code. Do not branch on it.
{
  "status": "success",
  "message": "Bulk update details fetched successfully",
  "data": {
    "successUsers": 1,
    "failedUsers": 1,
    "pagination": {
      "total": 2,
      "currentPage": 1,
      "totalPages": 1,
      "hasNextPage": false,
      "hasPrevPage": false
    },
    "data": [
      {
        "email": "trader@example.com",
        "coin": "500",
        "total coins_op": "add",
        "kyc status": "verified",
        "status": "success",
        "errors": []
      },
      {
        "email": "no.account@example.com",
        "coin": "500",
        "total coins_op": "add",
        "kyc status": "verified",
        "status": "failed",
        "errors": [
          {
            "field": "email",
            "code": "USER_NOT_AVAILABLE",
            "message": "User was not found in the target community"
          }
        ]
      }
    ]
  }
}

Errors#

The 401, 403 and 404 errors, and 500 AUTHENTICATION_FAILED, come from the API key check and carry the code in meta.code. Other errors from this endpoint have no code, so branch on the HTTP status.

Fix the request03

400Fix the request

There is no code, so read the body:

  • Bulk update ID is required and must be a valid ObjectId: send the job's 24-character _id from List bulk update jobs.
  • Get bulk update details endpoint validation error.: limit is over 100, or page or limit is not a number. detail names the field.
401AUTHENTICATION_REQUIREDFix the request
The key is missing, invalid or expired. Send Authorization: Bearer <API_KEY> with a current Community API key.
403API_KEY_PERMISSION_DENIEDFix the request
The key lacks getBulkUpdate, or it is a personal key. Use a Community API key and add the permission in Settings > Integration > API Keys.

Fix the data01

404COMMUNITY_NOT_FOUNDFix the data
The community this key belongs to no longer exists. Contact Returning.AI support.

Retry with backoff02

500Retry with backoff
The job could not be read. Retry the same request with exponential backoff.
500AUTHENTICATION_FAILEDRetry with backoff
The key could not be checked. Retry with backoff; nothing was read.
{
  "status": "fail",
  "message": "Bulk update ID is required and must be a valid ObjectId"
}

Next step#

Bulk update users from CSVPOST/v1/users/bulk-updateFix the failed rows and upload them as a new file with a new log_name. Leave out rows that succeeded, or their add and subtract changes apply twice.