Controlling SMTP Responses with HTTP Headers
By default, Conduit maps the HTTP status code your endpoint returns to an SMTP
response code: a 2xx accepts the message, a 5xx causes a transient failure,
and a 4xx (except 429) permanently rejects it.
Sometimes you want more precise control. Your endpoint may return 200 OK for
all requests but still want to temporarily defer certain messages, or reject
them with a specific SMTP reason. Conduit supports this through two special
response headers that your webhook endpoint includes in its HTTP response.
Web UI availability: This feature is controlled entirely by your webhook endpoint's HTTP response, not by any setting in Conduit. No configuration is required in the UI or via the API.
Response headers
| Header | Required | Description |
|---|---|---|
X-Conduit-SMTP-Code |
Yes (to activate override) | 3-digit SMTP response code (200–599) |
X-Conduit-SMTP-Message |
No | Free-text message appended to the SMTP response |
Both headers are read from the HTTP response your webhook target sends back to Conduit.
How Conduit interprets the headers
- Conduit checks for
X-Conduit-SMTP-Codein the response. - If the header is absent or not a valid 3-digit integer in the range 200–599, normal HTTP-status-based classification applies.
- If the header is valid, Conduit:
- Returns the custom code to the sending MTA.
- Uses
X-Conduit-SMTP-Messageas the SMTP message, or falls back to a default if the header is absent. - Derives the delivery outcome from the hundreds digit of the code, regardless of the HTTP status code:
| SMTP code range | Delivery outcome | MTA behaviour |
|---|---|---|
200–299 |
Success | Message accepted; MTA will not retry |
400–499 |
Transient failure | MTA will retry according to its schedule |
500–599 |
Permanent failure | MTA will bounce the message |
Message sanitisation
To prevent SMTP response injection, Conduit sanitises X-Conduit-SMTP-Message
before using it:
- CR (
\r) and LF (\n) characters are stripped. - The value is truncated to 512 bytes.
- If the result is empty, a class-appropriate default is used:
2xx→2.0.0 OK4xx→4.0.0 Temporary failure, please retry5xx→5.0.0 Delivery failed
Example — temporarily defer a message
Your endpoint receives an email but the downstream system is temporarily
unavailable. Return 200 OK (so Conduit does not log it as a failure on your
side) but instruct Conduit to tell the MTA to retry:
HTTP/1.1 200 OK
X-Conduit-SMTP-Code: 451
X-Conduit-SMTP-Message: 4.7.1 Downstream system unavailable, please retry later
Content-Length: 0
Conduit will:
- Record the delivery as a transient failure.
- Return
451 4.7.1 Downstream system unavailable, please retry laterto the sending MTA. - The MTA will retry the message on its normal schedule.
Example — permanently reject a message with a policy reason
Your endpoint inspects the payload and determines the message violates a content policy. Return a permanent rejection:
HTTP/1.1 200 OK
X-Conduit-SMTP-Code: 550
X-Conduit-SMTP-Message: 5.7.1 Message rejected by content policy
Content-Length: 0
Conduit will:
- Record the delivery as a permanent failure.
- Return
550 5.7.1 Message rejected by content policyto the MTA. - The MTA will generate a bounce/non-delivery report for the sender.
Example — accept a message with a custom SMTP reply
Your endpoint accepts the message but wants to return a customised acceptance message for auditing:
HTTP/1.1 200 OK
X-Conduit-SMTP-Code: 250
X-Conduit-SMTP-Message: 2.0.0 Alert received and logged
Content-Length: 0
Reviewing the outcome
The SMTP override is visible in the delivery log. When an override is active,
the error field in the log entry is set to SMTP <code>: <message>.
You can view this in the web UI by opening Webhooks → [your webhook] → Logs, or by calling:
GET /api/v1/webhooks/{id}/logs
Authorization: Bearer <access_token>
Interaction with default HTTP-status handling
The override only activates when X-Conduit-SMTP-Code is present and valid.
If you omit it, the existing HTTP-status mapping applies:
| HTTP status | Default SMTP code | Retryable |
|---|---|---|
2xx |
250 |
No |
429 |
450 |
Yes |
4xx (other) |
550 |
No |
5xx |
450 |
Yes |
| Network error / timeout | 450 |
Yes |