# Recover abandoned carts by email or WhatsApp

> Create an API Trigger campaign with a Delay and turn on re-entry with a 1-day cooldown. Your store calls /api/campaigns/trigger on every cart change and /api/campaigns/cancel when the order closes. Each call restarts the countdown, and a canceled reminder never goes out or counts as a send.

Fonte: https://wiki.bip.marketing/en-us/abandoned-cart · Atualizado: 2026-10-08

<UseCaseMeta effort="api" channel="email,whatsapp" plan="free" />

Anyone who leaves a cart behind gets a reminder with their own name and the product they left, at the time you choose. Anyone who buys first gets nothing.

<Checklist items="A BIP account (the free plan sends by email)|Access to your store's server, where carts and orders happen|The customer's name and email as soon as they identify themselves in the cart (their phone, for WhatsApp)|An API key with Campaigns Trigger and Leads Write|For WhatsApp: a paid plan, a connected number, and an approved template" />

## What you'll get

- A reminder for anyone who leaves a cart, with their first name and the product they left behind.
- No messages to people who are still shopping: every cart change restarts the countdown.
- No reminder for people who already bought: the order cancels the send, and a canceled send doesn't count against your balance.
- At most one reminder every 24 hours per person, for each cart they abandon.
- New contacts created in the same call, with no duplicates of anyone already in BIP.

## How it works

<Steps items="Your store calls BIP whenever the cart changes|BIP waits out the delay and restarts the countdown if the person comes back|They bought: the store cancels. They didn't: the reminder goes out" />

The campaign uses **API Trigger** in **Delay** mode. In practice:

- Your store calls `/api/campaigns/trigger` on every cart change for an identified customer. BIP schedules the reminder for one hour later (or whatever delay you set).
- A new call for the same person restarts the countdown. Each person has at most one reminder waiting in this campaign.
- When the order closes or the cart is emptied, the store calls `/api/campaigns/cancel`. The pending reminder is discarded: it doesn't go out and doesn't count as a send.
- If the time comes with no purchase, BIP checks the **Lead Re-entry Window** and builds the email with the contact's latest data. If the person switched products, the reminder shows the updated cart.

## Step by step

### 1. Create the cart fields

The reminder lands harder when it names the product. For that, the store writes the product to a contact field on every call.

1. In **Settings** → **General**, go to **Fields** and click **Add Field**.
2. In **Field Type**, choose **String**. In **Field Name**, type _Produto do carrinho_ ("cart product") and click **Save**.
3. For the WhatsApp version, also create the _Código do carrinho_ ("cart code") field, of type **String**. It takes the person straight to their cart.

<Shot src="/img/carrinho-abandonado/1-en-us.png" alt="Custom fields Produto do carrinho and Código do carrinho in Settings, General tab" url="dash.bip.marketing/…/settings" />

### 2. Create the API key

1. In **Settings**, open the **API Keys** tab and click **Add New API Key**.
2. In **Name**, type _Loja Horizonte — cart_.
3. In **Scopes**, turn on **Campaigns Trigger** and **Leads Write**. The second one lets the store send the contact as an object: BIP creates or updates the contact and schedules the reminder in the same call.
4. Click **Save** and copy the **API key**. Keep the key on the store's server, in an environment variable, and never in the website's code.

<Shot src="/img/carrinho-abandonado/2-en-us.png" alt="New API key with the Campaigns Trigger and Leads Write scopes turned on" url="dash.bip.marketing/…/settings?tab=api-keys" />

### 3. Build the reminder email

1. In **Emails Templates**, click **New Template**.
2. Click **Settings** and fill in **Name**, **Language** (**English**), **Subject**, and **Preview**. The copy is in [Ready to copy](#ready-to-copy).
3. Add the blocks: logo, heading, text, a button to the cart, and social networks.
4. In the text, type `@Produto` and pick _"Produto do carrinho" field value_. Click the variable and set the **Fallback Value** to `a few items`.
5. In the button block, set **Link URL** to your store's cart page.
6. Click **Save**.

<Shot src="/img/carrinho-abandonado/3-en-us.png" alt="Email editor with the cart reminder and the product variable in the text" url="dash.bip.marketing/…/emails-templates/…" />

### 4. Create the campaign with a delay

1. In **Campaigns**, click **New Campaign**.
2. In **Campaign Name**, type _Abandoned cart — email_ and pick the 🛒 emoji.
3. In **Schedule Type**, choose **API Trigger**.
4. In **Trigger Mode**, choose **Delay**. In **Delay (Seconds)**, type `3600` (one hour).
5. In **Channel**, choose **Email** and, in **Email Template**, link the template from step 3.

API campaigns have no segments section: each call from the store says who receives the message.

<Shot src="/img/carrinho-abandonado/4-en-us.png" alt="Campaign with API Trigger, Delay mode, and 3600 seconds" url="dash.bip.marketing/…/campaigns/…" />

### 5. Turn on re-entry

Someone who abandons a cart today may abandon another one next month. With the **Lead Re-entry Window** off (the default), each contact gets the campaign only once, ever: Marina's second cart wouldn't get a reminder.

1. In **Lead Re-entry Window**, turn on **Allow Lead Re-entry**.
2. In **Cooldown Period (Days)**, type `1`.

With 1 day, each person gets at most one reminder every 24 hours, even if they abandon several carts on the same day. BIP checks the window when the delay ends. A reminder blocked by the window shows up as **Rejected** in the stats and doesn't count as a send. Don't leave it at `0`: with no cooldown, the same person can get several reminders on the same day.

<Shot src="/img/carrinho-abandonado/5-en-us.png" alt="Lead Re-entry Window with Allow Lead Re-entry turned on and a 1-day cooldown" url="dash.bip.marketing/…/campaigns/…" />

### 6. Test and activate

1. Click **Save** → **Save Draft**.
2. Click **Test Campaign**, type your email, and click **Send Test**. If you're not a contact yet, add yourself first in **Leads** → **Create Lead**.
3. Check the email. The test goes out right away, without waiting for the delay, and doesn't use any sends. If your contact doesn't have the _Produto do carrinho_ field, you'll see the `a few items` fallback.
4. Click **Save** → **Save and Activate** and confirm with **Activate API Trigger**. The campaign switches to **Active** and starts accepting calls.

<Shot src="/img/carrinho-abandonado/6-en-us.png" alt="Activate API Trigger confirmation dialog" url="dash.bip.marketing/…/campaigns/…" />

### 7. Connect your store to BIP

1. In the campaign, open **API Integration**. The commands already include the campaign ID, which is also at the end of the page address.
2. Whenever an identified customer's cart changes (product added, product removed, quantity changed), call `/api/campaigns/trigger` with `lead` as an object: `name`, `email`, and the cart fields. The response includes `"mode": "delay"`.
3. When the order is completed, or the cart is emptied, call `/api/campaigns/cancel` with the same email. The response includes `"cancelled": true` when a reminder was waiting.
4. A new contact needs a `name` with at least 2 characters and an email or a phone. Always send the phone with `+` and the country code: `+5541998765432`.

The ready-made commands are in [Ready to copy](#ready-to-copy).

<Shot src="/img/carrinho-abandonado/7-en-us.png" alt="API Integration panel with the trigger and cancel commands" url="dash.bip.marketing/…/campaigns/…" />

## Ready to copy

### Campaign

| Option | Value |
| --- | --- |
| **Campaign Name** | 🛒 Abandoned cart — email |
| **Schedule Type** | **API Trigger** |
| **Trigger Mode** | **Delay** |
| **Delay (Seconds)** | `3600` |
| **Lead Re-entry Window** | **Allow Lead Re-entry** on, **Cooldown Period (Days)** `1` |
| **Channel** | **Email** |
| **Email Template** | Abandoned cart · Loja Horizonte |

Common values for **Delay (Seconds)**:

| Wait | Seconds |
| --- | --- |
| 30 minutes | `1800` |
| 1 hour | `3600` |
| 2 hours | `7200` |
| 4 hours | `14400` |
| 24 hours | `86400` |

### Fields

| **Field Name** | **Field Type** | Sample value | Where it appears |
| --- | --- | --- | --- |
| Produto do carrinho | **String** | Camisa de Linho Areia | Email and WhatsApp text |
| Código do carrinho | **String** | LH8F3K2 | WhatsApp button |

With more than one product, send a summary: _Camisa de Linho Areia and 2 more items_.

### Email

Wherever you see `[@first name]`, type `@first` and pick `lead.firstName()`. Wherever you see `[@Produto do carrinho]`, type `@Produto` and pick the field. Set each **Fallback Value** as shown in the table at the end of this section.

<Copy label="Subject">
[@first name], your cart is waiting
</Copy>

<Copy label="Preview">
We saved everything at Loja Horizonte. Check out whenever you're ready.
</Copy>

| Order | Block | Suggested setting |
| --- | --- | --- |
| 1 | **Add Image** | Loja Horizonte logo, **Max Width (px)** 160, store **Link URL** |
| 2 | **Add Heading** | Greeting with the first name |
| 3 | **Add Text** | The product left in the cart |
| 4 | **Add Button** | A single action, **Width** 50 |
| 5 | **Add Divider** | **Max Width (px)** 120, light color |
| 6 | **Add Text** | Help with sizing, shipping, and returns |
| 7 | **Add Social Networks** | Only the networks the store uses |

<Copy label="Heading">
[@first name], you left something behind
</Copy>

<Copy label="Text">
You left [@Produto do carrinho] in your Loja Horizonte cart. We saved everything for you: just come back and complete your purchase.

Items in your cart aren't reserved. If you love it, get yours before it sells out.
</Copy>

<Copy label="Button — Name">
Return to cart
</Copy>

<Copy label="Button — Link URL" code>
https://lojahorizonte.com.br/carrinho?origem=bip-carrinho
</Copy>

<Copy label="Closing text">
Questions about sizing, shipping, or returns? Find the answers at lojahorizonte.com.br/ajuda.
</Copy>

| Variable | Where | **Fallback Value** |
| --- | --- | --- |
| `lead.firstName()` | **Subject** | Hi |
| `lead.firstName()` | Heading | Hey |
| `"Produto do carrinho" field value` | Text | a few items |

The store always sends the name when it creates a contact, so the name fallback rarely appears.

### WhatsApp

For the WhatsApp version, create the template in the **Templates** tab of the **WhatsApp** screen, with **Category** **Marketing** and **Language** **English (US)**.

<WaTemplate name="horizonte_carrinho_lembrete" category="MARKETING" header="Your cart is waiting" footer="Loja Horizonte · lojahorizonte.com.br" buttons="View my cart|I have a question">
Hi {{1}}! You left {{2}} in your Loja Horizonte cart. We saved everything so you can check out whenever you like. Tap the button to go straight back to your cart.
</WaTemplate>

- Button 1, **URL**: `https://lojahorizonte.com.br/carrinho/{{1}}`, with the sample `LH8F3K2`.
- Button 2, **Quick reply**: anyone who taps it counts toward the **Reply Rate**, and the conversation continues in the WhatsApp Business app.
- Body **Variable Samples**: `{{1}}` Marina, `{{2}}` Camisa de Linho Areia.

| Campaign variable | Lead field | Sample value |
| --- | --- | --- |
| **Body · {{1}}** | **first name** | Marina |
| **Body · {{2}}** | **"Produto do carrinho" field value** | Camisa de Linho Areia |
| **Button 1 · {{1}}** | **"Código do carrinho" field value** | LH8F3K2 |

To create the template with **Import from JSON**:

<Copy label="horizonte_carrinho_lembrete.json" code>
[
  {
    "name": "horizonte_carrinho_lembrete",
    "category": "MARKETING",
    "language": "en_US",
    "components": [
      { "type": "HEADER", "format": "TEXT", "text": "Your cart is waiting" },
      { "type": "BODY", "text": "Hi {{1}}! You left {{2}} in your Loja Horizonte cart. We saved everything so you can check out whenever you like. Tap the button to go straight back to your cart.", "example": { "body_text": [["Marina", "Camisa de Linho Areia"]] } },
      { "type": "FOOTER", "text": "Loja Horizonte · lojahorizonte.com.br" },
      { "type": "BUTTONS", "buttons": [
        { "type": "URL", "text": "View my cart", "url": "https://lojahorizonte.com.br/carrinho/{{1}}", "example": ["https://lojahorizonte.com.br/carrinho/LH8F3K2"] },
        { "type": "QUICK_REPLY", "text": "I have a question" }
      ] }
    ]
  }
]
</Copy>

### API

Replace `YOUR_API_KEY` with your key and `CAMPAIGN_ID` with the ID from the **API Integration** panel.

<Copy label="Cart changed: schedules or restarts the reminder" code>
curl -X POST https://api.bip.marketing/api/campaigns/trigger \
  -H "Content-Type: application/json" \
  -H "x-bip-api-key: YOUR_API_KEY" \
  -d '{
    "campaignId": "CAMPAIGN_ID",
    "lead": {
      "name": "Marina Alves",
      "email": "marina.alves@exemplo.com",
      "phone": "+5541998765432",
      "fields": [
        { "key": "Produto do carrinho", "value": "Camisa de Linho Areia" },
        { "key": "Código do carrinho", "value": "LH8F3K2" }
      ]
    }
  }'
</Copy>

<Copy label="Order completed: cancels the reminder" code>
curl -X POST https://api.bip.marketing/api/campaigns/cancel \
  -H "Content-Type: application/json" \
  -H "x-bip-api-key: YOUR_API_KEY" \
  -d '{"campaignId": "CAMPAIGN_ID", "lead": "marina.alves@exemplo.com"}'
</Copy>

<Copy label="Trigger response" code>
{"campaignId": "CAMPAIGN_ID", "leadId": "CONTACT_ID", "mode": "delay"}
</Copy>

<Copy label="Cancel response" code>
{"campaignId": "CAMPAIGN_ID", "cancelled": true, "leadId": "CONTACT_ID"}
</Copy>

`"cancelled": false` means no reminder was waiting: the delay had already ended or nothing was triggered. It's not an error.

<Copy label="TypeScript" code>
// On the Loja Horizonte server
const API_KEY = process.env.BIP_API_KEY ?? 'YOUR_API_KEY';
const CAMPANHA_CARRINHO = 'CAMPAIGN_ID';

type Carrinho = {
  codigo: string;
  cliente: { nome: string; email: string; telefone?: string }; // phone with + and the country code
  itens: { nome: string }[];
};

async function bip(path: string, body: unknown) {
  const res = await fetch(`https://api.bip.marketing${path}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'x-bip-api-key': API_KEY },
    body: JSON.stringify(body)
  });
  const texto = await res.text();
  if (res.ok) return JSON.parse(texto);
  // Cancel for someone who was never in BIP: there was nothing to cancel
  if (path.endsWith('/cancel') && res.status === 404 && texto.includes('Lead not found')) {
    return { cancelled: false };
  }
  throw new Error(`BIP ${res.status}: ${texto}`);
}

const resumo = (itens: { nome: string }[]) =>
  itens.length > 1
    ? `${itens[0].nome} e mais ${itens.length - 1} ${itens.length === 2 ? 'item' : 'itens'}`
    : itens[0].nome;

// Product added, removed, or quantity changed: schedules or restarts the reminder
export const carrinhoMudou = (c: Carrinho) =>
  bip('/api/campaigns/trigger', {
    campaignId: CAMPANHA_CARRINHO,
    lead: {
      name: c.cliente.nome,
      email: c.cliente.email,
      ...(c.cliente.telefone ? { phone: c.cliente.telefone } : {}),
      fields: [
        { key: 'Produto do carrinho', value: resumo(c.itens) },
        { key: 'Código do carrinho', value: c.codigo }
      ]
    }
  }); // { campaignId, leadId, mode: 'delay' }

// Order completed or cart emptied: cancels the pending reminder
export const carrinhoFechado = (email: string) =>
  bip('/api/campaigns/cancel', { campaignId: CAMPANHA_CARRINHO, lead: email }); // { cancelled: true | false }
</Copy>

## How to measure

In **Campaigns**, open **Campaign Stats** (from the chart icon in the list or at the top of the campaign).

- **Processed (_N_ runs)**: reminders BIP processed after the delay. Canceled triggers don't appear: they were never processed.
- **Delivered**, **Open Rate**, and **Click Rate**: how many reminders arrived, were opened, and led to a click.
- **Top clicked links**: the button link, with the `origem=bip-carrinho` parameter, shows how many clicks went back to the cart.
- **Recipient Interactions**: filter by **Clicked** to see who went back to the cart. Filter by **Rejected** to see who the window held back, with the reason **Lead is within the re-entry window**.
- For the WhatsApp version: **Delivered**, **Read Rate**, and **Reply Rate** (including taps on _I have a question_).

In your own system, two readings complete the picture:

- The cancel response on each order. `"cancelled": true` means the person bought before the reminder time, and nothing went out. For a cart the store triggered, `"cancelled": false` means the delay had already ended: the purchase came after the reminder time. Cross-check with **Recipient Interactions** to see whether they received it and clicked.
- The `origem=bip-carrinho` parameter in visit URLs, to tie orders to the reminder in your store's reports.

On BIP Lite, the **Usage** card shows how much of the month's balance the reminders have used.

## Variations

### Email or WhatsApp

- **WhatsApp only** (paid plans): follow the same steps, but in step 4 choose **WhatsApp** in **Channel**, the `horizonte_carrinho_lembrete` template, and the variable mapping from [Ready to copy](#ready-to-copy). The store must send the customer's `phone`, with `+` and the country code.
- **Both channels**: each campaign uses a single channel. Create one email campaign and one WhatsApp campaign, with different delays, such as `3600` for email and `86400` for WhatsApp. The store calls trigger and cancel on both, with both IDs. Each reminder that goes out counts as one send.

### Contact already in BIP

If the customer is already a contact and you don't use the cart fields, send `lead` as a string, with the email or the phone. In that case, the key only needs **Campaigns Trigger**.

## Frequently asked questions

### Does someone who buys before the reminder time still get it?

No. When the order closes, the store calls `/api/campaigns/cancel` and the pending reminder is discarded. It doesn't go out and doesn't count as a send.

### What if the person comes back and changes the cart?

Each new call for the same person restarts the countdown. With `3600` seconds, the reminder goes out one hour after the last change, just once. And it shows the latest product, because BIP builds the message at send time.

### Do I need to build a segment?

No. **API Trigger** campaigns don't use segments: each call from the store says who receives the message. If the person isn't a contact yet, the same call creates the contact.

### How much of my plan does this use?

On BIP Lite, each processed reminder uses one send for the month. Canceled reminders and reminders held back by the **Lead Re-entry Window** don't count. The free plan includes 1,000 sends per month, by email.

## See also

- [Welcome in one call: from signup to first email](/en-us/welcome-in-one-call)
- [BIP API: keys, contacts, events and triggers](/en-us/api)
- [Campaigns: one-time, recurring and API-triggered](/en-us/campaigns)
- [Email editor: blocks, variables and preview](/en-us/email-editor)
- [WhatsApp templates: create, sync and use](/en-us/whatsapp-templates)
- [Plans and limits: sends, capacity and pricing](/en-us/plans-and-limits)
- [Abandoned cart recovery on the BIP website](https://bip.marketing/en-us/use-cases/abandoned-cart)
- [BIP for e-commerce](https://bip.marketing/en-us/solutions/ecommerce)
