Recover failed Stripe payments with Customer.io and HubSpot

When a Stripe payment fails, Customer.io sends a tone-matched recovery email and high-value accounts get a HubSpot task so a CSM can step in.

Agentic Task
StripeCustomer.ioHubSpotFinanceSalesEmail AutomationNotifications & Alerts

Build an agent workflow that runs every time Stripe fires an invoice.payment_failed webhook, recovers the customer through Customer.io, and escalates to a human in HubSpot when the account is high value or has failed several times in a row.

Trigger: Stripe webhook on the invoice.payment_failed event.

What the agent should do on every run:

1) Read the failed invoice and customer from the webhook payload: invoice id, amount_due (cents), attempt_count, customer id, customer email, the plan or subscription name, next_payment_attempt, and hosted_invoice_url. If anything is missing, call Stripe Retrieve Invoice and Retrieve Customer to fill in the gaps.

2) Call Customer.io Identify Person on the customer's email and upsert these traits: last_payment_failed_at (current ISO timestamp), failed_invoice_id, failed_invoice_amount (in dollars), failed_plan_name, dunning_attempt (mirror Stripe's attempt_count), and next_payment_attempt_at.

3) Call Customer.io Send Transactional Email using a pre-built dunning template, choosing tone from attempt_count: first failure is a warm, gentle reminder pointing to the billing portal link with the next retry date; second failure is a firmer reminder noting Stripe is still retrying and asking them to update payment; third or later failure is a serious tone explaining the risk of service interruption. Pass the template the customer first name, plan name, amount formatted as dollars, hosted_invoice_url, and next_payment_attempt_at as message data.

4) Decide whether to also escalate to a human. Escalate if EITHER the failed amount is above a configurable threshold (default $500, i.e. 50000 cents) OR attempt_count is 3 or more. When escalating: look up the contact in HubSpot with Get Contact by email, then call HubSpot Create Task associated with that contact. The task subject should read like 'Recover failed payment from {customer name}: ${amount} on {plan}', due tomorrow, priority HIGH, with a body that summarizes attempt count, plan, the failure reason if available, and the hosted invoice URL so the CSM can act from one place. If the HubSpot lookup returns no match, log the skip and finish the run without creating a task.

Keep the workflow idempotent. If Customer.io already holds the same failed_invoice_id with this dunning_attempt and the transactional email has already been sent for it, skip the resend and only run the HubSpot escalation step if it is also new.

Integrations used: Stripe (trigger plus Retrieve Invoice, Retrieve Customer), Customer.io (Identify Person, Send Transactional Email), HubSpot (Get Contact, Create Task).

Things I want to tweak later: the dollar threshold for HubSpot escalation, the attempt_count that always triggers escalation, the transactional message template id for each tone, and the HubSpot task owner (default to the contact's existing owner).

Additional information

What does this prompt do?
  • Listens for failed Stripe payments and pulls the invoice amount, plan, attempt count, and customer email automatically.
  • Updates the customer's profile in Customer.io and sends a recovery email with tone matched to whether this is their first miss or a repeat failure.
  • Escalates to a human in HubSpot by opening a dated task for the CSM whenever the amount is large or Stripe has already retried multiple times.
  • Keeps every nudge idempotent so customers never get spammed with duplicate emails when webhooks fire twice.
What do I need to use this?
  • A Stripe account where you can enable webhooks on failed invoice payments.
  • A Customer.io workspace with one or more transactional email templates built for dunning reminders.
  • A HubSpot account that holds your billing contacts so the workflow can look them up and create tasks against them.
How can I customize it?
  • Change the dollar amount that triggers human follow-up in HubSpot. Default is $500 and up.
  • Pick which Customer.io template fires for each stage: gentle nudge, firmer reminder, or final warning.
  • Decide who owns the HubSpot task: the contact's existing owner, a named CSM, or your billing team.
  • Tune the retry count that escalates to a human. By default the third failed attempt always gets a task, no matter the amount.

Frequently asked questions

Does this replace Stripe's automatic retry schedule?
No. Stripe keeps retrying the card on its normal schedule. This workflow runs alongside those retries to nudge the customer through Customer.io and pull in a human when the situation gets serious.
Will the customer get the same email twice if a webhook fires more than once?
No. The workflow only sends one email per retry attempt. If Stripe fires the same event again, the second run sees the attempt has already been handled and skips the duplicate send.
What happens if the customer is not in HubSpot yet?
The workflow looks for them by email. If there is no match, it still updates Customer.io and sends the recovery email, then skips the HubSpot task without failing the run.
Can the email tone really change based on how many times the card failed?
Yes. You point the workflow at separate Customer.io templates: a gentle one for the first miss, a firmer one for the second, and a final warning for the third. The workflow picks the right one using Stripe's attempt count.
Do I need a paid HubSpot plan for this to work?
Any HubSpot plan that lets you create tasks and look up contacts will work, including Free. The workflow only uses standard CRM features.

Stop losing revenue to silent churn.

Connect Stripe, Customer.io, and HubSpot once. Recovery runs itself, and humans get pulled in only when an account is worth the effort.