# Bookings API

A public, server-to-server API for selling Experience bookings directly from your own website or e-commerce store. Build the booking journey in your own branding and design instead of linking guests out to an Embed front end — while Embed keeps experiences, availability, and payments in sync.

**Tag:** Bookings

---

## Overview

The Bookings API is a public-facing, server-to-server API that lets you add Experience bookings to your own website or e-commerce platform. The goal is to give integrators full control over the guest experience: rather than sending customers to an Embed-hosted booking page, you build the browsing, selection, and checkout flow in your own style and brand, and use this API to read live experiences and availability, hold and confirm bookings, and record payment. Embed remains the source of truth for experiences, session capacity, and order state.

### What You Can Build

- A branded "Book an Experience" section on your own website or online store
- Location and experience listings pulled live from Embed
- A date and time-slot picker driven by real session availability
- A custom checkout that reserves a slot, captures guest details, and takes payment through your own payment provider
- Order confirmation and cancellation handling within your own account or booking-management screens
- Back-office sales reporting for the experiences you sell

### How It Fits Together

- **You own the front end.** The look, feel, and journey are entirely yours — this API only supplies the data and booking operations.
- **Server-to-server.** Calls are made from your backend using a client secret, not from the browser.
- **Embed stays authoritative.** Availability, capacity, and order status are always validated against the Embed platform to prevent overbooking.
- **Payment is yours to handle.** You process payment through your own gateway, then record the sale against the order to confirm the booking.

### Key Characteristics

| Feature | Details |
|---------|---------|
| Protocol | HTTPS only |
| Architecture | Cloud based RESTful, server-to-server (backend-to-backend only) |
| Authentication | Bearer token — exchange your client secret at `POST /api/v1/token` |
| Token Lifetime | 6 hours — request a new token when it expires |
| Data Format | JSON request and response bodies |

---

## Typical Booking Flow

Call the endpoints in this order to complete a booking from your own storefront:

```
1. Authenticate          →  POST /api/v1/token
2. Get locations         →  GET  /api/v1/locations
3. Get experiences       →  GET  /api/v1/experiences?locationId={id}
4. Check availability     →  GET  /api/v1/experiences/{id}/sessions/availability?fromDate={date}
5. Get session slots      →  GET  /api/v1/experiences/{id}/sessions?forDate={date}
6. Reserve a slot         →  POST /api/v1/orders/reservations/reserve
7. Create the order       →  POST /api/v1/orders/initiate
8. Record payment         →  POST /api/v1/sale
```

To release a held slot before payment, call `POST /api/v1/orders/reservations/cancel`. To cancel a confirmed order and its reservations, call `POST /api/v1/orders/{orderId}/cancel`. Sales data for the experiences you sell is available from `GET /api/v1/reports`.

> See the API Endpoints reference for the full request and response details, including field-level information and example payloads for every endpoint.

---

## Authentication

The Bookings API uses **Bearer Authentication with JWT (JSON Web Tokens)**. Every API request must include a valid access token in the `Authorization` header:

```
Authorization: Bearer <access_token>
```

There are two distinct token levels — an **admin access token** and a **guest (Cognito) access token** — each used for different operations. You must obtain them in sequence before any guest-level API calls can be made.

> Embed does not provide development support for Bookings API integrations. Developers must rely on this guide and the Swagger documentation.

### Token Types

| Token | Scope | Used for | How to obtain |
|-------|-------|----------|---------------|
| Admin access token | System-level | Guest sign-up and guest login endpoints only | POST Client Secret to `/api/v1/token` |
| Guest (Cognito) access token | Guest-level | All card, balance, activity, sale, and wallet pass endpoints | Returned after a successful call to `/api/v1/admin/login` |
| Refresh token | Guest-level | Renewing an expired Cognito access token without re-login | Returned alongside the Cognito access token at login |

> The admin access token is **not** used for card or balance operations. Once the guest is logged in, all subsequent calls must use the Cognito access token.

### Step 1 — Get Your Client Secret

The Client Secret is provisioned by Embed as part of the Bookings API setup. It is available in TOOLKIT Portal once your account has been configured.

1. Log in to TOOLKIT Portal at `https://toolkit.helixleisure.net`
2. Navigate to **API Integration** in the left-hand menu
3. Locate the `guest-portal` module row
4. Click **Show Key** to reveal the Client Secret value
5. Copy the key for use in your server-side environment

> 🔒 **Security:** Never store the Client Secret in your application source code or a client-side environment. Store it securely server-side — for example in an environment variable, a secrets manager, or a vault service. Do not commit it to version control.

### Step 2 — Get an Admin Access Token

Before a guest can sign up or log in, your server must obtain an admin access token by posting the Client Secret to the token endpoint.

```
POST /api/v1/token
Content-Type: application/json

{
  "clientSecret": "<your_client_secret>"
}

// Response
{
  "accessToken": "<admin_jwt_token>"
}
```

> The admin access token is short-lived. Your server should obtain a fresh token before making sign-up or login calls rather than caching it indefinitely.

### Step 3 — Register a Guest (Sign-up)

All guests must be registered with Embed before they can log in. Registration links a guest account to at least one physical game card.

**Requirements**

- Guest's **email address** — used as the username and must be unique
- Game card **10-digit barcode** and **3-digit CVV**
- The game card must already be **activated** in the on-premise TOOLKIT system

**Password rules**

| Rule | Requirement |
|------|-------------|
| Minimum length | 8 characters |
| Maximum length | 20 characters |
| Must contain | At least 1 number, 1 lowercase letter, 1 uppercase letter |
| Special characters | Allowed |

```
POST /api/v1/admin/signup
Authorization: Bearer <admin_jwt_token>
Content-Type: application/json

{
  "username":     "guest@example.com",
  "password":     "SecurePass1",
  "firstName":    "Jane",
  "lastName":     "Smith",
  "cardBarcode":  "1234567890",
  "cardCvv":      "123",
  "cardNickname": "My Blue Card"
}

// Response 201 Created
{ "message": "Guest account created successfully" }
```

### Step 4 — Guest Login

Once a guest is registered, they log in using their email address and password. A successful login returns both a **Cognito access token** and a **refresh token**.

```
POST /api/v1/admin/login
Authorization: Bearer <admin_jwt_token>
Content-Type: application/json

{
  "username": "guest@example.com",
  "password": "SecurePass1"
}

// Response 200 OK
{
  "accessToken":  "<cognito_access_token>",
  "refreshToken": "<refresh_token>"
}
```

### Step 5 — Refresh an Expired Token

```
POST /api/v1/admin/token
Content-Type: application/json

{
  "refreshToken": "<refresh_token>"
}

// Response 200 OK
{ "accessToken": "<new_cognito_access_token>" }
```

> If the refresh token itself has also expired, the guest will need to log in again via `/api/v1/admin/login`.

### Step 6 — Reset a Guest Password

```
// Step 6a — Trigger reset email
PUT /api/v1/admin/{emailAddress}/reset
// Returns 204 No Content. Guest receives email with confirmation code.

// Step 6b — Submit new password
PUT /api/v1/admin/{emailAddress}/confirmReset
Content-Type: application/json

{
  "confirmationCode": "123456",
  "newPassword":      "NewSecurePass1"
}
// Returns 204 No Content.
```

### Authentication Flow Summary

```
1. Server  →  POST /api/v1/token               (Client Secret)
              ← 200: admin_access_token

2. Server  →  POST /api/v1/admin/signup         (admin_access_token + guest details + card)
              ← 201: Guest account created

3. Server  →  POST /api/v1/admin/login          (admin_access_token + email + password)
              ← 200: cognito_access_token + refresh_token

4. App     →  GET  /api/v1/card                 (cognito_access_token)
              ← 200: Guest card list

       [Token expires]

5. Server  →  POST /api/v1/admin/token          (refresh_token)
              ← 200: new cognito_access_token
```

### Error Reference

| HTTP Status | Likely Cause | Recommended Action |
|-------------|--------------|--------------------|
| 400 Bad Request | Malformed request body or missing required fields | Check field names, types, and that all required fields are present |
| 401 Unauthorized | Missing, expired, or invalid Bearer token | Obtain a fresh token via `/api/v1/token` (admin) or `/api/v1/admin/token` (guest refresh) |
| 403 Forbidden | Using the wrong token type for the endpoint | Ensure admin token is used for sign-up/login, Cognito token for guest operations |
| 404 Not Found | Guest account or card not found | Verify the email address or card details are correct |
| 409 Conflict | Username already exists | Use a different email address or check if the guest is already registered |
| 422 Unprocessable Entity | Password does not meet rules, or card is not activated | Check password rules; ensure the game card has been activated in-store |
| 500 Internal Server Error | Server-side error | Retry the request; if the issue persists, contact Embed support |
