Create a product
Creates a manual catalog entry at one location. Scope: products:write.
Entries you create are source: "api". POS-sourced entries cannot be created here - they arrive through the location’s POS sync and carry a read-only pos_ref.
Supply a sku so transaction line items resolve to this product - 409 sku_already_exists when the key is already used at this location.
What the server owns
You supply the catalog data (location_id, name, sku, category, tags, price_minor). Everything else is server-computed and rejected with 422 parameter_unknown if supplied:
pos_ref- owned by the POS sync. If callers could set it, a manual entry could impersonate a till product and corrupt line-item resolution - same protection asTransaction.sourcesource- server-stamped"api"(provenance is never client-claimed)id,created_at,updated_at- platform-minted
Authorizations
Your secret API key as a Bearer token: Authorization: Bearer vxn_sk_.... Keys are created and scoped in the dashboard - see Authentication.
Headers
Required on every mutating POST. 24h byte-replay on the same key + body; 409 idempotency_key_conflict on body mismatch. See Idempotency.
8 - 255Body
Create a manual catalog entry. POS-sourced entries cannot be created via API - they arrive through the location's POS sync. Server-owned fields (pos_ref, source) are rejected with 422 parameter_unknown.
The location catalog to add to. Must belong to your organization.
^loc_[A-Za-z0-9]+$"loc_01HW9R4P2ZK7N8MTX1RVLOCS01"
Display name - plain string or per-language object, the same localization pattern as Card.name. POS-synced entries arrive with the till's single-language name; supply per-language names on manual/API entries when your audience is multilingual (border stores, tourist areas). Notifications and future product-facing surfaces resolve the name in the customer's language.
1 - 200"Flat White"
Strongly recommended - the correlation key line items resolve on. Unique per location (409 sku_already_exists on collision).
1 - 64"FW-12"
Free-form category.
1 - 100Your own filing labels. They join the product name in the search haystack, so a tag makes the product findable even when the search text appears nowhere in its name. Omit for none.
Reference price in minor units. Maximum 100,000,000,000.
0 <= x <= 100000000000Response
Product created.
A catalog entry for correlating transaction line items to real products. Catalogs are per-location (each location can run a different POS with a different menu), unified under the organization: list org-wide, filter by location_id.
This is a loyalty correlation catalog, not an inventory system - no variants, modifiers, or stock levels.
Product identifier, prd_ prefix. Stable forever - line items resolve to it.
^prd_[A-Za-z0-9]+$"prd_01HW9R7T2CD8N8MTX1RVPRDFW1"
Always "product".
"product"The location this catalog entry belongs to. The same drink sold at two locations is two Product records (each mapped to its own POS).
^loc_[A-Za-z0-9]+$"loc_01HW9R4P2ZK7N8MTX1RVLOCS01"
Display name - plain string or per-language object, the same localization pattern as Card.name. POS-synced entries arrive with the till's single-language name; supply per-language names on manual/API entries when your audience is multilingual (border stores, tourist areas). Notifications and future product-facing surfaces resolve the name in the customer's language.
1 - 200"Flat White"
Where this catalog entry originated. Known values: pos (synced from the location's connected POS), manual (dashboard), api (created via POST /v1/products). Server-stamped - treat as an open set.
"pos"
False = archived (delisted from the POS or manually retired). Archived products stay resolvable on historical line items - there is no hard delete.
true
Unix seconds.
1750000000
Your stable key, unique per location (two locations may both use FW-12). This is what transaction line items correlate on.
1 - 64"FW-12"
Free-form category for basket analytics.
1 - 100"drinks"
Merchant-owned filing aid, and part of the search haystack alongside the name. NEVER populated by POS sync: no POS system sends tags, so a source: "pos" entry arrives untagged. You can tag one yourself with PATCH /v1/products/{id} (tags are an overlay field, unlike pos_ref and source, which sync owns), and it becomes findable by that tag immediately. Sending [] clears them and removes them from the search haystack.
Reference price in minor units. Informational - the POS bill is authoritative at transaction time.
x >= 0350
The POS mapping for this location's adapter. Read-only, owned by the sync - never editable via API. Null for manual/API entries. The POS vendor name is never surfaced.
null
Unix seconds of last change.
1750000000