DOM Package

Customer Data

Build profile forms and use DOM Package helpers for profile, email, address, preference, and company data

After a customer signs in, the DOM Package can call Customer API endpoints on their behalf. These helpers add the authentication headers and handle token refresh automatically.

Created and first active

The customer returned by getUser(), authentication state callbacks, and profile create or update responses includes two read-only system timestamps in published DOM Package 1.6.3 and newer:

const session = await tiquo.getUser();
const customer = session?.customer;

if (customer) {
  console.log(customer.createdAt); // number | undefined
  console.log(customer.firstActiveAt); // number | undefined

  const created = customer.createdAt
    ? new Date(customer.createdAt)
    : undefined;
  const firstActive = customer.firstActiveAt
    ? new Date(customer.firstActiveAt)
    : undefined;
}
FieldTypeDefinition
createdAtreadonly number | undefinedUnix timestamp in milliseconds for when the customer profile was created
firstActiveAtreadonly number | undefinedUnix timestamp in milliseconds for the customer's first qualifying activity; absent until activity exists

Created and First Active are separate values. Customer activity never changes or backdates createdAt. firstActiveAt is not the database insertion time, and activities performed by staff, systems, integrations, or imports do not set it.

The values are equal when a customer creates their own account and is immediately active in the same flow. An imported or staff-created profile can have a later First Active timestamp, or no value until the customer becomes active.

Both fields use Unix milliseconds and do not include a display timezone. Convert them with new Date(timestamp) and format them using the venue or customer's timezone. They cannot be changed with updateProfile(); the SDK strips both keys if untyped JavaScript supplies them dynamically.

Updating the customer profile

await tiquo.updateProfile({
  firstName: 'John',
  lastName: 'Smith',
  phone: '+1234567890',
});

This calls the Update Profile endpoint under the hood. Only the fields you include change. It resolves to { success, customer } with the updated customer, and getUser() returns the updated customer afterwards.

FieldDescription
firstName, lastNameCustomer name. displayName is derived from these and can't be set
title, customTitle, nicknameSee Titles and nicknames
phone, phoneCountry, phonesThe primary phone or the full phone list. See National-format phone numbers
emailsThe full email list. See Email addresses
profilePhotoA photo URL, File, Blob, or data or blob URI. See Profile photos
profileParametersCustomer-editable parameters. See Editing customer preferences
portalSlugHolds the update to one customer portal's profile settings. See Portal profile fields
addressesThe full address list; needs portalSlug. See Addresses
taxIdPersonal tax number; needs portalSlug. See Tax ID

Only pass these editable fields. createdAt and firstActiveAt are system-managed and read-only. To put the fields together into a form, see Building a profile form.

Titles and nicknames

updateProfile() accepts title, customTitle, and nickname, and the customer from getUser() returns them. title is a code (mr, mrs, ms, miss, mx, dr, prof) or other with the text in customTitle. Any other text you send as title, such as 'Sir', is saved as a custom title. Send an empty string to clear either field. These fields are typed in DOM Package 1.11.0 and newer.

await tiquo.updateProfile({
  title: 'dr',
  nickname: 'Sam',
});

const session = await tiquo.getUser();
console.log(session?.customer?.title); // "dr"
console.log(session?.customer?.titleText); // "Dr"
console.log(session?.customer?.nickname); // "Sam"

titleText is the title written out in the venue's document language, or the customTitle text when title is other. It's read-only and absent when no title is set, and it's typed in DOM Package 1.16.1 and newer. To show the title in your website's own language instead, map the title code to your own wording, for example mrs as "Frau". See Update Profile for the full rules.

National-format phone numbers

Use the phone number field helper (TiquoPhone.createField()) on phone inputs so international numbers are formatted correctly, then send its e164 value and countryCode.

The SDK removes spaces and other formatting from phone numbers before sending them. A number without a calling code, such as 07911 123456, needs the country the customer picked so Tiquo can add the right calling code. Pass it as phoneCountry, or as countryCode on each entry of a full phones list:

await tiquo.updateProfile({
  phone: '07911 123456',
  phoneCountry: 'GB', // saved as +447911123456
});

await tiquo.updateProfile({
  phones: [
    { number: '07911 123456', countryCode: 'GB', isPrimary: true, order: 0 },
    { number: '+33 6 12 34 56 78', isPrimary: false, order: 1 },
  ],
});

Without a country, no calling code is inferred. Numbers that start with + or 00 keep their own calling code. The phone utilities in Utilities and Errors can detect and validate countries in your form.

Email addresses

emails replaces the customer's whole email list. Use it to reorder addresses, remove one, or change which one is primary:

const session = await tiquo.getUser();
const emails = session?.customer?.emails ?? [];

// Make the second address the primary one
await tiquo.updateProfile({
  emails: emails.map((entry, index) => ({ ...entry, isPrimary: index === 1 })),
});

Include every address the customer keeps; an address left out of the list is removed. At least one address must remain, and duplicates are rejected. Tiquo renumbers order by position in the list, and if no entry is primary, the first one becomes primary.

emails can't add a new address. An address that isn't already on the profile rejects the update with "Verify ADDRESS before adding it to your profile." To add a secondary email (up to five per profile), verify it with the Customer API's email verification endpoints, using the access token from getAccessToken(). The DOM Package has no helper for these endpoints.

await tiquo.getUser(); // refreshes the access token if needed

const apiBase = 'https://edge.tiquo.app/api/client/v1';
const headers = {
  Authorization: `Bearer ${tiquo.getAccessToken()}`,
  'Content-Type': 'application/json',
};

// 1. Email a six-digit code to the new address
await fetch(`${apiBase}/profile/email-verification/send`, {
  method: 'POST',
  headers,
  body: JSON.stringify({ email: 'second@example.com' }),
});

// 2. Check the code the customer entered
const response = await fetch(`${apiBase}/profile/email-verification/verify`, {
  method: 'POST',
  headers,
  body: JSON.stringify({ email: 'second@example.com', otp: codeInput.value }),
});
const result = await response.json();
if (!response.ok) throw new Error(result.error);

// 3. Save the returned list so getUser() includes the new address
await tiquo.updateProfile({ emails: result.data.emails });

The SDK caches the profile, so step 3 matters: without it, a later emails save built from getUser() would leave out the new address and remove it.

Profile photos

Profile photos can be passed as a hosted URL, a browser File or Blob, or a data:image/... or blob:... URI. File and blob uploads support JPEG, PNG, GIF, and WebP images up to 5 MB.

const file = fileInput.files?.[0];

if (file) {
  await tiquo.updateProfile({
    profilePhoto: file,
  });
}

You can also upload only the profile photo:

await tiquo.uploadProfilePhoto(file);

When a file or blob is provided, the SDK requests a secure upload URL, uploads the image to Tiquo storage, then saves the resulting image URL on the authenticated customer's profile.

Editing customer preferences

Authenticated customers can edit their own customer-editable parameters with profileParameters. The backend rejects staff-only parameters. Keys can be a system ID, custom customer parameter schema ID, name, or normalized name.

await tiquo.updateProfile({
  profileParameters: {
    system_marketing_opt_in: marketingCheckbox.checked,
    system_sms_opt_in: smsCheckbox.checked,
  },
});

Send an explicit boolean for each consent preference: true opts in and false opts out. Omitted keys stay unchanged. The server manages the opt-in timestamp; do not supply a date. Read the updated profile with getUser() or use the returned customer. Consent parameter values are serialized JSON strings such as {"Opt-In":"true"}, rather than plain booleans. The Hosted Package's profile checkboxes decode these saved values automatically.

Text, number, date, boolean, select, multiselect, and compound parameters follow their configured validation. Multiselects accept string arrays; compound values use JSON strings. Invalid options or edit permissions reject the update. See Update Profile.

Saved values are on customer.parameterValues, keyed by parameter ID and by normalized name, so Marketing Opt-In is under both system_marketing_opt_in and marketing_opt_in. customer.parameters lists the same values with each parameter's id, name, key, and type.

Listing the fields a customer can fill in

From version 1.23.0, getProfileFields() lists the custom profile fields the signed-in customer can fill in, so you can build an "Additional information" form without hard-coding the questions. It returns every active custom field whose Editable by setting includes the customer, in the order set in Tiquo, whether or not the customer has answered it yet. Staff-only, inactive, automatically filled and system fields are left out.

const { fields } = await tiquo.getProfileFields();

for (const field of fields) {
  console.log(field.name, field.description, field.type, field.value);
}
FieldDescription
idThe field's key in updateProfile({ profileParameters })
nameThe label, such as "Dietary requirements"
descriptionHelp text to show with the field; may be empty
typetext, number, date, datetime, select, multiselect, boolean, or compound
requiredWhether Tiquo marks the field as required
defaultValueThe answer to start from when the customer hasn't answered, when one is set
optionsChoices for select and multiselect fields. A choice can be { value, archived: true }: it can't be picked again, but stays valid when it is the saved answer
subFieldsThe parts of a compound field, each with name, type and optional options
validationLimits such as min and max for numbers or a pattern for text, when set
valueThe customer's saved answer, omitted when unanswered

value is always a string: the text itself, "true" or "false" for boolean, a JSON array such as '["Vegan"]' for multiselect, and a JSON object keyed by sub-field name for compound. Save answers with updateProfile(), keyed by each field's id:

await tiquo.updateProfile({
  profileParameters: {
    [aboutYou.id]: 'Training for a marathon',
    [dietary.id]: ['Vegan', 'Gluten free'],
  },
});

The list doesn't depend on any customer portal: every field it returns can be saved without portalSlug. It throws a TiquoAuthError with code GET_PROFILE_FIELDS_FAILED when the request fails. See List Profile Fields.

Portal profile fields

Each customer portal has its own profile settings in the dashboard: the customer parameters its profile editor shows (up to 25), and whether customers can edit their addresses and Tax ID. See Customer Portal. Pass a portal's slug as portalSlug to hold a website update to the same settings:

await tiquo.updateProfile({
  portalSlug: 'members',
  profileParameters: { membership_type: 'VIP' },
});

When the update includes profileParameters, addresses, or taxId, the portal must be active, or the update is rejected with "Customer portal is not available." Each profileParameters key must then also be one of that portal's profile fields, or the update is rejected with "NAME is not editable in this customer portal." This includes Marketing and SMS Opt-In.

Without portalSlug, profileParameters can update any customer-editable parameter, and addresses and taxId are rejected.

Addresses

addresses needs a portalSlug whose portal lets customers edit addresses. Otherwise the update is rejected with "Address editing is not enabled for this customer portal."

getUser() returns the saved addresses, each with its id. Start from that list so the saved addresses are kept:

const session = await tiquo.getUser();
const saved = session?.customer?.addresses ?? [];

// Add a shipping address and keep the saved ones
const result = await tiquo.updateProfile({
  portalSlug: 'members',
  addresses: [
    ...saved,
    {
      type: 'shipping',
      isDefault: false,
      addressLine1: '1 High Street',
      city: 'London',
      postalCode: 'SW1A 1AA',
      country: 'United Kingdom',
    },
  ],
});

console.log(result.customer.addresses); // saved addresses, each with an id
FieldDescription
idThe saved address's ID. Include it to keep or edit that address; omit it for a new address
typebilling, shipping, or both
isDefaultWhether this is the default address for its type
addressLine1, city, postalCode, countryRequired
label, company, addressLine2, stateOptional

addresses replaces the whole list, up to 10 addresses. A saved address left out of the list is deleted, and an id that isn't on this customer's profile rejects the update. Only one address can be the default for billing and one for shipping, and a both default counts for both. If no address is marked as the default, the first one becomes the default.

Always send the saved addresses' IDs back. An address sent without its id is created as a new address, and the saved one it replaced is deleted.

Tax ID

taxId needs a portalSlug whose portal lets customers edit their Tax ID. Otherwise the update is rejected with "Tax ID editing is not enabled for this customer portal."

await tiquo.updateProfile({
  portalSlug: 'members',
  taxId: 'PT 123456789', // saved as PT123456789
});

Tiquo removes spaces, dots, and hyphens and converts the value to uppercase. Up to 40 characters are accepted after that. An empty string clears the saved Tax ID. getUser() and updateProfile() return the saved value as customer.taxId, so a form can show it. Send taxId only when the customer changed it, since an empty value clears it.

Building a profile form

The DOM Package doesn't render forms. Build the form in your own markup, fill it from getUser(), and save it with updateProfile(). On a site builder without a build step, the Hosted Package's data-tiquo-form="profile" does the same without code.

<form id="profile-form" hidden>
  <label>First name <input name="firstName" autocomplete="given-name" required /></label>
  <label>Last name <input name="lastName" autocomplete="family-name" /></label>
  <label>Nickname <input name="nickname" /></label>
  <label>Phone <input id="profile-phone" type="tel" autocomplete="tel" /></label>
  <label><input name="marketing" type="checkbox" /> Email me news and offers</label>
  <label><input name="sms" type="checkbox" /> Text me news and offers</label>
  <p id="profile-message" role="status"></p>
  <button type="submit">Save profile</button>
</form>
import { TiquoPhone } from '@tiquo/dom-package';

const form = document.querySelector('#profile-form');
const message = document.querySelector('#profile-message');
const saveButton = form.querySelector('button[type="submit"]');
const phoneField = TiquoPhone.createField('#profile-phone');

// Consent values are saved as JSON strings such as {"Opt-In":"true"}
function isOptedIn(value) {
  try {
    const parsed = JSON.parse(value ?? 'false');
    return parsed === true || parsed?.['Opt-In'] === 'true';
  } catch {
    return value === 'true';
  }
}

function fill(customer) {
  form.elements.firstName.value = customer.firstName ?? '';
  form.elements.lastName.value = customer.lastName ?? '';
  form.elements.nickname.value = customer.nickname ?? '';
  phoneField.setValue(customer.phone ?? '');
  form.elements.marketing.checked = isOptedIn(customer.parameterValues?.system_marketing_opt_in);
  form.elements.sms.checked = isOptedIn(customer.parameterValues?.system_sms_opt_in);
}

const session = await tiquo.getUser();
if (session?.customer) {
  fill(session.customer);
  form.hidden = false;
}

form.addEventListener('submit', async (event) => {
  event.preventDefault();

  const phone = phoneField.getValue();
  if (phone.value && !phone.valid) {
    message.textContent = 'Check the phone number.';
    return;
  }

  saveButton.disabled = true;
  message.textContent = 'Saving...';

  try {
    const { customer } = await tiquo.updateProfile({
      firstName: form.elements.firstName.value.trim(),
      lastName: form.elements.lastName.value.trim(),
      nickname: form.elements.nickname.value.trim(),
      phone: phone.e164, // an empty value removes the primary phone
      phoneCountry: phone.countryCode,
      profileParameters: {
        system_marketing_opt_in: form.elements.marketing.checked,
        system_sms_opt_in: form.elements.sms.checked,
      },
    });
    fill(customer);
    message.textContent = 'Profile saved.';
  } catch (error) {
    message.textContent = error.message;
  } finally {
    saveButton.disabled = false;
  }
});

Check the phone number before saving: e164 is empty until the number is valid, and an empty phone removes the customer's primary phone. A failed save throws a TiquoAuthError with the code PROFILE_UPDATE_FAILED and a message from Tiquo, such as an invalid option for a parameter. See Utilities and Errors.

To add more fields:

  • Customer parameters: fill the control from customer.parameterValues and send it under profileParameters, as in Editing customer preferences.
  • Email addresses: follow Email addresses. Send the full emails list rather than a single email field.
  • Addresses and Tax ID: add portalSlug, fill the controls from customer.addresses and customer.taxId, and send the full address list with its IDs. See Addresses and Tax ID.

Orders, bookings, enquiries, and wallet cards

These helpers have their own guides:

  • Orders and Bookings: getOrders(), getBookings(), getUpcomingBookings(), cancelBooking(), getBookingRescheduleOptions(), rescheduleBooking(), getReceipt(), downloadReceipt(), and downloadBookingTicket()
  • Enquiries: createEnquiry() and getEnquiries()
  • Wallet Cards: getWalletCardLinks()

Company memberships

Use getCompanies() to show the authenticated customer's company memberships.

const { companies } = await tiquo.getCompanies();

for (const company of companies) {
  console.log(company.name, company.membership.relationship);
}

Each company includes the customer's membership details, including whether the customer is a company admin.

Company admins can call getCompanyColleagues() to retrieve basic contact-card information for colleagues in the same company.

const company = companies.find((item) => item.membership.isCompanyAdmin);

if (company) {
  const { colleagues } = await tiquo.getCompanyColleagues(company.id);
  console.log(colleagues);
}

If the authenticated customer is not a company admin for that company, the request returns a permissions error.

Guest invitations and passes

After customer sign-in, use the invitation helpers to build a guest-registration experience:

const { guestFlows } = await tiquo.getGuestFlows();
const flow = await tiquo.getGuestFlow('GUEST_FLOW_ID');
const { guestPasses } = await tiquo.getGuestPasses();
const invitation = await tiquo.getGuestInvitation(invitationToken);
// Invitations the member sent, with links and history (1.21.0 or newer).
const { invitations } = await tiquo.getGuestInvitations();
// The signed-in guest accepts the invitation.
const accepted = await tiquo.acceptGuestInvitation(invitationToken);

createGuestInvitations(input) accepts flowId, visitDate, optional locationId, and guests containing one UUID v4 token and guestFirstName per guest. Retain tokens when retrying an unchanged pending invitation. Share the returned inviteUrl and shareText.

Use requiresGuestPass to select the allowance: availableGuestPassQuantity for a limited flow, or maxGuestsPerSubmission otherwise. In limited mode, creating an invitation reserves a Guest Pass; staff registration consumes it.

See Custom Guest Invitations for the end-to-end walkthrough, including the guest's landing page on your own site, and Guest Invitations and Passes for fields, errors, and retry rules.

En esta página