Loading documentation…
{{ error }}
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.