> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anny.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Display Integration

Build a native room display integration that shows resource information, active and upcoming bookings, and allows on-premises booking creation via QR code or NFC.

**Capabilities:**

* Display room information and occupancy
* Show active and upcoming bookings
* Create bookings via QR code, NFC, or guest access
* Check-in and check-out bookings

***

## Pairing a New Display

**1. Register or fetch the display panel**

If no `displayPanel` UUID is stored on-device, register a new display:

```http theme={null}
POST /api/v1/display-panels
Authorization: Bearer {token}

{
  "data": {
    "type": "display-panels",
    "attributes": {
      "panel_type": "app"
    }
  }
}
```

Store the returned `uuid` and `secret` locally on-device. The response also includes `meta.pairing_code` and `meta.public_url`.

If a UUID is already stored, fetch the existing panel:

```http theme={null}
GET /api/v1/display-panels/{panel_uuid}?secret={secret}&include=organization
```

**2. Display pairing options**

If `displayPanel.organization` is `null`, the panel is not yet paired. Show the 6-digit `pairing_code` from the response metadata and optionally a QR code:

```http theme={null}
https://app.anny.co/displays/panels?code={pairing_code}
```

Users can also enter the code manually at `https://app.anny.co/displays/panels`.

**3. Subscribe to pairing updates**

Subscribe to WebSocket updates to detect when the display is paired:

| Setting | Value |
| - | - |
| WebSocket server | `wss://ws.anny.co` (prod) or `wss://ws.staging.anny.co` (staging) |
| Channel | `display-panels.{panel_uuid}` (public channel) |
| Events | `.display-panels.updated`, `.display-panels.deleted` |
| App key | `REQUEST` |
| Protocol | Pusher-compatible |

The event payload contains the JSON:API-encoded display panel. Alternatively, poll `GET /api/v1/display-panels/{uuid}` on app startup.

**4. Obtain a Bearer token**

After pairing, the display panel includes a `personal_access_token`. Use this as the Bearer token for authenticated endpoints. Fetch the panel config on each app startup as the token may rotate.

***

## Displaying the Grid

Display panels can show one or many resources simultaneously. Each resource is a **widget** in the `widgets` array of the display panel:

| Property | Type | Description |
| - | - | - |
| `id` | string | Widget ID |
| `resourceId` | string | Numeric resource ID |
| `orderIndex` | integer | Display order |
| `showOrganizer` | boolean | Show booking customer name |
| `allowGuestBooking` | boolean | Allow booking without authentication |
| `allowAdhocBooking` | boolean | Allow immediate booking without respecting booking intervals |

Build a grid from the widgets. Use `max_widgets_per_page` to set page size, and provide pagination or auto-rotation if more widgets exist.

`secondary_widgets` defines fallback resources shown when all primary resources are booked.

### Load Resource Data

```http theme={null}
GET /api/v1/resources?filter[ids]={ids}&include=category,cover_image,resource_properties.property,parent,services
Authorization: Bearer {token}
```

### Load Booking Previews

```http theme={null}
GET /api/v1/booking-previews?filter[resource_id]={id}&filter[date]={date}
Authorization: Bearer {token}
```

Apply widget settings to the display:

* Show/hide customer name based on `showOrganizer`
* Show/hide booking description based on `displayPanel.anonymize`; use a generic "Booked" label when anonymized

### Real-time Booking Updates

Join a private resource channel for each displayed resource using the panel's Bearer token:

| Setting | Value |
| - | - |
| WebSocket server | `wss://ws.anny.co` |
| Channel | `resources.{resourceId}` (private channel) |
| Auth URL | `https://b.anny.co/api/broadcasting/auth` |
| Events | `.bookings.updated`, `.bookings.completed` |
| App key | `REQUEST` |

On each WebSocket event, reload the booking previews for the resource.

***

## Creating Bookings

### Time Selection

**Quick selection:** Show up to 4 quick booking duration buttons.

1. Find the service with the smallest `min_duration`
2. Calculate options based on `min_duration`, `max_duration`, and booking `interval`

Example — min: 15, max: 60, interval: 15 → show **15, 30, 45, 60**

**Adhoc booking** (when `allowAdhocBooking` is enabled): Start immediately at the current minute without time selection. Pass `?adhoc=true` on the instant booking endpoint.

**Advanced selection:**

* `GET /api/v1/resources/{id}/start-times` — get available start times for a date
* `GET /api/v1/resources/{id}/end-times` — get available end times for a selected start (flexible duration services)

**Authenticated Bookings**

Pass the QR code or NFC payload as a `code` query parameter:

```http theme={null}
POST /api/v1/bookings/instant?code={scanned_code}&adhoc=true
Authorization: Bearer {token}

{
  "resource_id": "15",
  "service_id": "8",
  "start_date": "2025-10-16T09:00:00+02:00",
  "end_date": "2025-10-16T09:30:00+02:00"
}
```

**Guest Bookings**

When `allowGuestBooking` is enabled, omit the code. The booking is created as an anonymous "Ad-hoc booking".

***

## Managing Bookings from the Display

### Check-In

```http theme={null}
POST /api/v1/bookings/{booking_id}/check-in
Authorization: Bearer {token}
X-Supports-Dynamic-Form: true
```

If the booking still needs check-in-timed legal documents or custom fields, send `X-Supports-Dynamic-Form: true`. The response `meta.status` is `requires_details` with a `form_url`. Collect the details, then call check-in again with `form_data`. Without the header the API returns HTTP 400 `scans.check_in_requires_details_unsupported` instead. The same delayed-form flow is available on `POST /api/v1/scans` — see [X-Supports-Dynamic-Form](https://docs.anny.co/developers/guides/admin/booking-lifecycle.md#x-supports-dynamic-form) and [Scan](https://docs.anny.co/developers/models/scans.md).

### Check-Out

```http theme={null}
POST /api/v1/bookings/{booking_id}/check-out
Authorization: Bearer {token}
```

### Cancel

Bookings without an associated customer that are not blockers (`is_blocker=false`) can be cancelled directly from the display.

### Extend Duration

Bookings can be extended if `booking.charged_duration < service.max_duration`.

```http theme={null}
PATCH /api/v1/bookings/{booking_id}
Authorization: Bearer {token}

{
  "data": {
    "type": "bookings",
    "id": "{booking_id}",
    "attributes": {
      "end_date": "{new_end_datetime}"
    }
  }
}
```

***

## Map Display Mode

When configured for map mode, the display shows an interactive floor plan. See the [Map Client guide](https://docs.anny.co/developers/guides/internal/map-client.md) for the full implementation.

***

## Unpairing

When the admin deletes the panel via the dashboard:

* The app receives a `.display-panels.deleted` WebSocket event, **or**
* `GET /api/v1/display-panels/{uuid}` returns `404`

On either signal, clear the stored UUID and secret and return to the pairing screen.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.