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.

EndpointTypeDescription
GETpermissioncatalog.view
POSTpermissioncatalog.create
PATCH, image endpointspermissioncatalog.edit
DELETEpermissioncatalog.delete

The price list object

FieldTypeDescription
iduuidUnique id of the price list.
objectstringAlways price_list.
namestringUp to 200 characters.
descriptionstring | nullFree text, or null.
is_defaultbooleanRead-only. True for the workspace's single default list.
contractor_typestring | nullWhen set, this list's pricing applies to contractors of that type on work orders.
vendor_typestring | nullWhen set, this list's pricing applies to vendors of that type on material orders.
accountobject | nullThe linked vendor or contractor account, as { id, name }. Null when no account is linked.
created_atstringISO 8601 timestamp.
GET/v1/catalog/price-lists

List price lists

Returns the default price list first, then the rest by name.

Query parameterTypeDescription
namestringExact, case-insensitive match on the list name.
limitintegerRows per page, 1 to 100. Defaults to 25.
offsetintegerRows to skip. Combine with has_more to page.
POST/v1/catalog/price-lists

Create 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.

BodyTypeDescription
namestringRequired on create. Up to 200 characters.
descriptionstring | nullOptional.
contractor_typestring | nullApplies this list to contractors of that type on work orders.
vendor_typestring | nullApplies this list to vendors of that type on material orders.
account_iduuid | nullA vendor or contractor account in this workspace.
GET/v1/catalog/price-lists/{id}

Retrieve a price list

PATCH/v1/catalog/price-lists/{id}

Update a price list

Any subset of the create fields. is_default is read-only.

DELETE/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

FieldTypeDescription
iduuidUnique id of the catalog item.
objectstringAlways catalog_item.
namestringUp to 500 characters.
descriptionstring | nullFree text, or null.
skustring | nullUnique per workspace. A blank value is treated as none.
item_typeenumproduct, service, or bundle. Defaults to product.
categorystring | nullFree text grouping, e.g. "Roofing".
unitstring | nullFree text unit, e.g. "SQ", "EA", "LF".
unit_pricenumber | nullRounded to 2 decimals.
material_costnumber | nullRounded to 2 decimals.
labor_costnumber | nullRounded to 2 decimals.
optionsstring[]Choices a member picks from when adding the item to an order. Up to 100.
image_urlstring | nullRead-only. Set it with the image endpoint below.
price_listobject | nullAs { id, name, is_default }. Null when the item is on no price list.
unit_conversionsobject[]Each as { from_quantity, to_quantity, to_unit }: from_quantity of the item's unit equals to_quantity of to_unit.
measurement_field_iduuid | nullThe measurement field the item fills its quantity from on estimates and orders.
abc_item_numberstring | nullRead-only.
created_atstringISO 8601 timestamp.
updated_atstringISO 8601 timestamp.
GET/v1/catalog/items

List catalog items

Ordered by name, then id.

Query parameterTypeDescription
price_list_iduuid | "none"A price list id, or the literal none for items on no price list.
item_typeenumproduct, service, or bundle.
categorystringExact, case-insensitive match.
skustringExact match.
qstringSubstring match on name or SKU.
limitintegerRows per page, 1 to 100. Defaults to 25.
offsetintegerRows to skip. Combine with has_more to page.
POST/v1/catalog/items

Create 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.

BodyTypeDescription
namestringRequired on create. Up to 500 characters.
descriptionstring | nullOptional.
skustring | nullUnique per workspace. Blank is treated as none.
item_typeenumproduct, service, or bundle. Defaults to product.
categorystring | nullOptional.
unitstring | nullFree text like "SQ", "EA", "LF". Required for unit_conversions.
unit_pricenumber | nullRounded to 2 decimals.
material_costnumber | nullRounded to 2 decimals.
labor_costnumber | nullRounded to 2 decimals.
optionsstring[]Up to 100 choices.
price_list_iduuid | nullOptional.
unit_conversionsobject[]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_iduuid | nullA measurement field in this workspace, or null to unlink.
GET/v1/catalog/items/{id}

Retrieve a catalog item

PATCH/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.

DELETE/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.

PUT/v1/catalog/items/{id}/image

Set 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.

DELETE/v1/catalog/items/{id}/image

Remove 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.

  1. With the source key, list price lists and page through every item.
  2. With the target key, create each price list that does not already exist there by name. A 409 hands you existing_id; use it instead of creating a duplicate.
  3. Create each item, mapping its source price_list.name to the target price list id from the step above. Dedupe on SKU the same way: a 409 gives you existing_id, and you PATCH that item instead if you meant to update it.
  4. For every item with an image_url, download the bytes from that public link and PUT them to the target item's image endpoint. Never copy the image_url string itself: the source workspace owns that file, and the link will not resolve once the source item is deleted.