Skip to content

Call an external API from a rule

Post to Slack, notify a customer’s webhook, or hit any HTTP API when a record changes. This is the base outbound pattern the other integration recipes build on: define the endpoint once as a service, allow-list its host, and fire it from a call_webservice rule action.

Create a service config for the endpoint. The base_url is the host (the request path is supplied per call); credentials go in auth_config and are encrypted at rest.

POST /api/v1/applications/{application_id}/services
{
"name": "Slack Notifier",
"service_type": "webhook_out",
"base_url": "https://hooks.slack.com",
"auth_type": "none",
"headers": {"Content-Type": "application/json"}
}

auth_type is one of none, bearer (auth_config: {"token": "..."} → Authorization: Bearer ...), or api_key (auth_config: {"header_name": "X-API-Key", "api_key": "..."}). The base URL must resolve to a public host: the platform blocks private, loopback, and link-local addresses, so an internal service must be reached through a publicly routable name or tunnel.

Outbound calls are denied unless the host is on the application’s allow-list. Set it per governance scope:

PUT /api/v1/applications/{application_id}/outbound-destination-governance
{
"service_invoke": {"allowed_hosts": ["hooks.slack.com"]},
"webhook_out": {"allowed_hosts": []}
}

An empty list allows nothing on that path once governance is set; entries are exact hosts or a single-label wildcard (*.example.com). A call to any other host fails with service_destination_not_allowed.

Add a rule on the triggering model (for example record_create, phase post_save) with a call_webservice action. The body is a template resolved against the event, so record fields, the actor, and any require_query rows are available:

{
"type": "call_webservice",
"params": {
"service_config_id": "<service-uuid>",
"method": "POST",
"path": "/services/T000/B000/XXXX",
"mode": "sync",
"body": {"text": {"concat": ["New contact added: ", {"var": "after.first_name"}, " ", {"var": "after.last_name"}]}}
}
}

path is the request path appended to base_url (the webhook-specific part for Slack). mode: "sync" calls the service inline and records the response on the rule’s side effects; omit it to enqueue the call as a background job. Inline url, auth, or headers are rejected: always reference a service_config_id.

The response is written to the outbound service log but is not fed back into later rule actions. To use an API’s response (store it on the record, branch on it), call the service from a function instead (see the LLM recipe), whose return value is merged into the record.

SMS follows the same shape: a form-encoded POST to the provider’s API. Twilio authenticates with HTTP Basic, which the service auth layer does not yet inject, so pass its credentials via a header until basic auth lands.