Webhooks

A webhook-mode trigger fires the instant the source event happens, instead of waiting for the next polling check. Today, Stripe's Payment Event trigger and any Custom App's inbound trigger work this way — see Integrations for the full, current list of which triggers are polling vs. webhook.

Finding your webhook URL

Once a robot with a webhook-mode trigger step is at work, that step's card shows a real, unique URL — copy it from there. It only appears after the robot is put to work, because the URL is generated (and its security token issued) at that point, not when the step is first created.

Security model

Every webhook URL embeds a long, unguessable random token as part of the path itself — that token is the credential. There's nothing to configure beyond pasting the URL where the provider (or your own system) asks for a webhook endpoint. Stripe additionally supports a signing secret, entered as part of connecting Stripe, which Gate97 uses to verify each event really came from Stripe before acting on it — set this if you want that extra check; without it, events are still accepted, just unverified.

How delivery works

Gate97 acknowledges a webhook request immediately and processes it in the background, so the provider never times out waiting on you. Both POST and PUT are accepted. Duplicate deliveries (providers commonly retry if they don't get a fast enough response) are detected and only produce one run.

Custom App inbound webhooks

A Custom App with Supports inbound trigger enabled works exactly the same way — its trigger step gets a real webhook URL once the robot is at work, and whatever you send there is parsed according to the format you configured (JSON, XML, CSV, or plain text) and becomes the trigger data available to the rest of the robot as {{trigger.*}}. This is the mechanism for having your own systems push data into Gate97, rather than Gate97 polling them. See Custom apps for how to set one up.

Calling it with curl

The URL you copy from a trigger step on a working robot already has the exact shape below — nothing to fill in yourself. A few worked examples, one per supported format:

JSON (the default — a trigger configured for json):

curl -X POST "https://gate97.com/api/webhooks/custom:3f2a1b4c-9d21-4e3f-8a6b-1c2d3e4f5a6b/8f3e9c2a1b4d5e6f7a8b9c0d1e2f3a4b" \
  -H "Content-Type: application/json" \
  -d '{"orderId": "1234", "status": "paid"}'

PUT works identically — both methods are accepted on the same URL:

curl -X PUT "https://gate97.com/api/webhooks/custom:3f2a1b4c-9d21-4e3f-8a6b-1c2d3e4f5a6b/8f3e9c2a1b4d5e6f7a8b9c0d1e2f3a4b" \
  -H "Content-Type: application/json" \
  -d '{"orderId": "1234", "status": "shipped"}'

XML (a trigger configured for xml):

curl -X POST "https://gate97.com/api/webhooks/custom:3f2a1b4c-9d21-4e3f-8a6b-1c2d3e4f5a6b/8f3e9c2a1b4d5e6f7a8b9c0d1e2f3a4b" \
  -H "Content-Type: application/xml" \
  -d '<order><id>1234</id><status>paid</status></order>'

CSV (a trigger configured for csv) — each row becomes its own run, with that row's columns available as {{trigger.*}}:

curl -X POST "https://gate97.com/api/webhooks/custom:3f2a1b4c-9d21-4e3f-8a6b-1c2d3e4f5a6b/8f3e9c2a1b4d5e6f7a8b9c0d1e2f3a4b" \
  -H "Content-Type: text/csv" \
  --data-binary $'name,email\nJane Doe,jane@example.com\nJohn Smith,john@example.com'

The Content-Type header above is for readability — Gate97 always parses the body using the format you chose when defining the Custom App, not whatever header the sender happens to set. A successful call gets back a fast, empty 200 immediately; the run itself happens in the background; check that robot's Run History to see the result. An empty request body is dropped without creating a run rather than erroring.