Besides registered connectors, automations have built-in destination types. Each is a destination_config you set when defining the event, with {{source.*}} templates resolved from the changed record in the message fields of email, Slack and SMS and in field_mapping values. The webhook URL and the S3 key and metadata are different: they only replace {{name}} for a name you set in field_mapping, as shown below.
Messaging channels
Email, Slack, and SMS each have a dedicated handler because each speaks a different protocol. For Slack and SMS the tenant supplies the credential (Slack bot token, Twilio account) in their settings; if it's not configured the notification is silently skipped. Email needs no tenant credential: it is sent from the tenant's verified domain, else the organisation's, else the platform's own address.
// notification.slack
{ "channel": "#new-records", "text": "{{source.name}} was created" }
// notification.email
{ "to": "ops@example.com", "subject": "New: {{source.name}}",
"body": "<p>{{source.name}} was created.</p>", "format": "html" }
// notification.sms
{ "to": "+15551234567", "body": "{{source.name}} was created" } The email connector can send a saved template instead of its own subject and body: set template to the template's name, and send one field for each {{token}} the template uses. The tenant's own template of that name is used if it has one, otherwise the organisation's. The to address is filled in from the changed record, not from tenant settings, so store the owner's address on the record (for example owner_email).
// external.connector.email with a saved template
{ "connector_id": "email", "action_id": "send_email",
"field_mapping": {
"to": "{{source.owner_email}}", "template": "orderPlaced",
"order_number": "{{source.order_number}}", "total": "{{source.total}}" } } Direct webhook
external.webhook POSTs to a URL you set directly in the automation — no connector registration needed. Useful for Zapier catches and internal endpoints. The body is the field_mapping result as JSON. A {{name}} in the URL is replaced from that same result ({{source.*}} is not replaced there), and header values are sent as written. A webhook that runs before the write sends the whole record when there is no field_mapping, and by default a non-2xx answer stops the write; set fail_on_error to false to let it continue.
{ "url": "https://hooks.zapier.com/hooks/catch/...",
"method": "POST", "headers": { "X-Custom": "value" } } Write to the tenant's S3
external.s3 writes an object to the tenant's own bucket using the tenant's own AWS credentials. The bucket always comes from the stored credentials, so a tenant can only write to their own bucket. What is written is the field_mapping result, and a formula key replaces {{name}} from that same result.
{ "field_mapping": { "id": "source.id", "name": "source.name" },
"key_strategy": "formula", "key_template": "contacts/{{id}}.json",
"content_type": "application/json", "output_format": "json", "include_fields_mode": "all" } Test without real third parties
In non-production environments the messaging and connector handlers can be pointed at a simulator that records each call as a receipt — so integration tests can confirm delivery without hitting live APIs.