Skip to content

Products, payments, and bookings

Create a product

bash
curl -X POST "$BOOKING_URL/api/products" \
  -H "Authorization: Bearer $BOOKING_SESSION" \
  -H "Content-Type: application/json" \
  -d '{
    "type":"class_pass",
    "name":"Ten class pass",
    "prices":[{"amount":120000,"currency":"DKK"}],
    "classAllowance":10,
    "validityDays":180,
    "status":"active"
  }'

Supported types are drop_in, subscription, class_pass, workshop, teacher_training, and other.

Every product has one or more price options. Subscription prices additionally require a cadence:

json
{
  "type": "subscription",
  "name": "Membership",
  "prices": [
    {
      "amount": 69500,
      "currency": "DKK",
      "cadence": { "unit": "month", "count": 1 }
    },
    {
      "amount": 695000,
      "currency": "DKK",
      "cadence": { "unit": "year", "count": 1 }
    }
  ]
}

Purchase

An authenticated user purchases with POST /api/products/{productId}/purchases. Pass { "priceId": "PRICE_ID" } when the product has multiple options. A paid result creates an entitlement automatically. Purchases require an active payment connection.

Attendees list their active access with GET /api/entitlements. Class-pass entitlements include remainingClasses and may include expiresAt; subscription entitlements include renewsAt and a snapshot of the purchased price and cadence. A consumer should show these before offering a new purchase.

The hosted service checks due subscriptions hourly. A successful MobilePay charge creates a payment record and advances renewsAt by the purchased cadence; a failed charge revokes access. Operators can also trigger the same due-item scan with POST /api/subscriptions/renewals/run.

An attendee cancels their own membership with PATCH /api/entitlements/{entitlementId} and { "action": "cancel" }. Cancellation revokes the entitlement immediately, removes renewsAt, and prevents future renewal attempts. Owners and administrators may perform the same action for account support.

Book with an entitlement

json
{
  "instanceId": "INSTANCE_ID",
  "entitlementId": "ENTITLEMENT_ID"
}

Book with a drop-in payment

json
{
  "instanceId": "INSTANCE_ID",
  "productId": "DROP_IN_PRODUCT_ID",
  "priceId": "DROP_IN_PRICE_ID"
}

Post either body to /api/bookings. priceId can be omitted when a drop-in has exactly one price. The API checks cancellation, audience, group participation, capacity, entitlement ownership/expiry/allowance, and payment outcome atomically.

When an eligible active entitlement exists, submit its entitlementId directly. Do not start another payment flow. The booking consumes one remaining class for a limited pass and uses a subscription without creating a new payment.

Attendees see their own bookings with GET /api/bookings; elevated operational roles see all bookings in their account. Delete /api/bookings/{bookingId} to cancel. Cancelling an entitlement-funded booking restores one remaining class where applicable.

Booking SaaS implementation documentation