Skip to main content

Booking a Ticket

The aim of your booking application is obviously to enable users to book a ticket.

In details, this means managing user data and many parameters such as:

  • One or more places,and possibly subplaces → The (sub)place categories.
  • The motive(s) for queuing → The appointment types.
  • The number of people using the same ticket → The group size.
  • Which time slots are available for booking → The availabilities.

The logical progression from the moment you get the API Key until the user gets her/his ticket is the following.

Process of booking a ticket

info

See the Resources for detailed information on companies, places, and subplaces.

Step (1) Places

The first step consists in getting the place(s) managed by your company.

GET /places

Go to the route

or

GET /places/{placeId}

Go to the route

Then, go either to Step (2) Queues, or through optional steps if your places are organized into categories or divided into subplaces.

caution

Steps 1.a and 1.b are contradictory. A place either has subplaces or categories, but cannot have both.

Places Object Example

Below is an example of a complete data object of places.

{
"placeId": "placeId1",
"companyId": "companyId1",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
},
"address": "5 Avenue Anatole France",
"city": "Paris",
"country": "France",
"zip": "75007",
"language": "fr_FR",
"timezone": "Europe/Paris",
"position": {
"lat": 48.858471,
"lng": 2.294434
},
"distance": 2133,
"subplaces": {},
"categories": {}
}

In your code, you can do as follows to progress in the booking process.

// If you manage categories of places, you have to display the list
// of categories to let the user choose one before going
// to step 2.
if ( Object.keys( selectedPlace.categories ).length > 0 ) {
// Enter step 1.a
}
// If you manage subplaces, you have to display the list of
// subplaces that are available for a given place to let the
// user choose one before going to step 2.
else if ( Object.keys( selectedPlace.subplaces ).length > 0 ) {
// Enter step 1.b

// If you organized your subplaces into categories, you
// have to display the list of the categories for a given
// subplace to let the user choose one before going
// to step 2.
if ( Object.keys( selectedSubPlace.categories ).length > 0 ) {
// Enter step 1.c
} else {
// Continue to step 2
}
} else {
// Continue to step 2
}

Step 1.a - Place Categories

| Optional step |

GET /places/{placeId}/categories

Go to the route

Below is an example of a complete data object of categories.

{
"categoryId1": {
"categoryId": "categoryId1",
"companyId": "companyId1",
"placeId": "placeId1",
"subPlaceId": "",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
},
"language": "en_US",
"nbMaxTickets": 2
},
"categoryId2": {
"categoryId": "categoryId2",
"companyId": "companyId1",
"placeId": "placeId1",
"subPlaceId": "",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
},
"language": "en_US",
"nbMaxTickets": 1
}
}

Step 1.b – Subplaces

| Optional step |

GET /places/{placeId}/subplaces

Go to the route

Below is an example of a complete data object of subplaces.

{
"subPlaceId1": {
"subPlaceId": "subPlaceId1",
"placeId": "placeId1",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
},
"categories": {
"categoryId1": {
"categoryId": "categoryId1",
"companyId": "companyId1",
"placeId": "placeId1",
"subPlaceId": "subPlaceId1",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
},
"language": "en_US",
"nbMaxTickets": 2
},
"categoryId2": {
"categoryId": "categoryId2",
"companyId": "companyId1",
"placeId": "placeId1",
"subPlaceId": "subPlaceId1",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
},
"language": "en_US",
"nbMaxTickets": 1
}
}
},
"subPlaceId2": {
"subPlaceId": "subPlaceId2",
"placeId": "placeId1",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
},
"categories": {
"categoryId3": {
"categoryId": "categoryId3",
"companyId": "companyId1",
"placeId": "placeId1",
"subPlaceId": "subPlaceId2",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
},
"language": "en_US",
"nbMaxTickets": 2
},
"categoryId4": {
"categoryId": "categoryId4",
"companyId": "companyId1",
"placeId": "placeId1",
"subPlaceId": "subPlaceId2",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
},
"language": "en_US",
"nbMaxTickets": 1
}
}
}
}

Step 1.c – Subplace Categories

| Optional step |

GET /subplaces/{subplaceId}/categories

Go to the route

Below is an example of a complete data object of categories.

{
"categoryId1": {
"categoryId": "categoryId1",
"companyId": "companyId1",
"placeId": "placeId1",
"subPlaceId": "",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
},
"language": "en_US",
"nbMaxTickets": 2
},
"categoryId2": {
"categoryId": "categoryId2",
"companyId": "companyId1",
"placeId": "placeId1",
"subPlaceId": "",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
},
"language": "en_US",
"nbMaxTickets": 1
}
}

Step (2) Queues

The second step consists in displaying the list of the queues that are available for the selected place, so that the user can choose one.

GET /places/{placeId}/queues

Go to the route

Queues Object Example

Below is an example of a complete data object of queues.

{
"queueId1": {
"queueId": "queueId1",
"placeId": "placeId1",
"companyId": "companyId1",
"categoryId": "",
"subPlaceId": "",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
},
"language": "fr_FR",
"timezone": "Europe/Paris",
"position": {
"lat": 48.858471,
"lng": 2.294434
},
"distance": 2133,
"config": {
"bookingType": "FIRST_AVAILABLE_TIMESLOT",
"distanceEligibility": 1500,
"postponeEnabled": false,
"cancelEnabled": true,
"groupSizeLimit": {
"min": 1,
"max": 5
},
"mustAskToSendSms": false,
"mustAskToSendPush": false,
"mustAskToSendMail": false,
"phoneRequired": true,
"mailRequired": true,
"gpsRequired": true,
"availabilityType": {
"description": "Indicates which format will be returned for the queue on \"/queues/state\" and \"/queues/{queueId}/availabilities\", i.e. either FIFO or appointment. All the availabilities are the same for all the tickets of the queue. It is possible to add a \"detailsLevel = 10\" parameter in the GET request of a queue to obtain details on a format with its examples.\n",
"example": {
"firstAvailability": "2023-01-03T09:42:08.484Z",
"availabilities": {
"fifo": [
"2023-01-03T09:42:08.484Z"
],
"appointment": [
"2023-01-03T09:42:08.484Z"
]
}
},
"format": {
"availabilities": {
"fifo": [
"ArrayOfDate"
],
"appointment": [
"ArrayOfDate"
]
},
"firstAvailability": "Date"
},
"objects": {
"ArrayOfDate": {
"description": "A JS array of dates.",
"path": "",
"type": "dataResult"
},
"Date": {
"description": "A date in the string format (ISO-8601).",
"path": "",
"type": "dataResult"
}
},
"type": "Simple"
},
"extendedAttributes": {
"phoneNumber": {
"editAllowed": false,
"requiredOnBooking": false,
"validation": {
"type": "string",
"pattern": "^(\\\\+|00)([0-9]{1,2}\\\\-)?([0-9]{1,4})([0-9] ?){3,12}$"
}
},
"civility": {
"editAllowed": false,
"requiredOnBooking": false,
"validation": {
"type": "string",
"pattern": "^f|m$"
}
},
"name": {
"editAllowed": false,
"requiredOnBooking": false,
"validation": {
"type": "string",
"pattern": "^((?![\\\\.,\\\\|:\\\\!=%\\\\$€£\\\\(\\\\[\\\\]\\\\\\\"\\\\&@#\\\\\\\\\\\\`\\\\+\\\\-_\\\\?;§\\\\)]).){3,30}$"
}
}
}
},
"appointmentTypes": {
"appointmentTypeId1": {
"appointmentTypeId": "appointmentTypeId1",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
}
},
"appointmentTypeId2": {
"appointmentTypeId": "appointmentTypeId2",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
}
}
}
},
"queueId2": {
"queueId": "queueId2",
"placeId": "placeId1",
"companyId": "companyId1",
"categoryId": "",
"subPlaceId": "",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
},
"language": "fr_FR",
"timezone": "Europe/Paris",
"position": {
"lat": 48.858471,
"lng": 2.294434
},
"distance": 2133,
"config": {
"bookingType": "FIRST_AVAILABLE_TIMESLOT",
"distanceEligibility": 1500,
"postponeEnabled": false,
"cancelEnabled": true,
"groupSizeLimit": {
"min": 1,
"max": 5
},
"mustAskToSendSms": false,
"mustAskToSendPush": false,
"mustAskToSendMail": false,
"phoneRequired": true,
"mailRequired": true,
"gpsRequired": true,
"availabilityType": {
"description": "Indicates which format (FIFO or appointment) is used for a specific queue. All the availabilities are the same for all the tickets of the queue.\n",
"example": {
"firstAvailability": "2023-01-03T09:42:08.484Z",
"availabilities": {
"fifo": [
"2023-01-03T09:42:08.484Z"
],
"appointment": [
"2023-01-03T09:42:08.484Z"
]
}
},
"format": {
"availabilities": {
"fifo": [
"ArrayOfDate"
],
"appointment": [
"ArrayOfDate"
]
},
"firstAvailability": "Date"
},
"objects": {
"ArrayOfDate": {
"description": "A JS array of dates.",
"path": "",
"type": "dataResult"
},
"Date": {
"description": "A date in the string format (ISO-8601).",
"path": "",
"type": "dataResult"
}
},
"type": "Simple"
},
"extendedAttributes": {
"phoneNumber": {
"editAllowed": false,
"requiredOnBooking": false,
"validation": {
"type": "string",
"pattern": "^(\\\\+|00)([0-9]{1,2}\\\\-)?([0-9]{1,4})([0-9] ?){3,12}$"
}
},
"civility": {
"editAllowed": false,
"requiredOnBooking": false,
"validation": {
"type": "string",
"pattern": "^f|m$"
}
},
"name": {
"editAllowed": false,
"requiredOnBooking": false,
"validation": {
"type": "string",
"pattern": "^((?![\\\\.,\\\\|:\\\\!=%\\\\$€£\\\\(\\\\[\\\\]\\\\\\\"\\\\&@#\\\\\\\\\\\\`\\\\+\\\\-_\\\\?;§\\\\)]).){3,30}$"
}
}
}
},
"appointmentTypes": {
"appointmentTypeId3": {
"appointmentTypeId": "appointmentTypeId3",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
}
},
"appointmentTypeId4": {
"appointmentTypeId": "appointmentTypeId4",
"name": {
"en_US": "name_EN",
"fr_FR": "name_FR"
}
}
}
}
}

Then, go directly to Step (3) Booking a Ticket, or through other optional steps.

Prerequisites

Previous Step is 1.a or 1.c

If you come from step 1.a or step 1.c, you must filter the queues by using the categoryId of the selected category.

for ( const queue of queuesOfSelectedPlace ) {
if ( queue.categoryId === selectedCategory.categoryId ) {
// Display this queue
} else {
// Hide this queue
}
}

Previous Step is 1.b

If you come from step 1.b, you must filter the queues by using the subplaceId of the selected subplace.

for ( const queue of queuesOfSelectedPlace ) {
if ( queue.subPlaceId === selectedSubPlace.subPlaceId ) {
// Display this queue
} else {
// Hide this queue
}
}

You can also get queues directly from the subplace.

GET /subplaces/{subplaceId}/queues

Go to the route

Step 2.a – Appointment Types

| Optional step |

Booking a ticket means choosing an appointment. Some queues may have different appointment types, other may have only one.

  • If there are many, you have to display a list to let the user choose an appointment.
  • If there is only one, there is nothing to choose, simply display the appointment for validation.
info

The duration of the ticket depends on both appointment type and group size.

// If the selected queue has more than one appointmentType
if ( Object.keys( selectedQueue.appointmentTypes ).length > 1 ) {
// Enter step 2.a
} else {
// Choose the only one appointmentType and go to step 2.b
}

Step 2.b – Group Size

| Optional step |

You may allow more than one user to use the same ticket.

The size of the group may be either fixed, or be in a range that you get from Queue.config.groupSize.
In that case, you have to display a page on which the user can specify how many people are in the group.

// If the selected queue has more than one appointmentType
if (
selectedQueue.config.groupSizeLimit.min !==
selectedQueue.config.groupSizeLimit.max
) {
// Enter step 2.b
} else {
// Choose selectedQueue.config.groupSizeLimit.min as group size for the user and go to step 2.c
}

In the case of a range, indicate a value between the lower and the upper limits of the group size (both included).

selectedQueue.config.groupSizeLimit.min

and

selectedQueue.config.groupSizeLimit.max

Step 2.c – Availabilities

| Optional step |

By default, the API lists to the user all the booking time slots that are available for the queue. In this case, you display a complete list of availabilities.

GET /queues/{queueId}/availabilities

Go to the route

Below is an example of a complete data object of availabilities.

{
"fifo": [
"2023-11-01T12:00:00.000Z"
],
"appointment": [
"2023-11-01T12:00:00.000Z",
"2023-11-01T12:00:10.000Z"
]
}

SSE Route

If you want to get real-time information on the availabilities of a queue, each time an event occurs, use the following route.

GET /queues/{queueId}/availabilities/sse

Go to the route

Step (3) Booking a Ticket

The final step in the process is to consolidate all the previous information and the user information, if specified by the user, into a ticket.

POST /queues/{queueId}/ticket

Go to the route

Booking Object Example

Below is an example of a complete booking data object in version 2 of the Booking API.

{
"appointmentTypeId": "appointmentTypeId1",
"groupSize": 1,
"bookedFor": "2023-11-30T12:00:00:00,0000+0200",
"lang": "en_US",
"userData": {
"sendClientMail": true,
"sendClientPush": true,
"sendClientSms": true,
"phone": "1(800)123-4567",
"mail": "contact@lineberty.com",
"pushId": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1"
},
"position": {
"lat": 48.858471,
"lng": 2.294434
},
"source": "MOBILE_ANDROID"
}

Parameters

Some data are required, other are optional.

Required Parameter

ParameterDescription
queueIdSelected by the user at Step 2.

Mostly Optional Parameters

ParameterDescription
bookedForRequired either:
- If queue.config.bookingType is equal to USER_DEFINED_TIME_SLOT.
- Or if the user chooses a time slot and bookingType is set to MIXED.
These values are specified at Step 2.c.
positionRequired if queue.config.gpsRequired is specified in the queue object. This is the current GPS position of the user.
appointmentTypeIdSelected by the user at Step 2.a.
groupSizeSelected by the user at Step 2.b.

Optional Parameters

User data are optional.

ParameterDescription
langThe user's language, in the i18N format ("en_US", “"r_FR", "es_ES"...).
nameThe user's full name.
phoneRequired for queueConfig.phoneRequired, else optional. This is the user's phone number with the international country code.
mailRequired for queueConfig.mailRequired, else optional. This is the user's mail address.
sendClientMailBoolean to activate the subscription to specific channel.
sendClientPushBoolean to activate the subscription to specific channel.
sendClientSmsBoolean to activate the subscription to specific channel.
pushIdAn ID for push notifications. This ID must be unique for each user and for each device.

SSE Route

If you want to book a ticket and get real-time information on the tickets of a queue, each time an event occurs, use the following route:

GET /queues/{queueId}/ticket/sse

Go to the route