Destinations
Last updated recently
Destinations
A destination is somewhere a lead gets sent, and a description of what their "yes" looks like. Most of them are an HTTP endpoint belonging to a company that pays you per lead, but a destination is really just a configured outbound request - a CRM or an internal service works the same way.
Destinations is an account-level directory. The same destination can appear in as many routing setups and as many waterfalls as you like.

Fixing a destination's endpoint updates every waterfall that uses it, immediately. That is usually what you want - a destination changed their URL, you change it once. But it does mean an edit is never local to the routing setup you happen to have open.
If you need one routing setup to send something different to a shared destination, use an override from inside that routing setup rather than editing the directory entry.
Creating one

Name is yours. Endpoint URL, method and format are theirs - take them from the destination's routing setup documentation. Format decides how the body is encoded and how values are escaped: JSON, form-encoded, XML or raw.
Timeout defaults to ten seconds. Keep it short. A waterfall of three destinations at thirty seconds each is ninety seconds of a lead sitting unsold while it goes cold.
Headers and secrets
Most destinations authenticate with a header. Add them in the headers list and tick secret on anything sensitive.
A secret header is write-only in the interface, encrypted at rest, decrypted only at the moment of
sending, and redacted in the logs. Anything named like authorization, api-key or token is
redacted in logs whether or not you ticked the box.
Post, and ping/post

Post sends the whole lead in one call. If the destination accepts, the lead is sold.
Ping/post is two calls. First a ping - a stripped-down description of the lead with no personal details - asking whether they want it and often what they will pay. Only if the ping is accepted does the full post follow with the actual contact details.
Ping/post exists because it means you are not handing a name and phone number to four destinations in order to find the one who wants it. If your destinations support it, use it.
The ping has its own body template, and can optionally have its own headers, format and accept rule. Anything you leave blank falls back to the post's version.
Nothing stops you putting {{phone}} in a ping template. Formwork will send it. Keep pings to
things like state, ZIP and qualifying answers.
Accept rules - what counts as a yes
This is the part that makes a waterfall work. Formwork has to decide, from a destination's response, whether the lead was taken. That decision is the accept rule.

An empty accept rule means any 2xx response is a sale. That is right for a surprising number of
destinations and wrong for the ones that return 200 OK with {"status":"rejected"} in the body -
which is most of the serious ones.
A rule is built from:
Status codes - which HTTP statuses are even considered. Defaults to any 2xx.
Checks - tests against the response body. Each check is a path, an operator and a value:
status equals accepted, or payout greater than 0. Leave the path blank to test the
raw response text instead. Checks combine with AND or OR.
Price path - where in the response the payout is, so the sale is recorded with a value.
Something like data.price or bid.amount.
Formwork parses the response as JSON, then XML, then form-encoded, whichever works - it does not
trust the destination's Content-Type header. Paths support array indexes like items[0].price,
and fall back to case-insensitive key matching.
Test it before you trust it
The editor has a tester. Paste a real response the destination gave you and it shows you the verdict and every check's result, including the ones that passed.

Do this. An accept rule that is wrong in the "too generous" direction marks leads sold that nobody bought.
Body templates and tokens
The body is a template with {{token}} placeholders filled in from the lead.
1{
2 "first_name": "{{firstname}}",
3 "last_name": "{{lastname}}",
4 "email": "{{email}}",
5 "phone": "{{phone}}",
6 "state": "{{state}}",
7 "landing_page": "{{runtime.page_url}}",
8 "lead_id": "{{system.lead_id}}"
9}Tokens resolve in this order, and you can force one explicitly with a prefix:
| Prefix | Resolves from | Example |
|---|---|---|
| (none) | canonical fields, then runtime, then system | {{phone}} |
data. | canonical fields only | {{data.phone}} |
runtime. | attribution captured with the lead | {{runtime.page_url}} |
system. | Formwork's own identifiers | {{system.lead_id}} |
response. | the accepting destination's reply - events only | {{response.order_id}} |
Use triple braces - {{{token}}} - where you need the value inserted as a raw JSON literal rather
than a quoted string, such as a number or a boolean.
A missing token becomes an empty string rather than breaking the request, and is reported on the log row so you can see it happened.

Two special tokens are worth knowing: {questions} and {questions_text} send every question and
answer from the form, which is what destinations who want "whatever you have" usually mean.
Order, and what stops a waterfall
Within a branch, destinations are tried in list order and the first acceptance ends it. Destinations below the winner are never called.
The waterfall also stops early if:
- A destination is disabled. It keeps its place in every list and is skipped at run time - which is how you pause a destination without dismantling your routing.
- Your postback quota runs out. Formwork stops rather than burning the remaining budget on a waterfall it cannot finish.
Every call writes a row to the logs, including the skips, so the drawer for a lead reads as a complete account of what was tried and what came back.

Per-routing setup overrides
Sometimes one routing setup needs to send a shared destination a different body - an extra field, a different campaign identifier. Select the destination inside the routing builder and override just what differs. Everything you do not override keeps following the directory entry.
Identity - the destination's name, whether it is enabled, and its delivery mode - always comes from the directory and cannot be overridden.
Retrying
Any postback in the logs can be retried. The retry replays the request exactly as it was sent, from a frozen snapshot of the configuration at the time, and always writes a new log row - attempts are never overwritten.
An accepted retry of a post settles the lead as sold. An accepted retry of a ping does not, because interest is not delivery.