Passenger information capture in reservation sales can run through different paths depending on product configuration, check-in configuration, and selected trip data.
Please make sure you read the Conventions before continuing with this guide.
Prerequisites
You will need an X-API-KEY and a Basic Access Authentication token generated from your username and password.
What this guide solves
This guide explains how to decide, from API responses, which passenger-capture path to use when selling a reservation product:
- Common passenger form (no dynamic form, no check-in capture)
- Product dynamic form
- Dynamic form + people lookup
- Check-in capture during sale (simple and complex)
- Check-in combined with dynamic forms
It also lists the endpoints and response properties you must inspect to make each decision.
Endpoints you need
Use these endpoints in your decision flow:
GET https://api.betterez.com/inventory/products/{productId}
- Purpose: detect if the selected product uses a passenger dynamic form.
- Check:
product.dynamicForms(first entry usually used by sales flow).
- Provider check-in configuration source
- Integrator-friendly source: user login response from:
POST https://api.betterez.com/accounts/users
- In that response, read:
provider.preferences.checkIn
- The old shorthand
GET /accounts/:providerIdis not a public external endpoint in this context. - Internal/private equivalent exists as:
GET https://api.betterez.com/accounts/accounts/{id}(private/internal use).
- Fields needed for decisioning:
provider.preferences.checkIn.enableprovider.preferences.checkIn.captureDuringSaleprovider.preferences.checkIn.checkInFlow(simpleorcomplex)provider.preferences.checkIn.salesFlowFields.propertiesprovider.preferences.checkIn.salesFlowDynamicFormId
GET https://api.betterez.com/inventory/trips(trip search)
- Purpose: detect whether selected trips require check-in capture.
- Check in selected departure and return trips:
segments[].requiresCheckIn
GET https://api.betterez.com/accounts/dynamic-forms/{dynamicFormId}
- Purpose: load the form definition when a product dynamic form is configured, or when check-in complex flow uses a dynamic form.
- Check:
definitionmappedStandardBtrzFieldsmappingSeparatorspeopleLookupEnabled(product dynamic form use case)peopleLookupFields(product dynamic form use case)peopleLookupTrigger(product dynamic form use case)
GET https://api.betterez.com/accounts/people-lookups(dynamic-form path)
- Purpose: fetch a person profile for dynamic-form sales flows.
- Typical filters:
documentTypeId,documentNumber, optionallydynamicFormId,providerId. - Response to inspect:
people[].
POST https://api.betterez.com/accounts/people-lookupsorPUT https://api.betterez.com/accounts/people-lookups/{personId}(dynamic-form path)
- Purpose: create/update lookup records and persist data mapped from dynamic forms.
GET https://api.betterez.com/operations/passenger-check-in-info(check-in path)
- Purpose: fetch existing check-in information by document data in check-in flows.
- Required query:
documentType,documentNumber. - Optional query:
providerId. - Response to inspect:
passengerCheckInInfos[].
Decision model (runtime)
Use this order of evaluation after selecting the product and searching/selecting trips:
- Evaluate whether check-in capture is enabled for sale.
- Evaluate whether at least one selected trip segment requires check-in.
- If both are true, use check-in passenger capture.
- Otherwise, evaluate product dynamic form.
- If product has dynamic form, use dynamic form flow.
- If neither applies, use common/basic passenger capture.
The check-in path has priority over the product dynamic-form path when requiresCheckIn is true in selected trips.
Case 1: Common passenger capture (baseline)
Use this when:
- Product has no passenger dynamic form (
product.dynamicFormsempty or no usable id), and - Check-in capture path is not active for selected trips.
Behavior:
- Capture standard passenger fields in the reservation payload sent to
POST https://api.betterez.com/sales/cart. - Passenger object format (common flow, no dynamic form/check-in override):
- Minimum practical fields:
fareId(string, ObjectId)firstName(string)lastName(string)
- Fields that can actually carry user/business data in common flow:
firstName(string)lastName(string)email(string|null)comments(string)fare(string)fareId(string)fareIds(object)fareClassIds(object)ssrs(array)items(array)seats(array)extra(object, includesgenderandfolios)syncEntryId(string)extraInput(string)
- Minimum practical fields:
- Example passenger object:
{
"fareId": "665f5d9f3f3b4b0012345678",
"fare": "Adult",
"fareIds": {"outbound": "665f5d9f3f3b4b0012345678"},
"fareClassIds": {"outbound": ""},
"ssrs": [],
"items": [],
"seats": [],
"firstName": "Jane",
"lastName": "Doe",
"email": null,
"comments": "",
"extra": {"gender": "female", "folios": []},
"syncEntryId": "a8f9e7b2-9fcb-4f0f-b4f4-11b8d60c1d0a",
"extraInput": ""
}
- Do not call people lookup as a mandatory step.
- Do not use check-in-specific lookup endpoint.
Case 2: Product dynamic form (without people lookup)
Use this when:
- Product has dynamic form id, and
- Check-in capture path is not active for selected trips.
Behavior:
- Read
product.dynamicForms[0]._id. - Call
GET https://api.betterez.com/accounts/dynamic-forms/{dynamicFormId}. - Build your passenger UI from
definition. - Map data to standard fields using
mappedStandardBtrzFieldsandmappingSeparators.
If peopleLookupEnabled is false or missing, you can skip people lookup endpoints.
Case 3: Product dynamic form with people lookup
Use this when:
- Product dynamic form path is active, and
peopleLookupEnabledis true in the dynamic-form response.
Behavior:
- Use
peopleLookupFieldsandpeopleLookupTriggerto decide when to query lookup. - Call
GET https://api.betterez.com/accounts/people-lookupswith the configured lookup keys. - If
people[]has data, prefill passenger fields. - If no result, collect data and create/update lookup profile:
POST https://api.betterez.com/accounts/people-lookups, orPUT https://api.betterez.com/accounts/people-lookups/{personId}.
- Keep
peopleLookupIdper passenger to reuse in cart payloads.
Case 4: Check-in capture during sale
Check-in capture is active only when all of the following are true:
provider.preferences.checkIn.enableis trueprovider.preferences.checkIn.captureDuringSaleis true- Check-in has configured fields for sales flow (
provider.preferences.checkIn.salesFlowFields.propertiesnot empty orprovider.preferences.checkIn.salesFlowDynamicFormIdpresent) - At least one selected trip segment has
requiresCheckIn = true
If any condition fails, do not use check-in capture as the passenger form for that selection.
Simple vs complex check-in flow
Use provider.preferences.checkIn.checkInFlow to understand configuration mode:
simple- Uses provider default check-in configuration.
- No custom advanced configuration should be expected from integrator perspective.
complex- Sales check-in fields can be customized through:
salesFlowFields(JSON schema-like definition), orsalesFlowDynamicFormId(check-in dynamic form definition loaded from dynamic-forms API).
- Sales check-in fields can be customized through:
For complex, if salesFlowDynamicFormId exists, prefer loading that dynamic form to build check-in field definitions.
Check-in lookup endpoint
For check-in forms, use:
GET https://api.betterez.com/operations/passenger-check-in-info?documentType=...&documentNumber=...&providerId=...
Then:
- If
passengerCheckInInfos[0]exists, prefill check-in capture fields. - If empty, continue with manual capture.
This is different from product dynamic-form people lookup, which uses https://api.betterez.com/accounts/people-lookups.
Case 5: Check-in combined with dynamic forms
There are multiple combinations to consider.
Combination A: Product dynamic form + check-in capture
When both are configured, selected-trip requirement decides which one drives passenger capture:
- If any selected segment has
requiresCheckIn = true, use check-in capture. - If no selected segment requires check-in, use product dynamic form.
This means the same product can use different passenger-capture UIs depending on trip selection.
Combination B: Complex check-in using salesFlowDynamicFormId
In this case, check-in itself is built from a dynamic-form definition (type check-in configuration), but runtime behavior is still check-in flow:
- Trigger remains
requiresCheckIn+ provider check-in sale settings. - Lookup endpoint remains
https://api.betterez.com/operations/passenger-check-in-info. - Do not treat this as product passenger dynamic-form people-lookup flow unless your own business rule explicitly combines both.
Combination C: Check-in plus additional paid-in item dynamic forms
If your sale includes paid-in item forms, these can coexist with check-in capture:
- Passenger capture stays in check-in flow (when required by selected segments).
- Additional item-level forms may still use dynamic-form + people-lookup behavior per item form configuration.
Treat passenger-level check-in and item-level dynamic forms as separate capture layers.
Backoffice UI examples (betterez-app/app/modules/sales-reservations-2):
Check-in form confirms passengers and then persists paid-in item lookups before cart completion.
- UI behavior:
- When check-in form is active and
paidInDynamicFormFieldsexists,CheckInPaxFormService.confirmPassengers()callscreateOrUpdateLookupPaidIn()and only then callsfinishCart().
- When check-in form is active and
- This means paid-in people lookup persistence is part of the check-in confirmation step, not a separate checkout stage.
- UI behavior:
Paid-in dynamic form is detected from inventory items and loaded with its own definition.
- UI behavior:
getPaidInDynamicForm()picks the first item that matchestype === "paid_in",delayAttachToTicket === true, and hasdynamicForm._id.- It then loads dynamic-form metadata (
dynamicFormId,dynamicFormDefinition, lookup flags) specifically for item-level capture.
- UI behavior:
Paid-in people lookup create/update uses
https://api.betterez.com/accounts/people-lookupswithdynamicFormspayload.- UI behavior:
- For each selected paid-in item, the UI checks existing people lookup by document data.
- If found, it updates; if not, it creates.
- Payload shape used by the UI for each paid-in lookup upsert:
- UI behavior:
{
"person": {
"dynamicForms": {
"{{paidInDynamicFormId}}": {
"documentType": "{{documentTypeId}}",
"documentNumber": "{{documentNumber}}",
"...": "other paid-in dynamic form fields"
}
}
}
}
- After lookup upsert, UI maps returned person data back into item formData.
- UI behavior:
- Returned
person.dynamicFormsFieldsvalues are copied intopassengers[].items[].formData. peopleLookupIdis stored at the item form level for later cart submission consistency.
- Returned
- UI behavior:
Cart payload patterns (what to send)
All cases below are sent to:
POST https://api.betterez.com/sales/cart
For complete cart body context, see:
A) Dynamic form flow payload
Use this when product dynamic form is active and check-in is not active for selected trips.
Key points:
- Set
items.reservation[0].dynamicFormId. - In each passenger, include standard fields.
- If people lookup is used, include
peopleLookupIdper passenger.
{
"items": {
"reservation": [
{
"productId": "{{productId}}",
"from": {"_id": "{{originStationId}}"},
"to": {"_id": "{{destinationStationId}}"},
"selectedTrips": {"departureTripId": "{{departureTripId}}", "returnTripId": ""},
"roundTrip": false,
"dateFrom": "{{YYYY-MM-DD}}",
"dynamicFormId": "{{dynamicFormId}}",
"passengers": [
{
"fareId": "{{fareId}}",
"firstName": "Jane",
"lastName": "Doe",
"documentTypeId": "{{documentTypeId}}",
"documentNumber": "A1234567",
"email": "jane@example.com",
"phone": "+14165550101",
"peopleLookupId": "{{peopleLookupId}}",
"ssrs": [],
"seats": []
}
]
}
]
}
}
B) Check-in flow payload
Use this when check-in is active and selected trip segments include requiresCheckIn = true.
Key points:
- Send passenger capture in
passengers[].checkInInfo. - Do not set reservation
dynamicFormIdfor passenger capture in this case. - Keep standard passenger fields (
firstName,lastName,documentTypeId,documentNumber, etc.) for compatibility and downstream processes.
{
"items": {
"reservation": [
{
"productId": "{{productId}}",
"from": {"_id": "{{originStationId}}"},
"to": {"_id": "{{destinationStationId}}"},
"selectedTrips": {"departureTripId": "{{departureTripId}}", "returnTripId": ""},
"roundTrip": false,
"dateFrom": "{{YYYY-MM-DD}}",
"passengers": [
{
"fareId": "{{fareId}}",
"firstName": "Jane",
"lastName": "Doe",
"documentTypeId": "{{documentTypeId}}",
"documentNumber": "A1234567",
"ssrs": [],
"seats": [],
"checkInInfo": {
"documentType": "{{documentTypeId}}",
"documentNumber": "A1234567",
"dateOfBirth": "1992-03-14",
"nationality": "CA"
}
}
]
}
]
}
}
C) Product dynamic form + check-in both configured
There is no single combined passenger payload in runtime.
- If selected segments require check-in:
- Use payload pattern B (
checkInInfo), and do not use reservationdynamicFormIdfor passenger capture.
- Use payload pattern B (
- If selected segments do not require check-in:
- Use payload pattern A (set reservation
dynamicFormId, optionallypeopleLookupId).
- Use payload pattern A (set reservation
Practical decision checklist
For each reservation sale:
- Load product (
https://api.betterez.com/inventory/products/{productId}). - Load provider check-in preferences from user login response (
POST https://api.betterez.com/accounts/users), usingprovider.preferences.checkIn. - Search/select trips (
https://api.betterez.com/inventory/trips) and inspect selectedsegments[].requiresCheckIn. - Apply precedence:
- Check-in active + requiresCheckIn => check-in flow.
- Else product dynamic form => dynamic-form flow.
- Else common flow.
- In dynamic-form flow:
- Load dynamic form by id.
- If
peopleLookupEnabled, execute people lookup flow.
- In check-in flow:
- Resolve fields from configured sales-flow definition.
- Use passenger check-in info lookup endpoint for prefill.
Notes and implementation recommendations
- Always evaluate decisions after trip selection, not only after product selection, because
requiresCheckInis trip-dependent. - In round trips, evaluate departure and return segments; if either side requires check-in, choose check-in flow.
- Keep lookup flows distinct:
- Dynamic-form person profiles =>
https://api.betterez.com/accounts/people-lookups - Check-in passenger history =>
https://api.betterez.com/operations/passenger-check-in-info
- Dynamic-form person profiles =>
- When check-in is complex and field definitions are customized, rely on configured schema/dynamic-form definitions instead of hardcoding field lists.