# Create a user field definition

Add a custom user field to your community, such as a KYC status, and get back the key you use to write traders' values.

- Endpoint: `POST https://api.returning.ai/v1/communities/{communityId}/user-fields`
- Section: Users and data / User fields
- Authentication: `Authorization: Bearer <API_KEY>` (Community API key)
- Permission: `userFields` (Shown in the dashboard as "User Fields")
- Retries: Idempotency-Key makes retries safe
- Guide: hand-written
- Verified: live, 2026-09-27
- Last updated: 26 Sep 2026
- Web page: https://docs.returning.ai/api-reference/user-fields/create-a-user-field-definition

## When to use this

- You want to track a trader attribute from your platform, such as KYC status, account tier or trading volume.
- You are setting up a new integration and its fields don't exist yet.
- You want a typed field that milestones and data workflows can react to when a trader's value changes.

**Instead:** Use [Update a user field definition](https://docs.returning.ai/api-reference/user-fields/update-a-user-field-definition.md) instead to rename a field or change its type or default.

## Authentication

- Header: `Authorization: Bearer <API_KEY>`
- Permission: `userFields`

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

The key is cleaned up before it is saved: surrounding spaces are removed, capitals become lowercase and spaces inside become `_`, so `KYC Status` is stored as `kyc_status`. Save `data.field` from the response, not what you sent. The key can never change; the name, type and default can.

Select-dropdown fields can be created, but their values can't be written with Update a user field value yet.

## Request

### Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `communityId` | `string` | Yes | Your community's ID. It must be the community that owns your API key. (24 hex characters) |

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Authorization` | `string` | Yes | Community API key with `userFields`. (`Bearer <API_KEY>`) |
| `Idempotency-Key` | `string` | No | Optional. Send a unique value per new field, and the same value when you retry that request. Kept for 24 hours. (1-200 chars; `^[!-~]{1,200}$`) |
| `Content-Type` | `string` | Yes | Request body format. (`application/json`) |

### Body

Send only `name`, `field`, `type` and, optionally, `defaultValue`; any other property is rejected. A non-null `defaultValue` must match `type` exactly:

- `single-line-text`: a string of 1-255 characters with no line breaks.
- `multi-line-text`: any string, including an empty one.
- `numerical`: a JSON number, not a string.
- `boolean`: `true` or `false`.
- `date`: a real date as `YYYY-MM-DD`.
- `time`: `HH:mm:ss`, 24-hour.
- `date-time`: `YYYY-MM-DDTHH:mm:ss`, with optional milliseconds and an optional `Z` or offset such as `+08:00`.
- `single-select-dropdown`: a string or number. `multi-select-dropdown`: an array of them.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `string` | Yes | Display name shown in the dashboard. Unique in your community, ignoring case. (ASCII letters, numbers, spaces, `-` and `_`) |
| `field` | `string` | Yes | The field key. Unique in your community and can never be changed. Spaces become `_` and capitals become lowercase. (After cleanup: 1-120 chars of `a-z`, `0-9`, `_`, `-`) |
| `type` | `string` | Yes | The field type. It decides which values you can write later. (`single-line-text`, `multi-line-text`, `numerical`, `date`, `time`, `date-time`, `boolean`, `single-select-dropdown`, `multi-select-dropdown`) |
| `defaultValue` | `any` | No | Optional default, in the field's type. Omit it or send `null` for none. (Matches `type`; see Request body) |

### Example request

```bash
curl --request POST \
  --url https://api.returning.ai/v1/communities/66f000000000000000000010/user-fields \
  --header 'Authorization: Bearer <API_KEY>' \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: create-field-<unique-id>' \
  --data '{
    "name": "KYC status",
    "field": "kycstatus",
    "type": "single-line-text"
  }'
```

## Response

A `201` means the field is saved. Branch on the HTTP status and `meta.code`, never on `message`.

**Note:** The new definition also includes a `creator` object: for fields created with an API key, the community owner's account. Build against the fields listed here.

### Response fields

| Field | Type | Presence | Description |
| --- | --- | --- | --- |
| `meta` | `object` | always | - |
| `meta.status` | `string` | always | `success` |
| `meta.statusCode` | `integer` | always | `201` |
| `meta.code` | `string` | always | Machine-readable result code. (`USER_FIELD_CREATED`) |
| `meta.compatibilityEvent` | `string` | always | `attempted` normally. `failed` means open dashboards were not refreshed; the field is still saved. |
| `meta.idempotentReplay` | `boolean` | on replay | `true` when this repeats the first response for your `Idempotency-Key`. Nothing new was created. |
| `meta.idempotencyPersistence` | `string` | when degraded | `degraded` when the field was saved but a retry with the same key may not replay this response. Treat the field as created. |
| `message` | `string` | always | Human-readable summary. Do not branch on it. |
| `data` | `object` | always | The new definition. |
| `data._id` | `string` | always | The definition's ID. Accepted wherever a field key is. (`^[0-9a-fA-F]{24}$`) |
| `data.name` | `string` | always | The stored display name, trimmed. |
| `data.field` | `string` | always | The stored key. Save this value; it may differ from what you sent. (`a-z`, `0-9`, `_`, `-`) |
| `data.type` | `string` | always | The field type. (`single-line-text`, `multi-line-text`, `numerical`, `date`, `time`, `date-time`, `boolean`, `single-select-dropdown`, `multi-select-dropdown`) |
| `data.defaultValue` | `any` | - | The stored default. Left out when you sent none. |
| `data.isCustom` | `boolean` | always | Always `true` for fields you create. |
| `data.createdAt` | `string` | always | When the definition was created. |
| `data.updatedAt` | `string` | always | When the definition last changed. |

### Example response (201)

```json
{
  "meta": {
    "status": "success",
    "statusCode": 201,
    "code": "USER_FIELD_CREATED",
    "compatibilityEvent": "attempted"
  },
  "message": "Create user field api success.",
  "data": {
    "_id": "66f000000000000000000510",
    "name": "KYC status",
    "field": "kycstatus",
    "type": "single-line-text",
    "isCustom": true,
    "createdAt": "2026-09-26T08:30:00.000Z",
    "updatedAt": "2026-09-26T08:30:00.000Z"
  }
}
```

## 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`. A `400` or `409` creates nothing.

### Fix the request

| Status | Code | What to do |
| --- | --- | --- |
| 400 | - | No `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>`. |
| 400 | `INVALID_FIELD_DEFINITION` | `name`, `field`, `type` or `defaultValue` broke a rule, or the body has an extra property. `detail` names the field when it can. Nothing was created. |
| 400 | `INVALID_IDEMPOTENCY_KEY` | The `Idempotency-Key` must be 1-200 visible ASCII characters with no spaces. |
| 401 | `AUTHENTICATION_REQUIRED` | The key is invalid or expired, or no key was sent with an empty body. Send `Authorization: Bearer <API_KEY>` with a current Community API key. |
| 403 | `API_KEY_PERMISSION_DENIED` | The key lacks `userFields`. Add the permission in Settings > Integration > API Keys. |
| 403 | `API_KEY_COMMUNITY_MISMATCH` | `communityId` is not the community that owns your key. Use your own community's ID. |

### Fix the data

| Status | Code | What to do |
| --- | --- | --- |
| 409 | `USER_FIELD_KEY_CONFLICT` | A field with this key already exists, built-in or custom. Use the existing field, or choose another key. |
| 409 | `USER_FIELD_NAME_CONFLICT` | Another field already has this name, ignoring case. Choose another name. |
| 409 | `IDEMPOTENCY_KEY_CONFLICT` | This `Idempotency-Key` was already used for a different field. Use a new key for a new field. |
| 404 | `COMMUNITY_NOT_FOUND` | The community this key belongs to no longer exists. Contact Returning.AI support. |

### Retry with backoff

| Status | Code | What to do |
| --- | --- | --- |
| 500 | `USER_FIELD_OPERATION_FAILED` | The outcome is unclear. List your fields and check for the key before you create it again. |
| 503 | `IDEMPOTENCY_STORE_UNAVAILABLE` | Nothing was created. Retry the same request, with the same key, after a short wait. |

### Do not retry

| Status | Code | What to do |
| --- | --- | --- |
| 409 | `IDEMPOTENCY_REQUEST_IN_PROGRESS` | A request with this key is still running, or its outcome is unclear. List your fields to see whether the key exists before doing anything else. |

**Retries:** Send an `Idempotency-Key` header with a value unique to this new field. If the request times out, send it again with the same key and the same body: within 24 hours you get the original `201`, with `meta.idempotentReplay: true`, and no second field. The same key with a different body returns `409 IDEMPOTENCY_KEY_CONFLICT`. Without a key, a repeat of a saved request returns `409 USER_FIELD_KEY_CONFLICT`, so no duplicate is created either way; list your fields to confirm. Over the [rate limit](https://docs.returning.ai/how-it-works.md#rate-limits), requests return `429`; wait for the window to reset, then retry.

## Next step

- [Update a user field value](https://docs.returning.ai/api-reference/user-fields-user-field-history/update-a-user-field-value.md): `POST /v1/communities/{communityId}/users/{userId}/user-fields/{fieldIdOrName}/histories`. Write a trader's value for the new field, using `data.field` as the key.
- [Check all your fields with List user field definitions](https://docs.returning.ai/api-reference/user-fields/list-user-field-definitions.md): `GET /v1/communities/{communityId}/user-fields`.
