Controlling SMTP Responses with HTTP Headers
By default, Conduit maps the HTTP status code from your endpoint to an SMTP
response code. A 2xx accepts the message. A 5xx causes a transient failure. A
4xx (429 excepted) rejects the message permanently.
Sometimes you need more precise control. For example, your endpoint returns
200 OK for every request. You still want to defer some messages, or to reject
them with a specific SMTP reason. Two special response headers give you this
control. Your endpoint sends them in its HTTP response.
Web UI availability: The HTTP response from your endpoint controls this feature. No setting in Conduit affects it, so there is nothing to configure in the UI or through the API.
Response headers
| Header | Required | Description |
|---|---|---|
X-Conduit-SMTP-Code |
Yes, to activate the override | A 3-digit SMTP response code (200 to 599) |
X-Conduit-SMTP-Message |
No | Free text that Conduit adds to the SMTP response |
Conduit reads both headers from the HTTP response that your webhook target sends back.
How Conduit reads the headers
- Conduit looks for
X-Conduit-SMTP-Codein the response. - If the header is absent, or it is not a valid 3-digit integer from 200 to 599, Conduit uses the normal classification from the HTTP status.
- If the header is valid, Conduit does the following:
- It returns your code to the sending MTA.
- It uses
X-Conduit-SMTP-Messageas the SMTP message. If that header is absent, it uses a default. - It takes the delivery outcome from the first digit of the code, and it ignores the HTTP status code:
| SMTP code range | Delivery outcome | MTA behavior |
|---|---|---|
200 to 299 |
Success | The MTA accepts the message and does not retry |
400 to 499 |
Transient failure | The MTA retries on its own schedule |
500 to 599 |
Permanent failure | The MTA bounces the message |
Message sanitization
Conduit sanitizes X-Conduit-SMTP-Message before it uses the value. This
prevents SMTP response injection:
- Conduit removes the CR (
\r) and LF (\n) characters. - Conduit truncates the value to 512 bytes.
- If the result is empty, Conduit uses a default for the class:
2xx→2.0.0 OK4xx→4.0.0 Temporary failure, please retry5xx→5.0.0 Delivery failed
Example: defer a message for a short time
Your endpoint receives an email, but the downstream system is unavailable. Return
200 OK, so that Conduit does not record a failure on your side. At the same
time, tell Conduit to ask the MTA for a 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 then does the following:
- It records the delivery as a transient failure.
- It returns
451 4.7.1 Downstream system unavailable, please retry laterto the sending MTA. - The MTA retries the message on its normal schedule.
Example: reject a message permanently with a policy reason
Your endpoint examines the payload and finds that the message breaks 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 then does the following:
- It records the delivery as a permanent failure.
- It returns
550 5.7.1 Message rejected by content policyto the MTA. - The MTA creates a bounce (a non-delivery report) for the sender.
Example: accept a message with your own SMTP reply
Your endpoint accepts the message, and you want your own acceptance message for an audit:
HTTP/1.1 200 OK
X-Conduit-SMTP-Code: 250
X-Conduit-SMTP-Message: 2.0.0 Alert received and logged
Content-Length: 0
Reading the outcome
The delivery log shows the SMTP override. While an override is active, the
error field of the log entry holds SMTP <code>: <message>.
To see this in the web UI, open Webhooks → [your webhook] → Logs. Or call:
GET /api/v1/webhooks/{id}/logs
Authorization: Bearer <access_token>
Interaction with the default HTTP status handling
The override activates only when X-Conduit-SMTP-Code is present and valid. If
you omit the header, the normal mapping from the HTTP status 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 |