Skip to content
View Markdown

Loading documentation…

Product management

Manage an event's ticket catalog using its existing product actions.

Read configuration

Use GET /{event_id}/products/index.json for the catalog and GET /{event_id}/products/edit/{product_id}.json for current configuration. The latter includes Product, volume/pool/package rows, attachments, metadata and event context. GET /{event_id}/products/add/free.json prepares defaults and creates nothing.

Create a product

PUT /{event_id}/products/add/{type}.json with a Product object. Types are paid, free, donation and package. Set the corresponding payload fields: free tickets use price: "0.00"; donations use donation: 1; packages use package_flag: 1. The path alone does not populate every submitted flag.

{"Product":{"title":"General admission","category_new":"Tickets","price":"0.00","stock":100,"visible":1}}

Use an event-owned category_id, or category_new to create a group with the ticket. If omitted, the action chooses the first existing group. New product IDs and ordering are assigned by the server. Omitted/empty stock becomes -1 (unlimited); an explicit zero remains zero. Paid tickets require a configured payment provider. The first paid ticket can also update event fee/VAT settings.

Successful creation returns {"ok":true,"id":"..."}. Validation may return an array containing submitted data and errors with HTTP 200. Read writing data before treating the request as successful.

Product fields and defaults

Field inside Product Validation and meaning Creation/edit behavior
title String, at least 2 characters; permitted HTML is filtered. Required on creation and full configuration edits.
price Numeric amount from 0 through 999999.9999 in event currency. Send a decimal string such as "12.50". Required; free tickets use "0.00". Donations can send null with donation:1.
category_id / category_new Event-owned category UUID, or new name of 2–100 characters. Creation can select the first existing group when both are omitted. Supply one deliberately. Edits require the current category or a new group.
stock Whole quantity ≥ -1. -1 means unlimited; 0 means no capacity. Creation uses -1 when omitted/empty. Send current stock on edit; an empty edit value also becomes -1. This is configured capacity, not computed remaining stock.
max_per_sale Whole number from 1 through the deployment's Product.max_per_sale setting. Creation uses that setting when omitted/empty. Retain the returned value on edit; do not assume the same maximum in every environment.
visible 0/1 shop visibility. Supply deliberately; visibility, sales windows and event state all affect checkout availability.
available_from, available_till Server-local SQL timestamps. Retain existing values unless changing sales windows. They do not contain a UTC offset.
waitinglist, keep_soldout_in_shop 0/1 waiting-list and sold-out display settings. Optional; preserve the current flags on full edits.
package_flag, donation 0/1 product modes. The generic creation path does not assign every mode flag; set the relevant flag explicitly.
description, text_on_ticket Strings; description HTML is filtered. Optional content; keep the returned configuration when editing another field.
duplicate 0/1 command flag. On edit, 1 delegates to creation and can return a new product ID. Omit for an ordinary update.

For an edit, start with products/edit/{product_id}.json, map its fields into the write envelopes and retain title, price, category, stock and maximum per sale. The endpoint is a configuration form save rather than a field patch. Volume prices, pool membership and package members are replaced; omitting related selections can clear them. Read availability for computed stock and shared pools.

Pricing, pools and packages

Additional top-level envelopes are ProductsVolume, ProductsPool, PackagesProduct, Meta and first-paid-ticket Event settings. Metadata is limited by server policy.

{
  "Product": {"title":"Bundle","price":"30.00","stock":100,"package_flag":1},
  "ProductsVolume": {"5":{"price":"25.00"}},
  "ProductsPool": {"1":{"pool":1}},
  "PackagesProduct": [{"product_id":"22222222-2222-4222-8222-222222222222","amount":2,"active":1}]
}

Volume keys are quantities; pool keys identify shared pools. Package rows use event-owned product IDs, positive whole quantities and active:1 to select a member; rows without an active value are skipped. Do not send the read serializer's pool rows unchanged: submitted pool selections use the keyed structure above. products/add_package.json is a PUT alias that forces the package flag; package-mode form/failure rendering still has legacy behavior.

Edit and delete

PUT /{event_id}/products/edit/{product_id}.json with current required configuration and the changes. The route ID is authoritative. Success returns {"ok":true}. Setting Product.duplicate delegates to creation and can return a new ID. Historical purchases can retain their original product title/price.

POST /{event_id}/products/delete/{product_id}.json deletes a permitted product. Sold-ticket, package and other references can block deletion. Require ok:true and refresh the catalog; don't infer removal from HTTP 200.

Reorder the catalog

PUT /{event_id}/products/order.json with a complete Product map from category UUID to ordered product UUIDs. Include every category and every product exactly once. An empty group uses ["empty"]. Success is HTTP 204, without JSON.

{"Product":{"55555555-5555-4555-8555-555555555555":["22222222-2222-4222-8222-222222222222"]}}

Use categories and category questions for group configuration. The catalog recipe connects creation and availability reads.