Integrations

Iterable: Telltide Seed Address Guide

Place Telltide seeds in Iterable lists and journeys.

Iterable's project type (email-based, userId-based, or hybrid) fundamentally changes how seeds are identified and how they can be reset. Determine your project type before building seed management into any automation. Email-based projects reset a seed's journey history by changing the email address. UserId-based projects require deleting and recreating the user.

Quick reference

Send typeWhere to add seedsComplexity
Scheduled (Blast Campaign)Add seed user to a static list included in the campaign's Send ListsLow
Ongoing (Journey)Add seed to the entry trigger list, or fire the entry event against the seed userMedium
Recurring CampaignEnsure seed remains on (or qualifies for) the saved audience used by each recurring runLow
API-triggered JourneyInclude seed's email or userId in the triggerWorkflow payloadMedium

1. Scheduled / one-off sends (Blast Campaigns)

Iterable Blast Campaigns and Recurring Campaigns send to all users on selected static or dynamic lists. Seeds must be on at least one list included in the campaign's Send Lists.

Non-technical path

For CRM managers without API access. Use the Iterable web app.

  1. Navigate to Audience > Users and click New User. Provide the seed email (and userId for userId-based or hybrid projects), then save.
  2. Or, navigate to Audience > Lists, select or create a static list called "Telltide Seeds", click Import List, and upload a CSV with at minimum an email column (or userId column for userId-based projects). Map columns to user fields and confirm.
  3. Add the "Telltide Seeds" static list to the campaign under Send Lists alongside the real audience list.
  4. If the campaign uses merge fields (Handlebars), populate any required dataFields on the seed profile through the user detail screen so the rendered email matches what real recipients receive.

Technical path

For email-developers and ops engineers. All requests use the Api-Key header (server-side key).

  1. Upsert the seed profile:
POST https://api.iterable.com/api/users/update
Api-Key: {{api_key}}
Content-Type: application/json

{
  "email": "seed@example.com",
  "dataFields": {
    "firstName": "Seed",
    "lastName": "Monitor",
    "internalTestUser": true
  }
}
  1. Subscribe the seed to the static list used by the campaign:
POST https://api.iterable.com/api/lists/subscribe
Api-Key: {{api_key}}
Content-Type: application/json

{
  "listId": 12345,
  "subscribers": [
    {
      "email": "seed@example.com",
      "dataFields": {
        "firstName": "Seed"
      }
    }
  ]
}
  1. Optionally fire a custom event so the seed satisfies any segment criteria that look at recent activity:
POST https://api.iterable.com/api/events/track
Api-Key: {{api_key}}
Content-Type: application/json

{
  "email": "seed@example.com",
  "eventName": "telltideSeedHeartbeat",
  "dataFields": { "source": "telltide" }
}

Profile attributes required

Seeds need:

  • email (for email-based and hybrid projects)
  • userId (for userId-based and hybrid projects)
  • Any dataFields values referenced by merge variables in the campaign email template. Missing values render as empty or trigger fallback text in Handlebars templates.

Ensuring seeds receive the same version as real recipients

Blast campaigns and duplicate list membership: If the same user appears on multiple included Send Lists, Iterable sends them the email only once. There is no risk of duplicates from including the seed in multiple lists.

Suppression lists: Verify the seed user is not on any suppression list specified in the campaign's Suppression Lists configuration. Check both campaign-level suppression lists and the Global Suppression List.

Dynamic lists as the campaign audience: If the campaign targets a dynamic list (a saved segmentation query), Iterable evaluates the segment at send time. The seed user must satisfy the query criteria to be included. The simplest approach is to also add the seed to a static Send List alongside the dynamic list, or to add an OR clause to the segment definition that matches an internalTestUser = true data field on seed profiles.

Recurring Campaigns: Recurring Campaigns re-run on a schedule against the same Send Lists. Once the seed is on the static list (or qualifies for the dynamic list), each scheduled run includes the seed. No per-run action is needed.


2. Ongoing / automated journeys (Journeys)

Iterable Journeys are event-driven or scheduled automation sequences. Each user moves through the journey independently. The Start tile's entry trigger determines how the seed enters. (The legacy API name for a Journey is "workflow", which is why the trigger endpoint is /api/workflows/triggerWorkflow.)

Entry trigger types and seed approaches

Trigger typeDescriptionSeed approach
Event OccursUser enters when a named event fires for themFire the event via the Track Event API
Schedule (List-based)Users from a static or dynamic list enter on a scheduleAdd seed to the trigger list
API CallUsers enter when included in a triggerWorkflow API callCall the API with the seed's email or userId
Other JourneyUsers sent from a "Send to Journey" tileConfigure the upstream journey

Non-technical path

  1. Open the Journey in Engage > Journeys and inspect the Start tile to identify the entry trigger.
  2. If the entry is Schedule (List-based): open the trigger list under Audience > Lists and add the seed user (manually, or via CSV import for the static case). For dynamic-list triggers, edit the segment definition to include an OR clause that matches internalTestUser = true, then ensure the seed profile carries that flag.
  3. If the entry is Event Occurs: ask an engineer to fire the event, or use a saved internal test campaign that calls the event for you. There is no UI control to inject a custom event.
  4. If the entry is API Call: ask an engineer to include the seed in the triggerWorkflow payload.

Technical path

  1. Confirm the seed exists by upserting via POST /api/users/update.
  2. Match the entry trigger: - Event Occurs: call POST /api/events/track with the configured eventName. - Schedule (List-based, static): call POST /api/lists/subscribe with the seed. - Schedule (List-based, dynamic): ensure the seed's dataFields satisfy the segment query, or add an OR clause matching internalTestUser = true. - API Call: call POST /api/workflows/triggerWorkflow with workflowId plus the seed's email or userId.
  3. Verify entry by checking the Journey's run history for the seed user.

Always-on flow coverage

For Journeys and any API-triggered campaign that runs continuously, the seed has to satisfy the same entry condition that real users do, every time. Configure each entry-trigger primitive explicitly:

  • List-join trigger: Keep the seed permanently subscribed to the trigger list. For static lists, call POST /api/lists/subscribe once. For dynamic lists, either set the segment so the seed always qualifies (for example, an internalTestUser = true flag) or add an explicit OR clause to the segment definition: (original criteria) OR internalTestUser = true.
  • Event trigger: Schedule a recurring POST /api/events/track for the seed at the same cadence as the Journey expects real events. Use a stable eventName matching the Start tile and include any dataFields the journey filter tiles read.
  • Segment qualification (Schedule trigger): The seed must satisfy the dynamic list's segmentation query at evaluation time. Dynamic lists in Iterable refresh roughly hourly. Pre-load any data fields the segment reads (lastPurchaseDate, nextBirthday, etc.) and refresh them on the same schedule the Journey runs.
  • API-triggered Journey (triggerWorkflow): Include the seed's email (email-based or hybrid projects) or userId (userId-based or hybrid projects) in the payload's recipient list every time the workflow is triggered. If your trigger is fired by an upstream system, add the seed to whatever audience that upstream system pulls from.
  • Filter tiles: After entry, every Filter tile must evaluate truthy for the seed. Audit each tile's dataFields and event-property checks and ensure the seed profile carries matching values.
  • Frequency Management and Suppression: Mark the seed exempt from Frequency Management on each send tile, and verify the seed is not on any campaign-level Suppression List or the Global Suppression List.

Creating trap profiles per journey type

Welcome / onboarding

  • Entry trigger: "Event Occurs", typically signUp, accountCreated, or "Subscribe to List" (list-based)
  • Seed requirements: Email address (and userId for userId/hybrid projects), user profile existing in Iterable
  • How to trigger (event-based):
POST https://api.iterable.com/api/events/track
{
  "email": "seed@example.com",
  "eventName": "signUp",
  "dataFields": {
    "signupSource": "web"
  }
}
  • How to trigger (list-based): Add the seed user to the trigger list via POST /api/lists/subscribe.

Critical: For the "Subscribe to List" trigger to fire a journey, you must:

  1. Enable "Trigger journeys from this list" on the list itself
  2. Wait approximately 5 minutes after the journey is activated before adding the seed user (to avoid missing the trigger due to system propagation)
  • Entry limit: Set to 1 (once per lifetime) for welcome journeys. The seed can only enter once. To re-test: in email-based projects, update the seed's email address to reset their entry count. In userId-based projects, delete and recreate the user.

Abandoned cart

  • Entry trigger: "Event Occurs", updateCart (Iterable's built-in commerce event) or a custom cart abandonment event
  • Seed requirements: Email address, user profile with shoppingCartItems array in the profile data
  • How to trigger: Call updateCart to populate the seed's cart, then let the journey fire:
POST https://api.iterable.com/api/commerce/updateCart
{
  "user": { "email": "seed@example.com" },
  "items": [
    {
      "id": "SKU-123",
      "name": "Widget Pro",
      "price": 49.99,
      "quantity": 1,
      "sku": "WIDGET-PRO-001",
      "description": "Premium widget for monitoring",
      "url": "https://example.com/products/widget-pro",
      "imageUrl": "https://example.com/img/widget-pro.jpg",
      "categories": ["Electronics"],
      "dataFields": {}
    }
  ]
}
  • Template reference: In the journey email, cart items are referenced via profile.shoppingCartItems (the profile field), not from the event payload. Ensure the updateCart call populates the profile field correctly.
  • Entry limit: Set to Unlimited (customers may abandon cart multiple times).

Browse abandonment

  • Entry trigger: "Event Occurs", a custom event you define (e.g., productViewed)
  • Seed requirements: Email address, user profile
  • How to trigger:
POST https://api.iterable.com/api/events/track
{
  "email": "seed@example.com",
  "eventName": "productViewed",
  "dataFields": {
    "productId": "SKU-123",
    "productName": "Widget Pro",
    "productCategory": "Electronics",
    "price": 49.99,
    "productUrl": "https://example.com/products/widget-pro",
    "imageUrl": "https://example.com/img/widget-pro.jpg"
  }
}
  • Template reference: Browse abandonment event properties are available in the template via Handlebars as {{productName}}, {{price}}, etc. (the exact variable names depend on your event schema)

Winback / re-engagement

  • Entry trigger: "Schedule" (list-based), a dynamic list of users who haven't purchased in X days
  • Seed requirements: Email address, a lastPurchaseDate user profile field (or whatever field the dynamic list filters on) set to a date older than the lapse threshold
  • How to set up the seed profile:
POST https://api.iterable.com/api/users/update
{
  "email": "seed@example.com",
  "dataFields": {
    "lastPurchaseDate": "2025-12-01 00:00:00 +00:00"
  }
}
  • Dynamic list criteria: The list must filter on the lastPurchaseDate field using a date comparator ("Is Before X days ago"). The seed's field value must satisfy this condition.
  • Note: Iterable's trackPurchase endpoint does not automatically write a lastPurchaseDate field. Your integration must explicitly call users/update to set this field after each purchase.

Post-purchase

  • Entry trigger: "Event Occurs", the purchase event fired via POST /api/commerce/trackPurchase
  • Seed requirements: Email address, user profile
  • How to trigger:
POST https://api.iterable.com/api/commerce/trackPurchase
{
  "user": { "email": "seed@example.com" },
  "items": [
    {
      "id": "ORD-ITEM-001",
      "name": "Widget Pro",
      "price": 49.99,
      "quantity": 1
    }
  ],
  "total": 49.99,
  "dataFields": {
    "orderId": "ORD-001",
    "shippingAddress": "123 Test St"
  }
}
  • Template note: The trackPurchase call clears shoppingCartItems from the user profile. Reference purchased items in the email template via the event's shoppingCartItems property (the event payload), not profile.shoppingCartItems.

Birthday / anniversary

  • Entry trigger: "Schedule", a dynamic list matching users whose birthday month or day matches today
  • Seed requirements: Email address, user profile with a birthday or birthdate custom field set to a date
  • Critical gotcha: Iterable date segmentation compares full dates including year. A birthday stored as 1990-04-15 will NOT match a segmentation query for "today is April 15" because the years differ.

Recommended workaround (community best practice):

  1. Store a nextBirthday field on the user profile, the upcoming occurrence of their birthday in the current or next calendar year (as a full ISO date)
  2. Build a dynamic list where nextBirthday is within ±1 day of today
  3. Set up a daily scheduled journey against this list
  4. Recalculate nextBirthday annually via an automation or backend job

For seed testing:

  1. Set the seed's nextBirthday to today's date
  2. The dynamic list includes the seed at the next hourly refresh
  3. The scheduled journey picks up the seed at its next evaluation

Date format for API:

"nextBirthday": "2026-04-15 00:00:00 +00:00"

Where to create seed profiles in the Iterable UI

  1. Audience > Users: search for existing profiles or use the UI to navigate to a user profile.
  2. Audience > Lists > Import List: bulk import via CSV to create users and add them to lists simultaneously.
  3. Via API: the primary method for programmatic seed management.

For event-triggered journeys, there is no UI-based event injection tool. Use the Track Event API.

Ensuring seeds progress through the full journey

Start tile, Entry limit: The most important setting. Common blocking configurations:

  • "Maximum Entries: 1" prevents re-entry ever. Seeds can only go through the journey once.
  • "Wait period between entries" blocks re-entry within a defined window.

Filter tiles: Journey branches can route users based on user field values or event properties. Review every Filter tile to ensure the seed's profile data satisfies the expected path.

Frequency Management: Iterable's account-level frequency cap can skip messages if the seed has received too many emails in the configured window. Check whether Frequency Management is enabled in your project settings and whether individual send tiles are configured to ignore it.

Global Suppression List: Verify the seed's email is not on the project's Global Suppression List. Global suppression blocks delivery from all campaigns and journeys regardless of send list membership.


How to reset seeds for repeated journey testing

Email-based projects: Update the seed's email address to a new + subaddress variant (e.g., change seed@example.com to seed+cycle2@example.com). In email-based projects, email address is the unique identifier, so this creates a new user profile with a clean entry history while emails still deliver to the seed@example.com inbox.

UserId-based projects: Delete the user via DELETE /api/users/{email} and recreate them. Deletion resets their journey entry counts.

Hybrid projects: Delete and recreate the user, or change both email and userId to new values.

Clone the journey: Create a copy of the journey. Users have no entry history in the clone and can enter fresh. Useful for testing without affecting the live journey's entry tracking.

Change the entry limit to Unlimited: Updating the Start tile's Maximum Entries setting from "1" to "Unlimited" only applies to users added to the journey after the change. Previously exhausted users are not retroactively reset.


3. Platform-specific considerations

Authentication

Iterable supports two API key types:

  • Server-side keys, used in the Api-Key request header for all server-to-server calls (every endpoint shown in this guide). Never expose server-side keys in client code.
  • JWT-enabled keys, used together with a short-lived JSON Web Token signed with your project's JWT secret, for client-side calls (mobile and web SDKs). Telltide seeding is server-side only, so use a server-side key.

API options

Create or update a user:

POST https://api.iterable.com/api/users/update
Api-Key: {{api_key}}

Upserts by email (email-based) or userId (userId-based/hybrid). Creates if not exists.

Track a custom event:

POST https://api.iterable.com/api/events/track

Fires a named event against a user. Required for event-triggered journey entry.

Track a purchase:

POST https://api.iterable.com/api/commerce/trackPurchase

Records a purchase event and clears shoppingCartItems from the user profile.

Update shopping cart:

POST https://api.iterable.com/api/commerce/updateCart

Updates shoppingCartItems on the user profile and fires an updateCart event.

Subscribe user to a list:

POST https://api.iterable.com/api/lists/subscribe

Adds user to a static list. Creates the user if they don't exist.

Trigger a journey via API:

POST https://api.iterable.com/api/workflows/triggerWorkflow
{
  "workflowId": 12345,
  "email": "seed@example.com",
  "dataFields": { "key": "value" }
}

Only works for journeys with "API Call" as the entry trigger.

Delete a user:

DELETE https://api.iterable.com/api/users/{email}

Permanently deletes the user and resets their journey entry history (userId/hybrid projects).

API base URLs:

  • US: https://api.iterable.com/api/
  • EU: https://api.eu.iterable.com/api/

Rate limits

  • Rate limits vary by endpoint and are documented per-endpoint in Iterable's API reference. Some limits apply per API key, others per organization (additive across projects). Bulk endpoints such as events/trackBulk are commonly capped at around 10 requests per second per project.
  • 429 Too Many Requests is returned when rate limits are exceeded. Implement exponential backoff.
  • Allow at least 60 seconds between rapid successive updates to the same user profile to avoid data loss from race conditions.
  • GDPR endpoints: maximum 1,000 calls per 24-hour period.
  • API keys passed in query strings or request bodies are subject to stricter rate limits as of November 2025. Always pass keys in the Api-Key header.

Duplicate contact handling

Project typeUnique identifierBehavior on duplicate submission
Email-basedemailUpdates existing user
UserId-baseduserIdUpdates existing user
Hybridemail AND userIdUpdates existing user (both must be unique)

Attempting to create a user with an existing email in an email-based project updates the existing record rather than creating a duplicate. In userId-based projects, the same email can exist on multiple user records (each with a different userId). This is intentional for B2B or family account scenarios but can cause confusion for monitoring purposes.

Platform-specific terminology

Iterable termWhat it means
Blast CampaignA one-to-many email send to a list at a specific time (equivalent to a broadcast or campaign in other platforms).
JourneyAn automated multi-step sequence triggered by events or schedules (equivalent to a Flow/Canvas/Automation in other platforms).
Start tileThe configuration block at the beginning of a Journey that defines the entry trigger, limits, and suppression lists.
dataFieldsThe key-value store on a user profile or event where custom attributes are stored.
Static ListA manually managed list of users. Members must be explicitly added or removed.
Dynamic ListA segmentation-based list that auto-updates as users meet or stop meeting criteria.
Global Suppression ListAn account-level block list. Users on it receive no emails from any campaign or journey.
Frequency ManagementProject-level settings capping how many emails a user can receive per day/week/month.
Maximum EntriesStart tile setting controlling how many times a single user can enter a Journey.
shoppingCartItemsThe user profile field populated by updateCart containing the user's current cart items.

Known limitations and workarounds

Entry limit resets for individual users require email address change or user deletion. There is no endpoint to reset a specific user's entry count for a specific journey without affecting the rest of their profile.

Birthday triggers require a workaround. Iterable's date segmentation is year-aware, which breaks standard birthday/anniversary detection. Use a calculated nextBirthday field updated annually.

"Subscribe to List" trigger has a 5-minute activation delay. Adding users to the trigger list immediately after activating the journey may result in missed entries. Wait 5 minutes post-activation.

Dynamic list membership updates are hourly. The displayed user count and actual eligibility can lag by up to an hour. For blast campaigns, Iterable evaluates the list fresh at send time so this is not an issue for campaigns, but it affects scheduled journey evaluations.

trackPurchase clears shoppingCartItems. If your post-purchase email references cart items, reference them from the event payload (the purchase event), not from profile.shoppingCartItems.

Start monitoring your Iterable sends

Place a Telltide seed in your Iterable audience, and we will tell you when an expected email did not land.

Start free