PassPassPassPass
DOCS
Documentation
Updates
Create event
Home
PassPassPassPass

The ticketing service for event organizers.

Discover PassPass Pro →

Documentation

Quick startAll themesAll articlesUpdates

Quick links

PassPass ProFrequently asked questionsOpen ticketContact us

Quick access

© 2024 PassPass. All rights reserved.
Mentions légalesPassPass - Event ticketing

Public API: technical reference

Technical reference for consuming the PassPass REST API. Endpoints, permissions, pagination, response structure, and examples.

Last updated : August 7, 2026

This article is intended for the person consuming the PassPass REST API. For an overview and configuration from the dashboard, see Public API and Webhooks.

Interactive documentation (Swagger): api.passpass.be/api/public/docs


Authentication

Each request carries your API key in the Authorization header:

GET /api/v1/events HTTP/1.1
Host: api.passpass.be
Authorization: Bearer pk_live_9f2c4a1b...

The key is linked to your organization. All returned data is automatically filtered on your organization.

The key's secret is displayed only once at creation. If you lose it, perform a rotation from the dashboard (the old secret stops working immediately).

Validate your configuration at startup with GET /v1/me : it is the only endpoint that requires no scope.


Available Endpoints

The API is read-only. 7 GET endpoints:

  • GET /v1/me : introspection of your API key (scopes, organization, last use). No scope required

  • GET /v1/events : list of your events (all statuses, drafts, and private included). Scope: events:read

  • GET /v1/events/:id : detail of an event. Scope: events:read

  • GET /v1/orders : list of orders. Scope: orders:read

  • GET /v1/orders/:id : detail of an order. Scope: orders:read

  • GET /v1/events/:eventId/attendees : attendees of an event. Scope: attendees:read

  • GET /v1/orders/:orderId/attendees : attendees of an order. Scope: attendees:read


Permissions (scopes)

Scopes control the fields returned, not just access to endpoints. An unauthorized field is absent, not null.

  • events:read : access to event endpoints

  • orders:read : access to order endpoints (amount, buyer, billing, line items, status)

  • orders:read:financial : service_fee, payout_amount, refundable_amount, historical_service_fee

  • orders:read:form_answers : order form answers

  • attendees:read : access to attendee endpoints (minimal data, no personal data)

  • attendees:read:contact : first_name, last_name, email

  • attendees:read:financial : ticket_price

  • attendees:read:form_answers : attendee form answers

  • attendees:read:full : access_token (automatically includes contact and financial)

Scopes are fixed at key creation. To change permissions, create a new key and remove the old one. access_token is the secret identifier behind the ticket's QR code. Only request attendees:read:full if your system truly needs it.


Pagination

All lists are paginated:

{
  "data": [],
  "total": 137,
  "page": 1,
  "limit": 25,
  "total_pages": 6
}

Parameters: ?page= (default 1) and ?limit= (default 25, maximum 100). Always paginate up to total_pages and reread the returned limit.


Conventions

  • Fields in snake_case (exception: buyer and billing in camelCase)

  • Amounts in euros (not in cents). 25 = 25.00€

  • Dates in ISO 8601 UTC

  • Multilingual fields: {"fr": "...", "en": "...", "nl": "..."}

  • Field not authorized by scopes: absent, not null

  • New fields possible without notice, do not reject an unknown field


Events

GET /v1/events returns all your events (drafts and private included, only deleted are excluded). No status or date filter on the API side: filter on the client side on status / start_at.

Use case: gauge dashboard, ticket stock tracking, event list in a back-office.

{
  "id": 1535,
  "title": {"fr": "Mon événement"},
  "short_description": {"fr": "Description courte"},
  "address_label": {"fr": "Bruxelles"},
  "address_full": {"fr": "Rue de la Loi 1, 1000 Bruxelles"},
  "status": "published",
  "visibility": "public",
  "sold_count": 128,
  "max_capacity": 500,
  "start_at": "2026-06-01T18:00:00.000Z",
  "end_at": "2026-06-01T23:00:00.000Z",
  "timezone": "Europe/Brussels",
  "currency": "EUR",
  "tickets": [
    {
      "id": 4525,
      "type": "ticket",
      "name": {"fr": "Entrée Standard"},
      "description": null,
      "price": 25,
      "vat_rate": 21,
      "max_quantity": 300,
      "sold_count": 128,
      "visibility": "visible",
      "sell_start_at": null,
      "sell_end_at": null
    }
  ],
  "created_at": "2026-01-12T14:22:00.000Z",
  "updated_at": "2026-04-17T09:00:00.000Z"
}

Orders

GET /v1/orders returns the orders of your organization.

Available Filters

  • status : paid (default), pending, cancelled, or all

  • created_after : ISO-8601 date, orders created from this moment (inclusive)

  • created_before : ISO-8601 date, orders created up to this moment (inclusive)

By default, only paid orders are returned. Use ?status=all if you want the full picture (cancellations, ongoing carts). This is the most common trap for accounting reconciliations.

Statuses

  • paid : paid and not canceled

  • cancelled : canceled (whether paid or not). Expired orders (abandoned cart) are also found here

  • pending : neither paid nor canceled (ongoing cart, pending transfer)

Use case: accounting export, financial reconciliation, CRM synchronization.

{
  "id": 65817,
  "event_id": 1535,
  "status": "paid",
  "origin": "online",
  "currency": "EUR",
  "amount_total": 200,
  "buyer": {
    "firstName": "Marie",
    "lastName": "Dupont",
    "email": "marie@example.com"
  },
  "billing": {
    "identifierType": "vat",
    "companyName": "Demo Corp SRL",
    "vat": "BE0123456789",
    "vatCountry": "BE",
    "addressStreet": "Rue de la Loi 1",
    "addressCity": "Bruxelles",
    "addressZip": "1000",
    "addressCountry": "BE"
  },
  "line_items": [
    {
      "type": "ticket",
      "ticket_id": 4525,
      "name": {"fr": "Entrée Standard"},
      "quantity": 2,
      "unit_amount": 100,
      "subtotal": 200
    }
  ],
  "form_answers": null,
  "paid_at": "2026-04-17T10:00:00.000Z",
  "cancelled_at": null,
  "created_at": "2026-04-17T10:00:00.000Z",
  "updated_at": "2026-04-17T10:00:00.000Z",
  "service_fee": 4.6,
  "payout_amount": 195.4,
  "refundable_amount": 4.6,
  "historical_service_fee": 4.6
}

Understanding the Fields

buyer : identity entered at checkout (first name, last name, email). In camelCase.

billing : company billing data. Present only if the buyer checked "I order on behalf of a company" at checkout. Otherwise null. In camelCase. Contains: identifierType (vat or enterprise), companyName, vat, vatCountry, addressStreet, addressCity, addressZip, addressCountry.

line_items : order details. Each line has a type:

  • ticket : purchased ticket, unit_amount = unit price

  • fee : service fee (a single aggregated line)

  • discount : promo code / discount, negative unit_amount

Financial block (scope orders:read:financial) :

  • service_fee : current service fee

  • historical_service_fee : fees fixed at the time of sale

  • payout_amount : net amount to be paid to the organizer

  • refundable_amount : part of the fees still refundable

form_answers (scope orders:read:form_answers) : answers to the custom order form.


Attendees

GET /v1/events/:eventId/attendees or GET /v1/orders/:orderId/attendees returns the attendees. One attendee = one ticket.

Only attendees with status active are returned. Canceled or draft tickets are absent.

Use case: attendance list, offline access control, nominative export.

{
  "id": "019fdb0d-...",
  "event_id": 1535,
  "order_id": 65817,
  "status": "active",
  "ticket": {
    "id": 4525,
    "type": "ticket",
    "name": {"fr": "Entrée Standard"}
  },
  "first_name": "Marie",
  "last_name": "Dupont",
  "email": "marie@example.com",
  "ticket_price": 100,
  "access_token": "019fdb0d-...",
  "created_at": "2026-04-17T10:00:00.000Z",
  "updated_at": "2026-04-17T10:00:00.000Z",
  "form_answers": [
    {
      "type": "selection",
      "field_id": "abc123...",
      "question_title": {"fr": "Quel est votre statut actuel ?"},
      "answer": {"fr": "Indépendant"}
    },
    {
      "type": "unique-choice",
      "field_id": "def456...",
      "question_title": {"fr": "Régime alimentaire"},
      "answer": {"fr": "Végétarien"}
    },
    {
      "type": "short-text",
      "field_id": "ghi789...",
      "question_title": {"fr": "Nom entreprise"},
      "answer": "Demo Corp"
    },
    {
      "type": "formatted-field",
      "field_id": "jkl012...",
      "question_title": {"fr": "Téléphone"},
      "answer": "+32400000000"
    },
    {
      "type": "formatted-field",
      "field_id": "mno345...",
      "question_title": {"fr": "Code postal"},
      "answer": "1000"
    }
  ]
}

Understanding the Fields

Fields are conditioned by scopes. Absent if the scope is not granted:

  • first_name, last_name, email (scope attendees:read:contact)

  • ticket_price (scope attendees:read:financial) : amount in euros

  • form_answers (scope attendees:read:form_answers) : attendee form answers. Possible types: selection, unique-choice, short-text, formatted-field. The selection and unique-choice types have multilingual answers, the others have string answers

  • access_token (scope attendees:read:full) : the secret identifier behind the ticket's QR code, read by the PassPass Organizer mobile app during entry control. This token can, for example, be reassigned on a badge to replicate a QR code readable by PassPass ticket control. Transmitting this token is equivalent to giving access to the ticket


Introspection

GET /v1/me returns your key information. No scope required.

{
  "api_key_id": "019d-...",
  "api_key_prefix": "pk_live_9f2c4a1b",
  "api_key_name": "Production server",
  "organization_id": "019d-...",
  "scopes": ["events:read", "orders:read", "attendees:read"],
  "last_used_at": "2026-04-17T09:58:00.000Z",
  "created_at": "2026-01-12T14:22:00.000Z"
}

Limits

100 requests per minute per API key. Beyond that: HTTP 429. If your need is real-time, use webhooks instead of polling.


Error Codes

  • 401 : key absent, invalid or disabled

  • 403 : valid key but insufficient scope

  • 404 : resource nonexistent or outside your organization

  • 400 : invalid parameter (status, date format...)

  • 429 : quota exceeded (100 req/min)


API or Webhooks?

  • React to a sale in real-time → Webhooks order.paid

  • Synchronize a CRM continuously → Webhooks + API for catch-up

  • Accounting export, reconciliation → API /v1/orders?status=all&created_after=...

  • Attendance list, access control → API /v1/events/:id/attendees

  • Gauge dashboard / stocks → API /v1/events (sold_count, max_quantity)

  • Replay a past history → API (the webhooks do not replay the past)

The robust scheme: webhooks for real-time + periodic API call to catch up on what a network incident might have missed.


For any technical questions or need for support, feel free to contact our support.

Contact us

Related articles

3 articles in this category