What every integration has in common

  • Scale plan. Webhooks, the leads API, Google Sheets and HubSpot are part of Scale. If a workspace leaves Scale, the leads API answers 403, Google Sheets and HubSpot syncing stops, and webhooks stop sending lead data. Deletion signals, including opt_out, are still sent. Leads captured in the meantime are not sent later, and a Scale subscription whose payment is past due counts as having left Scale.
  • New leads only. Connecting a destination does not copy leads captured before it was connected. Use the CSV export for those.
  • No Instagram user ID, anywhere. Every lead is identified by Replavia’s own lead id. The person’s Instagram user ID is never sent to a webhook, the API, a sheet or HubSpot.
  • Deletions follow the data. When someone unsends the message that shared their email or phone, or asks for their data to be deleted, Replavia tells each connected destination, as described in each section below.

Google Sheets

Connect a Google account and Replavia creates a spreadsheet for your leads, then adds new ones about every 30 minutes. Replavia can only open files it created itself (Google’s drive.file access), so it cannot read the rest of your Drive.

ColumnWhat it holds
Lead IDReplavia’s id for the lead. Stays in place even after a deletion.
NameThe person’s Instagram username
EmailThe email they shared, if any
PhoneThe phone number they shared, if any
AutomationThe name of your automation that captured them
StatusNEW, CONTACTED, CONVERTED, IGNORED or JUNK
Captured (date, UTC)The day the lead was captured

The column order is fixed, so formulas and lookups that point at it keep working. When a person unsends a detail, opts out or asks for deletion, their Name, Email and Phone cells are cleared and the Lead ID stays.

HubSpot

Connect your HubSpot account and new leads are created or updated as contacts about every 30 minutes. A lead with an email address is matched on email, so an existing contact is updated rather than duplicated. A lead without one is matched on its Replavia Lead ID. Empty values are never written over a field you already have.

HubSpot propertyValue
EmailThe email they shared
Phone numberThe phone number they shared
First nameTheir Instagram username
Replavia Lead IDReplavia’s id for the lead (a unique property)
Replavia SourceThe automation that captured them
Replavia IntentWhat they were asking about, when Replavia detected it
Replavia StatusNEW, CONTACTED, CONVERTED, IGNORED or JUNK
Replavia Captured AtThe capture date, as YYYY-MM-DD

Replavia creates the five “Replavia” properties in your account on the first sync. If a person unsends a detail, their email and phone are cleared on the HubSpot contact. If they ask for their data to be deleted, the contact is archived.

Webhooks

A webhook sends each lead event to a URL you choose, as an HTTPS POST with a JSON body. Add up to 10 endpoints per workspace, and each one receives every event.

Endpoint requirements

  • https:// only, on the standard port 443, with a valid certificate (TLS 1.2 or later).
  • The host must resolve to a public internet address. Private and internal addresses are refused.
  • Redirects are not followed, so give the final URL.
  • Our sending IP address is not fixed. Verify the signature instead of allow-listing an address.

Verification before any lead is sent

When you add an endpoint, Replavia first sends it a webhook.verify event with no lead data in it. Reply with any 2xx status and the endpoint becomes active. Until it does, no lead is sent to it. Your signing secret (whsec_…) is shown once, when you create the endpoint. If you rotate it, the old secret keeps signing alongside the new one for 24 hours so you can switch over without dropping a delivery.

Events

EventWhen it is sent
lead.capturedSomeone becomes a contact for the first time — usually by triggering one of your automations, or by messaging you through an ice breaker or DM link. Email and phone may still be empty at this point.
lead.updatedReplavia records something about an existing contact again: they trigger an automation (the same one or another), share an email or phone number (including with the AI assistant), or opt out by replying STOP, which sets subscribed to false (if your workspace can receive lead data at that moment; otherwise see lead.deleted "opt_out"). A STOP from someone who was not yet a contact also arrives as lead.updated, so treat an id you have not seen as a new lead.
lead.deletedCarries only the lead id and a reason. "unsend": the person unsent the message that shared their email or phone. The lead still exists, so clear its email and phone (a later lead.updated can follow). "erasure": the person asked for their data to be deleted, so delete the record. "opt_out": the person replied STOP at a time Replavia could not confirm your workspace may receive lead data, for example after leaving the Scale plan. The lead is not deleted, so keep the record and stop marketing to them.

Changes you make yourself in the dashboard, such as a status, a note or a tag, are not sent as events. Read them with the leads API.

Headers

HeaderValue
Content-Typeapplication/json
User-AgentReplavia-Webhooks/1.0 (+https://replavia.com)
X-MDF-Webhook-IdThe event id, the same as the body’s top-level "id". It stays the same on every retry. Use it to ignore repeats.
X-MDF-Webhook-EventThe event type, for example lead.captured
X-MDF-Webhook-TimestampWhen this attempt was signed, in Unix seconds
X-MDF-Webhook-Signaturet=<timestamp>,v1=<signature> — see “Verifying the signature”

Payload

{
  "version": 1,
  "event": "lead.captured",
  "id": "evt_3xQ9m2KfT7vLp0aZ8nWc4Rj1bYs",
  "occurredAt": "2026-09-13T14:02:51.337Z",
  "data": {
    "id": "cmf1k8z2q0003xyz9abc4def5",
    "username": "jane.bakes",
    "email": "jane@example.com",
    "phone": null,
    "source": "Recipe giveaway",
    "account": "yourbrand",
    "status": "NEW",
    "subscribed": true,
    "note": null,
    "tags": [],
    "capturedAt": "2026-09-13T00:00:00.000Z"
  }
}

occurredAt is the exact moment the change happened. capturedAt is recorded to the day, so its time is always midnight UTC. account is your connected Instagram account, and source is the name of your automation. A lead.deleted event carries no personal data, only the id to match on:

{
  "version": 1,
  "event": "lead.deleted",
  "id": "evt_Vb7Tq1mX0pLs9dK2wZc8Hn4yRa6",
  "occurredAt": "2026-09-14T09:15:03.120Z",
  "data": {
    "id": "cmf1k8z2q0003xyz9abc4def5",
    "reason": "unsend"
  }
}

Verifying the signature

Each request is signed with HMAC-SHA256 using your endpoint’s secret. The signed text is mdf.webhook.v1. followed by the X-MDF-Webhook-Id value, a dot, the timestamp t, a dot, and the raw request body. The signature is base64-encoded. While a secret is being rotated, the header carries two v1= values, so accept the request if either one matches. Always verify against the body bytes exactly as received, because re-serialised JSON will not match. In Node.js:

import { createHmac, timingSafeEqual } from 'node:crypto';

// header   = the X-MDF-Webhook-Signature header
// id       = the X-MDF-Webhook-Id header
// rawBody  = the request body exactly as received (a Buffer, not re-serialised JSON)
// secret   = your endpoint's signing secret (whsec_…)
export function verifyReplavia(header, id, rawBody, secret) {
  if (!header || !id || !secret) return false;
  let ts = null;
  const sigs = [];
  for (const part of header.split(',')) {
    const [k, ...rest] = part.trim().split('=');
    const v = rest.join('='); // base64 can end in '='
    if (k === 't') ts = Number(v);
    else if (k === 'v1' && v) sigs.push(v);
  }
  if (ts === null || sigs.length === 0) return false;
  if (Math.abs(Math.floor(Date.now() / 1000) - ts) > 300) return false; // 5 minutes

  const signed = Buffer.concat([Buffer.from(`mdf.webhook.v1.${id}.${ts}.`, 'utf8'), Buffer.from(rawBody)]);
  const expected = createHmac('sha256', secret).update(signed).digest();
  return sigs.some((s) => {
    const got = Buffer.from(s, 'base64');
    return got.length === expected.length && timingSafeEqual(got, expected);
  });
}

Responses, timeouts and retries

Reply with a 2xx within 10 seconds, and do slow work after you have replied. Delivery is at least once, and the order of events is not guaranteed. De-duplicate on X-MDF-Webhook-Id, and use occurredAt to ignore an update older than one you have already applied.

Your endpoint returnsWhat Replavia does
2xxDelivered
A timeout or connection errorRetries. A hostname that cannot be resolved at all is not retried.
5xxRetries
408 or 429Retries, and waits as long as a Retry-After header asks (up to 6 hours)
410 GoneStops retrying that event. New events are still sent, so delete an endpoint you no longer use.
3xx, or any other 4xxStops retrying that event, because it is treated as a configuration problem

An event is tried up to 8 times, over up to about 20 hours. The waits between attempts grow from seconds to hours, and each one is randomised so a recovering server is not hit all at once. An endpoint that fails 20 times in a row, over at least 6 hours with no successful delivery in the last 24 hours, is switched off.

Zapier and Make

Zapier and Make connect through their own webhook triggers. There is no listed Replavia app in either directory.

  1. Zapier: create a Zap with the “Webhooks by Zapier” app and the Catch Hook trigger, then copy the URL it gives you. Check that your Zapier plan includes Webhooks by Zapier. Make: add the Webhooks module’s Custom webhook trigger and copy its address.
  2. In Replavia, open Contacts → Integrations and add that URL as a webhook endpoint.
  3. Both reply 2xx automatically, so the verification event activates the endpoint on its own. That first request is the verification event, so map your fields from a real lead.captured event once a lead arrives.
  4. Optionally, filter on the event field so each Zap or scenario handles only the events it needs.

Leads API

GET https://app.replavia.com/api/v1/leads returns your leads, newest capture first. Create a key in Contacts → Integrations. It starts with mdf_live_ and is shown once. A workspace can have up to 5 active keys, and you can revoke any of them at any time.

curl "https://app.replavia.com/api/v1/leads?limit=100&status=NEW" \
  -H "Authorization: Bearer mdf_live_…"

Query parameters

ParameterMeaning
limitLeads per page. The default is 50 and the maximum is 200.
cursorThe next_cursor value from the previous page. Pages are cursor-based, so leads arriving while you page do not shift the pages you have not read yet.
statusOnly leads in this status: NEW, CONTACTED, CONVERTED, IGNORED or JUNK
sinceAn ISO 8601 timestamp. Only leads that changed at or after it are returned, which suits an incremental sync. A value that is not a valid date is ignored, and every lead is returned.

Response

{
  "data": [
    {
      "id": "cmf1k8z2q0003xyz9abc4def5",
      "username": "jane.bakes",
      "email": "jane@example.com",
      "phone": null,
      "source": "Recipe giveaway",
      "account": "yourbrand",
      "status": "NEW",
      "subscribed": true,
      "note": null,
      "tags": [],
      "capturedAt": "2026-09-13T00:00:00.000Z",
      "updatedAt": "2026-09-13T14:02:51.337Z"
    }
  ],
  "has_more": true,
  "next_cursor": "Y21mMWs4ejJxMDAwM3h5ejlhYmM0ZGVmNQ"
}

The fields match the webhook payload, plus updatedAt. When has_more is false, you have reached the last page. Leads whose data has been erased are never returned.

Errors and limits

StatusMeaning
400 invalid_statusstatus is not one of the allowed values (the response lists them)
401 unauthorizedThe key is missing, wrong or revoked. All three return the same answer.
403 upgrade_requiredThe workspace is not on the Scale plan
429 rate_limitedToo many requests. Stay under 120 a minute per key, and wait for the Retry-After header (60 seconds) before trying again.

Questions about an integration? See pricing or all features.

Free forever plan · No card needed

Your next 100 customers are in your comments.

Create your free account, connect Instagram, and Replavia starts replying — and selling — around the clock.

Start free→

Free forever plan · Cancel anytime

Comments → DMs · demoInteractive preview
@sofia.bnow

LINK please 🔥

Auto-DM sent · instant
@noah.know

drop the link 👀

Auto-DM sent · instant
@priya.rnow

how much?? 🙏

Auto-DM sent · instant
@jay.makesnow

where do I buy this?

Auto-DM sent · instant
@mia.ktnow

send me the link

Auto-DM sent · instant
@theo.rnow

i need this 😍

Auto-DM sent · instant
@ava.stylesnow

price?

Auto-DM sent · instant
@kofi.tnow

how to order? 🛒

Auto-DM sent · instant
@sofia.bnow

LINK please 🔥

Auto-DM sent · instant
@noah.know

drop the link 👀

Auto-DM sent · instant
@priya.rnow

how much?? 🙏

Auto-DM sent · instant
@jay.makesnow

where do I buy this?

Auto-DM sent · instant
@mia.ktnow

send me the link

Auto-DM sent · instant
@theo.rnow

i need this 😍

Auto-DM sent · instant
@ava.stylesnow

price?

Auto-DM sent · instant
@kofi.tnow

how to order? 🛒

Auto-DM sent · instant