---
title: "Creates a catalog item."
method: POST
path: "/api/v1/items"
tags: ["Items"]
---

# Creates a catalog item.

`POST /api/v1/items`

## Request

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

Requires `ManageInventory` and an `Idempotency-Key` header. Same as adding an
item in the app: the item gets the organization's default "No Category" when
`categoryId` is omitted, its `Cost` and `Unit Price` properties are
created (0 unless `cost` / `unitPrice` are supplied, stored to 2 decimals) and
the category's properties are copied onto it. An active item with the same name is
refused with `409` (the rule the app's template builder applies). Creates no stock —
follow up with `POST /api/v1/items/{id}/stock/adjust`. No webhook fires for a
create. Permissions mirror the app exactly: the Items page creates items, sets price/cost
and category under `ManageInventory`, so that is all this endpoint needs — except
`description`, which in the app is edited only on the Item Templates page and so
additionally requires `ManageTemplates` (`403` otherwise; omit the field or
grant the permission to the key).
Example: `{ "name": "1/2\" PEX Coupling", "categoryId": 4, "unitPrice": 1.25, "cost": 0.40 }`.

## Request body

- CreateItemApiRequest: Body of `POST /api/v1/items`: create a catalog item (template). Same rules as adding an item in the app: the organization's default category when `categoryId` is omitted, `Cost` and `Unit Price` properties created (0 unless supplied) and the category's properties copied onto the item. Example: `{ "name": "1/2\" PEX Coupling", "categoryId": 4, "unitPrice": 1.25, "cost": 0.40 }`.
  - `name` string, required: Display name; required, 1-100 characters. An active item with the same name is refused (409).
  - `description` string, nullable: Optional free-text description, max 2000 characters.
  - `categoryId` integer, nullable: Primary category id (see the item's `categoryId` on GET). Omit or null for the organization's default "No Category". Unknown or another organization's id → 422.
  - `unitPrice` number, double, nullable: Starting selling price, 0 or more, stored to 2 decimals as the `Unit Price` property. Omit for 0.
  - `cost` number, double, nullable: Starting purchase cost, 0 or more, stored to 2 decimals as the `Cost` property. Omit for 0.

## Response `201`

Created; body is the item exactly as `GET /api/v1/items/{id}` returns it, `Location` points at it.

- ItemDetail: Full item as returned by `GET /api/v1/items/{id}`.
  - `item` ItemSummary: One sellable/stockable item (an `ItemTemplate`) as listed by `GET /api/v1/items`. Physical rows in inventory are instances of a template; the API exposes templates because that is what agents price, sell and reorder.
    - `id` integer: Numeric id, usable in `/api/v1/items/{id}`.
    - `syncId` string, uuid: Stable cross-device id; also the pagination cursor.
    - `name` string: Display name.
    - `description` string, nullable: Free-text description, if any.
    - `categoryId` integer, nullable: Primary category id, if any.
    - `categoryName` string, nullable: Primary category name, if any.
    - `unitPrice` number, double, nullable: Selling price from the template's `Unit Price` property; null when not priced.
    - `cost` number, double, nullable: Purchase cost from the template's `Cost` property; null when unknown.
    - `onHand` number, double: Units on hand across stock locations (job sites excluded). Decimal because items sold by sub-unit report partial rows; negative when the organization allows overselling.
    - `isMiscItem` boolean: True for ad-hoc "misc" items that never carry stock.
    - `isParentTemplate` boolean: True for a grouping template whose children are the stocked variants.
    - `sellByUnits` boolean: True when the item is sold in sub-units of a parent row.
    - `unitName` string, nullable: Name of the sub-unit when SellByUnits is true.
    - `createdAt` string, date-time: UTC creation time.
    - `updatedAt` string, date-time, nullable: UTC last modification time; null when never edited.
    - `preferredVendorId` integer, nullable: Id of the vendor this item is normally reordered from (see `GET /api/v1/vendors`); null when none is set. Set on Item Templates → Edit or with `PATCH /api/v1/items/{id}`. Example: `3`.
    - `preferredVendorName` string, nullable: Name of the preferred vendor; null when none is set. Example: `"Ferguson"`.
    - `vendorPartNumber` string, nullable: The vendor's own part / catalogue number for this item, max 100 characters; null when unknown. Example: `"PX-12C"`.
  - `properties` object: Every template property by name (includes `Unit Price` and `Cost` when set).
  - `priceTiers` ItemPriceTier[]: Quantity-break pricing, ascending by quantity. Empty when the item has a single price.
    - `minQuantity` integer: Smallest quantity at which UnitPrice applies.
    - `unitPrice` number, double: Price per unit at or above MinQuantity.
  - `barcodes` ItemBarcodeDto[]: Barcodes attached to the item.
    - `value` string: Encoded value.
    - `type` string: Symbology (e.g. `UPC`, `Code128`).
    - `isPrimary` boolean: True for the barcode printed on labels.
  - `stock` StockSummary: Stock position of an item.
    - `onHand` number, double: Total on hand; same semantics as `onHand`.
    - `byLocation` LocationStock[]: Per-location breakdown of physical rows. Sums to the row count, which may differ from OnHand for oversold or sub-unit items.
      - `locationId` integer: Location id.
      - `locationName` string: Location name.
      - `quantity` integer: Whole item rows at the location (not deleted, not in a job site).
  - `parentItemId` integer, nullable: Id of the parent template for variant children; null otherwise.

## Other responses

- `400`: Malformed body, missing `name`, out-of-range value, or missing `Idempotency-Key`.
- `401`: Unauthorized
- `403`: The key lacks `ManageInventory`, or it sent `description` without holding `ManageTemplates`.
- `409`: An active item with this name already exists, or the key's creator is unknown on this server.
- `422`: No such category in this organization (`categoryId`), or `Idempotency-Key` reused with a different body.
- `429`: Too Many Requests
- `500`: The item could not be saved; nothing was created.

---

[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)
