Events & Reporting

The read-only side of the v2 API: what you sold, what has been redeemed, who is coming to your events, and who signed up to your newsletter. For creating and redeeming items see Endpoints.

MethodPathScope
GET/v2/eventsitems:read
GET/v2/events/{slug}/participantsreports:read
GET/v2/reports/analyticsreports:read
GET/v2/reports/claimed-itemsreports:read
GET/v2/subscribersreports:read

All five act on the company that owns the API key — or on an assigned company when you pass the optional company parameter — and return the payload unwrapped. Pagination differs by operation:

  • GET /v2/events, event participants and subscribers are lists: they answer the one list envelope (object, data, has_more, next_cursor) and page with limit and starting_after. None accepts offset, and the participants list no longer takes its active/claimed order control: a forward cursor needs one stable order.
  • Analytics and claimed-items are reports, not lists. They accept offset, limit, and order (asc or desc), and answer their rows under items with an itemsCount. They are windows over a report rather than walks along a list, so they kept the offset.

See Endpoints — Conventions for defaults and the other list-route families.

Events

GET /v2/events — scope items:read

This is the reporting view of your events, not the sales one. It answers how many tickets an event has sold, held and started with, for a dashboard or a reconciliation. It publishes no prices, no ticket types and no ids an order can name, and nothing here can be bought.

To sell a ticket, read the catalog instead. An event is published as a product at GET /v2/products?type=event_ticket, with the id evt_<id>, one evto_<id> option per ticket type, the price and fee of each, the collection points, and what the organiser asks of a buyer. That product id is what POST /v2/quotes and POST /v2/orders take. See Endpoints — An event and its tickets and Headless checkout.

The two lists also answer different questions about which events exist. This one reports every event of the company, including undated and private ones, because a report that hid them would under-count. The catalog publishes only what is genuinely on sale — active, public, fully dated and inside its open window — because a product it lists is a product an order accepts.

Lists your events with their ticket counts. online=true narrows to events currently visible in the order form; private=true returns private events instead of public ones.

bash
curl -H "Authorization: Bearer lp_live_..." \
  "https://api.lifepeaks.dk/v2/events?online=true&limit=2"
json
{
  "object": "list",
  "data": [
    {
      "name": "Hotel Party nr.1",
      "slug": "hotel-party-nr1",
      "private": false,
      "online": true,
      "venue": "London",
      "venue_address": "",
      "venue_postcode": "",
      "date_activate": null,
      "date_deactivate": null,
      "sold": 435,
      "blocked": 0,
      "all": 1773
    }
  ],
  "has_more": false,
  "next_cursor": null
}

sold counts tickets sold, blocked counts tickets held in baskets awaiting payment, and all is the total the event was set up with. Count data for the size of the returned page; it is not the total number of events. Send next_cursor back as starting_after until has_more is false; never infer the end from a short page.

Event participants

GET /v2/events/{slug}/participants — scope reports:read

Uses the slug from the events list. This endpoint needs reports:read rather than items:read because the rows carry participant personal data.

ParameterNotes
searchFree-text search over the participant rows
limit, starting_afterPaging. The response carries has_more and next_cursor
json
{
  "object": "list",
  "data": [
    {
      "sender_name": "Jane Doe",
      "sender_email": "jane@example.com",
      "sender_phone": "",
      "sender_comment": "",
      "sender_code": null,
      "ticket_variation": "3 x Standard",
      "ticket_price": 255,
      "created_at": "2026-01-03 20:15:10",
      "payment_type": "API created - Payment with invoice",
      "payment_OTH": null,
      "claimed_at": null
    }
  ],
  "has_more": false,
  "next_cursor": null
}

An event with no participants answers 200 with an empty array. It used to answer 404, which could not be told from an event that does not exist. A 404 now means only that: no such event for this company.

Analytics

GET /v2/reports/analytics — scope reports:read

The same items as GET /v2/items, reduced to the analytic columns: type, titles, status, amounts, VAT, currency, remaining value, validity, creation time and language. No availability flags, no buyer identity, no claim or PDF links.

Unlike GET /v2/items this endpoint has no mandatory filter — a date range on its own is a valid call.

bash
curl -H "Authorization: Bearer lp_live_..." \
  "https://api.lifepeaks.dk/v2/reports/analytics?date_from=2026-01-01&date_to=2026-01-31&limit=100"
json
{
  "items": [
    {
      "item_type": "Gift Card",
      "item_type_identification": "Gift Card",
      "item_title": "Gift Card",
      "event_title": null,
      "ticket_title": null,
      "is_token": false,
      "item_info": null,
      "status": 6,
      "status_name": "Paid",
      "amount": 500,
      "vat": 25,
      "vat_value": 100,
      "currency": "DKK",
      "remaining_amount": 400,
      "remaining_pieces": null,
      "company_name": "Acme Hotels",
      "validity": "2029-05-10 00:00:00",
      "created_at": "2026-01-12 15:31:57",
      "language": "en-GB"
    }
  ],
  "itemsCount": 1
}

search, status, date_from and date_to filter the same way they do on GET /v2/items, except that search has no minimum length here — a short term is searched rather than refused.

Claimed items

GET /v2/reports/claimed-items — scope reports:read

The redemption ledger: one row per claim, refund or status transaction, across gift cards, special offers and tickets, newest first by default.

Send both date_from and date_to. The 422 fires only when both are missing, so a request carrying one of them is accepted — and answers an empty page, because the missing bound is compared against nothing and matches no row. An empty list from this route is therefore a formatting mistake more often than it is a quiet month.

bash
curl -H "Authorization: Bearer lp_live_..." \
  "https://api.lifepeaks.dk/v2/reports/claimed-items?date_from=2026-01-01&date_to=2026-01-31"
json
{
  "items": [
    {
      "transaction_id": 123465,
      "status": 3,
      "status_name": "Used",
      "date": "2026-01-12",
      "time": "01:36:48",
      "item_type": "Gift Card Variation",
      "item_type_identification": "Gift Card Variation",
      "code": "77xrB3e44T",
      "amount": 100,
      "claim_note": "Booking 55123",
      "claimed_by_username": null,
      "claimed_by_company_name": null,
      "currency": "DKK",
      "company_name": "Acme Hotels"
    }
  ],
  "itemsCount": 1
}

claim_note is whatever you passed when redeeming the item, which makes this the endpoint to reconcile Lifepeaks redemptions against your own bookings. claimed_by_username is null for anything redeemed with an API key — a key is not a user. Redemptions made by staff in the admin UI still carry their username.

Subscribers

GET /v2/subscribers — scope reports:read

The confirmed newsletter subscribers collected through your order pages, together with the marketing consents recorded on v2 orders through marketing_consent. When your company is a root company, the subscribers of its child companies are included.

A subscriber appears here only once the order that produced them is paid, whichever of the two sources it came from.

json
{
  "object": "list",
  "data": [
    { "id": "3183", "company": "acme-hotels", "email": "jane@example.com", "name": "Jane Doe" }
  ],
  "has_more": false,
  "next_cursor": null
}

date_from and date_to filter by signup date, but only when both are present and both parse as YYYY-MM-DD. If either is missing or malformed the interval is ignored entirely and every subscriber is returned — so check your date formatting before concluding that a month was unusually busy.

This list grows with every buyer, so it pages by default: without a limit you get the first 50 rows and has_more: true. To walk the whole list, raise limit to at most 100 and pass the id of the last row you received as starting_after until has_more is false. The event and participant lists page the same way; only the two reporting routes still use offset.

Treat a row's id as opaque. The list has two sources, so the ids are not all numbers — a consent recorded on a v2 order carries a prefix. Send the value back exactly as you received it and do not parse it or do arithmetic on it. The order is stable either way, so a walk visits every subscriber exactly once.