The REST API answers how does my system read the platform? Webhooks answer the inverse: how does the platform tell my system something happened? Without webhooks, integrations have to poll. They wake up every few minutes, ask "anything new?", and most of the time the answer is no. That wastes compute on both sides, introduces latency between an event and its downstream response, and scales badly as the integration graph grows. With webhooks, the platform becomes an event source that downstream systems can react to the moment something happens — which is how modern integrations should work.
On the outbound side, sending a webhook is just another step in an automation. The send-webhook action fires an HTTP request when the automation reaches it, with full control over the request details: method (GET, POST, PUT, DELETE, etc.), headers (static or driven by expressions), body (any JSON shape, composed from the automation context), and endpoint parameters for dynamic path and query values. That flexibility is what makes the action useful for real integrations — every target system has its own request expectations, and the webhook action can match them without writing any custom code.
Endpoint parameters deserve a specific mention. An automation triggered by a record update often needs to talk to a remote system using identifiers from the record being updated. Rather than constructing the URL manually with string concatenation, the action exposes the automation's context to the URL template directly. /partners/{customer.partnerId}/sync resolves naturally at send time, with the record's current values filled in. The same applies to headers and to the JSON body — the inline expression language is available everywhere a dynamic value makes sense.
Custom HTTP status expectations are the small feature that keeps webhook-heavy automations behaving well. Different remote systems have different conventions about success and failure — some return 200 for everything, some use 202 for async acceptance, some return 204 for no content, and some have their own codes. The action lets you define which codes count as success, which codes trigger a retry, and which codes should halt the automation. Retries happen automatically on transient failures, with sensible back-off.
On the inbound side, webhook triggers turn the relationship around. An automation can be configured to run when an external system POSTs to a tenant-specific URL. The incoming request — headers, body, query parameters — is handed to the automation as context, and the rest of the builder works exactly the same way: the automation can create records, send notifications, run other actions, and respond to the caller. That's how partner systems notify the platform of events, how public forms on marketing sites post submissions into the tenant, and how legacy systems bridge into the modern flow.
A special empty-endpoint action variant handles the opposite of the full webhook — a simple ping with no payload, useful for health checks, refresh triggers, and "I'm still here" signals. Similarly, the response a triggered automation sends back to its caller is configurable, so inbound webhooks can return structured data when the caller expects a response rather than fire-and-forget.
Authentication between the platform and remote systems is handled through headers, which gives integrators access to the full range of authentication schemes: bearer tokens, basic auth, signed requests, API keys in custom headers, and anything else a remote system requires. Credentials themselves should live in the secrets manager (covered in its own article) rather than being pasted into automation steps — referencing a secret by name in a header value means the credential rotates in one place, not in every automation that uses it.
Response handling captures what the remote system returns and makes it available to subsequent automation steps. If an external API call returns an ID, a confirmation, or a piece of data the automation needs, that value flows into the next step naturally. That's what lets the webhook action participate in multi-step workflows rather than being a dead end. Send a request, use the response to update the local record, send a follow-up notification: three steps in the builder, no custom glue.
Retries on transient failures happen automatically where configured, with back-off that respects the remote system's expected load. Errors are logged with full context — the request that was sent, the response that came back — so an implementer investigating a failed webhook has everything needed to diagnose it. When persistent failures accumulate, the automation's own error-reporting path (covered in the automations article) can alert admins, trigger a fallback path, or quarantine the problematic record until human attention arrives.
Request logging is a small but useful detail for debugging. Webhook calls — both outbound requests the platform sends and inbound requests it receives — appear in the API request log, where they can be inspected with the same tooling used for any other API traffic. That uniformity makes webhook-heavy integrations genuinely observable, not opaque.
A word on design patterns. Fire-and-forget is the right choice when the downstream system doesn't need to respond — logging a customer event to an analytics platform, posting a notification to a chat channel, triggering a downstream index update. Request-response is the right choice when the platform needs the remote system's output before continuing — an address validation service, a payment gateway's authorization response, an external ID generator. The action supports both equally. What it doesn't do is force you to choose between them at the architecture level: an automation can mix both patterns as it needs to.
Webhooks complete the integration picture. With the REST API, integrations pull from the platform. With webhooks, the platform pushes to them. Together, they make it possible to build genuinely event-driven systems around the tenant — systems where data and events flow both directions without anyone having to write the glue code that would otherwise consume the integration budget.