Send Email via Webhook

Send Email via Webhook connections are a guarded variant of HTTP connections for customer email delivery.

These connections are used by the customer email review flow:

  1. draft_customer_email creates a draft and pauses for review
  2. Firetiger waits for explicit approval in the UI
  3. send_customer_email delivers the approved payload to the configured webhook

Recommended: Create and manage connections via the web UI at https://ui.cloud.firetiger.com/settings/connections

Connection Parameters

An email-webhook connection requires the following configuration:

Required Parameters

Parameter Type Description
base_url string Full webhook URL including scheme, host, and path (e.g. https://api.example.com/customer-email)
allowed_routes string[] Fixed to ["POST /"] for customer email delivery

Optional Parameters

Parameter Type Description Default
headers map<string,string> Non-auth headers to include in every request None
max_response_size_bytes int64 Maximum response body size in bytes 10MB (10485760)
timeout_seconds int32 Request timeout in seconds 30 seconds
webhook_signing_secret string Optional HMAC signing secret for outbound webhook requests None
slack_connection_name string Slack connection to notify when a customer email is waiting for review None
slack_channel string Slack channel to notify when a customer email is waiting for review None

Important Notes

  • HTTPS Required: The base_url must use HTTPS.
  • Fixed endpoint: This connection type always sends POST requests to the configured webhook URL.
  • Slack is optional: If slack_connection_name and slack_channel are omitted, customer email drafting and sending still work normally.

Authentication

Authentication is configured via the auth oneof — set exactly one of the following methods.

OAuth Client Credentials

Automatically obtains and refreshes an OAuth 2.0 access token using the client credentials grant. The token is injected as an Authorization: Bearer header on each request.

"oauth_client_credentials": {
  "token_url": "https://auth.example.com/oauth/token",
  "client_id": "my-client-id",
  "client_secret": "my-client-secret",
  "scopes": "email.send",
  "extra_params": {
    "audience": "https://api.example.com"
  }
}

Bearer Token

A static Bearer token sent as Authorization: Bearer <token>.

"bearer_token": {
  "token": "sk-your-token"
}

Basic Auth

HTTP Basic authentication with username and password.

"basic_auth": {
  "username": "user",
  "password": "pass"
}

Static Headers

Raw auth headers included in every request.

"static_headers": {
  "headers": {
    "Authorization": "Bearer sk-your-token",
    "X-API-Key": "your-key"
  }
}

Non-auth headers (in the top-level connection details) can be used alongside any auth method for headers like Content-Type or X-Tenant-ID.

Request Payload

Approved customer emails are delivered as a fixed JSON payload:

{
  "customer": {
    "id": "tenant-123",
    "name": "Acme Corp"
  },
  "email": {
    "subject": "Issue update",
    "body": "We are aware of the issue and are investigating."
  }
}

The webhook transport details come from the connection. The approved email content comes from the guarded approval artifact created during draft_customer_email.

Optional Slack Notifications

When both slack_connection_name and slack_channel are configured, Firetiger posts a Slack message when a customer email is waiting for review.

  • Notification is sent only for the first pending draft in the Issues Expert session
  • Slack delivery is best-effort and does not block customer email review
  • The Slack message links back to the relevant issue and the Firetiger review surface

Webhook Signing

If webhook_signing_secret is configured, Firetiger signs outbound email webhook requests and includes the signature in the X-Webhook-Signature header.

The header value uses this format:

sha256=<hex digest>

The digest is computed as HMAC-SHA256 over the exact raw HTTP request body using the configured signing secret.

Validation Steps

On your receiving service:

  1. Read the raw request body bytes exactly as received
  2. Compute HMAC-SHA256(secret, raw_body)
  3. Hex-encode the digest
  4. Compare it to the value after the sha256= prefix in X-Webhook-Signature
  5. Reject the request if the values do not match

Example Validation

import hashlib
import hmac

secret = b"your-signing-secret"
raw_body = request_body_bytes
received = request.headers["X-Webhook-Signature"]

expected = "sha256=" + hmac.new(secret, raw_body, hashlib.sha256).hexdigest()

if not hmac.compare_digest(expected, received):
    raise ValueError("invalid webhook signature")

Important Notes

  • Use the raw request body bytes, not a re-serialized JSON object
  • Compare signatures with a constant-time comparison function when available
  • Rotate the signing secret if you suspect it has been exposed

Description Field

Document what the webhook expects and who owns it.

Example:

Customer email delivery webhook for incident notifications.

Expected request body:
- customer.id
- customer.name
- email.subject
- email.body

Owner: Support engineering
Response format: JSON

Example Connection

{
  "display_name": "Customer Email Delivery",
  "description": "Customer email delivery webhook for incident notifications...",
  "connection_details": {
    "email_webhook": {
      "base_url": "https://api.example.com/customer-email",
      "allowed_routes": [
        "POST /"
      ],
      "bearer_token": {
        "token": "sk-live-123"
      },
      "slack_connection_name": "connections/team-slack",
      "slack_channel": "#customer-emails",
      "timeout_seconds": 30
    }
  }
}

Best Practices

  • Use this instead of generic HTTP for customer email delivery
  • Keep the webhook fixed-purpose and document the payload shape in the description
  • Enable Slack notifications if you want a review link posted automatically for pending drafts
  • Prefer OAuth when the destination service supports it
  • Use webhook signing when the destination verifies HMAC signatures

This site uses Just the Docs, a documentation theme for Jekyll.