> ## Documentation Index
> Fetch the complete documentation index at: https://docs.enrow.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Email Enrichment (n8n)

> Build an n8n workflow that enriches Google Sheets contacts with verified emails using the Enrow Email Finder and Email Verifier APIs

This guide walks you through building an n8n workflow that reads contacts from a Google Sheet, finds professional emails with the [Email Finder](/api-reference/email-finder/find-single) endpoint, verifies them with the [Email Verifier](/api-reference/email-verifier/verify-single) endpoint, and writes the results back to the sheet — automatically, on a schedule. New to n8n with Enrow? Start with the [n8n getting started guide](/playbooks/n8n/getting-started).

## What will you build?

You'll build an automated workflow that:

1. **Reads contacts** from a Google Sheet
2. **Finds emails** using the Enrow Email Finder
3. **Verifies emails** for deliverability with the Enrow Email Verifier
4. **Writes results** back to the Google Sheet

**Time to build**: 15 minutes
**Difficulty**: Intermediate

## What do you need before starting?

Before building the workflow, make sure you have the following:

* n8n installed ([get n8n](https://n8n.io))
* An Enrow API key — see [Authentication](/authentication) for how to get and use your key
* A Google Account with a Sheet of contacts

Each search consumes credits, so confirm your balance first. See [Credits & billing](/credits-billing) for per-endpoint costs.

## How do you set up the Google Sheet?

Create a Google Sheet with these columns:

| First Name | Last Name | Company Domain | Email | Status | Verified |
| ---------- | --------- | -------------- | ----- | ------ | -------- |
| John       | Doe       | apple.com      |       |        |          |
| Jane       | Smith     | microsoft.com  |       |        |          |
| Bob        | Johnson   | meta.com       |       |        |          |

**Sheet name**: "Contacts"
**URL**: Save and copy the Sheet URL

## What does the workflow look like?

The workflow runs on a schedule, reads each contact, finds and verifies the email, then updates the sheet:

```mermaid theme={null}
graph LR
    A[Cron Trigger] --> B[Read Google Sheet]
    B --> C[Loop Over Contacts]
    C --> D[Find Email - Enrow]
    D --> E[Verify Email - Enrow]
    E --> F[Update Google Sheet]
```

## How do you build the workflow step by step?

Follow these steps to assemble each node in n8n.

### Step 1: Set up the Google Sheets connection

1. Add **"Google Sheets"** node
2. Operation: **"Read"**
3. Authenticate with Google
4. Select your spreadsheet
5. Sheet Name: **"Contacts"**
6. Range: **"A:F"** (all columns)

### Step 2: Add the Cron trigger

1. Add **"Cron"** node as trigger
2. Mode: **"Every Hour"** (or custom schedule)
3. Connect to Google Sheets node

This will automatically process new rows every hour.

### Step 3: Filter empty emails

1. Add **"IF"** node after Google Sheets
2. Condition:
   * **Value 1**: `{{$json["Email"]}}`
   * **Operation**: `Is Empty`
3. Route "true" to next step (only process contacts without emails)

### Step 4: Find the email with Enrow

Send each contact to the Email Finder endpoint. Authenticate with your API key in the `x-api-key` header.

1. Add **"HTTP Request"** node
2. Configure:

```
Method: POST
URL: https://api.enrow.io/email/find/single
Authentication: Header Auth
  Name: x-api-key
  Value: YOUR_ENROW_API_KEY

JSON Body:
{
  "company_domain": "{{$json["Company Domain"]}}",
  "fullname": "{{$json["First Name"]}} {{$json["Last Name"]}}"
}
```

### Step 5: Wait for the result

The Email Finder runs asynchronously, so you either wait for a webhook or poll the GET endpoint for the result.

**Option A: Webhook (Recommended)**

1. Add **"Webhook"** node
2. Path: `enrow-email-webhook`
3. Copy webhook URL
4. Update Find Email JSON body:

```json theme={null}
{
  "company_domain": "{{$json["Company Domain"]}}",
  "fullname": "{{$json["First Name"]}} {{$json["Last Name"]}}",
  "settings": {
    "webhook": "YOUR_WEBHOOK_URL"
  }
}
```

For the payload Enrow sends and how to register endpoints, see [How webhooks work](/how-webhooks-work).

**Option B: Wait & Poll**

1. Add **"Wait"** node: 5 seconds
2. Add **"HTTP Request"** node:
   * Method: `GET`
   * URL: `https://api.enrow.io/email/find/single?id={{$json["id"]}}`
3. Add **"IF"** node to check status
4. Loop back if still `ongoing`

See the [Get Single Result](/api-reference/email-finder/get-single-result) endpoint for the full response format.

### Step 6: Verify the email

Pass the found email to the Email Verifier to confirm deliverability before writing it back.

1. Add **"HTTP Request"** node after email is found
2. Configure:

```
Method: POST
URL: https://api.enrow.io/email/verify/single
Authentication: Header Auth (reuse Enrow credential)

JSON Body:
{
  "email": "{{$json["email"]}}"
}
```

### Step 7: Update the Google Sheet

1. Add **"Google Sheets"** node
2. Operation: **"Update"**
3. Select same spreadsheet
4. Sheet Name: **"Contacts"**
5. Range: **Match row** (use row index from original data)
6. Map fields:
   * **Email**: `{{$json["email"]}}`
   * **Status**: `{{$json["confidence"]}}`
   * **Verified**: `{{$json["status"]}}`

## Where is the complete workflow JSON?

Import the JSON below to get the full workflow in one step.

<Note>
  Import this workflow to get started immediately. Just update your API keys!
</Note>

<details>
  <summary>Click to view complete workflow JSON</summary>

  ```json theme={null}
  {
    "name": "Enrow - Email Enrichment from Google Sheets",
    "nodes": [
      {
        "parameters": {
          "rule": {
            "interval": [{"field": "hours", "hoursInterval": 1}]
          }
        },
        "name": "Every Hour",
        "type": "n8n-nodes-base.cron",
        "typeVersion": 1,
        "position": [240, 300]
      },
      {
        "parameters": {
          "operation": "read",
          "sheetId": "YOUR_SHEET_ID",
          "range": "Contacts!A:F",
          "options": {}
        },
        "name": "Read Contacts",
        "type": "n8n-nodes-base.googleSheets",
        "typeVersion": 3,
        "position": [460, 300]
      },
      {
        "parameters": {
          "conditions": {
            "string": [
              {
                "value1": "={{$json[\"Email\"]}}",
                "operation": "isEmpty"
              }
            ]
          }
        },
        "name": "Filter Empty Emails",
        "type": "n8n-nodes-base.if",
        "typeVersion": 1,
        "position": [680, 300]
      },
      {
        "parameters": {
          "method": "POST",
          "url": "https://api.enrow.io/email/find/single",
          "authentication": "headerAuth",
          "sendBody": true,
          "bodyContentType": "json",
          "jsonBody": "={\n  \"company_domain\": \"{{$json[\"Company Domain\"]}}\",\n  \"fullname\": \"{{$json[\"First Name\"]}} {{$json[\"Last Name\"]}}\"\n}"
        },
        "name": "Find Email",
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 3,
        "position": [900, 300],
        "credentials": {
          "headerAuth": {"name": "Enrow API"}
        }
      },
      {
        "parameters": {
          "amount": 5,
          "unit": "seconds"
        },
        "name": "Wait 5s",
        "type": "n8n-nodes-base.wait",
        "typeVersion": 1,
        "position": [1120, 300]
      },
      {
        "parameters": {
          "method": "GET",
          "url": "=https://api.enrow.io/email/find/single?id={{$json[\"id\"]}}",
          "authentication": "headerAuth"
        },
        "name": "Get Result",
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 3,
        "position": [1340, 300],
        "credentials": {
          "headerAuth": {"name": "Enrow API"}
        }
      },
      {
        "parameters": {
          "conditions": {
            "string": [
              {
                "value1": "={{$json[\"status\"]}}",
                "value2": "completed"
              }
            ]
          }
        },
        "name": "Check if Complete",
        "type": "n8n-nodes-base.if",
        "typeVersion": 1,
        "position": [1560, 300]
      },
      {
        "parameters": {
          "method": "POST",
          "url": "https://api.enrow.io/email/verify/single",
          "authentication": "headerAuth",
          "sendBody": true,
          "bodyContentType": "json",
          "jsonBody": "={\n  \"email\": \"{{$json[\"email\"]}}\"\n}"
        },
        "name": "Verify Email",
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 3,
        "position": [1780, 300],
        "credentials": {
          "headerAuth": {"name": "Enrow API"}
        }
      },
      {
        "parameters": {
          "operation": "update",
          "sheetId": "YOUR_SHEET_ID",
          "range": "=Contacts!A{{$json[\"row\"]}}:F{{$json[\"row\"]}}",
          "options": {
            "valueInputMode": "USER_ENTERED"
          }
        },
        "name": "Update Sheet",
        "type": "n8n-nodes-base.googleSheets",
        "typeVersion": 3,
        "position": [2000, 300]
      }
    ],
    "connections": {
      "Every Hour": {
        "main": [[{"node": "Read Contacts", "type": "main", "index": 0}]]
      },
      "Read Contacts": {
        "main": [[{"node": "Filter Empty Emails", "type": "main", "index": 0}]]
      },
      "Filter Empty Emails": {
        "main": [[{"node": "Find Email", "type": "main", "index": 0}]]
      },
      "Find Email": {
        "main": [[{"node": "Wait 5s", "type": "main", "index": 0}]]
      },
      "Wait 5s": {
        "main": [[{"node": "Get Result", "type": "main", "index": 0}]]
      },
      "Get Result": {
        "main": [[{"node": "Check if Complete", "type": "main", "index": 0}]]
      },
      "Check if Complete": {
        "main": [
          [{"node": "Verify Email", "type": "main", "index": 0}],
          [{"node": "Wait 5s", "type": "main", "index": 0}]
        ]
      },
      "Verify Email": {
        "main": [[{"node": "Update Sheet", "type": "main", "index": 0}]]
      }
    }
  }
  ```
</details>

## How can you improve the workflow?

Once the base workflow runs, these additions make it more robust and cost-efficient.

### Add error handling

1. Add **"Error Trigger"** node
2. Add **"Send Email"** or **"Slack"** node to notify on errors
3. Log failed rows to a separate sheet

For the status codes you may encounter and how to react to them, see [Error handling](/error-handling) and [Status codes](/status-codes).

### Process in bulk

For better performance with many contacts, switch from single searches to a batch:

1. Change Find Email node to use `/email/find/bulk`
2. Batch contacts into groups of 100
3. Process all at once instead of one-by-one

Example bulk request:

```json theme={null}
{
  "searches": [
    {
      "company_domain": "{{$json[\"Company Domain\"]}}",
      "fullname": "{{$json[\"First Name\"]}} {{$json[\"Last Name\"]}}"
    }
  ]
}
```

The [Find Bulk Emails](/api-reference/email-finder/find-bulk) endpoint accepts up to 5,000 searches per batch.

### Add conditional verification

Only verify high-confidence emails to save credits:

```javascript theme={null}
// In IF node
{{$json["confidence"]}} === "high"
```

This saves credits by skipping verification for uncertain emails.

## When should you use this workflow?

This workflow is a good fit for:

* **Sales teams**: Enrich prospect lists before outreach
* **Recruiters**: Find candidate contact information
* **Marketers**: Build email lists from company databases
* **Data teams**: Clean and enrich CRM data

## What are the best practices?

<AccordionGroup>
  <Accordion title="Rate Limits">
    If processing large lists (1000+ contacts):

    * Use bulk endpoints
    * Add delays between batches
    * Or upgrade your Enrow plan

    See [Rate limits](/rate-limits) for the current request thresholds.
  </Accordion>

  <Accordion title="Data Quality">
    * Always verify emails before using them
    * Check confidence scores
    * Remove low-quality results
  </Accordion>

  <Accordion title="Cost Optimization">
    * Deduplicate contacts before processing
    * Cache results to avoid re-searching
    * Use conditional logic to skip unnecessary API calls
  </Accordion>

  <Accordion title="Error Recovery">
    * Log all API responses
    * Track failed enrichments
    * Retry failed searches automatically
  </Accordion>
</AccordionGroup>

## How do you troubleshoot common problems?

**Workflow not running automatically?**

* Check that the Cron trigger is activated
* Verify the schedule is correct

**No emails found?**

* Check that company domains are correct
* Verify names are spelled correctly
* Try with just company domain (without names)

**Sheet not updating?**

* Check Google Sheets permissions
* Verify the range matches your data
* Test the Update node manually

## FAQ

<AccordionGroup>
  <Accordion title="Do I need a webhook, or can I just poll for results?">
    Both work. The Email Finder runs asynchronously, so you can either register a webhook in the `settings.webhook` field (Option A) or poll the [Get Single Result](/api-reference/email-finder/get-single-result) endpoint on a timer (Option B). Webhooks are recommended because they avoid wasted polling requests and return results as soon as the search completes. See [How webhooks work](/how-webhooks-work).
  </Accordion>

  <Accordion title="How many credits does the workflow use?">
    Each Email Finder search and each Email Verifier check consumes credits per call. Running both on every contact uses more credits than finding alone, so add conditional verification to skip uncertain emails. For exact per-endpoint costs, see [Credits & billing](/credits-billing).
  </Accordion>

  <Accordion title="How do I authenticate the HTTP Request nodes?">
    Use n8n's Header Auth credential with the header name `x-api-key` and your Enrow API key as the value, then reuse the same credential across every Enrow node. Full details are in [Authentication](/authentication).
  </Accordion>

  <Accordion title="What if I have thousands of contacts?">
    Switch the Find Email node to the [Find Bulk Emails](/api-reference/email-finder/find-bulk) endpoint, which accepts up to 5,000 searches per batch, and stay within your plan's [rate limits](/rate-limits).
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Verification Workflow" icon="shield-check" href="/playbooks/n8n/lead-verification-workflow">
    Verify existing email lists with n8n and Enrow.
  </Card>

  <Card title="Find Single Email" icon="envelope" href="/api-reference/email-finder/find-single">
    Explore the Email Finder endpoint used in this workflow.
  </Card>

  <Card title="Verify Single Email" icon="circle-check" href="/api-reference/email-verifier/verify-single">
    Check whether an email address is valid and deliverable.
  </Card>

  <Card title="Webhooks" icon="bell" href="/how-webhooks-work">
    Get notified automatically when a search completes.
  </Card>
</CardGroup>
