CRM Integration
Routing Experience Registration Data to Your CRM
When a consumer completes a registration in any io.tt experience, the platform automatically routes their data to the brand's configured CRM. You do not need to write any CRM-specific code — collect the data in your form, pass it to the SDK signup method, and the platform handles the rest.
How It Works
The SDK is CRM-agnostic. The flow is always the same regardless of which CRM the brand uses:
- Consumer fills in the registration form
- You call the SDK signup method (
signupForCompetition,signupForBasicExperience, etc.) - io.tt receives the registration data over HTTPS
- io.tt applies the experience's field transformer server-side
- io.tt writes the data to the configured CRM — creating or updating a record
CRM configuration is managed by io.tt on behalf of the brand. You do not configure the CRM target in code.
Standard Fields
These fields are available on all signup methods and are supported by all native CRM integrations. The Type column reflects what the SDK expects from your code.
| Field | Type | Format / Constraints | Notes |
|---|---|---|---|
uid | string | Any unique identifier — typically the user's email address | Required on all signup methods. You provide this value — it is not generated by io.tt. io.tt uses it internally to detect whether this person has already registered for this experience. When email is used as the uid (the most common pattern), pass the same value for both uid and email. |
email | string | Valid email address. Max 80 chars | The actual email address — mapped to the email field in your CRM. Required for Klaviyo. Used as the primary key when deduplicating CRM records across all integrations. If you are already passing email as uid, pass it here too. |
firstName | string | Max 40 chars | |
lastName | string | Max 80 chars | |
title | string | e.g. Mr., Ms., Dr. | Mapped to a salutation/title field where supported |
phoneNumber | { countryCode: number; number: string } | e.g. { countryCode: 44, number: "7700900000" } | Normalised to E.164 format server-side |
dateOfBirth | string | ISO 8601 date: YYYY-MM-DD | Not available on all CRM object types (e.g. Salesforce Lead) |
addressLine1 | string | Max 255 chars | |
addressLine2 | string | Max 255 chars | |
locality | string | City / town. Max 40 chars | |
administrativeDivision | string | State / county / region. Max 80 chars | |
postalCode | string | Max 20 chars | |
country | string | ISO 3166-1 alpha-2 recommended (e.g. "GB"). Max 80 chars | |
marketingConsent | boolean | true or false | Controls list subscription and opt-out flags — always pass explicitly if your form collects it |
profilingConsent | boolean | true or false | Passed through to integrations that support it |
All fields except uid are optional. Only collect what your experience requires.
The extra Field
All signup methods accept an extra object for any custom data your form collects beyond the standard fields. Pass it in your call and the platform routes it to the CRM:
await sdk.signupForCompetition(EXPERIENCE_ID, {
uid: userEmail,
email: userEmail,
firstName: "Jane",
lastName: "Smith",
marketingConsent: true,
extra: {
ageRange: "25-34",
productPreference: "single-malt",
retailer: "harrods-london",
},
});How extra is handled depends on the integration:
| Integration | How extra is handled |
|---|---|
| Salesforce | Each key maps to a custom field named io_tt_extra_{key}__c — must be pre-created by the Salesforce admin |
| Klaviyo | Each key is spread directly as a custom profile property — no pre-creation needed |
| Custom webhook | The full extra object is included in the payload as-is |
extra values must be strings or primitives. Nested objects are not supported for CRM field mapping.
Integration Types
Native Integrations (recommended)
Native integrations are direct API connections where io.tt writes data to the CRM on the brand's behalf. No additional endpoint is required on your side. io.tt applies a per-experience transformer server-side that maps io.tt fields to the target CRM's field names and formats automatically.
| Integration | What it does | Guide |
|---|---|---|
| Salesforce | Creates or updates Leads and Contacts via the Salesforce REST API | Developer · Setup |
| Klaviyo | Creates profiles and subscribes opted-in participants to a configured list | Developer · Setup |
Custom Webhook (fallback)
A webhook sends an HTTP POST to a URL you control each time a consumer registers. The payload is sent in io.tt's participant data structure (with fields nested under participant.personalInfo.* and participant.address.*). A custom transform function can optionally be configured per webhook in the io.tt dashboard — this runs server-side and can reformat the payload before it is sent to your endpoint. Without a transform function, your endpoint receives the raw io.tt structure and is responsible for mapping it into your destination CRM. This is a fallback option for platforms that do not have a native io.tt integration.
Marketing Consent
The marketingConsent field is respected by all integrations, but the behaviour differs:
| Integration | Behaviour |
|---|---|
| Salesforce | marketingConsent: true → HasOptedOutOfEmail: false. Inverted to match Salesforce's opt-out model |
| Klaviyo | marketingConsent: true → profile subscribed to list. false or omitted → profile created but not subscribed |
| Custom webhook | marketingConsent value included in payload as-is — your endpoint is responsible for acting on it |
Always pass marketingConsent explicitly if your form collects it. Omitting it is treated the same as false by all integrations.
Notes
- CRM configuration is managed by io.tt staff — you do not configure the CRM target in SDK code.
- All field mapping and transformation is applied server-side. Treat SDK responses as the source of truth.
- The SDK does not validate field values against CRM-specific constraints before submission.