Skip to main content
A Product is a row in your Awardee catalog, keyed by GTIN, with a routable destination, typed key/value properties, and a gallery of product images. Products power three jobs:

Catalog mirror

Sync your ERP, PIM, or e-commerce catalog into Awardee. One product per GTIN, kept fresh through PATCH.

Packaging-to-content

A consumer scans the GS1 barcode on the pack. The scan resolves the GTIN against your catalog and routes them to a chatbot, page, or knowledge base.

Retail integrations

Look up a product by raw GTIN from a POS, scanner, or mobile app via GET /products/by-gtin/{gtin}.

Lifecycle

A product moves through four statuses: A product becomes active as soon as a destination is set (the dashboard does this automatically; the API leaves it to you). Flip back to inactive or archived whenever — the row is never lost.

GTIN normalization

The gtin field is always stored and returned in canonical GTIN-14 form, left-padded with zeros. On write the value must be exactly 8, 12, 13, or 14 digits — GTIN-8, GTIN-12 (UPC-A), GTIN-13 (EAN-13), or GTIN-14. Anything else (a 9- or 11-digit string, non-digit characters) is rejected with 400 invalid_payload. Valid input is normalized to GTIN-14 before persisting. Pass null to clear a stored GTIN.
This matters because GS1 Digital Link readers silently pad to 14 digits when parsing. Storing the canonical form means a 13-digit EAN scanned from a pack and a 12-digit UPC typed into your ERP resolve to the same row.
The check digit is not verified on write. Operators paste codes with typos and we’d rather surface that as a duplicate-detection error than reject silently. If you need strict validation, run it client-side before posting.

Anatomy

Two satellite resources hang off each product:
  • Properties — typed K/V rows (vintage, region, case_size). Replaced as a set via PUT /products/{id}/properties.
  • Images — uploaded photos. See Images below.

CRUD workflow

A typical first sync from your system:
1

Pull your catalog

Iterate every SKU in your source of truth. You’ll need at minimum a name and a GTIN; everything else is optional.
2

Create products

POST /products for each SKU. Set status: "active" if you’re shipping a destination at the same time, otherwise leave it default and configure later. Pass image_urls to attach images in the same call (see Images).
3

Attach properties

PUT /products/{id}/properties replaces the property set atomically. Send the full desired state — Awardee upserts it, then drops only the keys you left out, so a failed request never destroys your existing properties. A body that repeats a key is rejected with 422 unprocessable before anything is written.
4

Attach images

POST /products/{id}/images with a public URL — or pass image_urls on the create call to handle them in one shot. See Images.
5

Reconcile on changes

On every catalog change, look the product up by GTIN (GET /products/by-gtin/{gtin}) and PATCH the fields that drift. If the GTIN doesn’t exist, POST a new one.
Use the metadata field for anything that doesn’t deserve a first-class column or a property row. It’s a free-form JSON object — your vintage, abv, internal SKU codes, feature flags, etc. It’s returned verbatim on every read.

Images

Send a public image URL and Awardee fetches the bytes (SSRF-checked), stores them in your organization’s asset library, and links the resulting asset to the product. The API never accepts raw binary — always pass a URL we can reach over HTTPS. Asset bytes are content-addressed and de-duplicated within the organization: attaching the same image to multiple products stores it once. The file_url returned for two products with the same image will be identical, and the storage cost is paid once. There are two ways to attach images:
  • Bulk on create. Pass image_urls: ["…", "…"] on POST /products. URLs are stored in the order given.
  • Incremental. POST /products/{id}/images with one URL at a time. Use this to add a single image to an existing product, or to control sort_order per image.
Either path returns a product_image row per image once it’s stored. Subsequent reads expose images via GET /products/{id}/images and you can remove one with DELETE /products/{id}/images/{imageId}.

Accepted formats and size

JPEG, PNG, WebP, and animated GIF up to 15 MiB each. The content is sniffed from the bytes — content-type headers alone aren’t trusted.

Bulk on create

cURL
If any URL fails (unreachable, blocked, oversized, not a real image), the product is still created. Failed URLs come back on the response under failed_image_urls. The field is omitted entirely when every URL succeeded.

Incremental attach

cURL
The response is the new image row:
Some CDNs (Shopify, Akamai, etc.) block non-browser fetches. If a URL keeps failing, host the bytes somewhere reachable — S3, your own CDN — and pass that URL instead.

Ordering

sort_order controls display order on the product. Lower values come first. When using image_urls, the array order is used directly. There’s no first-class reorder endpoint today — to change the order, delete and re-attach with explicit sort_order values.

Delete an image

DELETE /products/{id}/images/{imageId} detaches the image from the product. Returns 204 No Content. The underlying asset bytes stay in your organization’s library and may still be referenced by other products or features; orphan assets (zero references) are reclaimed by a background GC job. Deleting the product itself detaches every image attached to it.

Attach knowledge

Link the articles and folders that are specific to this product. Visitors who scan it get them on top of the chatbot’s own knowledge, so a question like “how do I turn the TV on?” is answered from this product’s manual, not a guess across every one. You can also send them on POST /products. Both arrays are replace-only: send the full desired set every time.
See Scan-specific knowledge for how scan links combine with the chatbot’s.

Webhook events

Subscribe and verify signatures per the webhooks guide.