Libro API
Welcome to the Libro API — your comprehensive toolkit for integrating restaurant reservation management into your applications.
This documentation will guide you through the process of integrating with our platform, from authentication to making your first API call and implementing advanced features.
Base URLs
| Environment | Base URL | Purpose |
|---|---|---|
| Production | https://api.libroreserve.com |
Live system integration |
| Staging | https://api.staging.libro.app |
Testing and development |
API Overview
The Libro API follows REST principles and provides JSON:API-compliant responses. Here's a quick overview of the main resources:
| Resource | Description |
|---|---|
| Restaurants | Restaurant profiles, availability, and settings |
| Bookings | Reservation creation and management |
| People | Guest information |
| Payment Intents | Payment intent initialization for no-show and ticketing |
| Reviews | Customer reviews and feedback |
| Webhooks | Real-time event notifications |
Introduction
Welcome to the Libro API — your comprehensive toolkit for integrating restaurant reservation management into your applications. This documentation provides detailed guidance on accessing our API endpoints for booking management, restaurant information, and customer data.
API Architecture
The Libro API uses RESTful principles with JSON:API-compliant responses. All data is exchanged in JSON format, and authentication is handled through OAuth 2.0 bearer tokens.
Key Features
- JSON:API Format: Standardized response structure for consistent parsing
- OAuth 2.0 Authentication: Secure access through bearer tokens
- Versioned Endpoints: Explicit versioning through Accept headers
- Comprehensive Error Handling: Detailed error messages and codes
Documentation Conventions
Throughout this documentation, you'll find examples in multiple programming languages. Select your preferred language using the tabs at the top of each code example.
The API uses resource-oriented URLs, standard HTTP response codes, and accepts request bodies in JSON format.
Getting API Access
To begin your integration:
- Request API Access: Contact us at admin@libroreserve.com to be added to our partner program
- Obtain OAuth Credentials: You'll receive client ID and secret for authentication
- Set Up Your Environment: Choose between production or staging based on your development phase
Here's what is required to get API access:
- Name of your app
- Scopes you need for your integration
- Redirect URIs
Authentication
The Libro API uses OAuth 2.0 for authentication, providing secure access to resources while maintaining fine-grained control over permissions.
Authentication Methods
OAuth 2.0 Bearer Tokens
Request:
curl https://api.libroreserve.com/oauth/token \
-X POST \
-d "grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET"
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 7200
}
The Libro API uses OAuth 2.0 bearer tokens for authentication. This method is secure and allows for token rotation and management.
The response will contain your access token:
Step 1: Implement the Authorization Request
Authorization URL
HTML Button example
<a href="https://accounts.libroreserve.com/oauth/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&response_type=code&scope=REQUESTED_SCOPES"
>Connect to Libro</a>
Create a “Connect” or “Link Account” button in your application that directs users to the Libro authorization URL. The URL should be structured as follows:
https://accounts.libroreserve.com/oauth/authorize?client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&response_type=code&scope=REQUESTED_SCOPES
Replace CLIENT_ID with your client ID, REDIRECT_URI with your encoded callback URL, and REQUESTED_SCOPES with the permissions you need (e.g., bookings). For multiple scopes, separate each with a space (i.e., bookings people reviews).
User Authentication and Authorization
When users click this button, they will be redirected to Libro’s system to log in (if not already logged in) and authorize your application to access their data based on the requested scopes.

Step 2: Handle the Callback
Receive the Authorization Code
After the user authorizes the connection, Libro’s system will redirect the user back to your application via the redirect_uri you provided during authentication, including an authorization_code as a URL parameter.
Example Callback URL:
https://app.example.io/oauth/callback?code=AUTHORIZATION_CODE
Step 3: Exchange the Authorization Code for an Access Token
Request:
curl https://accounts.libroreserve.com/oauth/token \
-X POST \
-d "grant_type=authorization_code&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
code=AUTHORIZATION_CODE&
redirect_uri=ENCODED_REDIRECT_URI"
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IKSJFGDKJF...",
"token_type": "Bearer",
"expires_in": 7200
}
Libro’s system will respond with an access and refresh token that your application can use to make authenticated requests to Libro’s API on behalf of the user.
The expires_in value returned in the token response is expressed in seconds, as per the OAuth 2.0 specification.
Step 4: Make Authenticated Requests
Request:
curl https://accounts.libroreserve.com/restricted/restaurant/bookings/63873433 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS_TOKEN"
Response:
{
"data": {
"id": "63873433",
"type": "bookings",
"attributes": {
"status": "confirmed",
"date": "2023-01-01T12:00:00Z",
"party_size": 4
...
}
}
}
Your application can now access Libro’s API by including the access token in the Authorization header of your requests
Step 5: Handle Token Expiration
Request:
curl https://accounts.libroreserve.com/oauth/token \
-X POST \
-d "grant_type=refresh_token&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
refresh_token=REFRESH_TOKEN"
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 7200
}
Access tokens expire after a period (typically 2 hours). Your application should handle token expiration and renewal:
- Store the
expires_invalue when receiving a token - Implement a token refresh mechanism before expiration
- If you receive a
401 Unauthorizederror, request a new token
Token Endpoint
Make a POST request to Libro’s token endpoint with the refresh token to obtain a new access token.
Error Handling
Ensure your application gracefully handles errors such as invalid or expired refresh tokens by prompting the user to reauthorize if necessary.
By adding these details, the guide provides a comprehensive approach to handling token refresh, ensuring continuous and secure access to Libro’s API.
Restaurants
The Restaurants API allows you to access restaurant profiles, manage availability, and retrieve detailed information about dining establishments in the Libro network.
ENDPOINTS
GET /restricted/restaurants
GET /restricted/restaurants/:id
GET /restricted/restaurants/:id/seatings
The Restaurant Object
THE RESTAURANT OBJECT
{
"type": "restaurant",
"id": "0000000000",
"attributes": {
"name": "Example Restaurant",
"active": true,
"code": "ZZ000000000000",
"latitude": 45.5001,
"longitude": -73.5665,
"tz": "America/Montreal",
"created-at": "2024-01-01T12:00:00.0-04:00",
"online-booking-lead-months": 6,
"cancelation-delay": 86400,
"service-scopes": [
{
"name": "Diner",
"startTime": "10:00",
"endTime": "15:00"
},
{
"name": "Souper",
"startTime": "15:00",
"endTime": "03:00"
}
],
"classifications": [
{
"id": "1",
"names": {
"en": "Adult",
"fr": "Adulte"
}
},
{
"id": "2",
"names": {
"en": "Child",
"fr": "Enfant"
}
}
]
}
}
Attributes
Get Restaurant
This endpoint retrieves a specific restaurant by its code.
Query Parameters
GET /restricted/restaurants?restaurant-code=QC01621448XXXX
REQUEST:
curl "https://api.libroreserve.com/restricted/restaurants?restaurant-code=QC016214487921" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.libro-restricted-v2+json" \
-H "Authorization: Bearer ACCESS_TOKEN"
RESPONSE:
{
"data": [
{
"id": "0000000000",
"type": "restaurant",
"attributes": { /* restaurant attributes */ }
}
]
}
List Restaurants
GET /restricted/restaurants
REQUEST:
curl "https://api.libroreserve.com/restricted/restaurants" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.libro-restricted-v2+json" \
-H "Authorization: Bearer ACCESS_TOKEN"
RESPONSE:
{
"data": [
{
"id": "0000000000",
"type": "restaurant",
"attributes": { /* restaurant attributes */ }
},
{
"id": "0000000001",
"type": "restaurant",
"attributes": { /* restaurant attributes */ }
}
]
}
This endpoint retrieves all restaurants available to your connected account. You can filter results by name, geographic location, or features.
Query Parameters
Examples
- Search by name:
GET https://api.libroreserve.com/restricted/restaurants?query=Chez Marcel
- Search by location (nearby restaurants sorted by distance):
GET https://api.libroreserve.com/restricted/restaurants?latitude=45.5017&longitude=-73.5673&radius=10&unit=km
- Filter by features:
GET https://api.libroreserve.com/restricted/restaurants?features=bookings,orders
- Sort by name:
GET https://api.libroreserve.com/restricted/restaurants?sort=name:asc
See Pagination for more details.
Get Restaurant Availability
GET /restricted/restaurant/seatings
REQUEST:
curl "https://api.libroreserve.com/restricted/restaurant/seatings?restaurant-code=QC01621448XXXX&date=2026-07-09&size=2" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.libro-restricted-v2+json" \
-H "Authorization: Bearer ACCESS_TOKEN"
RESPONSE:
# Response with experiences and no-show authorization and offer selection are not required.
{
"2025-09-11": {
"2025-09-11T16:00:00": [
{
"id": 4253,
"name": {
"en": "Dining Room",
"fr": "Salle à Diner"
},
"payment-required": false
}
],
"2025-09-11T16:15:00": [
{
"id": 4253,
"name": {
"en": "Dining Room",
"fr": "Salle à Diner"
},
"payment-required": false
}
]
}
}
# Response with experiences and no-show authorization is required.
{
"2025-09-11": {
"2025-09-11T21:00:00": [
{
"id": 4253,
"name": {
"en": "Dining Room",
"fr": "Salle à Diner"
},
"payment-required": {
"payment-type": "NO_SHOW",
"amount-cents": 25000,
"payment-delay": 7200
}
}
],
"2025-09-11T21:15:00": [
{
"id": 4253,
"name": {
"en": "Dining Room",
"fr": "Salle à Diner"
},
"payment-required": {
"payment-type": "NO_SHOW",
"amount-cents": 25000,
"payment-delay": 7200
}
}
]
}
}
# Response with experiences and an eligible offer is available.
{
"2025-09-11": {
"2025-09-11T20:00:00": [
{
"id": 4253,
"name": {
"en": "Dining Room",
"fr": "Salle à Diner"
},
"payment-required": {
"payment-type": "TICKETING",
"amount-cents": 1500,
"offer": {
"id": 42,
"name": "Sunday Night Football",
"number": "PR123ABC",
"description": "Come watch the game!"
}
}
}
],
"2025-09-11T20:15:00": [
{
"id": 4253,
"name": {
"en": "Dining Room",
"fr": "Salle à Diner"
},
"payment-required": {
"payment-type": "TICKETING",
"amount-cents": 1500,
"offer": {
"id": 42,
"name": "Sunday Night Football",
"number": "PR123ABC",
"description": "Come watch the game!"
}
}
}
]
}
}
# Response without experiences:
{
"2025-09-11": {
"2025-09-11T11:00:00": [
{
"payment-required": false
}
],
"2025-09-11T11:15:00": [
{
"payment-required": false
}
],
"2025-09-11T11:30:00": [
{
"payment-required": false
}
]
}
}
This endpoint retrieves restaurant availabilities.
Query Parameters
Payment Requirements
The payment-required attribute indicates whether payment is required for a time slot. When payment is required,
it includes additional details based on the type of payment.
Priority Logic
When both offers and no-show policies exist for a time slot, the following priority order applies:
- Non-optional offers (with available tables) → Returns
TICKETINGpayment type (overrides no-show policies) - Optional offers + no-show policy → Returns
NO_SHOWpayment type (policy takes precedence) - Optional offers + no policy → Returns
TICKETINGpayment type - No offer + no-show policy → Returns
NO_SHOWpayment type - No offer + no policy → Returns
false
No-Show Policies
The following attributes are available if the restaurant has configured a no-show policy for the selected time slot.
Once a booking is submitted, the guest will receive an email or SMS to confirm the reservation with a credit card.
The deposit grace period defines the amount of time the guest has to enter their credit card information to finalize the reservation. If the card details are not provided within this time frame, the pending reservation will be automatically canceled and the table will be released.
| Name | Type | Description |
|---|---|---|
| payment-type | string | Defaults to "NO_SHOW" |
| amount-cents | integer | Payment amount in cents that will be charged for a no-show |
| payment-delay | integer | Deposit Request Grace Period in seconds |
Offers
The following attributes are available if the restaurant has an eligible published offer for the selected time slot.
An offer is only included if it matches the service time, day of week, experience, and the required tables are available.
| Name | Type | Description |
|---|---|---|
| payment-type | string | Defaults to "TICKETING" when an offer is available |
| amount-cents | integer | Ticket price in cents for the offer |
| offer | object | Offer details (see below) |
Offer Object Attributes
| Name | Type | Description |
|---|---|---|
| id | integer | Unique identifier for the offer |
| name | string | Display name of the offer |
| number | string | Offer reference number |
| description | string | Description of the offer |
Bookings
ENDPOINTS
GET /restricted/restaurant/bookings/:id
GET /restricted/restaurant/bookings
POST /restricted/restaurant/bookings
PATCH /restricted/restaurant/bookings/:id
PUT /restricted/restaurant/bookings/:id
DELETE /restricted/restaurant/bookings/:id
The Bookings API allows you to create, retrieve, and manage reservations.
The Booking Object
THE BOOKING OBJECT
{
"type": "booking",
"id": "0000000000",
"attributes": {
"size": 2,
"status": "approved",
"time": "2025-07-09T17:00:00.000-04:00",
"note": "We would like to have a table near the window",
"table-number": null,
"private-note": null,
"expected-leave-at": "2025-07-09T18:30:00.000-04:00",
"source": "google",
"read-token": "17ca6f639",
"edit-token": "17ca6f604",
"booking-type": "reservation",
"locale": "en",
"created-at": "2025-07-08T22:54:26.708-04:00",
"arrived-at": null,
"confirmed-at": null,
"seated-at": null,
"completed-at": null,
"canceled-at": null,
"tags": ["vegan", "children"],
"flags": {
"children": false,
"reduced-mobility": false,
"do-not-move": false
},
"invoice": null,
"affiliate": null,
"cancelation-allowed-until": null,
"modification-restricted": {
"restricted": false,
"reason": null
}
}
}
Booking Attributes
Booking Tags
Booking Flags
Flags are boolean properties that are distinct from tags.
The following flags are available:
- children: indicates whether guests have signaled that their reservation includes children
- reduced-mobility: indicates if the reservation requires accessibility accommodations
- do-not-move: indicates that a Libro user has marked this reservation as immovable from its assigned table
Modification Restricted
The modification-restricted attribute indicates whether a booking can be modified via partner APIs or if it requires manual intervention by restaurant staff through the Libro dashboard.
{
"modification-restricted": {
"restricted": true,
"reason": "validation.service.party-size-exceeds-maximum"
}
}
restricted(boolean): Whentrue, the booking cannot be modified via the API and requires restaurant staff to make changes manually.reason(string|null): When restricted, contains an error code explaining why modification is not allowed. Common reasons include:validation.service.party-size-exceeds-maximum: The party size exceeds the maximum allowed for online bookingsvalidation.service.party-size-below-minimum: The party size is below the minimum requiredvalidation.service.date-out-of-range: The booking date is beyond the allowed booking window
Booking Relationships
Booking object will be returned with the following relationships:
Experience
The experience relationship refers to a dining section or area within the
restaurant (e.g., Dining Room, Bar, Patio). Experiences are side-loaded in
API responses as follows:
{
"id": "1234",
"type": "experience",
"attributes": {
"name": "Dining Room"
}
}
Get Booking
GET /restricted/restaurant/bookings/:id
REQUEST:
curl "https://api.libroreserve.com/restricted/restaurant/bookings/63873441" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.libro-restricted-v2+json" \
-H "Authorization: Bearer ACCESS_TOKEN"
RESPONSE:
{
"data": {
"id": "63873433",
"type": "booking",
"attributes": {
"size": 2,
"status": "seated",
"time": "2025-04-30T10:45:00.000-04:00",
"note": "",
"table-number": null,
"note": "Birthday party",
"private-note": "",
"expected-leave-at": "2025-04-30T12:15:00.000-04:00",
"source": "dashboard",
"read-token": "17cb72002",
"edit-token": "17cb72018",
"booking-type": "reservation",
"locale": null,
"created-at": "2025-04-30T10:51:01.730-04:00",
"arrived-at": "2025-04-30T10:51:01.729-04:00",
"confirmed-at": null,
"seated-at": null,
"completed-at": null,
"canceled-at": "2025-05-06T17:32:19.190-04:00"
},
"relationships": {
"person": {
"data": {
"id": "XXXXXXXXX",
"type": "person"
}
},
"restaurant": {
"data": {
"id": "XXXX",
"type": "restaurant"
}
},
"recommendation": {
"data": {
"id": "XXXXXXXXX",
"type": "recommendation"
}
},
"experience": {
"data": {
"id": "XXXXXXXXX",
"type": "experience"
}
}
}
},
"included": []
}
This endpoint retrieves a specific booking by its ID.
Path Parameters
List Bookings
GET /restricted/restaurant/bookings
REQUEST:
curl "https://api.libroreserve.com/restricted/restaurant/bookings" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.libro-restricted-v2+json" \
-H "Authorization: Bearer ACCESS_TOKEN"
RESPONSE:
{
"data": [
{
"id": "0000000000",
"type": "booking",
"attributes": { /* booking attributes */ },
"relationships": { /* booking relationships */ }
},
{
"id": "0000000001",
"type": "booking",
"attributes": { /* booking attributes */ },
"relationships": { /* booking relationships */ }
}
]
}
This endpoint retrieves a list of bookings for the restaurant.
Query Parameters
Examples
- Search by date:
GET https://api.libroreserve.com/restricted/restaurant/bookings?date=2025-05-30
- Search by date and time:
GET https://api.libroreserve.com/restricted/restaurant/bookings?date=2025-05-30&time=13:00
- Search by guest phone number:
GET https://api.libroreserve.com/restricted/restaurant/bookings?guest-phone=15141111111
See Pagination for more details.
Create Booking
POST /restricted/restaurant/bookings
REQUEST:
curl "https://api.libroreserve.com/restricted/restaurant/bookings" \
-X POST \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.libro-restricted-v2+json" \
-H "Authorization: Bearer ACCESS_TOKEN"
REQUEST BODY WITH PERSON DATA:
{
"restaurant-code": "0000000000",
"data": {
"attributes": {
"size": 2,
"date": "2025-05-07",
"time": "12:00",
"note": "Birthday party",
"locale": "en",
"table-number": "12",
"classifications": [{"1": 1}, {"2": 1}]
},
"relationships": {
"person": {
"data": {
"type": "people",
"attributes": {
"first_name": "John",
"last_name": "Doe",
"email": "john@doe.com",
"phone": "5141111111",
"preferred-communication-channel": "email",
"locale": "en"
}
}
}
}
},
"device-infos": {
"id": "XXXXXXXXXXX",
"ip": "192.168.1.1"
},
"payment-intent-id": 10
}
REQUEST BODY WITH PERSON ID:
{
"restaurant-code": "0000000000",
"data": {
"attributes": {
"size": 2,
"date": "2025-05-07",
"time": "12:00",
"note": "Birthday party",
"locale": "en",
"table-number": "12",
"classifications": [{"1": 1}, {"2": 1}]
},
"relationships": {
"person": {
"data": {
"type": "people",
"id": 1234567890
}
}
}
},
"device-infos": {
"id": "XXXXXXXXXXX",
"ip": "192.168.1.1"
},
"payment-intent-id": 10
}
RESPONSE:
{
"data": {
"id": "0000000000",
"type": "booking",
"attributes": {
"size": 2,
"status": "approved",
"time": "2025-05-07T12:00:00.000-04:00",
"table-number": "3",
"note": "Birthday party",
"private-note": null,
"expected-leave-at": "2025-05-07T13:30:00.000-04:00",
"source": "widget",
"read-token": "17cb72339",
"edit-token": "17cb722f3",
"booking-type": "reservation",
"locale": "en",
"created-at": "2025-05-06T17:13:14.250-04:00",
"arrived-at": null,
"confirmed-at": null,
"seated-at": null,
"completed-at": null,
"canceled-at": null
},
"relationships": { /* booking relationships */ }
},
"included": []
}
This endpoint allows you to create a new booking.
Body Parameters
Body Attributes
Relationship Person
Person Attributes
Device Infos
Payment Intent ID
Update Booking
PATCH /restricted/restaurant/bookings/:id
REQUEST:
curl "https://api.libroreserve.com/restricted/restaurant/bookings/63873441" \
-X PATCH \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.libro-restricted-v2+json" \
-H "Authorization: Bearer ACCESS_TOKEN"
REQUEST BODY:
{
"restaurant-code": "0000000000",
"data": {
"attributes": {
"note": "We would like to have a table near the window",
"locale": "en"
}
}
}
RESPONSE:
{
"data": {
"id": "63873441",
"type": "booking",
"attributes": { /* booking attributes */ },
"relationships": { /* booking relationships */ }
},
"included": []
}
This endpoint allows you to update some attributes of a booking.
Parameters
Body Attributes
Cancel Booking
DELETE /restricted/restaurant/bookings/:id
REQUEST:
curl "https://api.libroreserve.com/restricted/restaurant/bookings/63873441" \
-X DELETE \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.libro-restricted-v2+json" \
-H "Authorization: Bearer ACCESS_TOKEN"
RESPONSE:
{
"data": {
"id": "63873441",
"type": "booking",
"attributes": { /* booking attributes */ },
"relationships": { /* booking relationships */ }
},
"included": []
}
This endpoint allows you to cancel a booking.
Query Parameters
Example
DELETE /restricted/restaurant/bookings/63873441?restaurant-code=0000000000
Errors
Reschedule Booking
PUT /restricted/restaurant/bookings/:id/reschedule
REQUEST:
curl "https://api.libroreserve.com/restricted/restaurant/bookings/63873441/reschedule" \
-X PUT \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.libro-restricted-v2+json" \
-H "Authorization: Bearer ACCESS_TOKEN"
REQUEST BODY:
{
"restaurant-code": "0000000000",
"data": {
"attributes": {
"date": "2025-05-07",
"time": "17:00",
"booking-experience-id": 123,
"size": 2
}
}
}
RESPONSE:
{
"data": {
"id": "63873441",
"type": "booking",
"attributes": { /* booking attributes */ },
"relationships": { /* booking relationships */ }
},
"included": []
}
This endpoint allows you to reschedule a booking. It will be used to increase/descrease booking size, change date and time or change booking experience.
Body Parameters
Body Attributes
Examples
- Increase booking size:
PUT https://api.libroreserve.com/restricted/restaurant/bookings/63873441/reschedule?restaurant-code=0000000000&date=2025-05-07&time=17:00&size=4
- Change booking date and time:
PUT https://api.libroreserve.com/restricted/restaurant/bookings/63873441/reschedule?restaurant-code=0000000000&date=2025-05-07&time=17:00
Person
ENDPOINTS
GET /restricted/people/:id
PATCH /restricted/people/:id
The People API allows you to retrieve and update people data.
The Person Object
THE PERSON OBJECT
{
"id": "18476003",
"type": "person",
"attributes": {
"first-name": "Johnny",
"last-name": "Doe",
"email": "john@gmail.com",
"phone": "225-951-4433",
"note": "Lorem Ipsum",
"tags": ["vvip", "vegan"]
}
}
Person
Person Attributes
Person Tags
List People
GET /restricted/people
REQUEST:
curl "https://api.libroreserve.com/restricted/people?restaurant-code=QC0000000001" \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.libro-restricted-v2+json" \
-H "Authorization: Bearer ACCESS_TOKEN"
RESPONSE:
{
"data": [
{
"id": "18476004",
"type": "person",
"attributes": {
"first-name": "Jane",
"last-name": "Smith",
"email": "jane@gmail.com",
"phone": "225-955-7788",
"note": "Prefers window seating",
"tags": ["regular"]
}
},
{
"id": "18476003",
"type": "person",
"attributes": {
"first-name": "Johnny",
"last-name": "Doe",
"email": "john@gmail.com",
"phone": "225-951-4433",
"note": "Lorem Ipsum",
"tags": ["vvip", "vegan"]
}
}
],
"meta": {
"page": {
"limit": 25,
"offset": 0,
"total": 2
}
}
}
This endpoint retrieves a list of people associated with a restaurant. Results are sorted by ID in descending order (newest first).
Query Parameters
Examples
- Search by phone number:
GET https://api.libroreserve.com/restricted/people?restaurant-code=QC0000000001&guest-phone=%2B15141111111
- Paginate results:
GET https://api.libroreserve.com/restricted/people?restaurant-code=QC0000000001&limit=10&offset=20
See Pagination for more details.
Get Person
GET /restricted/people/:id
REQUEST:
curl "https://api.libroreserve.com/restricted/people/18476003" \
-X GET \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.libro-restricted-v2+json" \
-H "Authorization: Bearer ACCESS_TOKEN"
RESPONSE:
{
"data": {
"id": "18476003",
"type": "person",
"attributes": {
"first-name": "Johnny",
"last-name": "Doe",
"email": "john@gmail.com",
"phone": "225-951-4433",
"note": "Lorem Ipsum",
"tags": ["vvip", "vegan"]
}
}
}
This endpoint allows you to retrieve a person's details.
Path Parameters
Update Person
PATCH /restricted/people/:id
REQUEST:
curl "https://api.libroreserve.com/restricted/people/18476003" \
-X PATCH \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.libro-restricted-v2+json" \
-H "Authorization: Bearer ACCESS_TOKEN"
REQUEST BODY:
{
"data": {
"attributes": {
"first-name": "Johnny",
"last-name": "Doe",
"email": "john@gmail.com",
"phone": "225-951-4433",
"note": "Lorem Ipsum",
"tags": ["vvip", "vegan"]
}
}
}
RESPONSE:
{
"data": {
"id": "18476003",
"type": "person",
"attributes": {
"first-name": "Johnny",
"last-name": "Doe",
"email": "john@gmail.com",
"phone": "225-951-4433",
"note": "Lorem Ipsum",
"tags": ["vvip", "vegan"]
}
}
}
This endpoint allows you to update a person's details.
Path Parameters
Body Parameters
Payment Collection Flows
The payment collection integration supports both synchronous and asynchronous flows to accommodate different integration scenarios. This section explains the differences between these flows, when to use each, and how they work.
Overview
Libro's payment system uses a hybrid model that supports two types of payment collection:
- Synchronous Flow: Payment is initiated and completed during the reservation process (disruptive to the user flow)
- Asynchronous Flow: Reservation is created and payment is completed later via a link sent to the guest (non-blocking)
The choice between synchronous and asynchronous flows depends on your integration context and user experience requirements.
Payment Types
| Payment Type | Purpose | Sync Flow | Async Flow |
|---|---|---|---|
| NO_SHOW | Authorize credit card to protect against no-shows | ✓ Implemented | ✓ Implemented |
| TICKETING | Capture payment for events/experience tickets | ✓ Implemented | 🚧 Work in Progress |
Synchronous Flow
In the synchronous flow, payment authorization or capture happens during the reservation process. The user must complete the payment step before the booking can be finalized.
When to Use Synchronous Flow
- Immediate payment confirmation required before finalizing reservation
- Provides immediate feedback and confirmation to user
- When you need instant confirmation and payment processing
PaymentIntent Usage in Sync Flow
The synchronous flow uses a PaymentIntent for both NO_SHOW and TICKETING payment types:
- Sync NO_SHOW: Uses PaymentIntent ✓
- Sync TICKETING: Uses PaymentIntent ✓
How Synchronous Flow Works
Step 1: Check Availability
Call GET /restricted/restaurant/seatings to check availability for the desired date and time:
curl "https://api.libroreserve.com/restricted/restaurant/seatings?restaurant-code=QC01621448XXXX&date=2026-07-09&size=2" \
-H "Authorization: Bearer ACCESS_TOKEN"
The response indicates if payment is required:
{
"2025-09-11": {
"2025-09-11T20:00:00": [
{
"id": 4253,
"name": {
"en": "Dining Room",
"fr": "Salle à Diner"
},
"payment-required": {
"payment-type": "NO_SHOW",
"amount-cents": 2500,
"payment-delay": 7200
}
}
]
}
}
If payment-required is false, proceed directly to booking creation without payment. If payment-required contains NO_SHOW or TICKETING, continue to Step 2.
Step 2: Initialize Payment Intent
Call POST /restricted/payment-intents/initialize with reservation and guest details:
{
"restaurant-code": "QC01621448XXXX",
"data": {
"attributes": {
"amount_cents": 2500,
"currency": "CAD",
"payment_type": "no_show",
"classifications": [
{"1": 2},
{"3": 2}
],
"guest_info": {
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"phone": "+1234567890",
"party_size": 4,
"time": "2025-01-15T19:30:00Z",
"locale": "en"
}
}
}
}
The API returns a PaymentIntent with a payment-url:
{
"data": {
"id": "0000000000",
"type": "payment-intent",
"attributes": {
"amount-cents": 2500,
"currency": "CAD",
"status": "pending",
"payment-type": "no_show",
"payment-url": "https://pay.libro.app/..."
}
}
}
Step 3: Display Payment Portal in iFrame or with Redirection
The portal displays:
For NO_SHOW: Credit card authorization form
For TICKETING: Offer selection and payment capture form
Option 1: Iframe Integration
You can embed the payment URL in an iframe on your site:
<iframe src="PAYMENT_URL" width="100%" height="600px"></iframe>
When the user completes the payment authorization, we will send a postMessage to the parent window that you can listen for:
window.addEventListener('message', (event) => {
if (event.data.status === 'payment_success') {
const paymentIntentId = event.data.paymentIntentId;
// Close the iframe and continue with booking creation
}
});
The payment success message structure is:
{
status: 'payment_success',
paymentIntentId: 'INTENT_ID'
}
Height Updates:
The payment page also sends automatic height updates to help you dynamically resize the iframe based on the content. You can listen for these updates to provide a better user experience:
window.addEventListener('message', (event) => {
if (event.data.event === 'payment-page-height-update') {
const height = event.data.data.height;
// Update iframe height dynamically
document.getElementById('payment-iframe').style.height = `${height}px`;
}
});
The height update message structure is:
{
event: 'payment-page-height-update',
data: {
height: NUMBER_IN_PIXELS
}
}
Height updates are sent: - Initially after the page loads (with a 500ms delay) - Whenever the content height changes by more than 10px - Updates are debounced by 150ms to avoid excessive messages
Option 2: Redirect Integration
You can redirect the user directly to the payment URL. To specify where the user should be redirected after completing the payment authorization, include a redirect_url query parameter:
https://payment-url.example.com?redirect_url=https://yoursite.com/booking/complete
After the user completes payment authorization, he will be redirected to the URL you provided.
Step 4: Create Booking with PaymentIntent
Once the user completes payment (via either iframe or redirect), create the booking using the PaymentIntent ID.
Call the booking creation endpoint with the PaymentIntent ID:
{
"restaurant-code": "QC01621448XXXX",
"data": {
"attributes": {
"time": "2025-01-15T19:30:00Z",
"party-size": 4,
"payment-intent-id": "0000000000"
// ... other booking attributes
}
}
}
Step 5: Booking Approved
- For NO_SHOW: Booking is approved with no show authorization confirmed
- For TICKETING: Payment is captured, ticket is emitted, and booking is approved
Asynchronous Flow
In the asynchronous flow, the reservation is created, and payment is completed later as a follow-up action. The guest receives a link via SMS or email to complete the payment. If is not completed in time, the link expires and the reservation is automatically canceled.
When to Use Asynchronous Flow
- Create reservations without disrupting guest experience
- Guest completes payment at their convenience via link
How Asynchronous Flow Works
Step 1: Create Booking
Call the booking creation endpoint with the reservation details.
{
"restaurant-code": "QC01621448XXXX",
"data": {
"attributes": {
"time": "2025-01-15T19:30:00Z",
"party-size": 4
// ... other booking attributes
}
}
}
Step 2: Guest Receives Payment Link
The guest automatically receives a link via SMS or email:
- For NO_SHOW: Link to complete no show authorization
- For TICKETING: Link to select an offer and complete payment (Work In Progress)
Step 3: Guest Completes Payment
The guest clicks the link and completes the payment action:
- For NO_SHOW: Enters credit card information for authorization
- For TICKETING: Selects an offer and completes payment capture
Step 4: Booking Approved
Once the payment is complete:
- For NO_SHOW: No-show protection is activated, booking is approved
- For TICKETING: Payment is captured, ticket is emitted, booking is approved
Step 5: Link Expiration
The payment link expires after the grace period specified in payment-delay (from the availabilities endpoint):
- If the guest completes payment within the grace period → Booking is approved
- If the link expires without payment → Booking is automatically cancelled
Flow Comparison
Synchronous Flow
Partner → Check Availability → Initialize PaymentIntent → Display Portal → Guest Pays → Create Booking → Approved
(seatings endpoint) (with reservation info) (iframe/redirect) (with intent ID)
Key Characteristics:
Payment authorization happens before booking creation
Requires PaymentIntent object (for both NO_SHOW and TICKETING)
User must complete payment to proceed (via iframe or redirect)
Immediate confirmation
Asynchronous Flow
Partner → Create Booking → Booking Pending → Send Link → Guest Pays → Booking Approved
(payment pending) (SMS/Email)
Key Characteristics:
Booking created first
Guest completes payment at their convenience
Link has expiration time
Booking can be automatically cancelled if payment is not completed in time
Next Steps
- Review the PaymentIntent Object structure
- Learn how to Initialize a PaymentIntent for synchronous flows
- Explore the Bookings API for creating reservations
- Check the Restaurant Availabilities endpoint for payment requirements
Payment Intents
ENDPOINTS
POST /restricted/payment-intents/initialize
The Payment Intents API allows you to initialize payment intents for no-show policies and ticketing.
The Payment Intent Object
THE PAYMENT INTENT OBJECT
{
"type": "payment-intent",
"id": "0000000000",
"attributes": {
"amount-cents": 2500,
"currency": "USD",
"payment-type": "no_show",
"status": "pending",
"guest-info": {
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"phone": "+1234567890",
"party_size": 4,
"time": "2025-01-15T19:30:00Z",
"locale": "en"
},
"created-at": "2025-01-10T12:00:00.000Z"
}
}
Payment Intent Attributes
| Attribute | Type | Description |
|---|---|---|
amount-cents |
integer | The amount in cents |
currency |
string | The currency code (ISO 4217) |
payment-type |
string | Type of payment: no_show or ticketing |
status |
string | Status of the payment intent |
guest-info |
object | Guest information associated with the payment |
created-at |
datetime | Timestamp when the payment intent was created |
Initialize Payment Intent
POST /restricted/payment-intents/initialize
REQUEST:
curl "https://api.libroreserve.com/restricted/payment-intents/initialize" \
-X POST \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.libro-restricted-v2+json" \
-H "Authorization: Bearer ACCESS_TOKEN"
REQUEST BODY:
{
"restaurant-code": "0000000000",
"data": {
"attributes": {
"amount_cents": 2500,
"currency": "CAD",
"payment_type": "no_show",
"classifications": [
{"1": 2},
{"3": 2}
],
"guest_info": {
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"phone": "+1234567890",
"party_size": 4,
"time": "2025-01-15T19:30:00Z",
"locale": "en"
}
}
}
}
RESPONSE:
{
"data": {
"id": "0000000000",
"type": "payment-intent",
"attributes": {
"amount-cents": 2500,
"currency": "CAD",
"status": "pending",
"payment-type": "no_show",
"guest-info": {
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"phone": "+1234567890",
"party_size": 4,
"time": "2025-01-15T19:30:00Z",
"locale": "en"
},
"classifications": [
{"1": 2},
{"3": 2}
],
"created-at": "2025-09-30T11:00:36-04:00",
"updated-at": "2025-09-30T11:00:36-04:00",
"payment-url": { /* payment url */ }
},
"relationships": {{ /* payment intents relationships */ }}
}
}
This endpoint allows you to initialize a payment intent for no-show policies or ticketing.
Body Parameters
Body Attributes
| Parameter | Type | Required | Description |
|---|---|---|---|
amount_cents |
integer | Required for no_show |
The amount in cents (must be greater than 0). Not required for ticketing |
currency |
string | Required for no_show |
The currency code (ISO 4217 format, e.g., "USD", "EUR"). Not required for ticketing |
payment_type |
string | No | Type of payment: no_show or ticketing (default: no_show) |
classifications |
array | Conditional | Array of guest classifications as hashes with classification_id as key and quantity as value (e.g., [{"1": 2}, {"3": 2}]). Required if restaurant has classifications enabled. Sum of quantities must equal party_size |
guest_info |
object | Yes | Guest information object (see below) |
Guest Info Attributes
| Parameter | Type | Required | Description |
|---|---|---|---|
first_name |
string | Yes | Guest's first name |
last_name |
string | Yes | Guest's last name |
email |
string | Yes | Guest's email address |
phone |
string | Yes | Guest's phone number |
party_size |
integer | Yes | Number of guests (must be greater than 0) |
time |
string | Yes | Booking datetime in ISO8601 format |
locale |
string | Yes | Guest's locale (e.g., "en", "fr") |
HTTP Status Codes
| Code | Description |
|---|---|
| 201 | Payment intent created successfully |
| 400 | Bad request: Classifications validation failed |
| 401 | Unauthorized: Missing or invalid token |
| 403 | Forbidden: Missing required scopes |
| 422 | Unprocessable entity: Validation errors |
Classifications Validation
When a restaurant has classifications enabled (e.g., for ticketing with different guest types like adults, children, seniors), the following validation rules apply:
Classifications are required: If the restaurant has classifications enabled and configured, the
classificationsparameter must be provided in the request. Otherwise, you will receive a400error with the message:"Classifications are required for this restaurant"Sum must equal party size: The sum of all classification quantities must exactly match the
party_sizein the guest info. For example:- If
party_sizeis 4 - And you send
[{"1": 2}, {"3": 2}](classification ID 1 with quantity 2, classification ID 3 with quantity 2) - The sum is 2 + 2 = 4, which matches the party size ✓
- If
If the sum does not match, you will receive a 400 error with the message: "Total classification count (X) must equal party size (Y)"
Example of valid classifications:
json
{
"classifications": [
{"1": 2},
{"3": 1},
{"5": 1}
],
"guest_info": {
"party_size": 4,
...
}
}
In this example, classification IDs 1, 3, and 5 have quantities of 2, 1, and 1 respectively, totaling 4, which matches the party size.
Using the Payment URL
The payment_url returned in the response is the URL you should send your users to so they can complete the payment authorization. You have two integration options:
Option 1: Iframe Integration
You can embed the payment URL in an iframe on your site:
<iframe src="PAYMENT_URL" width="100%" height="600px"></iframe>
When the user completes the payment authorization, we will send a postMessage to the parent window that you can listen for:
window.addEventListener('message', (event) => {
if (event.data.status === 'payment_success') {
const paymentIntentId = event.data.paymentIntentId;
// Close the iframe and continue with booking creation
}
});
The payment success message structure is:
{
status: 'payment_success',
paymentIntentId: 'INTENT_ID'
}
Height Updates:
The payment page also sends automatic height updates to help you dynamically resize the iframe based on the content. You can listen for these updates to provide a better user experience:
window.addEventListener('message', (event) => {
if (event.data.event === 'payment-page-height-update') {
const height = event.data.data.height;
// Update iframe height dynamically
document.getElementById('payment-iframe').style.height = `${height}px`;
}
});
The height update message structure is:
{
event: 'payment-page-height-update',
data: {
height: NUMBER_IN_PIXELS
}
}
Height updates are sent: - Initially after the page loads (with a 500ms delay)
Whenever the content height changes by more than 10px
Updates are debounced by 150ms to avoid excessive messages
Option 2: Redirect Integration
You can redirect the user directly to the payment URL. To specify where the user should be redirected after completing the payment authorization, include a redirect_url query parameter:
https://payment-url.example.com?redirect_url=https://yoursite.com/booking/complete
After the user completes payment authorization, he will be redirected to the URL you provided.
Reviews
ENDPOINTS
GET /restricted/restaurant/reviews
The Reviews API allows you to retrieve reviews.
The Review Object
THE REVIEW OBJECT
{
"id": "1234567890",
"type": "review",
"attributes": {
"rating": 5.0,
"ambiance-rating": 4.0,
"food-rating": 5.0,
"service-rating": 5.0,
"menu-rating": null,
"presentation-rating": null,
"custom-question-1": null,
"custom-question-2": null,
"description": "Perfect!",
"reply": "Glad you enjoyed your meal!",
"source": "libro",
"created-at": "2024-07-08T10:40:31.660-04:00",
"updated-at": "2024-07-12T11:50:21.615-04:00"
}
}
Review Attributes
Review Relationships
Review object will be returned with the following relationships:
List Reviews
GET /restricted/restaurant/reviews
REQUEST:
curl "https://api.libroreserve.com/restricted/restaurant/reviews?restaurant-code=QCXXXXXXXXXX" \
-X GET \
-H "Content-Type: application/json" \
-H "Accept: application/vnd.libro-restricted-v2+json" \
-H "Authorization: Bearer ACCESS_TOKEN"
RESPONSE:
{
"data": [
{
"id": "1234567890",
"type": "review",
"attributes": { /* review attributes */ },
"relationships": { /* review relationships */ }
},
{
"id": "1234567891",
"type": "review",
"attributes": { /* review attributes */ },
"relationships": { /* review relationships */ }
},
{
"id": "1234567892",
"type": "review",
"attributes": { /* review attributes */ },
"relationships": { /* review relationships */ }
}
],
"included": []
}
This endpoint allows you to retrieve a list of reviews for a specific restaurant.
Query Parameters
See Pagination for more details.
Webhooks
Webhooks allow your application to receive real-time notifications when events occur in Libro. Instead of polling the API for changes, Libro will send HTTP POST requests to your configured endpoint whenever relevant events happen.
Overview
When you configure a webhook for a restaurant, Libro will send HTTP POST requests to your specified URL whenever resources are updated. Each webhook request includes:
- A JSON payload following the same JSON:API format as the equivalent API resources
- An
X-Libro-Signatureheader for verifying the request authenticity (when a signing secret is configured) - Standard HTTP headers including
Content-Type: application/json
Responding to Webhooks
Your endpoint should return a 2xx status code to acknowledge receipt of the webhook. If Libro receives a non-2xx response or the request times out, it may retry the webhook delivery.
Signature Verification
When your webhook has a signing secret configured, Libro signs every webhook request using HMAC-SHA256. This allows you to verify that the request came from Libro and hasn't been tampered with.
The Signature Header
Example Signature Header
X-Libro-Signature: t=1705123456,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Each signed webhook request includes an X-Libro-Signature header with two components:
| Component | Description |
|---|---|
t |
Unix timestamp (seconds since epoch) when the signature was generated |
v1 |
The HMAC-SHA256 signature |
How the Signature is Computed
The signature is computed by:
- Concatenating the timestamp and the raw request body with a period:
{timestamp}.{payload} - Computing an HMAC-SHA256 hash using your signing secret as the key
- Encoding the result as a lowercase hexadecimal string
Verifying Signatures
To verify a webhook signature:
- Extract the timestamp (
t) and signature (v1) from theX-Libro-Signatureheader - Check that the timestamp is within your tolerance window (we recommend 5 minutes / 300 seconds)
- Recreate the signed payload by concatenating the timestamp and the raw request body:
{timestamp}.{payload} - Compute the expected signature using HMAC-SHA256 with your signing secret
- Compare your computed signature with the signature in the header
Timestamp Tolerance
The timestamp in the signature header allows you to reject old webhook deliveries, protecting against replay attacks. We recommend a tolerance of 5 minutes (300 seconds).
If a webhook arrives with a timestamp outside your tolerance window, reject it and return a 4xx status code.
Security Best Practices
- Always verify signatures - Never process webhook payloads without verifying the signature first
- Validate the timestamp - Reject webhooks with timestamps that are too old or in the future
- Keep your signing secret secure - Store it securely and never expose it in client-side code
- Use HTTPS - Always use HTTPS endpoints for your webhooks
Pagination
Every resource LIST endpoint returns a paginated list of records, and can use the pagination mechanics.
Retrieve the first 20 bookings:
GET restricted/restaurant/bookings?limit=20&offset=0
Retrieve the next 20 bookings:
GET restricted/restaurant/bookings?limit=20&offset=20
Retrieve 50 bookings per page:
GET restricted/restaurant/bookings?limit=50&offset=0
Query Parameters
To paginate through the payload, use the limit and offset parameters. The limit parameter controls the number of records returned in a single request, with a maximum value of 250. The offset parameter specifies the starting point for the query in terms of database records.
Common Errors
The Libro API uses the following HTTP status codes:
| HTTP Code | Meaning |
|---|---|
| 400 | Bad Request -- Your request is invalid. |
| 401 | Unauthorized -- The token is missing or invalid. |
| 402 | Unauthorized -- Your subscription plan does not allow API access, please contact success@libroreserve.com to subscribe the Premium Plan |
| 403 | Forbidden -- The token has expired or is invalid. |
| 404 | Not Found -- The specified resource could not be found. |
| 405 | Method Not Allowed -- The token does not allow access to this endpoint. |
| 406 | Not Acceptable -- You requested a format that isn't json. |
| 409 | Conflict -- The request could not be completed due to a conflict with the current state of the resource. |
| 422 | Unprocessable Entity -- The request could not be completed due to a validation error. |
| 429 | Too Many Requests -- You're requesting too many resources! |
| 500 | Internal Server Error -- We had a problem with our server. Try again later. |
Error Response Format
Example error response:
{
"error": "validation.service.slots-unavailable-not-specific",
"code": "2005",
"message": "Party size is outside the allowed range for online booking. Please contact the restaurant directly."
}
Error responses include the following fields:
| Field | Description |
|---|---|
| error | A machine-readable error key for backwards compatibility |
| code | A unique error code for programmatic error handling |
| message | A human-readable description of the error |
Error Codes
The API uses structured error codes to help you programmatically handle errors. Error codes are grouped into ranges by category:
Restaurant-level Errors (1xxx)
| Code | Error Key | Description |
|---|---|---|
| 1001 | validation.restaurant.login-required | Customer login is required for this restaurant |
| 1002 | validation.restaurant.recaptcha-required | Recaptcha verification is required |
| 1003 | validation.restaurant.recaptcha-invalid | Invalid recaptcha response format |
| 1004 | validation.restaurant.recaptcha-failed | Recaptcha verification failed |
| 1005 | You must select a restaurant | Restaurant selection is required |
| 1006 | You must select a date & time | Date and time selection is required |
Service/Availability Errors (2xxx)
| Code | Error Key | Description |
|---|---|---|
| 2001 | validation.service.slots-unavailable-not-specific | The requested time slot is not available |
| 2002 | validation.service.slots-unavailable-not-specific | The booking window for this time has closed |
| 2003 | validation.service.slots-unavailable-not-specific | Not enough availability for the requested party size |
| 2004 | validation.service.slots-unavailable-not-specific | Not enough availability at the requested time |
| 2005 | validation.service.slots-unavailable-not-specific | Party size is outside the allowed range for online booking |
| 2006 | exception.no-table | No suitable table is available for this reservation |
| 2007 | validation.service.date-out-of-range | The requested date is outside the booking window |
| 2008 | service-not-found | The requested date and time is not available |
Booking Data Errors (3xxx)
| Code | Error Key | Description |
|---|---|---|
| 3001 | validation.experience.unavailable | The selected experience is not available at this restaurant |
| 3001 | experience-not-found | The selected experience is not available at this restaurant |
| 3002 | validation.classifications.invalid-restaurant | One or more classifications are not valid for this restaurant |
| 3003 | validation.booking.invalid | Booking validation failed |
| 3004 | validation.offer.empty | A prepaid offer selection is required for this reservation |
Operation Errors (4xxx)
| Code | Error Key | Description |
|---|---|---|
| 4001 | validation.booking.not-cancelable | This booking cannot be canceled via the API |
Changelog
January 2026
Structured Error Codes
New Feature - API error responses now include structured error codes to help you programmatically handle errors.
Error responses include three fields:
code: A unique error code (e.g., "2005")message: A human-readable descriptionerror: The legacy error key for backwards compatibility
Error codes are grouped into ranges:
- 1xxx: Restaurant-level errors (authentication, configuration)
- 2xxx: Service/availability errors (slots, capacity)
- 3xxx: Booking data errors (validation)
- 4xxx: Operation errors (cancel, payment)
See the Error Codes section for the complete list.
Booking Modification Restricted Attribute
New Feature - The Booking object now includes a modification-restricted attribute that indicates whether a booking can be modified via partner APIs.
When restricted is true, the booking requires manual intervention by restaurant staff through the Libro dashboard. The reason field provides a specific error code explaining why the booking cannot be modified programmatically.
See the Modification Restricted section in the Booking documentation for details.
October 2025
Payment Intents API
New Feature - We've added a new Payment Intents flow to the API, allowing you to initialize payment intents for no-show protection and ticketing scenarios.
Key features:
- Initialize payment intents with booking details
- Support for Stripe and Moneris payment processing
- Secure payment handling for reservations
See the Payment Intents section for complete documentation and integration examples.