Skip to main content

Starting a flow by webhook

The Webhook trigger gives a flow its own URL. Any platform that can send a webhook — your store, a form, a CRM, a course platform — calls that URL with its own data, in its own format, and the flow starts. Every field of what arrived can be used in messages, templates, conditions and contact fields.

The difference from the API trigger: there, your system calls Joinotify with a key and in Joinotify's format. Here there is no key and no format to follow — you paste a URL into a setting of the platform and choose in the editor where each piece of data comes from.

Preparing the flow​

  1. In the editor, add a trigger and choose Webhook. The URL shows right away, before publishing — it is what you paste into the platform.
  2. Under Platform, choose WordPress / WooCommerce or Any platform.
  3. Click Wait for event and send a test from the platform (or paste a sample under Paste JSON). What arrives becomes a tree: from then on you pick fields by clicking instead of typing paths.
  4. Under Contact, say where the phone is — and, if you want, the name and the e-mail.
  5. Connect the trigger to the first step, publish and activate the flow.

The URL looks like https://api.joinotify.com/hooks/whk_… and it is the trigger's password: whoever has the URL can start the flow. If it leaks, Generate a new URL changes the address — with the option of keeping the old one working for 24 hours, to leave time to paste the new one.

WooCommerce​

  1. In WordPress, open WooCommerce → Settings → Advanced → Webhooks and click Add webhook.
  2. Status: Active. Topic: Order created — or Order updated, to follow payment and shipping.
  3. Delivery URL: the trigger's URL.
  4. Secret: on the trigger, click Turn on the WooCommerce signature and paste the generated secret here.
  5. Save. With Wait for event on, place a test order in the store to capture a sample.

With WooCommerce chosen as the platform and nothing else set, the contact comes from the order's billing block: billing.phone, billing.first_name, billing.last_name and billing.email. A phone without a country code, like (11) 98765-4321, is read as belonging to the country chosen on the trigger (Brazil by default).

The test WooCommerce sends when the webhook is saved (webhook_id=…) is answered without becoming an event.

One webhook, several moments

The Order updated topic fires on every change to the order. Use the trigger's Filter to start only on what matters — status equal to processing, for example — and let the same order with the same status count only once (the default, see Repeated events).

What the platform can send​

  • JSON (application/json) — an object; a list arrives as items.
  • Form (application/x-www-form-urlencoded), including nested fields the way PHP writes them: customer[phone]=… becomes customer.phone.
  • Text, which arrives as text.
  • The URL's query string is in $query, and the platform's headers (such as X-WC-Webhook-Topic) in $headers — never the ones that authenticate.

The body can be up to 256 kB. POST, PUT and PATCH are accepted; a GET on the URL answers 200, for platforms that test the address before saving it.

Signature​

On top of the URL, the platform can prove it was the one sending. On the trigger, under Signature:

TypeWhat Joinotify checks
The URL onlyNothing but the address (default)
WooCommerceX-WC-Webhook-Signature: HMAC-SHA256 of the body, in Base64, with the webhook's secret
HMAC-SHA256The header you choose, in hex or Base64, with an optional prefix (sha256=)
Fixed headerThe header you choose holds exactly the agreed value

The signature is checked over the bytes that arrived, before the body is read. A call with the wrong signature gets 401 and does not become an event.

Responses​

The platform only gets an error when the call is wrong:

StatusWhen
202The event was accepted — { "ok": true, "event_id": "…" }
404The URL does not exist (or was replaced and the old one's grace period is over)
401The signature does not match
400The JSON is not valid
413The body is over 256 kB
415A format Joinotify does not read (multipart/form-data)
429Too many calls — up to 20 per second per URL and 60 per account

Everything else — a paused flow, an account without access, an order without a phone, a repeated event — is 202 and is recorded under Received events. On purpose: a platform that gets errors retries and, after a few failures, disables the webhook, and nobody finds out why.

Received events​

In the editor, Received events lists every call and what happened to it:

StatusWhat it means
Started the flowA run started for the contact
Filtered outThe event did not pass the trigger's filter
RepeatedThe same event had already arrived
No phoneNo valid phone at the chosen path
Unknown contactThe trigger is set not to create contacts, and the phone belongs to none
Flow inactiveThe flow is a draft or paused
Trigger not publishedThe published version does not have this trigger
Trigger with no next stepThe contact was updated, but nothing was sent
Within the intervalIt arrived within the minimum interval per contact
Delivered to a waiting runA run on Wait for event went on with it
Account blockedThe account has no access to sending

Each event's detail shows the data (with CPF, CNPJ and card numbers masked until you ask), the headers and the run it started, and has Run again. An event's data is kept for 30 days, or your plan's message retention, when it is shorter. After that only its status stays in the log. On the flows list, a flow whose webhook had three or more events that went nowhere in the last 24 hours shows a warning.

Repeated events​

OptionCounts as the same event
Automatic — WooCommerceThe same order with the same status, for 30 days: editing the order does not confirm it again
Automatic — othersAn idempotency header (Idempotency-Key, X-Webhook-Id, X-Event-Id, X-Delivery-Id), for 24 hours; without one, the same body within 5 minutes
By a fieldThe same value in the chosen field, for 24 hours
By a headerThe same value in the chosen header, for 24 hours
NeverEvery event starts the flow

A filtered event does not count as seen: the next one that passes the filter starts normally.

Alongside the contact's other automations​

By default, a run started by a webhook runs alongside whatever the contact already has going on: an order does not interrupt the survey or the automated service the person is answering. When the contact writes, the reply goes to the run that is waiting for text — a question, a Wait for reply —, not to a template waiting for a button tap.

On the trigger you can switch to Replace only this flow's previous run or Replace any automation in progress (like the conversation triggers), and set a minimum interval per contact.

Keeping data on the contact, and the timeline​

  • Keep on the contact copies event fields to the contact's fields before the flow starts — the last order's value, the tax id, the purchase date. They are later useful to segment campaigns and in other flows. The name and e-mail the platform sends only fill in what is empty.
  • Timeline writes a line in the contact's history, which the team sees in the inbox: Order {{trigger.number}} — {{trigger.total:money}}.

Waiting for another event​

The Wait for event step holds a run until a webhook trigger — of this flow or another — receives an event with the same value in a field. It is what makes a cart recovery stop when the order is paid:

  1. A flow starts on the abandoned-cart event and sends the first reminder.
  2. Wait for event: the paid-order trigger, this run's value {{trigger.cart_token}}, the path cart_token in the awaited event, for up to 1 day.
  3. Through the Arrived output, say thanks; through Did not arrive, send the second reminder.

The event that arrived is in {{flow.<name>.…}}, if you give it a name on the step.

Testing​

Test, in the editor, can carry the captured sample: the run starts from the webhook trigger on your WhatsApp, with the variables filled in as they would be for the customer.

Next: Variables and data in flows.