---
title: "How Do I Trigger Iterable Journeys on Return Visits?"
description: "Use Leadpipe return-visit webhooks to trigger personalized Iterable journeys. Field mapping, deduping, and GDPR-safe patterns for lifecycle marketers."
canonical_url: "https://www.leadpipe.com/blog/leadpipe-iterable-journeys/"
source_url: "https://www.leadpipe.com/blog/leadpipe-iterable-journeys/"
md_url: "https://www.leadpipe.com/blog/leadpipe-iterable-journeys.md"
content_type: "article"
pubDate: "2026-04-11T00:00:00.000Z"
last_updated: "2026-04-11T00:00:00.000Z"
---

# How Do I Trigger Iterable Journeys on Return Visits?

In this guide

Use Leadpipe return-visit webhooks to trigger personalized Iterable journeys. Field mapping, deduping, and GDPR-safe patterns for lifecycle marketers.

Your Iterable journeys are triggered by form fills, purchases, or email opens. The signal you are missing is the most predictive one of all: someone who already engaged with your brand coming back to your site and reading pricing, a feature page, or a case study. Return visits convert 3-5x better than first-time visits, and most lifecycle marketers have no way to trigger on them.

I am Elene, and this is the recipe I give lifecycle marketers who want Iterable to fire behaviorally against website visits, not just app or email events. Leadpipe identifies the visitor, a webhook carries the event, and Iterable receives a custom event that your journey builder can gate on. Setup is about 25 minutes.

---

## What you will build

A pipeline where every identified US B2B return visitor (or first-time high-intent visitor) triggers a custom event in Iterable, populating User fields with visit context and starting (or advancing) a journey you design in the Iterable Studio.

---

## Prerequisites

| Requirement | Notes |
| --- | --- |
| Leadpipe account | [Start free with 500 leads](https://dashboard.leadpipe.com/auth/signup) |
| Iterable account | Growth or Enterprise tier |
| Iterable API key | Server-side, with Custom Events and Users permissions |
| Zapier or your own middleware | Optional for no-code |
| Time | 25 minutes |

If you are on Marketo, Customer.io, or HubSpot Marketing instead, the same pattern applies; the [Clay + HubSpot recipe](/blog/leadpipe-clay-hubspot-integration/) covers HubSpot and the [Salesforce recipe](/blog/leadpipe-salesforce-integration/) shows how to write to a marketing automation platform backed by Salesforce data.

---

## The event model

Iterable’s journey builder gates on custom events and user field changes. You want Leadpipe to supply both.

```
Leadpipe webhook
      │
      ▼
  Middleware (or Zapier)
      │
      ├─ Iterable User update   (or create)
      │    → fields: lastSeenAt, lastPage, intentScore, topics, seniority, industry
      │
      └─ Iterable Custom Event  "website_return_visit" or "website_high_intent_visit"
             → payload: page_url, pages_viewed, visit_duration, intent_score, return_visit
```

You send a user update even for first-time visitors (so the user is in Iterable), then emit the event. Journey builder triggers on the event, and branch logic reads user fields.

---

## Step 1: Decide what counts as a journey trigger

Not every identified visit deserves a journey start. Pick a narrow event definition. Options:

| Event | Definition | Use case |
| --- | --- | --- |
| `website_return_visit` | return_visit = true AND intent_score > 40 | Re-engagement nurture |
| `website_high_intent_visit` | page_url starts with /pricing OR /enterprise AND visit_duration > 120 | Sales-assist, MQL escalation |
| `website_feature_evaluation` | page_url starts with /features/* AND visit_duration > 90 | Feature-specific nurture |
| `website_docs_deep_dive` | page_url starts with /docs/* AND pages_viewed >= 3 | Technical decision-maker journey |

Start with two. You can add more later. Lifecycle teams who try to fire on every page event end up muting themselves.

---

## Step 2: Configure Leadpipe webhooks

In Leadpipe: Settings, Integrations, Webhooks, Add Webhook.

1. Destination URL: generated in Step 3.
2. Trigger: **Every Update** if you want to catch return visits. Leadpipe only fires Every Update from identified visitors who return, which is exactly the signal you want. Add a separate **First Match** webhook if you also want first-time high-intent visits to trigger.
3. Fields: all.

Sample payload (full schema in our [webhook payload reference](/blog/webhook-payload-reference-every-visitor-data-field/)):

```
{
  "email": "raj.mehta@orbit.dev",
  "first_name": "Raj",
  "last_name": "Mehta",
  "phone": "+1-415-555-0175",
  "company_name": "Orbit.dev",
  "company_domain": "orbit.dev",
  "company_industry": "Developer Tools",
  "company_employee_count": 95,
  "job_title": "VP Marketing",
  "seniority": "VP",
  "department": "Marketing",
  "linkedin_url": "linkedin.com/in/rajmehta",
  "city": "Austin",
  "state": "Texas",
  "country": "US",
  "page_url": "/enterprise",
  "pages_viewed": ["/", "/blog/midbound-a-new-era-in-marketing", "/pricing", "/enterprise"],
  "visit_duration": 342,
  "intent_score": 82,
  "matched_topics": ["ABM", "intent data"],
  "return_visit": true
}
```

GDPR note. For EU and UK visitors, Leadpipe defaults to company-level identification unless the visitor has given affirmative consent. Those payloads arrive with company fields and no email, which means you cannot write a User record in Iterable (no identifier). For EU traffic, log the event against a company-level record or a special “anonymous-eu-{{domain}}” user record if you want to track it, but do not attempt to create a personal user without consent. Our [GDPR compliance post](/blog/gdpr-compliant-visitor-identification/) goes deeper.

---

## Step 3: Send to Iterable

Iterable’s API has two endpoints you need: Users update (`POST /api/users/update`) and Custom Events track (`POST /api/events/track`). Both take JSON and your server-side API key as a header.

### Zapier path

1. New Zap. Trigger: Webhooks by Zapier, Catch Hook. Paste URL in Leadpipe.
2. Fire a test event.
3. Filter step. Options:

   - Event `website_return_visit`: `return_visit` is true AND `intent_score` is greater than 40
   - Event `website_high_intent_visit`: `page_url` starts with `/pricing` OR `/enterprise` AND `visit_duration` greater than 120
4. Filter out personal emails and EU traffic missing email. Add a condition: `email` exists AND `email` does not contain `gmail.com, yahoo.com`.
5. Action 1: Webhooks by Zapier, Custom Request (POST). URL: `https://api.iterable.com/api/users/update`. Headers: `Api-Key: {{ITERABLE_API_KEY}}`. Body (JSON):

```
{
  "email": "{{email}}",
  "dataFields": {
    "firstName": "{{first_name}}",
    "lastName": "{{last_name}}",
    "phoneNumber": "{{phone}}",
    "company": "{{company_name}}",
    "companyDomain": "{{company_domain}}",
    "industry": "{{company_industry}}",
    "employeeCount": {{company_employee_count}},
    "jobTitle": "{{job_title}}",
    "seniority": "{{seniority}}",
    "department": "{{department}}",
    "linkedinUrl": "{{linkedin_url}}",
    "country": "{{country}}",
    "state": "{{state}}",
    "city": "{{city}}",
    "lastSeenAt": "{{zap_meta_timestamp}}",
    "lastPage": "{{page_url}}",
    "intentScore": {{intent_score}},
    "intentTopics": {{matched_topics}}
  }
}
```

1. Action 2: Webhooks by Zapier, Custom Request (POST). URL: `https://api.iterable.com/api/events/track`. Body (JSON):

```
{
  "email": "{{email}}",
  "eventName": "website_return_visit",
  "dataFields": {
    "pageUrl": "{{page_url}}",
    "pagesViewed": {{pages_viewed}},
    "visitDuration": {{visit_duration}},
    "intentScore": {{intent_score}},
    "matchedTopics": {{matched_topics}},
    "returnVisit": {{return_visit}}
  }
}
```

Duplicate the Zap for the high-intent event with a different filter and `eventName`.

### Direct middleware path

For volume and cleaner dedup logic:

```
// POST /leadpipe-webhook → Iterable
// Env: ITERABLE_API_KEY
import fetch from 'node-fetch';

const headers = {
  'Api-Key': process.env.ITERABLE_API_KEY,
  'Content-Type': 'application/json',
};

function classify(v) {
  if (v.return_visit && (v.intent_score || 0) > 40) return 'website_return_visit';
  const url = v.page_url || '';
  if (
    (url.startsWith('/pricing') || url.startsWith('/enterprise')) &&
    (v.visit_duration || 0) > 120
  )
    return 'website_high_intent_visit';
  if (url.startsWith('/features') && (v.visit_duration || 0) > 90)
    return 'website_feature_evaluation';
  if (url.startsWith('/docs') && (v.pages_viewed || []).length >= 3)
    return 'website_docs_deep_dive';
  return null;
}

export async function handler(req) {
  const v = req.body;
  if (!v.email) return { statusCode: 200, body: 'no email, skipping per GDPR defaults' };
  const eventName = classify(v);
  if (!eventName) return { statusCode: 200, body: 'no trigger' };

  // Upsert user
  await fetch('https://api.iterable.com/api/users/update', {
    method: 'POST',
    headers,
    body: JSON.stringify({
      email: v.email,
      dataFields: {
        firstName: v.first_name,
        lastName: v.last_name,
        phoneNumber: v.phone,
        company: v.company_name,
        companyDomain: v.company_domain,
        industry: v.company_industry,
        employeeCount: v.company_employee_count,
        jobTitle: v.job_title,
        seniority: v.seniority,
        department: v.department,
        linkedinUrl: v.linkedin_url,
        country: v.country,
        state: v.state,
        city: v.city,
        lastSeenAt: new Date().toISOString(),
        lastPage: v.page_url,
        intentScore: v.intent_score,
        intentTopics: v.matched_topics,
      },
    }),
  });

  // Fire event
  await fetch('https://api.iterable.com/api/events/track', {
    method: 'POST',
    headers,
    body: JSON.stringify({
      email: v.email,
      eventName,
      dataFields: {
        pageUrl: v.page_url,
        pagesViewed: v.pages_viewed,
        visitDuration: v.visit_duration,
        intentScore: v.intent_score,
        matchedTopics: v.matched_topics,
        returnVisit: v.return_visit,
      },
    }),
  });

  return { statusCode: 200, body: `fired ${eventName}` };
}
```

---

## Step 4: Build the journey in Iterable Studio

Open Iterable Studio and create a new journey. Trigger: Custom Event = `website_return_visit`.

Branches I recommend:

1. **Entry split.** Is `user.employeeCount` greater than 500? If yes, skip to the enterprise branch. If no, mid-market.
2. **Mid-market branch.** Wait 1 hour. Send email “Saw you were back, here is what customers your size usually ask first” with dynamic fields pulled from user data (firstName, company, industry). Wait 2 days. If no open, send a case study email. If open but no click, send a one-question nudge.
3. **Enterprise branch.** Wait 30 minutes. Send internal Slack notification to the named AE via an Iterable webhook (or pair with [Slack alerts](/blog/leadpipe-slack-visitor-alerts/) directly from Leadpipe). Send an AE-from email template rather than marketing-from.
4. **Exit criteria.** If user triggers `website_high_intent_visit` or fills a demo form, exit this journey and enter the sales-assist journey.

The nice part: you are not creating a new list for return visitors, you are treating them as the lifecycle signal they actually are.

---

## What this looks like in practice

Raj, VP Marketing at Orbit.dev (a 95-person dev tools company in Austin), read your midbound blog post three weeks ago from a LinkedIn share. He did not convert. This morning he returned, read your blog again, hit pricing, then spent five and a half minutes on `/enterprise`.

1. Leadpipe identifies Raj. Every Update webhook fires.
2. Middleware classifies: `website_return_visit` (return_visit=true, intent_score=82) and `website_high_intent_visit` (/enterprise, 342s). Middleware emits the higher-priority event, `website_high_intent_visit`.
3. Iterable updates Raj’s user record: lastPage=/enterprise, intentScore=82, intentTopics=ABM, intent data.
4. Iterable journey fires. Enterprise branch (95 employees, so actually mid-market in our example thresholds). 1 hour later, email sent: “Raj, saw you were checking out enterprise tiers at Orbit.dev. Here’s the one-pager our ABM customers use most.”
5. Raj opens the email at 2:40 pm. Iterable records the engagement. Journey advances.
6. Raj books a demo the next day.

None of that happens without the return-visit trigger. Lifecycle used to rely on form fills and email opens. Now the signal is “the person who already knows you came back,” which is a much earlier and stronger one.

---

## Troubleshooting and edge cases

**Two events per visit.** If a single return visit matches both `website_return_visit` and `website_high_intent_visit`, pick one, do not fire both, or you will pull the user into two journeys at once. The middleware example above does that by checking in priority order.

**Journey re-entry.** Iterable lets users re-enter a journey by default. For return-visit journeys, enable “Do not allow re-entry within X days” to avoid nagging return visitors with the same email every time.

**EU visitors with no email.** Expected. Leadpipe defaults to company-level for EU/UK. Iterable is a personal-identifier system (email or userId), so you cannot act on company-level events here. Either skip them (safest) or route them to a separate ABM-style tool that can work on domain alone (see [LinkedIn Ads audiences](/blog/leadpipe-linkedin-ads-integration/) for a domain-friendly activation).

**Data field type mismatches.** Iterable is strict about field types. If `employeeCount` is sometimes null and sometimes a number, your user updates will fail silently on the null ones. Default missing numbers to 0 or omit the field.

**Duplicate Iterable users.** Iterable uses email as the primary identifier. As long as you consistently normalize the email (lowercase, trim), you will not duplicate. If you ever switch to userId as your primary key, map Leadpipe email to userId consistently.

**Rate limits.** Iterable’s bulk endpoints cap at 2,000 ops per request and a few hundred requests per minute. For most marketing teams this is never an issue. If you hit it, batch writes in middleware with a 1-second queue.

**Events arriving for people who are in your suppression list.** Iterable will happily record an event against a suppressed user but will not email them. If you want to short-circuit before the event fires, check the user’s `channel.email.status` first.

---

## Extending the recipe

- Pair with [Slack visitor alerts](/blog/leadpipe-slack-visitor-alerts/) so the AE gets a direct ping on the same event Iterable triggers on.
- Send the same event to [Segment](/blog/leadpipe-segment-identity-layer/) if you want your other destinations (warehouse, product analytics, CDP) to receive the signal at the same time.
- Enrich the event payload with [Clay data](/blog/how-to-add-visitor-identification-to-clay-waterfall/) before it hits Iterable if you want tech stack, revenue, or waterfall-enriched phone in your journey branching logic.
- Write the event into a CRM like [Salesforce](/blog/leadpipe-salesforce-integration/) or [Attio](/blog/leadpipe-attio-integration/) too so sales has the same signal.
- If you run paid retargeting, pipe the high-intent event into [LinkedIn Ads audiences](/blog/leadpipe-linkedin-ads-integration/) for coordinated multi-channel follow-up.

---

## Why return-visit triggers are underused

Most lifecycle stacks only know about users after they convert. By then the intent is already losing heat. Wiring Leadpipe into Iterable gives you a lifecycle trigger on the exact moment when the buyer is warmest, and the content in the journey can match the page they just viewed. That is the difference between a nurture and a conversation.

> The highest-converting lifecycle event is a return visit to your pricing page. Build your journey around it.

Leadpipe identifies 30-40%+ of your US B2B visitors with full contact data on the Pro plan at $147/mo. No credit card to start the 500-lead trial. [Start identifying visitors →](https://dashboard.leadpipe.com/auth/signup)
