Update a user field value
Set, increase or decrease one trader's value for one user field, and record the change in that field's history.
- Method
- POST
- Path
https://api.returning.ai / v1/ communities/ {communityId}/ users/ {userId}/ user-fields/ {fieldIdOrName}/ histories - Permission
- userFields
- Retries
- Send an Idempotency-Key; reuse it to retry
When to use this
- A trader's status changes on your platform, such as passing KYC, and your rewards community should know.
- You sync a running metric, such as deposits or challenges purchased, by adding to it after each event.
- You set the broker identifier for a trader who doesn't have one yet.
- You only keep your own customer ID for each trader, and want to update them by it.
Authentication#
- Header
Authorization: Bearer <API_KEY>- Permission
- userFieldsShown in the dashboard as “User Fields”
Use a Community API key with the userFields permission, and keep it on your server. communityId must be the ID of the community that owns the key.
Behaviour#
Each successful write adds one entry to the field's history and updates the trader's current value, which Get User Data returns when you list the key in customFields. Milestones, referral conditions and data workflows that use the field react after the response.
increaseanddecreasestart from the trader's latest total for the field, or from0if they have none; the field's default is not used. Totals can go below zero.- Numerical writes to the same trader and custom field run one at a time. A write that waits more than 5 seconds returns
409 USER_FIELD_MUTATION_IN_PROGRESSand changes nothing. - The built-in
total_xpandtotal_coinsfields change the trader's XP or coin balance:overwritesets it,increaseanddecreaseadjust it. - If the field is your broker identifier field, each value can belong to one trader only, and once a trader has one it can only be replaced with Update User Data.
To find the trader by your own customer ID, put it in userId and add identifierType=custom_user_identifier:
POST /v1/communities/{communityId}/users/<brokerCustomerId>/user-fields/kycstatus/histories?identifierType=custom_user_identifier- The customer ID is the one you sent as
externalIdin Create User. It is trimmed, then matched exactly: case and leading zeros count. - Always send the flag with a customer ID. Without it, a numeric customer ID is read as a platform user ID and can match a different trader.
- The request and response are otherwise the same. Lookup errors such as
USER_NOT_FOUNDincludemeta.identifierType, so you can tell which lookup was used.
Request#
Path parameters#
Rule24 hex characters
Eg"66f000000000000000000010"
identifierType=custom_user_identifier, use your customer ID for the trader instead. URL-encode an email or any customer ID with special characters.RulePlatform user ID, or your customer ID with identifierType
Eg"3247779"
_id. Not the display name.RuleField key or 24-character ID
Eg"kycstatus"
Query parameters#
custom_user_identifier when userId is your customer ID, the value you sent as externalId in Create User. Omit it and userId is read as a Returning.AI identifier, so a numeric customer ID would match the trader with that platform user ID instead.RuleOnly custom_user_identifier; send it once
Eg"custom_user_identifier"
Headers#
userFields.RuleBearer <API_KEY>
Rule1-200 chars; ^[!-~]{1,200}$
Eg"kyc-evt-1001-kycstatus"
Ruleapplication/json
Body#
value must suit the field's type. Values are not converted between types except as listed:
single-line-text: a string of 1-255 characters with no line breaks.multi-line-text: any string, including an empty one.numerical: a number, or a string holding one, such as"12.5". The only type that takesincreaseanddecrease.boolean:true,false,"true","false",1or0.date:YYYY-MM-DD.time:HH:mm:ss, 24-hour.date-time:YYYY-MM-DDTHH:mm:ss, with optional milliseconds, and optionally ending inZor an offset such as+08:00. For example2026-09-26T08:30:00Zor2026-09-26T08:30:00.000Z.single-select-dropdownandmulti-select-dropdown: not supported yet.
false, 0 and an empty multi-line string are real values and are kept.
increase and decrease the amount. Must suit the field's type; see Request body.RuleMatches the field type
Eg"verified"
overwrite sets the value. increase and decrease add or subtract, on numerical fields only.Ruleoverwrite, increase, decrease; default overwrite
Eg"overwrite"
curl --request POST \
--url https://api.returning.ai/v1/communities/66f000000000000000000010/users/3247779/user-fields/kycstatus/histories \
--header 'Authorization: Bearer <API_KEY>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: write-field-<unique-id>' \
--data '{
"value": "verified",
"action": "overwrite"
}'
Response#
A 201 means the value is saved. Branch on the HTTP status and meta.code, never on message. storedValue is the trader's value now; for numerical fields it is the new total, also in meta.sum.
Eg{ ... }
Rulesuccess
Rule201
RuleUSER_FIELD_VALUE_UPDATED
true.Ruletrue
degraded when the write was saved but a retry with the same key may not replay it. Treat the write as done; read it back before any other change.Ruledegraded
true when this repeats the first response for your Idempotency-Key. Nothing new was written.Ruletrue
Eg"Update user field value api success."
Eg{ ... }
Rule^[0-9a-fA-F]{24}$
Eg"66f000000000000000000511"
Rule^[0-9a-fA-F]{24}$
Eg"66f000000000000000000010"
Rule^[0-9a-fA-F]{24}$
Eg"<userObjectId>"
Eg3247779
_id.Rule^[0-9a-fA-F]{24}$
Eg"66f000000000000000000510"
Eg"kycstatus"
Rulesingle-line-text, multi-line-text, numerical, date, time, date-time, boolean, single-select-dropdown, multi-select-dropdown
Eg"single-line-text"
Eg"verified"
Eg"verified"
storedValue.Eg"verified"
Ruleoverwrite, increase, decrease
Eg"overwrite"
RuleDate-time
Eg"2026-09-26T08:30:00.000Z"
RuleDate-time
Eg"2026-09-26T08:30:00.000Z"
true.Ruletrue
{
"meta": {
"status": "success",
"statusCode": 201,
"code": "USER_FIELD_VALUE_UPDATED",
"updated": true
},
"message": "Update user field value api success.",
"data": {
"_id": "66f000000000000000000511",
"communityID": "66f000000000000000000010",
"userID": "<userObjectId>",
"userNumericID": 3247779,
"fieldID": "66f000000000000000000510",
"fieldName": "kycstatus",
"fieldType": "single-line-text",
"value": "verified",
"storedValue": "verified",
"updatedValue": "verified",
"action": "overwrite",
"createdAt": "2026-09-26T08:30:00.000Z",
"updatedAt": "2026-09-26T08:30:00.000Z",
"updated": true
}
}
Errors#
Every JSON error carries its code in meta.code. The exception is a request with no Authorization header and a JSON body: it returns 400 with a non-JSON body instead of 401. On a 500, meta.mutationState says what happened: rolled-back means nothing was kept, ambiguous means the write may have been kept.
Fix the request08
Authorization header was sent. With a JSON body this returns 400 with a non-JSON body (Request body must encrypted), not 401. Send Authorization: Bearer <API_KEY>.INVALID_FIELD_VALUEFix the requestvalue is missing or doesn't suit the field's type, or the field is a select-dropdown, which can't be written yet. Nothing was written. detail gives the reason.INVALID_FIELD_ACTIONFix the requestaction isn't overwrite, increase or decrease, or you sent increase or decrease to a field that isn't numerical.INVALID_IDEMPOTENCY_KEYFix the requestIdempotency-Key must be 1-200 visible ASCII characters with no spaces.AUTHENTICATION_REQUIREDFix the requestAuthorization: Bearer <API_KEY> with a current Community API key.API_KEY_PERMISSION_DENIEDFix the requestuserFields. Add the permission in Settings > Integration > API Keys.API_KEY_COMMUNITY_MISMATCHFix the requestcommunityId is not the community that owns your key. Use your own community's ID.INVALID_IDENTIFIER_TYPEFix the requestidentifierType is empty, isn't custom_user_identifier, or was sent more than once. Nothing was written. Send identifierType=custom_user_identifier once, or leave it out.Fix the data08
EXTERNAL_IDENTIFIER_NOT_CONFIGUREDFix the dataidentifierType=custom_user_identifier, but your community doesn't have exactly one customer ID field set up. Nothing was written. Use the platform user ID, or contact Returning.AI support to set up the field.USER_NOT_FOUNDFix the datauserId. Check it, or create the trader first. Without identifierType, a 12-character username or email can't be matched; use the platform user ID. With identifierType=custom_user_identifier, the customer ID must match exactly, including case and leading zeros.USER_FIELD_NOT_FOUNDFix the dataUSER_NOT_IN_COMMUNITYFix the dataUSER_IDENTIFIER_UPDATE_REJECTEDFix the dataoverwrite is allowed, the value can't be empty, a trader's identifier can't be changed here once set (use Update User Data), and it can't be changed while identifierType=custom_user_identifier is used to find the trader. Rarely it is a 500 after a failed save; nothing was kept, so send it again.USER_IDENTIFIER_CONFLICTFix the dataidentifierType=custom_user_identifier, more than one trader has the customer ID in userId (meta.reason is identifier_matches_multiple_users). Nothing was written. Reconcile the accounts first.IDEMPOTENCY_KEY_CONFLICTFix the dataIdempotency-Key was used with a different trader, field, value or action. Use a new key for a new change.COMMUNITY_NOT_FOUNDFix the dataRetry with backoff05
USER_IDENTIFIER_NOT_READYRetry with backoffUSER_FIELD_MUTATION_IN_PROGRESSRetry with backoffUSER_FIELD_VALUE_PROJECTION_FAILEDRetry with backoffmeta.mutationState is rolled-back). Retry the same request, with the same key.IDEMPOTENCY_STORE_UNAVAILABLERetry with backoffUSER_FIELD_MUTATION_LOCK_UNAVAILABLERetry with backoffDo not retry03
IDEMPOTENCY_REQUEST_IN_PROGRESSDo not retryUSER_FIELD_HISTORY_RECONCILIATION_REQUIREDDo not retrymeta.mutationState is ambiguous). Read the field's history for this trader before any other change.USER_FIELD_HISTORY_INTERNAL_ERRORDo not retryRetries
Send an Idempotency-Key with every write. If a request times out, send it again with the same key, the same userId and field in the path, and the same body: within 24 hours you get the original 201 with meta.idempotentReplay: true, and nothing is written twice. The same trader under a different identifier, such as email instead of platform user ID, or the same userId with and without identifierType, counts as a different request. Without a key, never retry increase or decrease blindly, because each retry adds again; read the history first. Over the rate limit, requests return 429; wait for the window to reset, then retry.
{
"meta": {
"status": "error",
"statusCode": 400,
"code": "INVALID_FIELD_VALUE",
"fieldName": "kycstatus",
"fieldType": "single-line-text"
},
"message": "Update user field value api error.",
"detail": "This value can't be assigned as single-line-text type.",
"solution": "Check the request fields and try again."
}