Webhook steps

A webhook step calls a URL of yours as a journey reaches it. It is how a sequence tells your own systems something happened — create a task, update a CRM record, start your own workflow. AgentsMail only ever sends to that URL. Nothing is read back from your answer: only the HTTP status code is kept.

The two shapes

Which one you receive depends on the kind of sequence the step belongs to, and the event field tells you without guessing.
SequenceeventCarries
A journey people are enrolled insequence.node.webhookOne contact, as they pass the step
A series on a fixed datesequence.series.webhookA batch of contacts — the audience of that step
A series acts on a whole audience at once. Posting one request per contact would mean twenty thousand requests at your door for a twenty-thousand-contact list, so the audience is cut into batches and each batch is one request. Both shapes are permanent: the batch one does not replace the single-contact one.

One contact

json
{
  "event": "sequence.node.webhook",
  "occurredAt": "2026-09-21T09:00:00.000Z",
  "sequence": {"id": "…", "name": "Onboarding"},
  "node": {"id": "…"},
  "contact": {
    "id": "…",
    "email": "alice@example.com",
    "firstName": "Alice",
    "lastName": null
  }
}

A batch

json
{
  "event": "sequence.series.webhook",
  "occurredAt": "2026-09-21T09:00:00.000Z",
  "sequence": {"id": "…", "name": "Spring launch"},
  "node": {"id": "…"},
  "batch": {"index": 3, "size": 500},
  "contacts": [
    {
      "id": "…",
      "email": "alice@example.com",
      "firstName": "Alice",
      "lastName": null
    }
  ]
}
batch.index is the rank of that batch inside one run of the step, counted from zero, and batch.size is how many contacts it carries. The pair is stable: if you ever see the same node.id + batch.index for the same run twice, it is the same contacts — which is what lets you recognise a batch you already handled. The audience is frozen when the step starts. Contacts who arrive while the batches are going out belong to the next step, not to this one.

What is never in it

Three things, and none of them by accident:
  • no secret — no API key, and no unsubscribe link or token, which is a capability and not an identifier;
  • no organization id;
  • no response of yours is read — the body you send back is discarded unopened.
If you need to know which of your accounts a call belongs to, put it in the URL itself: a distinct path or query string per sequence works, and it never travels in the payload.

What we require of your URL

  • https only. A plain http URL is refused at publication.
  • A publicly reachable address. Private, local and internal addresses are refused — including a perfectly public host name that resolves to one, and including an address reached through a redirect. Every hop is checked again.
  • At most three redirects.
  • An answer within five seconds. Past that the call is a timeout. Answer first, work afterwards: acknowledge the request, then do your processing out of band. A batch is sized to fit inside that window, so keep your handler fast rather than thorough.
Any 2xx is a success. Anything else is an error, and nothing about it is guessed at.

When it fails

A failed call is never retried. A webhook call goes out at most once — retrying could create the same order, the same task or the same record twice on your side. A call that fails is a call that never happened for you.
A failure does not stop the journey: the contact moves on, and the rest of the series goes out. The outcome of every call is kept, and the sequence screen shows it on the step itself:
ShownMeaning
succeededYour endpoint answered a 2xx.
errorYour endpoint answered something else — the status code is shown.
timed outNothing came back within five seconds.
destination refusedOur rules refused the address before anything was sent.
On a series, the step shows how many batches went out and how many failed, with the reason of the last failure — for example 38 batches · 2 failed (timed out). Since nothing is retried, that line is the only place a partial failure ever appears: a step that looks fine at a glance can still have lost two batches.

What it implies elsewhere

Webhook steps are part of the sequence graph, and a step whose URL breaks our rules makes the sequence refuse to publish — the message names the step.