Integration Guide9 min readJuly 13, 2026

HeyGen + Apollo.io Integration: Automate Personalized Video in Your Sequences

By Umar Asghar, AI Integration Engineer - July 13, 2026

TL;DR

Apollo.io is the email delivery layer, not the trigger. Your CRM fires the render, HeyGen generates the personalized video, your automation layer writes the URL to an Apollo custom field, and Apollo’s email step references that field. The main production risk is the 7-day URL expiry - re-host on S3 or Cloudflare R2 for anything beyond same-day outreach.

Architecture Overview

The most common mistake in this integration is treating Apollo as the trigger. Apollo sequences do not natively fire webhooks when a contact enters a sequence. The trigger has to come from your CRM - a HubSpot deal stage change, a Salesforce Opportunity stage, or a manual list enrollment in your CRM.

The correct flow is:

  1. CRM event fires (deal stage changes, lead scores, manual trigger)
  2. Automation layer fetches contact data from Apollo
  3. HeyGen renders a personalized video using that data
  4. Automation layer writes the video URL to an Apollo custom field
  5. Apollo sequence email step references the custom field variable

Apollo’s job is delivery. It holds the video URL as a contact property and injects it into the email at send time. Everything upstream of that write is handled outside Apollo.

CRM trigger

> Apollo GET /api/v1/contacts/{id} (enrich)

> HeyGen POST /v2/video/generate (render)

> HeyGen GET /v1/video_status.get (poll)

> Apollo PATCH /api/v1/contacts/{id} (write URL)

> Apollo sequence email step sends (deliver)

Step 1: Set Up the CRM Trigger

Pick the CRM event that signals intent. A deal moving to “Demo Scheduled” or “Proposal Sent” is typically the right moment - early enough that the video lands before the prospect’s next touchpoint, late enough that they are qualified.

HubSpot: Create a Workflow with enrollment trigger “Deal stage is any of: [your stage]”. Add a “Send a webhook” action pointing at your n8n or Make webhook URL. Include the deal ID, associated contact ID, and contact email in the payload.

Salesforce: Use a Flow with an entry trigger on Opportunity Stage change. Add a Flow Action (HTTP callout) or use a Process Builder webhook step to fire the automation. Pass the Opportunity ID and primary Contact ID.

One important detail: include the Apollo contact ID in your CRM data if you have it stored. If not, you will match by email in the next step, which adds an extra API call and one more point of failure if the email does not match exactly.

Step 2: Pull Personalization Data from Apollo

Add an HTTP Request node calling the Apollo Contacts API to get the data you will pass to HeyGen. You need at minimum: first_name, email, and organization_name.

GET https://api.apollo.io/api/v1/contacts/{`{id}`}

Headers:

X-Api-Key: your-apollo-api-key

Content-Type: application/json

The response returns the full contact object. Extract contact.first_name, contact.email, and contact.organization_name using a Set node. If you do not have the Apollo contact ID, call the search endpoint instead:

Add a guard after the fetch: if first_name is empty, route to a Slack alert. A missing name produces a video that opens with silence or a broken template variable.

Step 3: Render the HeyGen Video

With the personalization data extracted, call the HeyGen generate endpoint. The request body passes your template ID, avatar ID, voice settings, and the contact variables mapped to your template’s input fields.

POST https://api.heygen.com/v2/video/generate

Headers: X-Api-Key: your-heygen-key

{

“video_inputs”: [{

“character”: {

“type”: “avatar”,

“avatar_id”: “your_avatar_id”,

“avatar_style”: “normal”

},

“voice”: {

“type”: “text”,

“input_text”: “Hi {{first_name}}, I noticed {{company}} is…”,

“voice_id”: “your_voice_id”

}

}],

“test”: true,

“aspect_ratio”: “16:9”

}

Keep “test”: true during development. Test renders are watermarked and free. Switch to false only when the full pipeline is verified end to end.

The response contains data.video_id. Store it in a Set node for the next step.

Then poll for completion. Add a Wait node (90 seconds), then GET /v1/video_status.get?video_id={{video_id}}, then an IF node on data.status: if completed, proceed; if processing, loop back; if failed, route to error handler. Cap at 10 polling iterations.

Step 4: Write the Video URL to Apollo

Once the render completes, data.video_url contains the hosted video link. Write it to a custom field on the Apollo contact using the update endpoint.

First, create the custom field in Apollo: Settings > Custom Fields > New Field. Name it something like personalized_video_url, type URL. Apollo will generate an ID for it.

PATCH https://api.apollo.io/api/v1/contacts/{`{id}`}

Headers: X-Api-Key: your-apollo-api-key

{

“typed_custom_fields”: {

“personalized_video_url”: “{{video_url}}”

}

}

Add a retry handler on this call. Apollo rate-limits the contacts API and a 429 here means the video URL never lands in Apollo - the sequence email sends with an empty variable. Retry on 429 with a 1-second wait and 3 max attempts.

Step 5: Reference the Field in Your Apollo Sequence

In Apollo, open your sequence and add or edit an email step. In the email body, insert the custom field variable:

Hi {{first_name}},

I recorded a quick video specifically for {{company}}:

{{custom.personalized_video_url}}

Worth 90 seconds of your time.

Apollo renders the variable at send time using the contact’s stored field value. If the field is empty when the email sends - because the render failed or the Apollo write failed - the prospect sees a blank line where the link should be. Handle this in your error workflow before the sequence step fires.

Error Handling

Two failure modes that will actually happen in production:

Apollo rate limits on contact writes. If you enroll a batch of contacts at once, the PATCH calls will stack up and some will return 429. Add exponential backoff retry on all Apollo write calls. If retries exhaust, log the contact ID and video URL to a separate store (a simple Google Sheet or a Supabase table works) so you can replay the writes without re-rendering.

HeyGen render failures. Renders fail for several reasons: avatar ID changed, voice ID not found, template variable missing. Add an Error Workflow in n8n that posts the failed video_id, contact ID, and error message to Slack. Do not retry render failures blindly - most are configuration errors that retry will not fix.

The 7-Day URL Expiry Problem

HeyGen’s video URLs expire 7 days after render. For same-day outreach this is fine. For multi-step sequences where a follow-up email fires on day 5 or 8, the link will be dead when the prospect clicks.

The fix: immediately after the render completes, download the video file and upload it to a permanent host. S3 or Cloudflare R2 both work. Use the permanent CDN URL in the Apollo write, not the HeyGen URL.

# After render completes

1. GET data.video_url (HeyGen direct URL)

2. Download video bytes to temp storage

3. PUT to S3 or R2 bucket

4. Use permanent CDN URL in Apollo write

This adds one step to your automation but removes the expiry risk entirely. If you are on a short sequence (same day or next day outreach), skip it. If your sequence has a day 7 or later step that references the video, the re-host is non-negotiable.

Want This Built for Your Sequence?

The Pipeline Audit covers your current CRM setup, Apollo sequence structure, and video volume - and returns a production architecture you can hand to any developer or have Kastiv build. 30 minutes, yours regardless of what you do next.

Get your Pipeline Audit

FAQ

Does Apollo.io have a native HeyGen integration?

No. Apollo does not have a HeyGen node or direct webhook trigger. The integration requires an automation layer (n8n, Make, or custom code) that bridges your CRM trigger, the HeyGen API, and the Apollo Contacts API. Apollo's role is email delivery - it reads a custom field variable you populate from outside.

What happens when the HeyGen video URL expires?

HeyGen video URLs expire after 7 days. If your Apollo sequence sends the email within a few hours of the render, the direct URL is fine. If your sequence has delays - a follow-up email on day 5 or 8 - the link will already be dead when the prospect clicks. The fix is to download the rendered video immediately after completion and re-host it on S3 or Cloudflare R2, then use the permanent CDN URL in Apollo instead of the HeyGen URL.

Apollo rate-limited my contact update calls - what now?

Apollo's API enforces rate limits at the plan level. If you are bulk-enrolling contacts into a sequence and writing video URLs in parallel, you will hit limits. Add a 500ms delay between write calls in your automation layer, or batch updates using Apollo's bulk update endpoint if your plan supports it. Your automation layer should catch 429 responses and retry with exponential backoff - do not let them fail silently or the video URL never lands in Apollo and the sequence email sends without the link.

Can I use this with HubSpot sequences instead of Apollo?

Yes. The HeyGen render steps (Steps 3 and 4 in this guide) are identical. For HubSpot, replace the Apollo contact fetch with a HubSpot Contact Get node, and replace the Apollo custom field write with a HubSpot Contact Update node using a custom contact property. The email step in HubSpot then references the property token instead of the Apollo variable syntax.

ABOUT THE AUTHOR

Umar Asghar is the CTO of Kastiv. He builds production AI video and API integration pipelines.