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:
draft_customer_emailcreates a draft and pauses for review- Firetiger waits for explicit approval in the UI
send_customer_emaildelivers 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_urlmust use HTTPS. - Fixed endpoint: This connection type always sends
POSTrequests to the configured webhook URL. - Slack is optional: If
slack_connection_nameandslack_channelare 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:
- Read the raw request body bytes exactly as received
- Compute
HMAC-SHA256(secret, raw_body) - Hex-encode the digest
- Compare it to the value after the
sha256=prefix inX-Webhook-Signature - 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