Traveler profile
Read and update traveler identity, travel documents, companions, and loyalty programs.
Every profile endpoint lives under /v1/profile/* and requires the
consumer HMAC and a traveler bearer token. The
traveler is always the one identified by the bearer token — there is no
travelerId path or body parameter on these routes.
Identity
GET /v1/profile/identity returns the traveler's identity record. PATCH /v1/profile/identity updates a subset of fields — every field is optional, so
send only what changed.
# $SIGNATURE is the HMAC over PATCH + /v1/profile/identity + $TIMESTAMP + the
# SHA-256 of the request body — see Authentication for the full algorithm.
curl -X PATCH "$TRAVELTECH_BASE_URL/v1/profile/identity" \
-H "TravelTech-Consumer: $CONSUMER" \
-H "TravelTech-Timestamp: $TIMESTAMP" \
-H "TravelTech-Signature: $SIGNATURE" \
-H "Authorization: Bearer $TRAVELER_JWT" \
-H "Content-Type: application/json" \
-d '{"phoneNumber": "+44 20 7946 0958", "knownTravelerNumber": "TSA1234567"}'import httpx
from your_signing_module import sign # see Authentication for the full algorithm
method, path = "PATCH", "/v1/profile/identity"
body = b'{"phoneNumber": "+44 20 7946 0958", "knownTravelerNumber": "TSA1234567"}'
timestamp = str(int(time.time()))
response = httpx.patch(
f"{BASE_URL}{path}",
content=body,
headers={
"TravelTech-Consumer": CONSUMER,
"TravelTech-Timestamp": timestamp,
"TravelTech-Signature": sign(method, path, body, timestamp),
"Authorization": f"Bearer {traveler_jwt}",
"Content-Type": "application/json",
},
)const method = "PATCH";
const path = "/v1/profile/identity";
const body = JSON.stringify({
phoneNumber: "+44 20 7946 0958",
knownTravelerNumber: "TSA1234567",
});
const timestamp = String(Math.floor(Date.now() / 1000));
await fetch(`${BASE_URL}${path}`, {
method,
body,
headers: {
"TravelTech-Consumer": CONSUMER,
"TravelTech-Timestamp": timestamp,
"TravelTech-Signature": sign(method, path, body, timestamp),
Authorization: `Bearer ${travelerJwt}`,
"Content-Type": "application/json",
},
});Response (IdentityOut, abbreviated):
{
"id": "018f2c1a-9b3e-7c4d-8a1f-3e5b7c9d1a2f",
"accountId": "acc-018f2c1a",
"email": "[email protected]",
"firstName": "Ada",
"lastName": "Lovelace",
"contactEmail": "[email protected]",
"phoneNumber": "+44 20 7946 0958",
"dateOfBirth": "1990-12-10",
"nationality": "GB",
"countryOfResidence": "GB",
"defaultCurrency": "GBP",
"knownTravelerNumber": "TSA1234567",
"redressNumber": null,
"employeeNumberIsAdminManaged": false,
"departmentIsAdminManaged": false
}language must be one of the account's configured locales (lowercase
<lang>_<region>, for example en_us); an unsupported value returns 422.
Sending roleId (in either casing) is rejected the same way — role assignment
is not editable through this endpoint:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed.",
"details": [
{ "field": "roleId", "code": "UNKNOWN_FIELD", "message": "Role assignment is not permitted via profile update." }
],
"recoveryAction": "FIX_INPUT"
}
}GET /v1/profile returns a summary (TravelerProfileSummaryOut) that nests
inferred preferences and a loyalty-program summary in one call — use it for a
dashboard view instead of composing several detail fetches.
Emergency contacts
GET /v1/profile/emergency-contacts
POST /v1/profile/emergency-contacts
PATCH /v1/profile/emergency-contacts/{contact_id}
DELETE /v1/profile/emergency-contacts/{contact_id}
{
"name": "Grace Hopper",
"relationship": "PARENT",
"phone": "+1 202 555 0199",
"email": "[email protected]",
"isPrimary": true
}relationship is one of SPOUSE, PARTNER, PARENT, SIBLING, CHILD,
FRIEND, COLLEAGUE, OTHER. Response (EmergencyContactResponse):
{
"id": "018f2c1a-9b3e-7c4d-8a1f-3e5b7c9d1a2f",
"travelerProfileId": "018f2c1b-1a2b-7c3d-9e4f-5a6b7c8d9e0f",
"name": "Grace Hopper",
"relationship": "PARENT",
"phone": "+1 202 555 0199",
"email": "[email protected]",
"address": null,
"isPrimary": true,
"adminVisible": false,
"createdAt": "2026-02-08T09:00:00Z",
"updatedAt": "2026-02-08T09:00:00Z",
"display": { "locale": "en_us", "createdAt": "2026-02-08T09:00:00Z", "updatedAt": "2026-02-08T09:00:00Z" }
}adminVisible is a duty-of-care disclosure flag — always false today;
there is no traveler-facing write path for it. An unknown contact_id on
PATCH/DELETE returns 404.
Travel documents
GET /v1/profile/documents
POST /v1/profile/documents
PATCH /v1/profile/documents/{document_id}
DELETE /v1/profile/documents/{document_id}
Create request (CreateTravelDocumentRequest) — issuingCountry is ISO
3166-1 alpha-2; provide either expiryDate or noExpiry: true, never both:
{
"type": "PASSPORT",
"documentNumber": "P1234567",
"issueDate": "2022-01-15",
"expiryDate": "2032-01-14",
"issuingCountry": "GB",
"issuingCity": "London"
}type is one of PASSPORT, NATIONAL_ID_CARD, DRIVING_LICENSE, OTHER.
expiryDate must be in the future and after issueDate; violating either
rule, or setting both expiryDate and noExpiry: true, returns 422. The
stored document number is returned masked on every read
(maskedDocumentNumber), alongside a derived expiryStatus
(VALID, EXPIRING_SOON, EXPIRED, NO_EXPIRY) you can use to prompt a
traveler to renew before a trip.
Document extraction (OCR)
POST /v1/profile/documents/extract reads travel-document fields from a
photo, so a traveler can prefill the form above instead of typing it. Send
one JPEG or PNG (max 10 MB) as multipart form data under the
document_image field. This is the one profile route reachable with either a
traveler bearer token or a guest checkout session token, so a guest can
scan a document before an account exists.
POST /v1/profile/documents/extract
Content-Type: multipart/form-data; boundary=...
--...
Content-Disposition: form-data; name="document_image"; filename="passport.jpg"
Content-Type: image/jpeg
<binary image bytes>
--...--
Response (DocumentExtractionOut) — every field is nullable independently,
since OCR confidence varies per field:
{
"type": "PASSPORT",
"documentNumber": "P1234567",
"expiryDate": "2032-01-14",
"issuingCountry": "GB",
"issueDate": "2022-01-15",
"issuingCity": null,
"firstName": "ADA",
"lastName": "LOVELACE",
"dateOfBirth": "1990-12-10",
"nationality": "GB",
"gender": "F",
"confidence": { "documentNumber": 0.98, "expiryDate": 0.95, "firstName": 0.99 },
"extractionMethod": "mrz",
"mrzValid": true
}The extracted fields are not saved automatically — submit them to POST /v1/profile/documents (after traveler confirmation) to persist a document.
An image with no readable text returns 422:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "No readable text found in the uploaded image.",
"details": [],
"recoveryAction": "FIX_INPUT"
}
}Companions
Companions are people the traveler books for repeatedly (a partner, a child,
a parent) who never have their own account.
GET /v1/profile/companions
POST /v1/profile/companions
PATCH /v1/profile/companions/{companion_id}
DELETE /v1/profile/companions/{companion_id}
{
"firstName": "Grace",
"lastName": "Hopper",
"birthday": "1985-06-01",
"gender": "F",
"nationality": "US",
"type": "adult"
}type is one of adult, child, infant. firstName, lastName, and
type cannot be cleared to null on PATCH — omit a field to leave it
unchanged, but an explicit null on one of those three returns 422. A
companion can be named as a booking traveler — see
Bookings and trips.
Loyalty programs
GET /v1/profile/loyalty
POST /v1/profile/loyalty
PATCH /v1/profile/loyalty/{program_id}
DELETE /v1/profile/loyalty/{program_id}
{
"program": "star-alliance",
"membershipNumber": "SA1234567",
"tier": "gold",
"carrierCode": "LH"
}membershipNumber cannot be cleared to null on PATCH for the same reason
as the companion fields above — delete and recreate the record instead.
See the API Reference for the complete request and
response schemas, and Errors for
the full error envelope and recovery-action vocabulary.
Reporting issues
This page is maintained by the BizAway integration team. To request changes or
report an error, contact your BizAway integration contact. Edits made directly
in the ReadMe dashboard are overwritten on the next publish.
Updated 8 days ago