AshDispatch.Workers.SendWebhook (AshDispatch v0.6.12)
View SourceOban worker for sending webhooks asynchronously.
This worker:
- Fetches the DeliveryReceipt by ID
- Marks receipt as
:sending - Sends HTTP POST to webhook URL
- Marks receipt as
:sentor:failed
Usage
Jobs are enqueued automatically by webhook transports (Discord, Slack, generic Webhook):
# Discord transport enqueues job
Discord.deliver(receipt, context, channel, event_config)
# Worker processes job asynchronously
%{
"receipt_id" => "...",
"webhook_url" => "https://discord.com/api/webhooks/...",
"payload" => %{
"content" => "Order #1234 created",
"embeds" => [...]
},
"headers" => %{
"Content-Type" => "application/json"
}
}Retries
Oban handles retries automatically:
- Max 5 attempts (configurable)
- Exponential backoff (configurable)
- Failed jobs can be manually retried
HTTP Client
Uses Req for HTTP requests with:
- Automatic retries for network errors
- Timeout handling (10 seconds default)
- JSON encoding/decoding
- Comprehensive error reporting
Webhook Formats
Discord and Slack use different JSON formats:
Discord
{
"content": "Message text",
"embeds": [{
"title": "Order Created",
"description": "Order #1234",
"color": 5814783
}]
}Slack
{
"text": "Message text",
"blocks": [{
"type": "section",
"text": {"type": "mrkdwn", "text": "Order #1234"}
}]
}
Summary
Functions
Which Req option carries the request body.
Processes webhook sending job.
Whether a failure is permanent — i.e. retrying sends the identical request and gets the identical answer.
Functions
Which Req option carries the request body.
raw_body is an ALREADY serialised body and wins when present: a signed
webhook must transmit exactly the bytes that were signed. Letting the HTTP
client re-encode a map can change key order or float formatting, and the
signature then fails sometimes — which is worse than always, because it
looks like a flake instead of a bug.
Without raw_body the behaviour is unchanged (json: payload), so the
Discord and Slack transports are unaffected.
Processes webhook sending job.
Job Args
receipt_id- DeliveryReceipt UUIDwebhook_url- Full webhook URLpayload- JSON payload to send (encoded by the HTTP client)raw_body- optional pre-serialised body, sent verbatim. Takes precedence overpayload; required when the request is signed, so the signed bytes and the sent bytes are the same bytes.headers- Optional HTTP headers (defaults to JSON content type)
Returns
:okon success (2xx response){:error, reason}on failure (Oban will retry)
Whether a failure is permanent — i.e. retrying sends the identical request and gets the identical answer.
4xx is the receiver saying this request is wrong, with two exceptions
that are explicitly about time rather than content:
408 Request Timeout— the receiver wants it again.429 Too Many Requests— the receiver wants it later.
Everything else (network errors, timeouts, 5xx) stays retryable: those say
not now, not not ever.
A consumer can lean on this: answering 422 when a notification cannot be
delivered gives an honest failed receipt on the first attempt instead of
five identical attempts and a receipt that only tells the truth minutes later.