Skip to main content
A member does not have to spend points to start talking to a concierge. An interest registers that they want an experience, opens a concierge conversation, and gives them somewhere to put their dates - with no points debited and no inventory held. When they commit, the redemption inherits that same conversation and everything they told us.
An interest holds no stock. A member who takes a week to decide can still lose the last unit - which is honest, where a silent hold would let one undecided member freeze inventory indefinitely. Check availability on the offer to show what is left.

Registering an interest

POST /v1/interests takes the offer and, optionally, everything the member already knows about their plans.
The response carries a conversationId. That thread is live immediately - pass it to sendMessage or the SSE stream and the member is talking to a concierge.
string | null
required
The concierge conversation. The redemption that follows inherits it, so this id stays valid for the whole journey.
Intake | null
required
What the member told us, or null if they told us nothing yet.

Dates are calendar dates

preferredDates and blackoutDates are YYYY-MM-DD strings, not datetimes. A datetime is rejected rather than parsed: it moves a date across a day boundary for anyone west of UTC, in the direction that loses the member their first choice. Preferred dates are stored in the order you send them - first choice first. Up to five preferred and twenty blackout dates.

Two kinds of intake, handled differently

Dietary requirements, allergies and accessibility needs are never stored.They are health data. We forward them straight to the concierge team and keep only a timestamp saying they were sent. You can submit them; you cannot read them back, because we will not be holding them.
The three health fields are accepted only on PUT /v1/interests/{id}/intake, never on the create. Sending one to POST /v1/interests is refused with INTAKE_SENSITIVE_INLINE rather than ignored - a create can fail, and details for a booking that never existed are details we should not have received.
PUT replaces the whole intake, so send the complete answer each time. A partial merge over a set of dates has no sane meaning - is an omitted preferredDates “unchanged” or “I have none”?

About phone

Optional, E.164, and stored. It is the only contact detail Expys ever holds for a member - your platform owns their identity, and we deliberately keep no email address. contactMethod: "PHONE" is therefore an instruction to you, not to us. We record it and show it to the concierge team, and we reach the member in chat.

Verifying the phone number

A number is display-only until it is verified. The concierge team will not text an unverified one — a single typo is all it takes to send a stranger somebody else’s itinerary.
The confirm returns { "verified": true | false }. false is not an error — it means the code was wrong or expired, which is an ordinary thing for somebody to do. Ask again. We store no code: Twilio generates it and owns its expiry, so there is no column here holding a one-time code and none to leak one from. Once verified and with contactMethod: "SMS", concierge messages are relayed by text and the member can simply reply. Those replies land in the same conversation the app shows — one thread, two transports.
STOP works, and it is honoured before anything else. A member who texts STOP is switched to app chat across every booking carrying that number, not just the one they replied to. START turns it back on.

Committing

Pass the interest when you create the redemption:
The redemption:
  • inherits the interest’s conversation - no second thread is opened, and redemption.conversationId is the interest’s id, not the redemption’s
  • carries the intake across, dates and order included
  • closes the interest, so it leaves our concierge queue
The interest keeps its own copy of the intake, so the conversation still reads correctly afterwards.
interest is optional. Redeeming without one behaves exactly as it always has.

Errors