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 (200599)
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

  1. Conduit checks for X-Conduit-SMTP-Code in the response.
  2. If the header is absent or not a valid 3-digit integer in the range 200–599, normal HTTP-status-based classification applies.
  3. If the header is valid, Conduit:
    • Returns the custom code to the sending MTA.
    • Uses X-Conduit-SMTP-Message as 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
200299 Success Message accepted; MTA will not retry
400499 Transient failure MTA will retry according to its schedule
500599 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:
    • 2xx2.0.0 OK
    • 4xx4.0.0 Temporary failure, please retry
    • 5xx5.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 later to 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 policy to 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