Idempotency in Marketing Integrations
What idempotency means and why marketing systems need it everywhere
An operation is idempotent if performing it multiple times produces the same end state as performing it once. Marketing integrations are full of situations where the same logical event can legitimately be delivered, retried, or re-processed more than once — a webhook retry after a timeout, a queue consumer crashing mid-processing and restarting, a batch job re-running after partial failure — and without idempotency, each of these turns into a duplicate conversion, a double-charged customer in a billing sync, or a contact enrolled twice in the same email nurture sequence.
Idempotency keys: the standard mechanism
The standard pattern is attaching a unique idempotency key to each logical operation — generated by the sender, not the receiver — and having the receiving system check, before performing any side effect, whether it has already processed that exact key. If so, it returns the previously recorded result without repeating the side effect. Stripe popularized this pattern for payment APIs; the same approach applies directly to marketing operations like 'send this email,' 'fire this conversion event,' or 'enroll this contact in this flow.'
async function sendConversionEvent(order: Order) {
// Deterministic key derived from the business event itself, not a random UUID,
// so retries of the SAME logical event always produce the SAME key.
const idempotencyKey = `purchase_${order.id}_${order.updatedAt.getTime()}`;
const existing = await db.sentConversions.findUnique({
where: { idempotencyKey },
});
if (existing) return existing.result; // already sent, don't double-fire
const result = await metaConversionsApi.send({
event_name: "Purchase",
event_id: idempotencyKey, // Meta also dedupes client/server events on this field
value: order.total,
currency: order.currency,
});
await db.sentConversions.create({ data: { idempotencyKey, result } });
return result;
}Deterministic keys beat random ones for retry safety
Client-side and server-side deduplication together
Meta and TikTok's Conversions APIs explicitly support deduplication between a browser-side pixel event and a server-side CAPI/Events API event representing the same real action, using a shared event_id. This is a different idempotency concern than retry-safety within your own backend — it's cross-system deduplication between two independent tracking paths intentionally sending the same event for redundancy. Both need the same discipline: a stable, deterministic identifier shared across every path that might report the same real-world event.
Idempotency at the database layer
A unique constraint on the idempotency key column, enforced at the database level (not just checked in application code before an insert), is what actually prevents duplicates under concurrent processing — two workers racing to process the same retried webhook will otherwise both pass an application-level 'does this exist' check before either has committed, and both proceed to double-process. Use INSERT ... ON CONFLICT DO NOTHING (or equivalent) patterns so the database itself is the final authority on uniqueness, not a race-prone check-then-act in application code.
What's Next?
Idempotency protects against duplicate processing, but it says nothing about whether the data itself — the shape and content of each event — is actually well-formed, which is where schema validation becomes the next line of defense.
I build these systems professionally.
Whether it's a RAG pipeline, analytics migration, or AI workflow — let's talk.