List products by merchant

PUBLIC

Returns the list of products belonging to the specified merchant.

Recent Requests
Log in to see full request history
TimeStatusUser Agent
Retrieving recent requests…
LoadingLoading…
📘

CacheCache-Control: public, max-age=7200 (2 hours)

Part of the catalog — The product catalog explains how merchants, products, calendars and timeslots fit together.

Why this endpoint exists

A product is a specific purchasable thing at a merchant: an adult day ticket, a child ticket, a family bundle, a spa arrangement. This is the list you render on the merchant page as the customer's actual choice, and each item carries everything you need to decide how it must be sold.

Note the response has no meta.pagination — a merchant's product list is small and comes back whole. Pair it with the merchant detail read to build a merchant page.

How to use it

curl "https://api.acc.funtrips.io/v2/merchants/8a2f4c61-9d3e-4b57-a0c8-1e5f7b2d9a30/products" \
  -H "x-campaign-id: 3c1e5a90-7b2d-4f61-9a83-1d4e7f2b8c05" \
  -H "Accept-Language: nl-NL"
{
  "data": [
    {
      "id": "d4b8f2a1-6c3e-4957-b18d-2f5a9c7e3b04",
      "title": "Dagticket volwassene",
      "product_type": "TICKET",
      "physical_product": false,
      "sku": "ZOO-ADULT-DAY",
      "age_group": "adult",
      "unit_price": { "value": "24.00", "currency": "EUR" },
      "list_price": { "value": "24.00", "currency": "EUR" },
      "booking_costs": { "value": "0.99", "currency": "EUR" },
      "visit_guarantee_cost": { "value": "1.50", "currency": "EUR" },
      "no_date": false,
      "requires_timeslot": true,
      "best_effective_price": { "value": "18.50", "currency": "EUR" },
      "best_list_price": { "value": "24.00", "currency": "EUR" },
      "best_discount_amount": { "value": "5.50", "currency": "EUR" },
      "best_discount_percentage": 22.9,
      "first_available_date": "2026-09-04",
      "last_available_date": "2026-12-31",
      "sold_out": false,
      "conditions": "Geldig op de gekozen datum."
    }
  ]
}

The four flags that decide your UI

Most fields are content or money. These four are behavioural, and reading them wrong produces checkouts that fail at the last step.

no_date — show a date picker?

false (default) means the product sells on specific calendar days. Fetch the calendar and make the customer choose one.

true means there are no per-day entries. Hide the date picker and submit today's date at checkout — that is accepted and expected. Trying to render a calendar for a no_date product gets you an empty calendar and a dead end.

requires_timeslot — show a time picker?

Independent of no_date. A product can be calendar-dated yet valid all day.

true means the customer must also pick an entry window, from POST /fulfillment/timeslots.

When requires_timeslot is true and the timeslot list for the chosen date comes back empty, that date has no availability. Block it and prompt for another date.

Do not fall back to a midnight visit time. Timeslotted providers reject it, and because midnight UTC is 02:00 in Amsterdam in summer, the failure surfaces as a booking on the wrong day. When requires_timeslot is false, an empty timeslot list simply means "all day" and the date alone is enough.

physical_product — collect an address?

true means something gets shipped, so the checkout needs a full shipping address. You do not have to derive this yourself: create checkout returns requires_shipping_address, resolved authoritatively server-side. Use this flag for previews ("shipped to your home"), and the checkout's flag as the rule.

sold_out — can it still be bought?

true means this product sells on dates and no purchasable date remains — everything left is sold out or past. Show it as unavailable and do not offer a date picker.

When sold_out is true, the best_* price fields and the availability dates are omitted. Do not treat their absence as €0.

A product without a calendar is always purchasable and reports false.

Prices: which one to show

FieldMeaning
unit_priceCurrent price for one unit
list_priceUndiscounted reference price — the strikethrough
booking_costsSurcharge per ticket
visit_guarantee_costSurcharge per ticket for the flexible-date option
best_effective_priceCheapest actual price across available days
best_list_priceThat day's pre-discount price
best_discount_amount / best_discount_percentageThe deepest discount available

The best_* family answers "from €X" and "up to Y% off" across the product's remaining days, computed stock-aware — a sold-out cheap day does not set the headline. For a specific day's price, read the calendar.

Never total these yourself for display at checkout. Surcharges are per-ticket, discounts depend on the customer's wallet, vouchers and memberships, and the authoritative arithmetic comes back in the checkout totals.

Other fields worth knowing

  • sku — the upstream catalog identifier. It travels with the checkout line and is what commission settlement keys on. Log it; it makes reconciliation conversations concrete.
  • age_group — populated only when product_type is TICKET (adult, child, …). Use it to group ticket types.
  • ticket_validity_type / ticket_validity_date — when ticket_validity_type is VALIDITY_PERIOD, the ticket is valid through ticket_validity_date inclusive. This is how evergreen products print a real "valid until" date instead of the purchase day. Surface it, or customers will assume same-day-only.
  • conditions — merchant terms to display before purchase.

Errors

StatustypeCause
400merchant_id is not a UUID, or x-campaign-id is missing
500urn:qup:error:product-internal-errorUnexpected fault

A merchant with no products in this campaign returns 200 with an empty array.

Path Params
uuid
required
Headers
uuid
required

Campaign scope for this request. Filters content to the specified campaign.

string

RFC 7231 language preference. The best match against supported locales (nl-NL, en-GB, de-DE) is selected. Defaults to nl-NL when omitted.

string
enum
Defaults to application/json

Generated from available response content types

Allowed:
Responses

Language
URL
LoadingLoading…
Response
Click Try It! to start a request and see the response here! Or choose an example:
application/json
application/problem+json