Tell us something happened in your own system — a cart abandoned, a trial expiring — and start the automation waiting for it. Requires the automations:trigger scope.
POST /api/v1/automations/events is the one trigger PristineSend cannot observe for itself. Segment and inactivity triggers run off data we already hold; a cart is abandoned in your shop, and nothing in our database will ever tell us.
This endpoint cannot send mail on its own. It starts a run of an automation you already built and activated, and activation is where every check runs — your account, your sending domain, the postal address in the footer, the content scan. An event just says the moment arrived.
Two things have to be true, and both are easy to miss.
An automation must be listening for the name. Create one from an event blueprint, set its event name, and activate it. A draft does not receive events. The name is matched exactly, lowercased at both ends — so Cart_Abandoned and cart_abandoned are the same event, but cart-abandoned is a different one.
Four blueprints start from an event. Each ships with the name below already filled in — you can change it, but if you post the default you will not have to.
cart_abandonedproduct_viewedback_in_stockorder_placedOne thing to get right on Follow up on a browse: use a session id for the event_key, or a date like browse_2026-09-21 — not the product id. Somebody shopping properly fires that event eight or ten times, and only one run per contact is live at a time, so the burst is already capped at one email whatever key you send. What the key decides is what can trigger again later: a key that has run once never runs again, so keying per product means that product can never browse-trigger for that person again, even a year on.
The contact must already exist. An event never adds somebody to your list. If the address is unknown we return 404 not_found rather than creating a contact, because adding people to a marketing list should be something you did on purpose — not a side effect of telling us about a cart. Call POST /api/v1/contacts first if you are not sure.
contact takes an id or an email; payload is optional.
Response
If nothing is listening you get a 200 with matched: 0 and a sentence saying so, rather than an error. The event was accepted and there is nothing to retry — but a silent success while you are wiring this up is the least useful answer we could give, so it says what to check. A typo is the usual cause:
event_key is required, and it is your id for this occurrence — the cart id, the invoice id. We run each automation once per key, for ever, so a retry after a timeout is safe and a queue replay costs nothing.
A repeat comes back with status: "duplicate" for that automation — not an error, and not an ambiguous success. It means we already have this one and you can stop retrying.
The same person abandoning a different cart is a different key, so it runs again. Editing the automation does not reset this: a customer who was already mailed about cart 8f21 will not be mailed about it a second time because you changed the wording.
One run at a time per contact. If someone abandons a second cart while the first sequence is still going, the second returns duplicate — two overlapping sequences to one person is not a thing anybody wants to receive.
Top-level keys of payload are readable in the automation's content as {{ event.cart_total }}. Give it a fallback — {{ event.cart_total | "your cart" }} — so a missing field reads as a sentence rather than a gap.
Values are frozen when the run starts, so every email in a multi-step sequence shows the same total — the one that was true when the cart was abandoned, not whatever your shop says three days later.
Strings, numbers and booleans are merged. Nested objects and arrays are not — flatten them on your side, where you know the shape. A payload value is always treated as text: it cannot introduce a link, an image or any other markup into the email, whatever it contains.
Send the same call with a ps_test_ key. The run executes for real — the same steps, the same conditions, the same merge fields — and nothing leaves the building.
Waits collapse to zero. A welcome series that waits three days between emails finishes in seconds, so you find out now whether step three is right rather than on Thursday.
Nothing is delivered and nothing changes. Each email is captured rather than sent, and recorded in your logs with its rendered subject, its recipient and a Test badge — so you can see which steps fired, in what order, and what each subject line came out as. Tag steps do not tag anyone. No send is counted against your monthly allowance. Your contacts are untouched.
Two things behave slightly differently, and it is worth knowing which. Suppressions and opt-outs still apply — a test run to someone who has unsubscribed is skipped, exactly as a live one would be, so the test tells you the truth. Address verification is not bought: a check we already hold is applied as usual, and a contact with no recent one is sent to rather than verified, because a test should not spend money.
Runs started by a test key dedupe on event_key exactly as live ones do, so use a fresh key each time you want the sequence to run again.
60 events per minute per automation. This is the only trigger whose rate you control, and automation sends draw from the same monthly allowance as your campaigns — so a loop in an integration could otherwise spend a month of sending before anyone noticed. Over the limit, that automation comes back rate_limited and the call still succeeds; retry with the same event_key.
payload is capped at 8 KB of JSON and event_key at 200 characters. One event starts at most 10 automations.
404 not_found — no such contact in this workspace. Create it first; we will not create it for you.
400 missing_field — event, event_key or contact is absent. 400 invalid_field names the one that is wrong: an event name with spaces or capitals, a payload that is not an object, or a value over the caps above.
403 insufficient_scope — the key lacks automations:trigger. See Errors.