Resources
Catalog
The product catalog: price lists and the items on them. An item carries a price, a cost, a unit, and optionally a photo. A price list groups items and can be tied to a vendor or contractor account, so its pricing applies automatically on that account's orders.
Permissions
Every endpoint checks the permissions of the workspace member who generated the key, exactly as the app would for that member.
| Endpoint | Type | Description |
|---|---|---|
| GET | permission | catalog.view |
| POST | permission | catalog.create |
| PATCH, image endpoints | permission | catalog.edit |
| DELETE | permission | catalog.delete |
The price list object
| Field | Type | Description |
|---|---|---|
| id | uuid | Unique id of the price list. |
| object | string | Always price_list. |
| name | string | Up to 200 characters. |
| description | string | null | Free text, or null. |
| is_default | boolean | Read-only. True for the workspace's single default list. |
| contractor_type | string | null | When set, this list's pricing applies to contractors of that type on work orders. |
| vendor_type | string | null | When set, this list's pricing applies to vendors of that type on material orders. |
| account | object | null | The linked vendor or contractor account, as { id, name }. Null when no account is linked. |
| created_at | string | ISO 8601 timestamp. |
/v1/catalog/price-listsList price lists
Returns the default price list first, then the rest by name.
| Query parameter | Type | Description |
|---|---|---|
| name | string | Exact, case-insensitive match on the list name. |
| limit | integer | Rows per page, 1 to 100. Defaults to 25. |
| offset | integer | Rows to skip. Combine with has_more to page. |
/v1/catalog/price-listsCreate a price list
Launch-plan workspaces are limited to one price list; going over returns 403 forbidden. A duplicate name returns 409 conflict with error.existing_id naming the price list that already holds it.
| Body | Type | Description |
|---|---|---|
| name | string | Required on create. Up to 200 characters. |
| description | string | null | Optional. |
| contractor_type | string | null | Applies this list to contractors of that type on work orders. |
| vendor_type | string | null | Applies this list to vendors of that type on material orders. |
| account_id | uuid | null | A vendor or contractor account in this workspace. |
/v1/catalog/price-lists/{id}Retrieve a price list
/v1/catalog/price-lists/{id}Update a price list
Any subset of the create fields. is_default is read-only.
/v1/catalog/price-lists/{id}Delete a price list
Returns 204. The default price list cannot be deleted (400 validation_error). Items on a deleted list stay in the catalog with no price list.
The catalog item object
| Field | Type | Description |
|---|---|---|
| id | uuid | Unique id of the catalog item. |
| object | string | Always catalog_item. |
| name | string | Up to 500 characters. |
| description | string | null | Free text, or null. |
| sku | string | null | Unique per workspace. A blank value is treated as none. |
| item_type | enum | product, service, or bundle. Defaults to product. |
| category | string | null | Free text grouping, e.g. "Roofing". |
| unit | string | null | Free text unit, e.g. "SQ", "EA", "LF". |
| unit_price | number | null | Rounded to 2 decimals. |
| material_cost | number | null | Rounded to 2 decimals. |
| labor_cost | number | null | Rounded to 2 decimals. |
| options | string[] | Choices a member picks from when adding the item to an order. Up to 100. |
| image_url | string | null | Read-only. Set it with the image endpoint below. |
| price_list | object | null | As { id, name, is_default }. Null when the item is on no price list. |
| unit_conversions | object[] | Each as { from_quantity, to_quantity, to_unit }: from_quantity of the item's unit equals to_quantity of to_unit. |
| measurement_field_id | uuid | null | The measurement field the item fills its quantity from on estimates and orders. |
| abc_item_number | string | null | Read-only. |
| created_at | string | ISO 8601 timestamp. |
| updated_at | string | ISO 8601 timestamp. |
/v1/catalog/itemsList catalog items
Ordered by name, then id.
| Query parameter | Type | Description |
|---|---|---|
| price_list_id | uuid | "none" | A price list id, or the literal none for items on no price list. |
| item_type | enum | product, service, or bundle. |
| category | string | Exact, case-insensitive match. |
| sku | string | Exact match. |
| q | string | Substring match on name or SKU. |
| limit | integer | Rows per page, 1 to 100. Defaults to 25. |
| offset | integer | Rows to skip. Combine with has_more to page. |
/v1/catalog/itemsCreate a catalog item
A duplicate SKU returns 409 conflict with error.existing_id naming the item that already holds it. image_url cannot be set here; use the image endpoint below.
| Body | Type | Description |
|---|---|---|
| name | string | Required on create. Up to 500 characters. |
| description | string | null | Optional. |
| sku | string | null | Unique per workspace. Blank is treated as none. |
| item_type | enum | product, service, or bundle. Defaults to product. |
| category | string | null | Optional. |
| unit | string | null | Free text like "SQ", "EA", "LF". Required for unit_conversions. |
| unit_price | number | null | Rounded to 2 decimals. |
| material_cost | number | null | Rounded to 2 decimals. |
| labor_cost | number | null | Rounded to 2 decimals. |
| options | string[] | Up to 100 choices. |
| price_list_id | uuid | null | Optional. |
| unit_conversions | object[] | Each { from_quantity?: number > 0 (defaults to 1), to_quantity: number > 0, to_unit: string }. Up to 20, to_unit unique. On PATCH, sending this field replaces the whole set. |
| measurement_field_id | uuid | null | A measurement field in this workspace, or null to unlink. |
/v1/catalog/items/{id}Retrieve a catalog item
/v1/catalog/items/{id}Update a catalog item
Any subset of the create fields. Sending unit_conversions replaces the whole set rather than merging into it.
/v1/catalog/items/{id}Delete a catalog item
Returns 204. Estimates, orders, and invoices that already used the item keep their own copy of the line; only the link back to the catalog is cleared.
/v1/catalog/items/{id}/imageSet an item's image
The request body is the raw image bytes, not multipart and not JSON. Set Content-Type to one of image/jpeg, image/png, image/webp, image/gif, image/heic, or image/heif. Max 4 MB.
Coastline re-encodes the image to JPEG with the longest side capped at 1800px, and replaces any previous image. Returns 200 with the updated item.
/v1/catalog/items/{id}/imageRemove an item's image
Returns 200 with the updated item.
Copying a catalog between workspaces
A key is scoped to one workspace, so moving a catalog from one workspace to another takes two keys: a source key and a target key.
- With the source key, list price lists and page through every item.
- With the target key, create each price list that does not already exist there by name. A
409hands youexisting_id; use it instead of creating a duplicate. - Create each item, mapping its source
price_list.nameto the target price list id from the step above. Dedupe on SKU the same way: a409gives youexisting_id, and youPATCHthat item instead if you meant to update it. - For every item with an
image_url, download the bytes from that public link andPUTthem to the target item's image endpoint. Never copy theimage_urlstring itself: the source workspace owns that file, and the link will not resolve once the source item is deleted.