SendBeam

Automations that listen to your other tools, and answer back

A named event starts an automation from anything that can make an HTTP request, and a new step tells your own systems when a contact reaches a point in a flow.

SendBeam team

5 min read

Most small email platforms have the same shape of integration story. There is a list of logos, and behind each logo is a recipe that creates a subscriber. Data goes one way: into the tool. If you want the tool to tell something else what happened — a CRM, a helpdesk, your own application — you are usually told to poll an API on a schedule, or to wait for the vendor to build a partnership.

We have just shipped the two halves that remove that. Anything that can make an HTTP request can now start a SendBeam automation by saying what happened, and an automation can now tell your systems that a contact has reached a point in a flow. Neither needs a partnership, an app review or a marketplace listing.

The inbound half: say what happened, by name

There is a new endpoint. You post an event name and an email address:

POST /api/v1/events
x-api-key: sb_live_...
Content-Type: application/json

{ "event": "deal_won", "email": "[email protected]" }

In the automation builder there is a matching trigger, Something happened elsewhere, where you type the same name: deal_won. Every active automation listening for that name runs for that contact.

That sounds small. The design decision inside it is not, and it is worth explaining because it is the reason this works without us calling anyone.

SendBeam already had a way to start an automation from outside — POST /api/v1/automations/{id}/trigger — and it has a flaw that only shows up in practice. It names one automation, by id. So the integration has to know that id, which means somebody has to copy it out of our UI and paste it into theirs, and it breaks the day the workspace rebuilds the automation. Worse, a partner cannot ship a recipe that works for anybody but the person who configured it, because every customer’s id is different. That is exactly the coupling that turns a five-minute job into a business-development conversation.

A name inverts the dependency. The sending system says what happened; the workspace decides what that should mean; either side can change without telling the other. deal_won is the same string in every workspace on the planet, so a recipe built once works for everyone — and it is a string a person can guess, which matters more than it sounds when you are debugging at eleven at night.

In practice that means a Pipedrive webhook for a deal moving to Won, a Typeform partial submission, a Calendly booking, or a row changing in a spreadsheet, each becomes a Zapier webhook step or an n8n HTTP Request node pointing at one URL. No app review anywhere.

What it deliberately will not do

An event never creates a contact. Somebody else’s system mentioning an address is not consent to email that address, and we are not going to launder a purchased list through an integration endpoint. If we do not already hold the contact, the response says so:

{ "ok": true, "event": "deal_won", "matched": false, "enrolled": 0 }

Note that it is a 200, not a 404. An unknown address is a completely normal thing for a busy CRM to send, and returning an error for it would have every sending system treat a non-event as a failure and retry it for ever. It is accepted, it is not acted on, and it says which.

Two other limits worth knowing before you build on it: the event carries a name and an address and nothing else — there is no custom payload to read inside the automation yet — and a contact who has unsubscribed or been suppressed is not enrolled by one, the same as every other trigger.

The outbound half: a step that calls your systems

The other direction is a new automation step, Call Webhook. Drop it anywhere in a flow and, when a contact reaches it, your systems hear about it: a CRM to update, a task to raise, an order to check.

The interesting part is what the step does not have: a URL field.

We could have put a box on the step and posted to whatever you typed. Every one of those requests would then be an unvalidated outbound call from our infrastructure, and we would need to build signing, retries, timeouts and failure handling for a second time — a worse copy of what our webhook endpoints already do.

So the step sends an event, automation.step_reached, and the endpoints you have already set up deliver it: signed, retried, and switched off automatically if your server goes away. One endpoint serves every automation you ever build, and a URL that changes is changed in one place rather than hunted through a dozen flows.

You give the step a labelnotify-crm, say — and that is what arrives:

{
  "event": "automation.step_reached",
  "data": {
    "contact": { "id": "…", "email": "[email protected]", "first_name": "Sam" },
    "automation": { "id": "…", "name": "Trial day 3" },
    "step": { "id": "…", "label": "notify-crm" }
  }
}

Branch on the label, not on the automation’s name: names get edited, and step ids change when a flow is rebuilt. The label is the bit you control and nobody else will touch by accident.

Because an endpoint has to be subscribed for any of this to happen, the step tells you whether one is. If nothing in the workspace is listening for automation.step_reached, the step says so while you are building it and links straight to the form with the event already ticked — and the pre-flight check warns you again if you try to activate an automation containing a step nothing would hear. A step that silently does nothing is the worst possible outcome for a feature like this, so we spent the effort on making that state impossible to miss rather than on a cleverer builder.

If you run more than one site

Because the trigger matches on a name rather than an id, one recipe covers every workspace you run. An agency with fifteen client workspaces writes the deal_won automation once and copies it into the rest with a workspace blueprint, and the same integration, pointed at the right API key, feeds all of them. Nothing in the sending system needs to know which workspace it is talking to beyond the key.

Try it in five minutes

  1. Build an automation, give it the Something happened elsewhere trigger, and type an event name.
  2. Activate it — only active automations enrol.
  3. Send yourself one event with curl, using an API key that has contacts:write:
curl -X POST https://sendbeam.io/api/v1/events \
  -H "x-api-key: $SENDBEAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event":"deal_won","email":"[email protected]"}'
  1. For the return path, add a Call Webhook step, give it a label, and make sure an endpoint subscribes to automation.step_reached.

The full reference is in the docs: triggers, step types and webhooks. If your store sends order events, the same machinery already carries cart, product and order triggers.

Both halves are live on every plan now. If you build something with them that we did not think of, we would like to hear about it — that is rather the point of not requiring a partnership.

Written by

SendBeam teamThe people building SendBeam

Written by the people building SendBeam. We run five of our own sites on it.

In the docs

The SendBeam blog, by email

Running email for small sites, honestly.

New posts when we have something worth saying — what we learn running five sites' email, pricing checks, deliverability changes and UK rules. No drip sequence, no "tips".

Double opt-in: we send one confirmation email and nothing else until you click it. Unsubscribe in one click, any time. Privacy.