DOM Package

Custom Guest Invitations

Build your own guest invitation experience: invite guests, share links, accept invitations on your site, and track them

Members can invite guests to your venue before a visit. Tiquo offers three ways to do this, all driven by the same Guest Flow:

  • Embed the ready-made Guest Pre-Registration form (Copy embed code).
  • Link to the hosted form (Copy guest flow URL).
  • Build your own interface with the DOM Package, the Hosted Package or the Customer API. This page covers that option.

The DOM Package (1.9.1 or newer) and the Hosted Package (window.Tiquo) use the same method names. Every method needs a signed-in customer with a completed profile, and the session decides the organisation and customer.

1. Set up the Guest Flow

  1. In Settings > Registration, under Guest Pre-Registration, create and save a Guest Flow. See Guest Passes for allowances and locations.
  2. Under Guest invitation destination, choose where guests land: a Customer portal, or My own website with your HTTPS page URL (see step 4).
  3. Open the flow's menu and choose Copy Guest Flow ID. The flow editor's Custom UI · DOM or hosted package section also has the ID and a starter snippet.
  4. Allow your website's domain under Settings > Website SDK.

2. Invite guests

// After OTP sign-in and profile completion.
const flow = await tiquo.getGuestFlow('GUEST_FLOW_ID');
if (!flow) throw new Error('This Guest Flow is unavailable.');

// How many guests can be invited right now.
const guestLimit = flow.requiresGuestPass
  ? flow.availableGuestPassQuantity
  : flow.maxGuestsPerSubmission;
// Disable the form when guestLimit is 0.

// Build this once per submission and reuse it exactly on retry.
const submission = {
  flowId: flow.id,
  locationId: selectedLocationId, // from flow.locations; needed when there are several
  visitDate: selectedVisitDate,   // YYYY-MM-DD in the location's timezone
  guests: guestFirstNames.map((guestFirstName) => ({
    token: crypto.randomUUID(),
    guestFirstName,
  })),
};
const { invitations } = await tiquo.createGuestInvitations(submission);

// Store invitation.token and invitation.inviteUrl to track and re-share later.
await navigator.clipboard.writeText(invitations[0].shareText);
  • Show a location selector when flow.locations has more than one entry.
  • All guests in a submission are created together, or none are.
  • In limited mode each invitation reserves one of the member's Guest Passes until it's used, expires or is revoked.
  • Creation returns links and messages only; it doesn't email the guest.

Validation rules, retries and errors are listed in Guest Invitations and Passes.

3. Show the member's invitations

getGuestPasses() lists the member's live invitations: ones they sent (status: 'assigned') and ones they received and accepted (status: 'accepted'). Each entry is one invitation, not a count. To show how many Guest Passes the member has left, use getPassBalances().

It doesn't include share tokens or links, and it shows a sent invitation as assigned whether or not the guest has accepted yet. To show "waiting for your guest" or a Share again button, keep the tokens you stored at creation and read each one:

const invitation = await tiquo.getGuestInvitation(storedToken);
// invitation.status: 'pending' | 'accepted' | 'revoked' | 'expired'
// invitation.inviteUrl: the link to share again

With the Hosted Package you can render the list without code:

<div data-tiquo-guest-passes data-tiquo-guest-pass-status="assigned" data-tiquo-limit="20">
  <article data-tiquo-guest-pass-item>
    <h3 data-tiquo-guest-pass="personName"></h3>
    <time data-tiquo-guest-pass="visitDate"></time>
    <p data-tiquo-guest-pass="venueName"></p>
  </article>
  <p data-tiquo-empty>No upcoming guests.</p>
</div>

4. Accept on your own page

With My own website, each invitation link is your page URL plus ?invitation=<token>. On that page:

  1. Keep the invitation parameter in the URL through sign-in.
  2. Sign the guest in with the same organisation's public key, and complete their profile.
  3. Call acceptGuestInvitation(token), then remove the parameter.
  4. Optionally show their invitations with getGuestPasses(), filtered to status === 'accepted'. Accepting adds nothing to the guest's own pass balance; the Guest Pass stays with the host.
const url = new URL(window.location.href);
const token = url.searchParams.get('invitation');

tiquo.onAuthStateChange(async (session) => {
  if (!token || !session?.customer?.id) return;
  try {
    await tiquo.acceptGuestInvitation(token);
    url.searchParams.delete('invitation');
    history.replaceState({}, '', url.pathname + url.search + url.hash);
  } catch (error) {
    // Keep the token so the guest can retry. Show expired, already-accepted
    // and own-invitation errors from error.message.
  }
});

An invitation can't be read before the guest signs in, so show a general welcome until then. With a Customer portal destination, Tiquo hosts this page at /invite/<token> on the portal.

5. At reception

Staff register accepted guests from the host's registration modal or through See Upcoming Guests. Registration uses the reserved Guest Pass. See Customer Registration.

Limitations

  • Members can't cancel an invitation they sent. Staff can revoke pending invitations in Settings > Registration under Guest Flow invitations. An unaccepted invitation expires at the end of its visit date.
  • Invitations can't be edited. Create a new one with a new token.
  • The member's invitation list has no tokens, links or history. Store tokens yourself to track or re-share.
  • In the Hosted Package, availableGuestsLabel on a guest flow reads "N Guest Passes available" even for unlimited flows. Use maxGuestsLabel when requiresGuestPass is false.
  • Booking attendees (guests added to a booking at checkout) are a separate feature and don't use Guest Flows or Guest Passes.

Errors

DOM Package methods throw a TiquoAuthError with code GUEST_PRE_REGISTRATION_FAILED and the server's message, for example This guest invitation has expired. Hosted Package methods reject with an Error carrying the same message.

En esta página