GraphQL reference
Operations and types available to partner integrations (the React SDK and direct API clients alike). The schema is filtered server-side by @PartnerSdkExposed; this page mirrors what __schema returns when introspected with a valid partner API key.
Queries
bookingsByMember
Get bookings by member
bookingsByMember(limit: Int, memberId: String!, offset: Int): [Booking!]!loyaltyTransactionsWithMetrics
Get loyalty transactions with tier metrics from v_booking_metrics view
loyaltyTransactionsWithMetrics(excludeDuplicates: Boolean, limit: Int, memberId: String!, offset: Int, partnerId: String): LoyaltyTransactionsWithMetricsResult!memberByEmail
Partner-scoped server-to-server read: member identity + tier by email. Requires a partner API key; limited to members the calling partner holds a qualifying tie to.
memberByEmail(email: String!): PartnerMemberRead!memberByExternalId
Partner-scoped server-to-server read: member identity + tier by the partner’s own external ID. Requires a partner API key; limited to members enrolled with that partner.
memberByExternalId(externalId: String!): PartnerMemberRead!memberProfile
Get comprehensive member profile with statistics
memberProfile(id: ID!): MemberProfilememberTierHistory
Get member tier change history
memberTierHistory(memberId: String!, pagination: PaginationInput): MemberTierHistoryConnection!tierBenefits
Get all tier benefits for a partner - rewards grouped by tier and scope
tierBenefits(locale: String, originPartnerId: String, partnerId: String!): [TierBenefits!]!tierConfigs
Get tier configs for a partner (with global fallback) or global configs if no partnerId
tierConfigs(locale: String, partnerId: String): [LoyaltyTierConfig!]!Mutations
enrollMember
Partner SDK: enrol a member by email + profile (server-to-server, no user login). Requires a partner API key with the member:write scope.
enrollMember(input: EnrollMemberInput!): EnrollMemberResult!loginMemberGIP
Website member login via GIP (Google Identity Platform)
loginMemberGIP(input: LoginMemberGIPInput!): GIPMemberAuthPayload!passkeyAuthenticate
Verify passkey authentication and return Firebase custom token with tenant ID
passkeyAuthenticate(authenticationResponse: JSONObject!, expectedChallenge: String): PasskeyAuthResponse!passkeyAuthenticationOptions
Generate WebAuthn authentication options
passkeyAuthenticationOptions(tenantId: String, userEmail: String, userId: String, userType: String): JSONObject!recordLoyaltyTransaction
Partner SDK: credit or debit a member’s AKIN points (server-to-server, idempotent). Requires a partner API key with the loyalty:write scope.
recordLoyaltyTransaction(input: RecordLoyaltyTransactionInput!): RecordLoyaltyTransactionResult!redeemReward
Partner SDK: record that a member redeemed one of the partner’s rewards (server-to-server, idempotent). Requires a partner API key with the loyalty:write scope.
redeemReward(input: RedeemRewardInput!): RedeemRewardResult!requestMagicLink
Request a magic link for login. Sends email to existing users. With createIfMissing (the apps, akin-travel/issues#6745), an address with no account gets a join link instead, and both emails carry a 6-digit code for verifyAuthCode; the response is the same either way.
requestMagicLink(captchaToken: String, createIfMissing: Boolean, email: String!, forApp: Boolean, locale: String, partnerId: ID, partnerSlug: String, redirectUrl: String, returnUrl: String, signupLocation: SignupLocation, userType: String!): RequestMagicLinkResult!signUpPasswordless
Creates a member account via an email verification link (website/white-label). NOT the passkey flow (passkeys are enrolled post-signup); NOT the removed iOS OTP login.
signUpPasswordless(input: SignUpPasswordlessInput!): SignUpResult!updateMember
Update member
updateMember(id: ID!, input: UpdateMemberInput!): Member!verifyAuthToken
Verify auth token from email link and return Firebase custom token for sign-in.
verifyAuthToken(token: String!): AuthTokenResult!Types
AuthTokenResult
Object
Result of token verification
| Field | Type | Description |
|---|---|---|
| customToken | String | Firebase custom token for sign-in |
| error | String | Error message if failed |
| initialSync | InitialSyncSummary | Always null — the welcome sync was removed. Kept for installed mobile builds that select it |
| isNewUser | Boolean | Whether this is a new user (first verification) |
| legalAcceptanceRequired | Boolean | True when the signed-in member has recorded no Terms or Privacy acceptance at all — a member created at this sign-in, or one synced from a PMS without signing up (akin-travel/issues#6555). A client then shows the member the same Terms + Privacy clickwrap as sign-up (on the web, at magic-link sign-in, before the page continues) and records it with reacceptLegalTerms, which stamps the in-force versions server-side. It is advisory, not a gate: no operation is refused without it (akin-travel/issues#3855). False for a member who has accepted any version, and for a sign-up, which records both versions itself |
| memberData | Member | Member data if user is a member |
| requiresPasskey | Boolean | Whether passkey setup is required |
| success | Boolean! | Whether verification was successful |
| tenantId | String | Tenant ID used when creating the custom token |
| userId | String | User ID |
| userType | String | User type: MEMBER or DASHBOARD_USER |
Booking
Object
Booking/reservation record
| Field | Type | Description |
|---|---|---|
| adultCount | Int | |
| amountDueAtProperty | Float | What the guest still owes the property on arrival: stayTotal − the AKIN deposit already collected (bookingFee). NULL when no AKIN deposit was taken. This is balanceOwed’s tax-aware sibling — prefer it on member-facing surfaces. |
| averageNightlyRate | Float! | Average nightly rate (total price / nights), in the booking currency — cents preserved |
| balanceOwed | Float | Balance the guest owes the property on arrival: stay total − the AKIN 15% reservation deposit (bookingFee). NULL when no AKIN deposit was collected (a PMS-synced reservation where the property takes the whole amount at the desk) — clients render NULL as a fallback (the dashboard uses ”—”). |
| bookingDate | DateTimeISO | |
| bookingFee | Float | Booking fee amount (10% of total) |
| bookingFeePaid | Boolean! | Whether the booking fee has been paid |
| bookingFeePaidAt | DateTimeISO | When the booking fee was confirmed paid |
| bookingSourceId | String | FK to booking_sources table for channel attribution |
| cashDepositCollected | Float | Actual cash Stripe collected for this booking (the 15% deposit minus any travel credit applied), in the booking currency. Computed by the Stripe webhook as booking_fee * (chargeAmount / baseChargeAmount). The PMS push orchestrator posts THIS to the Cloudbeds folio instead of total_price, so the folio matches what AKIN charged the card. NULL = not recorded; 0 = travel credit covered the full deposit. |
| channelGuestConfirmationNumber | String | OTA guest-facing confirmation number (e.g. Booking.com 6044.913.102 the guest sees on their confirmation email). Distinct from pms_confirmation_number, which carries the channel-feed property-facing reservation id. Populated for OTA-channel bookings only. See akin-travel/issues#1551. |
| channelName | String | Booking channel from PMS (e.g., Direct, Booking.com, Expedia). Used for loyalty eligibility. |
| checkIn | String | Check-in date (YYYY-MM-DD, property-local). NULL for bad-data rows with no recorded check-in. |
| checkOut | String | Check-out date (YYYY-MM-DD, property-local). NULL for bad-data rows with no recorded check-out. |
| childCount | Int | |
| confirmationNumber | String | |
| consolidatedFrom | [String!] | Array of original booking IDs that were merged into this one |
| createdAt | DateTimeISO! | |
| currency | String | |
| depositRefundAmount | Float | Deposit amount refunded on an in-window (flexible-rate) cancellation, in depositRefundCurrency |
| depositRefundCurrency | String | ISO currency of the deposit refund |
| depositRefundType | DepositRefundType | How the deposit refund was issued: CASH (Stripe) or CREDIT (travel credit) |
| depositRefundedAt | DateTimeISO | When the deposit refund was issued |
| eligibilityReason | String | ⚠️ Deprecated. Sunset 2026-12-08: Use eligibilityReasonCode and translate client-side. Removal is blocked by GET_MEMBER_PROFILE (packages/api-client/src/operations/member.ts), which the dashboard member page fetches for BookingHistoryTable — akin-travel/issues#5572 — Deprecated alias of eligibilityReasonCode. Populated identically during migration. |
| exchangeRate | Float | USD exchange rate used for conversion |
| fees | Float | |
| freeCancellationDeadline | DateTimeISO | The instant free cancellation ends for THIS booking, snapshotted at confirm from the rate plan’s stored Mews terms and never re-read afterwards (akin-travel/issues#5279). NULL means NO DEADLINE WAS RECORDED — never ‘no deadline applies’ and never ‘free forever’: it covers a rate plan with no stored policy (every non-Channel-Manager rate today), terms that charge nothing at any point, an applicability code we cannot place in time, and a property whose timezone does not resolve. Render NULL as the rate’s own terms, never as a promise of free cancellation. Read it on propertyTimezone — the property enforces the cut-off on its own clock. |
| guaranteeCardReference | String | PMS-side id of the stored guarantee card — for Mews the creditCards/getAll Id, i.e. the paymentMethodId the Checkout embed returns. A vault reference, never card data: AKIN never sees a PAN. It may be set with guaranteeCollectedAt still NULL, and that combination is meaningful rather than inconsistent (akin-travel/issues#5535): the status poll stamps this reference as soon as it VERIFIES a card, because AKIN cannot delete a stored Mews card and this row is the only durable trace of one it caused to be stored, while guaranteeCollectedAt is written only by the confirm and is what marks a booking GUARANTEED. So this field alone means OBSERVED, not collected — never read it as evidence of a guarantee, and never as evidence a booking confirmed. |
| guaranteeCollectedAt | DateTimeISO | When the guest’s card was stored as a guarantee for this booking — no charge was taken (akin-travel/issues#4586). NOT NULL means AKIN collected no deposit by design: bookingFee and cashDepositCollected are legitimately NULL, and balanceOwed is therefore NULL (the guest owes the property the whole stay). NULL on every deposit-model and PMS-imported booking. |
| guestEmail | String | Guest email for non-member bookings (for future member linking) |
| guestIdentityWithheld | Boolean | True when guest identity on this row was withheld by the Customer Data Availability Policy post-stay cutoff (the stay checked out over 60 days ago and the caller is partner/property staff). NULL = the policy did not act. Read this instead of inferring ‘not linked’ from a blank memberId — a withheld row and a genuinely member-less row look identical otherwise. Discloses nothing about whether a member is attached: every lapsed row is anonymised, linked or not. |
| guestPhone | String | Guest phone (E.164) for non-member bookings — orphan-link signal alongside guest_email |
| guests | Int | |
| id | ID! | |
| isAkinManaged | Boolean! | True when AKIN owns this booking’s lifecycle — created through AKIN direct-booking checkout (source=WEBSITE / trip flow) — vs a reservation synced in from the property PMS. Member-facing cancel / perks / edit controls gate on this so a synced reservation can’t be cancelled or have rewards changed through AKIN. See akin-travel/issues#1925. |
| isConsolidated | Boolean! | True if this booking was created by merging manual + PMS bookings |
| isLinked | Boolean! | True when the booking is linked to a member account (memberId is set). False = OTA/non-member booking. |
| isOriginBooking | Boolean! | True if booking is at member origin partner (members.origin_partner_id == property.partnerId). Never the vestigial members.partner_id — akin-travel/issues#4857 |
| loyaltyForfeitFraction | Float | The forfeited share behind loyaltyForfeitVerdict, 0-1: (0, 1] on EARNS, exactly 0 on FORFEITS, NULL on HELD and on every unstamped row (akin-travel/issues#5686). Points are round(fraction × spend_usd) and Travel Credit is fraction × the booking’s 2.5%, so a fraction below 1 is the pro-rata line the activity statement shows. |
| loyaltyForfeitVerdict | String | Which way this booking’s loyalty went on a cancel or no-show (akin-travel/issues#5686, epic #5683): EARNS (the member forfeited money and keeps the Points and Travel Credit on the forfeited share), FORFEITS (nothing was forfeited, nothing earns) or HELD (undecidable from the data — the folio settlement sweep or a human decides). NULL on every booking that has not been cancelled or no-showed since LOYALTY_ON_FORFEIT went live, and on every PMS-imported one. Read forfeitPreview for the copy a member sees BEFORE cancelling. |
| memberDiscountAmount | Float | The money memberDiscountRateApplied comes to on this booking’s CURRENT totalPrice, in the booking’s own currency. Written at checkout and rewritten by every date change: the change re-prices at the tier in effect at the time of the change and writes the rate it applied and the discount that rate came to on the new dates in the same statement, so this is always the saving on the stay the member now holds. 0 when the rate took nothing off it (the rate is 0, or the rail could not carry an AKIN price); NULL exactly when memberDiscountRateApplied is NULL. |
| memberDiscountRateApplied | Float | The member discount fraction AKIN last applied to this booking: stamped at checkout from the tier the member held then, and rewritten by every date change from the tier the member holds at the time of the change, up or down (akin-travel/issues#5997). 0.15 is 15%. 0 = the per-tier path ran and applied nothing (a guest, no partner rate, a rail that cannot carry one, a plan excluded from the member rate, or a plan that already carried the discount). NULL = the path has not run for this booking, because ENABLE_TIER_MEMBER_RATE was off at booking and at every date change since. |
| memberId | String | Member ID (null for non-member OTA bookings) |
| memberTierAtStay | String | Loyalty tier code the member held on arrival for this stay (e.g. TIER2), stamped once by the database on first CHECKED_IN/CHECKED_OUT/COMPLETED. Read-only. NULL = unknown, not a tier. |
| membershipYear | Int | Membership year (-1 = pre-membership, 1+ = membership years, null = unknown). Computed, not stored. |
| mergedIntoBookingId | String | If this booking was merged, points to the consolidated booking ID |
| metadata | BookingMetadata | Booking metadata |
| nights | Int! | Number of nights (minimum 1 for same-day stays) |
| numberOfGuests | Int | ⚠️ Deprecated. Sunset 2026-10-10: duplicate of guests (same bookings.number_of_guests column); read guests instead. Removed with the bookings.guests column drop — akin-travel/issues#2656 Phase B. |
| paymentStatus | PaymentStatus | |
| pmsBookingId | String | |
| pmsCancelStatus | String | Outbound PMS cancel push state — tracked separately from create |
| pmsConfirmationNumber | String | PMS confirmation number (from property management system) |
| pmsCustomerId | String | Guest ID in the PMS (Mews CustomerId etc.) |
| pmsLastError | String | Most recent PMS push error message |
| pmsLastSyncedAt | DateTimeISO | Last successful PMS push timestamp |
| pmsPaymentPostedAt | DateTimeISO | When the Stripe-collected payment was posted to the PMS folio (Cloudbeds postPayment). NULL = not yet posted. Guards against double-post on retry. Set ONLY after a confirmed post (akin-travel/issues#1909), so a crash before the post leaves this NULL and the deposit stays retryable. |
| pmsReportedTotal | Float | What the PMS last reported this AKIN-managed booking costs, in the booking currency, once it has differed from totalPrice (akin-travel/issues#6309). AKIN keeps totalPrice/subtotal/taxes/fees (the checkout figures and the commission basis); a genuine PMS modification lands its total here instead. NULL when the PMS never reported a differing total, or the booking is PMS-imported and totalPrice follows the PMS. |
| pmsReportedTotalAt | DateTimeISO | When the PMS report in pmsReportedTotal was received (akin-travel/issues#6309). NULL exactly when pmsReportedTotal is NULL. |
| pmsSyncAttempts | Int | Number of attempts made to push this booking to the PMS |
| pmsSyncStatus | String | Outbound PMS push state: NOT_APPLICABLE, PENDING, SYNCING, SYNCED, FAILED |
| pointsEarned | Int | Points earned from this booking (derived from total_price + currency exchange rate) |
| propertyId | String! | |
| rateCurrency | String | Currency of the rate charged (from availability) |
| ratePlanId | String | Rate plan ID |
| roomCount | Int | |
| roomTypeId | String | Room type ID |
| source | String | |
| specialRequests | String | |
| spendUsd | Float | Spend converted to USD via exchange rate |
| status | BookingStatus | |
| staySubtotal | Float | The pre-tax portion of stayTotal, resolved on the SAME basis. Render this — never the raw subtotal column — above a taxesFeesTotal row: the raw columns hold the checkout QUOTE, which at a Colombian property is a tax the folio does not charge, so stacking them under stayTotal shows a sum that does not add up. |
| stayTotal | Float | The full tax-inclusive stay total. Resolved folio-first (the property PMS is authoritative), falling back to the persisted subtotal+taxes+fees, then to the pre-tax total_price. Use this, not totalPrice, for any member-facing “what this stay costs” figure. |
| stripePaymentIntentId | String | Stripe PaymentIntent ID for booking fee |
| subtotal | Float | |
| taxIncluded | Boolean! | Whether the stay total genuinely accounts for tax. False means no tax was ever established for this booking (non-Cloudbeds property, no connection, failed quote, or a pre-#4904 row), so surfaces must disclose that property taxes may apply on top. A genuinely quoted zero is TRUE with a zero tax figure — the two are not the same fact. |
| taxQuoteSource | String | Provenance of subtotal/taxes/fees: PMS_QUOTE = quoted from the property PMS at checkout (the figure the deposit was sized off); UNQUOTED = no quote was obtainable, so the deposit fell back to pre-tax. NULL on rows predating akin-travel/issues#4904 — read as unknown, NEVER as zero-tax. |
| taxes | Float | |
| taxesFeesTotal | Float! | Tax + fees inside stayTotal. Zero both when tax is genuinely zero and when it was never established — read taxIncluded to tell those apart. |
| totalPrice | Float | |
| tripId | String | Trip ID (when booked via trip flow) |
| updatedAt | DateTimeISO! |
BookingMetadata
Object
Booking metadata
| Field | Type | Description |
|---|---|---|
| confirmationSent | Boolean | |
| internalNotes | String | |
| notes | String | |
| reminderSent | Boolean | |
| source | String |
BookingStatus
Enum
| Value | Description |
|---|---|
CANCELLED | |
CHECKED_IN | |
CHECKED_OUT | |
COMPLETED | |
CONFIRMED | |
MERGED | |
NO_SHOW | |
PENDING |
DateTimeISO
Scalar
A date-time string at UTC, such as 2007-12-03T10:15:30Z, compliant with the date-time format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar.This scalar is serialized to a string in ISO 8601 format and parsed from a string in ISO 8601 format.
DepositRefundType
Enum
| Value | Description |
|---|---|
CASH | |
CREDIT |
DeviceInfoInput
Input
Device information for session tracking
| Field | Type | Description |
|---|---|---|
| browser | String | |
| device | String | |
| ipAddress | String | |
| os | String | |
| userAgent | String |
EnrollMemberInput
Input
Server-to-server member enrollment by a partner
| Field | Type | Description |
|---|---|---|
String! | Member email (matched case-insensitively for idempotency) | |
| externalId | String | Partner’s own loyalty key for this member on their side. Unique per partner. |
| firstName | String! | |
| lastName | String! |
EnrollMemberResult
Object
Result of a partner server-to-server member enrollment
| Field | Type | Description |
|---|---|---|
| created | Boolean! | True if a new member account was created (vs an existing one reused) |
| enrollmentChanged | Boolean! | True if the partner enrollment was newly created or reactivated |
| memberId | ID! | AKIN member id for the partner to persist |
EnrollmentSource
Enum
| Value | Description |
|---|---|
DASHBOARD | |
MANUAL | |
MIGRATION | |
SDK | |
SIGNUP | |
URL_PARAM | |
WHITE_LABEL |
GIPMemberAuthPayload
Object
GIP member authentication payload
| Field | Type | Description |
|---|---|---|
| memberData | Member! | Loyalty member data |
| user | MemberUser! | GIP user information |
InitialSyncSummary
Object
Summary of initial signup sync (past + future stays)
| Field | Type | Description |
|---|---|---|
| failedStaysCount | Int! | Stays that failed to import and were skipped. Never populated: the welcome sync was removed |
| futureStays | [SyncedStay!]! | Future stays (no points yet) |
| futureStaysCount | Int! | Number of future stays synced |
| message | String | Summary message |
| pastStays | [SyncedStay!]! | Past stays with points awarded |
| pastStaysCount | Int! | Number of past stays synced |
| pointsAwarded | Int! | Total points awarded from past stays |
| success | Boolean! | Whether sync was successful |
JSONObject
Scalar
The JSONObject scalar type represents JSON objects as specified by ECMA-404 .
LoginMemberGIPInput
Input
GIP (Google Identity Platform) authentication input for website members
| Field | Type | Description |
|---|---|---|
| countryCode | String | Country code (e.g., AU, US, GB) |
| deviceInfo | DeviceInfoInput | |
| enrollmentSource | EnrollmentSource | Where the sign-in originated from (white-label site, URL param, SDK, etc.). Used to tag partner enrollments for existing members signing in with a new partner context. Defaults to WHITE_LABEL when partnerId is set and this is omitted, or SDK when the request is partner-SDK-authenticated. Issue #1160. |
| firstName | String | First name for new member registration |
| idToken | String! | GIP ID token from client authentication |
| lastName | String | Last name for new member registration |
| marketingOptIn | Boolean | Marketing email opt-in preference |
| partnerId | String | Partner ID for attribution tracking |
| phone | String | Phone number for SMS/MFA |
| termsAccepted | Boolean | Terms and conditions acceptance |
LoyaltyTierConfig
Object
Loyalty tier configuration
| Field | Type | Description |
|---|---|---|
| active | Boolean! | |
| cardBodyData | String | |
| cardFooterData | String | |
| createdAt | DateTimeISO! | |
| directBookingDiscountRate | Float | Fraction off the direct-booking price for a member holding this tier, on a rail that carries a price (the Mews channel-manager push). 0.15 is 15%; the stored value is range-checked to [0,1]. NULL means the rung is unset — NOT a zero-discount rung. |
| directBookingRatePlanName | String | Name of the PMS rate plan a member holding this tier books, resolved per property by name at quote time (the Cloudbeds delivery, where the property prices the stay). A name rather than a rate plan id because one rung serves every property in the partner. NULL means this rung delivers no tagged plan. |
| displayName | String! | |
| displayOrder | Int! | Ladder position, 1-based ascending. NOTE: backed by loyalty_tier_configs.tier_level, NOT the table’s display_order column. Sort ladder axes on this. |
| eligibility | String | |
| id | ID! | |
| maxPerksAllowed | Int! | |
| partnerId | String | |
| secretBenefits | Boolean! | Secret benefits — hide benefit details unless member has reached this tier |
| sourceLocale | String! | BCP-47 locale this tier config was authored in. Drives the source language used by translation.service. |
| tierByline | String | |
| tierColor | String | |
| tierIcon | String | |
| tierName | TierName! | |
| tierTextColor | String | |
| tierType | TierType! | |
| translations | JSONObject | Auto-translated content per locale |
| updatedAt | DateTimeISO! |
LoyaltyTransaction
Object
Loyalty points transaction record
| Field | Type | Description |
|---|---|---|
| balanceAfter | Int | ⚠️ Deprecated. Sunset 2026-12-08: No longer computed. Use getMemberBalance query instead. Removal is blocked by the published partner SDK document GET_LOYALTY_TRANSACTIONS_WITH_METRICS (packages/sdk-core/src/graphql/server-operations.ts), and by GET_LOYALTY_TRANSACTIONS_WITH_METRICS, GET_ACCOUNT_PAGE_DATA, GET_WHITE_LABEL_LOYALTY_DATA and GET_LOYALTY_TRANSACTIONS_BY_MEMBER (packages/api-client/src/operations/member.ts). GET_LOYALTY_TRANSACTIONS (member.ts) and the LOYALTY_TRANSACTION_FIELDS fragment (packages/api-client/src/fragments/booking.fragments.ts) also select it but nothing uses them; delete them with the field — akin-travel/issues#5572 |
| bookingChannelName | String | Booking channel from PMS (e.g., Direct, Booking.com) |
| bookingCheckIn | String | Booking check-in date YYYY-MM-DD (for booking transactions) |
| bookingId | String | |
| bookingSourceType | String | Booking source type (web_direct, admin_direct, direct, pms, consolidated) |
| createdAt | DateTimeISO! | |
| currency | String | Original booking currency code |
| description | String | |
| earnBasis | String | What this BOOKING leg is earned on (akin-travel/issues#5688, epic #5683): FORFEIT for a cancel / no-show whose loyalty_forfeit_verdict is EARNS — the member forfeited money and keeps the Points on the forfeited share, dated at the forfeit — or COMPLETED_STAY for every other booking leg, including one that earns nothing. NULL on the referral and adjustment legs. The activity-statement label (‘Forfeited booking’ vs ‘Completed stay’) keys on this, never on bookingStatus. |
| eligibilityReasonCode | String | Eligibility reason code for BOOKING transactions (null = eligible). Codes: STATUS_NOT_COMPLETED, PRE_MEMBERSHIP_OUTSIDE_WINDOW, OTA_NOT_ELIGIBLE, NON_REVENUE_CHANNEL. |
| exchangeRate | Float | Exchange rate to USD at time of booking |
| expiresAt | DateTimeISO | When these points expire (null = never) |
| id | ID! | |
| isDuplicate | Boolean | True when this transaction is a same-day same-property duplicate of another transaction for this member — computed server-side via ROW_NUMBER so pagination cannot split a duplicate group across pages (akin-travel/issues#1231). |
| isPermanent | Boolean | True if points never expire (tattoo, etc.) |
| loyaltyForfeitFraction | Float | The forfeited share behind a FORFEIT earnBasis, 0-1 (akin-travel/issues#5686): below 1 the statement adds a pro-rata line (‘25% of the stay was charged’). NULL on every other leg. |
| memberId | String! | |
| membershipYear | Int | Membership year (-1 = pre-membership, 1+ = membership years, null = unknown) |
| nightsMemberOrigin | Int | Nights (member origin scope) |
| nightsNetwork | Int | Nights (network scope) |
| nightsPartner | Int | Nights (partner scope - requires context) |
| pointsChange | Int! | |
| propertiesMemberOrigin | Int | Distinct properties (member origin scope) |
| propertiesNetwork | Int | Distinct properties (network scope) |
| propertiesPartner | Int | Distinct properties (partner scope - requires context) |
| propertyId | String | Property ID of the booking that produced this transaction (populated only via loyaltyTransactionsWithMetrics; null on the plain ledger query) |
| reason | String | |
| relatedMemberEmail | String | Obfuscated email of the related member (for referral transactions) |
| source | String | Source of this transaction (booking, referral, preference, adjustment) |
| spendMemberOrigin | Float | Spend in USD (member origin scope) |
| spendNetwork | Float | Spend in USD (network scope) |
| spendOriginal | Float | Spend in original booking currency |
| spendPartner | Float | Spend in USD (partner scope - requires context) |
| staysMemberOrigin | Int | Stays (member origin scope) |
| staysNetwork | Int | Stays (network scope) |
| staysPartner | Int | Stays (partner scope - requires context) |
| transactionType | LoyaltyTransactionType! |
LoyaltyTransactionDirection
Enum
Direction of a server-to-server partner points write
| Value | Description |
|---|---|
CREDIT | |
DEBIT |
LoyaltyTransactionType
Enum
Type of loyalty transaction
| Value | Description |
|---|---|
ADJUSTED | |
ADJUSTMENT | |
BONUS | |
BOOKING | |
EARN | |
EARNED | |
EXPIRATION | |
EXPIRED | |
REDEMPTION | |
REFERRAL | |
REFERRAL_REFERRED | |
REFERRAL_REFERRER | |
SPENT |
LoyaltyTransactionsWithMetricsResult
Object
Paginated loyalty transactions with tier metrics
| Field | Type | Description |
|---|---|---|
| items | [LoyaltyTransaction!]! | |
| totalCount | Int! |
Member
Object
Member of the loyalty program
| Field | Type | Description |
|---|---|---|
| accessibilityNeeds | String | Member-supplied accessibility needs, free text (may be GDPR Art. 9 health data — optional, never inferred). |
| addressCity | String | Postal address — city. |
| addressCountryCode | String | Postal address — ISO 3166-1 alpha-2 country code, upper-case. |
| addressLine1 | String | Postal address — line 1. |
| addressLine2 | String | Postal address — line 2. |
| addressPostalCode | String | Postal address — postal code. |
| addressRegion | String | Postal address — state / region. |
| attribution | MemberAttribution | Why this member is in the viewing partner’s cohort, and until when. NULL means not resolved for this viewer (GLOBAL scope / policy dark) — it is NOT a tie and must never render as a default one |
| avatarModeration | String | Avatar moderation status: null (pending), approved, rejected |
| avatarUrl | String | URL of member profile avatar image |
| closedAt | DateTimeISO | When the member closed their account (soft-delete); row is hard-purged 3 years after this |
| countryCode | String | |
| createdAt | DateTimeISO! | |
| dateOfBirth | String | Member-supplied date of birth, YYYY-MM-DD (a DATE column, no timezone). Refused when in the future or under 16 years ago. |
String! | ||
| emailPreferences | JSONObject | Per-template-code email opt-in/out map. Shape: { “<templateCode>”: boolean }. Absent entry = use default (transactional always on; marketing falls back to marketing_opt_in). See akin-travel/issues#1563 |
| emergencyContactName | String | Emergency contact — name. |
| emergencyContactPhone | String | Emergency contact — phone number, validated like the member phone. |
| firstName | String | |
| gender | String | One of FEMALE | MALE | NON_BINARY | SELF_DESCRIBED | PREFER_NOT_TO_SAY (MEMBER_GENDER_VALUES). A String rather than an enum so the hand-rolled clients need no codegen; validated against the set on write. |
| genderSelfDescription | String | Free-text gender, carried only when gender is SELF_DESCRIBED; cleared for every other token. |
| id | ID! | |
| initialBonusAwardedAt | DateTimeISO | When initial signup sync was performed (one-time bonus) |
| lastName | String | |
| loyaltyNumber | String | |
| marketingOptIn | Boolean | |
| networkYearStart | DateTimeISO | When member network year began (for annual reset) |
| originPartnerId | String | Partner where member originally signed up |
| originPropertyId | String | Property where member originally signed up (for QR code tracking) |
| perkPreferences | [String!] | |
| phone | String | |
| placeTypePreferences | [String!] | |
| preferredLanguage | String | |
| privacyAcceptedAt | DateTimeISO | Server-stamped time the member accepted the Privacy Policy |
| privacyAcceptedVersion | String | Published Privacy Policy version the member accepted (e.g. v24.05.2026). See akin-travel/issues#1820 |
| processingObjectionAt | DateTimeISO | GDPR Art. 21 (AKIN-1023#AKIN-1039): when a staff-upheld objection to legitimate-interests processing took effect. Non-NULL suppresses every MARKETING-category email at the send-time gate, ahead of marketingOptIn and emailPreferences. NULL = no active objection. Set only by resolveProcessingObjection, never by the member. |
| signedUpByUserId | String | Dashboard/staff user who enrolled this member (for staff competition leaderboard) |
| signupLocation | SignupLocation | Where the member originally signed up |
| status | MemberStatus | |
| termsAccepted | Boolean | |
| termsAcceptedAt | DateTimeISO | Server-stamped time the member accepted the Customer T&C |
| termsAcceptedVersion | String | Published Customer T&C version the member accepted (e.g. v24.05.2026); pre-v24.05.2026 for legacy rows. See akin-travel/issues#1820 |
| updatedAt | DateTimeISO! | |
| utmCampaign | String | UTM campaign parameter captured at signup (e.g., spring2026) |
| utmMedium | String | UTM medium parameter captured at signup (e.g., newsletter, cpc) |
| utmSource | String | UTM source parameter captured at signup (e.g., email, google) |
| vibePreference | String |
MemberAttribution
Object
Why a member is in the viewing partner’s cohort, and when that ceases
| Field | Type | Description |
|---|---|---|
| checkIn | String | Check-in of the booking behind the tie (YYYY-MM-DD). Null for WHITE_LABEL — a standing enrolment owns no booking |
| checkOut | String | Check-out of the booking behind the tie (YYYY-MM-DD). Null for WHITE_LABEL |
| tie | MemberQualifyingTie! | The member’s strongest qualifying tie to the viewing partner |
| visibleUntil | String | Last day this tie keeps the member in the cohort (YYYY-MM-DD): check-in for CANCELLED_UPCOMING, check-out + 60 days for POST_STAY_WINDOW. Null when the tie has no date-bounded expiry (BOOKING_WINDOW rolls into the post-stay window; WHITE_LABEL is standing) — never a guess |
MemberProfile
Object
Comprehensive member profile with statistics
| Field | Type | Description |
|---|---|---|
| bookingHistory | [Booking!]! | Booking history for this member |
| favoriteReward | String | Most frequently selected reward/perk |
| lifetimeStats | MemberStats! | Lifetime statistics |
| member | Member! | Core member data |
| membershipYearStats | MemberStats! | Statistics for current membership year (from join date anniversary) |
| originPartnerName | String | Name of origin partner |
| originPropertyName | String | Name of origin property (for QR code tracking) |
MemberQualifyingTie
Enum
| Value | Description |
|---|---|
BOOKING_WINDOW | |
CANCELLED_UPCOMING | |
POST_STAY_WINDOW | |
WHITE_LABEL |
MemberStats
Object
Member statistics for a time period
| Field | Type | Description |
|---|---|---|
| akinPoints | Float! | AKIN points in this period (derived from bookings; masked to 0 for partner staff when the Data Availability Policy withholds it) |
| originStays | Int! | Number of stays at origin partner |
| totalNights | Int! | Total number of nights across all properties |
| totalStays | Int! | Total number of stays |
MemberStatus
Enum
| Value | Description |
|---|---|
ACTIVE | |
BLOCKED | |
CLOSED | |
INACTIVE | |
VIP |
MemberTierHistory
Object
Member tier change audit record
| Field | Type | Description |
|---|---|---|
| changeReason | TierChangeReason! | |
| createdAt | DateTimeISO! | |
| id | ID! | |
| memberId | String! | |
| metadata | String | |
| newTier | String! | |
| oldTier | String! | |
| pointsAtChange | Int | Points balance snapshot at the tier change. Withheld (null) from partner-facing reads under Data Availability Policy §4 — null means withheld, not zero. |
| staysAtChange | Int! |
MemberTierHistoryConnection
Object
| Field | Type | Description |
|---|---|---|
| edges | [MemberTierHistoryEdge!]! | |
| pageInfo | PageInfo! | |
| totalCount | Int! |
MemberTierHistoryEdge
Object
| Field | Type | Description |
|---|---|---|
| cursor | String! | |
| node | MemberTierHistory! |
MemberUser
Object
Member user info from GIP
| Field | Type | Description |
|---|---|---|
| countryCode | String | |
String! | ||
| emailVerified | String! | |
| firstName | String | |
| id | ID! | |
| lastName | String | |
| marketingOptIn | String | |
| name | String! | |
| phone | String | |
| phoneVerified | Boolean! | |
| termsAccepted | String | |
| termsAcceptedAt | String |
PageInfo
Object
| Field | Type | Description |
|---|---|---|
| endCursor | String | |
| hasNextPage | Boolean! | |
| hasPreviousPage | Boolean! | |
| startCursor | String |
PaginationInput
Input
| Field | Type | Description |
|---|---|---|
| after | String | |
| before | String | |
| first | Int | |
| offset | Int |
PartnerMemberRead
Object
Partner-scoped member identity + current loyalty standing (tier).
| Field | Type | Description |
|---|---|---|
String! | ||
| firstName | String | |
| lastName | String | |
| memberId | ID! | AKIN member ID |
| tier | TierName | Member’s current calculated tier with this partner (current membership year) |
PasskeyAuthResponse
Object
Passkey authentication response
| Field | Type | Description |
|---|---|---|
| customToken | String! | Firebase custom token for authentication |
| tenantId | String! | GIP tenant ID the token was created for |
PaymentStatus
Enum
| Value | Description |
|---|---|
FAILED | |
PAID | |
PENDING | |
REFUNDED |
RecordLoyaltyTransactionInput
Input
Server-to-server partner points credit/debit
| Field | Type | Description |
|---|---|---|
| description | String | Optional longer description |
String | Member email (matched case-insensitively) | |
| externalId | String | Partner’s own external id for the member (as set on enrollment) |
| idempotencyKey | String! | Idempotency token. A retry with the same token returns the original transaction and writes nothing. |
| memberId | ID | AKIN member id |
| points | Int! | Positive points magnitude to credit or debit |
| reason | String! | Short reason, surfaced in the member activity statement |
| type | LoyaltyTransactionDirection! |
RecordLoyaltyTransactionResult
Object
Result of a server-to-server partner points write
| Field | Type | Description |
|---|---|---|
| balance | Float | Member’s network-wide points balance after the write. Withheld (null) from server-to-server partner-key callers under Data Availability Policy §4 — null means withheld, not zero. |
| idempotentReplay | Boolean! | True when this matched a prior idempotency key — no new write happened |
| memberId | ID! | AKIN member id the points were applied to |
| pointsChange | Int! | Signed delta applied (+credit / -debit) |
| transactionId | ID! | The loyalty transaction (adjustment) id |
| type | LoyaltyTransactionDirection! |
RedeemRewardInput
Input
Server-to-server partner reward redemption
| Field | Type | Description |
|---|---|---|
String | Member email (matched case-insensitively) | |
| externalId | String | Partner’s own external id for the member (as set on enrollment) |
| idempotencyKey | String! | Idempotency token. A retry with the same token returns the original redemption and writes nothing. |
| memberId | ID | AKIN member id |
| note | String | Optional note stored with the claim |
| propertyId | ID! | Property where the reward was redeemed. Must belong to the key’s partner. |
| rewardInstanceId | ID! | Reward instance id — the id returned by rewardInstancesForTier |
RedeemRewardResult
Object
Result of a server-to-server partner reward redemption
| Field | Type | Description |
|---|---|---|
| idempotentReplay | Boolean! | True when this matched a prior idempotency key — no new write happened |
| memberId | ID! | AKIN member id the reward was recorded against |
| propertyId | ID! | Property the redemption was recorded against |
| redeemedAt | DateTimeISO! | When the redemption was recorded |
| redemptionId | ID! | The member activity (REWARD_CLAIM) id |
| remainingRedemptions | Int | Redemptions of this reward still available to the member under the tightest limit enforced on this claim. Null when no limit is enforced. |
| rewardId | ID! | The underlying reward template id — the identity network limits are counted on |
| rewardInstanceId | ID! | The reward instance that was redeemed |
| rewardName | String | Effective reward name at redemption time |
| tier | String | Member’s tier at redemption time |
RequestMagicLinkResult
Object
Result of magic link request
| Field | Type | Description |
|---|---|---|
| error | String | Error message if failed |
| success | Boolean! | Whether the request was successful |
| verificationUrl | String | Verification URL (E2E testing only - returned when E2E_AUTO_CONFIRM=true) |
SignUpErrorCode
Enum
Why a sign-up was refused. LEGAL_VERSION_STALE: a legal version is no longer in force — read currentLegalVersions again and ask the member to confirm. LEGAL_ACCEPTANCE_REQUIRED: one or both legal versions are missing (akin-travel/issues#6734)
| Value | Description |
|---|---|
LEGAL_ACCEPTANCE_REQUIRED | |
LEGAL_VERSION_STALE |
SignUpPasswordlessInput
Input
Passwordless signup input for website/white-label members
| Field | Type | Description |
|---|---|---|
| attributionToken | String | Time-bounded HMAC-signed assertion, minted by the white-label server, of the partner whose HOST this sign-up came from. Every other partner signal on this input is client-chosen by design (QR / referral / cookie attribution); this is the only one the API can verify, so it is what gates the Row 1 program-terms write — a consent record must not attach to a partner the caller merely named. It expires a day after it is minted, which is a whole sign-up session and not a page render (akin-travel/issues#5570) — what makes a scraped copy useless is the signature, not the clock. Omitted by the website and the SDK, neither of which writes a program-terms version. See akin-travel/issues#3944 |
| captchaToken | String | Cloudflare Turnstile token from the shared invisible widget (akin-travel/issues#3199). Transport-only — consumed by the RequireCaptcha middleware before the resolver body runs and never persisted on the member. Nullable because enforcement is dark-launched behind ENABLE_AUTH_CAPTCHA while the native clients have no Turnstile path; a non-null field would break every existing caller on schema validation alone. |
| countryCode | String | Country code (e.g., AU, US, GB) |
String! | Email address | |
| firstName | String! | First name |
| forApp | Boolean | The native app is asking for this link (akin-travel/issues#6652). The link then carries the one path the apps claim (/\{locale\}/auth/app-verify) instead of the web path, so it opens the app on a phone that has one; the website forwards the app path to the web sign-in page, so a phone without the app still signs in. Omit it on the web: a web link must open in the browser |
| lastName | String! | Last name |
| locale | String | User locale for email links (e.g., en, de, fr) |
| marketingOptIn | Boolean | Row 3 — marketing opt-in for AKIN, across the whole AKIN network (spec §2.3). On the website this is the single marketing opt-in |
| partnerId | ID | Partner ID for attribution |
| partnerMarketingOptIn | Boolean | Row 2 — marketing opt-in for the attributed white-label Partner specifically (spec §2.2). Recorded separately from marketingOptIn: §2.4 forbids collapsing rows 2 and 3 into a single marketing flag. See akin-travel/issues#3427 |
| partnerSlug | String | Partner slug for URL building (preferred over partnerId) |
| phone | String | Phone number |
| privacyAcceptedVersion | String | Published Privacy Policy version the member agreed to (must match the current LEGAL_PRIVACY_VERSION). See akin-travel/issues#1820 |
| programTermsAccepted | Boolean | Row 1 of the white-label consent screen included this program’s WL Program Terms & Conditions (Sign-Up Flow & Consent Spec 18 Jul 2026 §2.1). A boolean, NOT a version: the accepted version is resolved server-side from the attributed program, because the program documents are server-rendered and a client cannot be the authority on which one it showed. See akin-travel/issues#3427 |
| propertyId | ID | Property ID for signup tracking (from QR code links) |
| redirectUrl | String | Custom verification page URL for SDK integrations (e.g., https://viajero.com/auth/verify ) |
| referralCode | String | Referral code from referrer |
| signedUpByUserId | ID | Staff user id credited for this signup (the su param on a promotions QR link). Spoofable — server-validated against the attributed partner and dropped to NULL when it does not belong. See akin-travel/issues#3434 |
| signupLocation | SignupLocation | Surface the signup originated from. WEBSITE and WHITE_LABEL both make legal-version acceptance mandatory (white-label since its own clickwrap shipped in akin-travel/issues#3427); SDK stays relaxed until it has an acceptance surface. See akin-travel/issues#1820 |
| termsAccepted | Boolean | Terms and conditions acceptance |
| termsAcceptedVersion | String | Published Customer T&C version the member agreed to (must match the current LEGAL_TERMS_VERSION). See akin-travel/issues#1820 |
| utmCampaign | String | UTM campaign parameter for campaign tracking |
| utmMedium | String | UTM medium parameter for campaign tracking |
| utmSource | String | UTM source parameter for campaign tracking |
| viaHandle | String | Readable QR handle credited for this signup (the via param on a cascading-QR link, e.g. paula-diaz). Resolved to a dashboard user and validated against the attributed partner exactly like su; wins over su when both are present. See akin-travel/issues#3507 |
SignUpResult
Object
Result of passwordless signup
| Field | Type | Description |
|---|---|---|
String | Email address | |
| error | String | Error message if failed |
| errorCode | SignUpErrorCode | Typed reason for a refusal, when it has one (akin-travel/issues#6734). Branch on this, not on error: LEGAL_VERSION_STALE means read currentLegalVersions again and ask the member to confirm. Null on success and on refusals without a code |
| success | Boolean! | Whether signup was successful |
| userId | String | User ID if successful |
| verificationUrl | String | Verification URL (E2E testing only - returned when E2E_AUTO_CONFIRM=true) |
SignupLocation
Enum
| Value | Description |
|---|---|
ANDROID | |
DASHBOARD | |
IOS | |
SDK | |
WEBSITE | |
WHITE_LABEL |
SyncedStay
Object
A synced stay from initial signup sync
| Field | Type | Description |
|---|---|---|
| checkIn | String! | Check-in date (YYYY-MM-DD) |
| checkOut | String! | Check-out date (YYYY-MM-DD) |
| confirmationNumber | String | Confirmation number |
| points | Int! | Points awarded (0 for future stays) |
| propertyName | String! | Property name |
| source | String | Booking source |
TierBenefitGroup
Object
A group of rewards scoped to all properties or a specific property
| Field | Type | Description |
|---|---|---|
| propertyId | String | Property ID (only when scope=PROPERTY) |
| propertyName | String | Property name (only when scope=PROPERTY) |
| rewardNames | [String!]! | Reward names in this group |
| scope | String! | ALL_PROPERTIES or PROPERTY |
TierBenefits
Object
Tier benefits - rewards available at a specific tier
| Field | Type | Description |
|---|---|---|
| groups | [TierBenefitGroup!] | Rewards grouped by scope (all properties vs per-property) |
| rewardNames | [String!]! | Flat list of all reward names at this tier (backward compat) |
| tierName | String! |
TierChangeReason
Enum
Reason for tier change
| Value | Description |
|---|---|
MANUAL | |
POINTS | |
PROMOTION | |
STAYS |
TierName
Enum
Loyalty tier levels
| Value | Description |
|---|---|
TIER1 | |
TIER2 | |
TIER3 | |
TIER4 | |
TIER5 |
TierType
Enum
How a tier can be achieved - POINTS for activity-based progression, INVITATION for manual assignment only
| Value | Description |
|---|---|
INVITATION | |
POINTS |
UpdateMemberInput
Input
| Field | Type | Description |
|---|---|---|
| accessibilityNeeds | String | |
| addressCity | String | |
| addressCountryCode | String | ISO 3166-1 alpha-2; upper-cased on write |
| addressLine1 | String | |
| addressLine2 | String | |
| addressPostalCode | String | |
| addressRegion | String | |
| avatarUrl | String | URL of member profile avatar image |
| countryCode | String | |
| dateOfBirth | String | YYYY-MM-DD; a real past date, member at least 16 |
| emergencyContactName | String | |
| emergencyContactPhone | String | Validated like the member phone |
| firstName | String | |
| gender | String | FEMALE | MALE | NON_BINARY | SELF_DESCRIBED | PREFER_NOT_TO_SAY |
| genderSelfDescription | String | Accepted only with gender = SELF_DESCRIBED; cleared otherwise |
| lastName | String | |
| loyaltyNumber | String | |
| marketingOptIn | Boolean | |
| perkPreferences | [String!] | |
| phone | String | |
| placeTypePreferences | [String!] | |
| preferredLanguage | String | |
| termsAccepted | Boolean | |
| vibePreference | String |