---
title: "Adds an item-level custom field to an item."
method: POST
path: "/api/v1/items/{id}/properties"
tags: ["ItemProperties"]
---

# Adds an item-level custom field to an item.

`POST /api/v1/items/{id}/properties`

## Request

- Base URL: `https://app.flexonthejob.com`
- URL: `https://app.flexonthejob.com/api/v1/items/{id}/properties`
- Auth: HTTP bearer

Requires `ManageInventory` and an `Idempotency-Key` header. Same as "Add property"
in the Items page's item modal: the property belongs to this item only (`isItemLevel`;
the Item Template never changes it) and is also added, with the same value, to every
existing inventory unit that lacks it (new stock copies it automatically). The value is
checked against `dataType`. List types start without options; set them with
`PATCH .../properties/{propertyId}` (`ManageTemplates`).
Example: `{ "name": "Warranty (years)", "dataType": "Number", "value": "2" }`.

## Path parameters

- `id` integer, required

## Request body

- CreateItemPropertyApiRequest: Body of `POST /api/v1/items/{id}/properties`: add an item-level custom field to one item. Example: `{ "name": "Warranty (years)", "dataType": "Number", "value": "2" }`.
  - `name` string, required: Property name, 1-100 characters, unique on the item (case-insensitive); `Cost` and `Unit Price` are reserved.
  - `dataType` 'Text' | 'Number' | 'Date' | 'Boolean' | 'Dropdown' | 'MultiSelect'
  - `value` string, nullable: Starting value in the type's format (see `value` on GET), at most 4000 characters; omit for none.

## Response `201`

Created; body is the item's properties (the new one included), `propagated` = inventory units it was added to. `Location` points at the list.

- ItemPropertiesResponse: Body of `GET /api/v1/items/{id}/properties` and of every custom-field write: all of an item's properties (custom fields) with their definitions and current values, ordered by name.
  - `itemId` integer: The item (template) id, e.g. `12`.
  - `properties` ItemPropertyDto[]: Every property of the item, including the pricing properties `Cost` and `Unit Price`.
    - `id` integer: Property id, e.g. `341`; used by the per-property routes.
    - `name` string: Property name as shown in the app, e.g. `"Color"` (unique per item, case-insensitive).
    - `value` string: Current value as stored, never null (`""` = no value). Numbers are decimal strings (`"12.5"`), booleans `"true"`/`"false"`, dates `yyyy-MM-dd`, MultiSelect a comma-separated list of options (`"Red,Blue"`). A calculated property shows its last computed value.
    - `dataType` 'Text' | 'Number' | 'Date' | 'Boolean' | 'Dropdown' | 'MultiSelect'
    - `options` string[]: Allowed values for `Dropdown` / `MultiSelect`, e.g. `["Red","Blue"]`; empty for other types (and for a list property that has no options yet, which then accepts any value).
    - `isCalculated` boolean: True when the value comes from a calculation (a formula authored in the app). Calculated properties are read-only here: value writes answer `409`.
    - `calculationExpression` string, nullable: The calculation's formula as stored (references appear as `{n}` placeholders), or null when not calculated. Informational only.
    - `isItemLevel` boolean: True for an item-level property added to this item only (Items page "add property", or `POST /api/v1/items/{id}/properties`); false for a property that came from the item's Item Template (template builder), its category or item creation.
    - `isCustomized` boolean: True when a template-builder property's value was set on this item and so no longer follows the Item Template ("Customized" in the app). `POST .../properties/{propertyId}/revert` undoes it.
    - `isPricing` boolean: True for `Cost` and `Unit Price`: required on every item and changed through `PATCH /api/v1/items/{id}` (`cost` / `unitPrice`), never through the custom-field routes (`409`).
  - `propagated` integer: Writes only (0 on GET): how many inventory units' copies of the property were changed by the write (a new value flowing to units that were not individually customized, a new item-level property added to existing units, or the copies removed with a deleted one).

## Other responses

- `400`: Malformed body, missing `name`, or missing `Idempotency-Key`.
- `401`: Unauthorized
- `403`: Forbidden
- `404`: No such active item in this organization.
- `409`: The item already has a property with this name, or the key's creator is unknown on this server.
- `422`: Reserved name (`Cost` / `Unit Price`), or the value does not fit `dataType`; or `Idempotency-Key` reused with a different body.
- `429`: Too Many Requests

---

[API](https://skmtc.dev/flexonthejob/apis/flex-api.md) · [All operations](https://skmtc.dev/flexonthejob/apis/flex-api/llms.txt) · [OpenAPI document](https://skmtc.dev/flexonthejob/apis/flex-api/revisions/19c8f4d27acf?raw)
