Skip to content

What happens after POST /api/v2/emails returns 202

Follow a Koltrix send from the 202 Accepted response through the queue, DKIM signing, SMTP delivery and the webhook events your app receives.

Koltrix Team6 min read
Close-up of a server front panel with green status lights
Photo by Tyler on Unsplash
On this page(8 sections)
  1. The request
  2. What 202 means
  3. When the answer is 429 instead
  4. The same pipeline for both send paths
  5. Step by step after the 202
  6. Queued
  7. Sent
  8. Bounced
  9. Opens and clicks
  10. What your application can observe
  11. Message status
  12. Webhooks
  13. Each webhook is attempted once
  14. Putting it together
  15. Key takeaways

When your code calls the Koltrix send API, the response usually comes back fast as 202 Accepted. That status code is a promise about what has happened so far, not a report that the email has arrived.

Here is what happens after it, step by step, what your application can observe, and how to build an integration that stays correct when something goes wrong.

The request

A send is a single authenticated call:

curl https://api.koltrix.com/api/v2/emails \
  -H "Authorization: Bearer kx_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 4c1f8e2a-0b7d-4e55-a3f9-2d6b9e1c7a40" \
  -d '{
    "from": "[email protected]",
    "to": ["[email protected]"],
    "subject": "Welcome aboard",
    "body_html": "<p>Your account is ready.</p>",
    "body_text": "Your account is ready."
  }'

The API key must include the send scope; without it the call returns 403. The from address must be an active address on a verified sending domain for your account, and the API is strict about it: an unknown local part on a verified domain is rejected with an error that says which case failed. to needs at least one recipient, subject is required, and you provide body_html, body_text or both. cc and bcc are optional, and bcc recipients are stripped from the visible headers.

What 202 means

{
  "id": "01HX9C8...",
  "status": "queued",
  "queued": true,
  "tracking_url": "/api/v2/messages/01HX9C8...",
  "recipient_count": 1
}

A 202 Accepted means the request was valid and the send job is on the queue. It does not mean the receiving mail server has the message yet. Delivery over SMTP happens in a worker after the API has responded, usually within a second.

Keep the id. It is how you look the message up later and how webhook events refer to it.

When the answer is 429 instead

Sends count against your plan's quota. When a send would exceed it, the API answers 429 with a body that says exactly which limit you hit:

Field What it tells you
code Always quota_exceeded for this case
error A human-readable message
kind Which quota you hit
limit and used The cap and how much of it you have consumed
window The period the quota applies to
resets_at When the window resets
upgrade_url Where to raise the limit

The monthly window resets on the 1st of the month, UTC. The /api/v2 endpoints never return 402; a quota problem is always this 429.

Treat quota_exceeded differently from a generic rate limit. Retrying in a tight loop will not help before resets_at. Surface the error to whoever operates the integration, queue the email on your side if it still matters later, and use upgrade_url if the limit is simply too small for your volume.

The same pipeline for both send paths

Koltrix accepts mail through the REST API and through the SMTP relay at smtp.koltrix.com on port 2525, authenticating with your API key as the password. Both share the same pipeline: the same suppression list, the same DKIM signer and the same webhook stream. Everything below applies whichever path you use.

Step by step after the 202

Queued

The message exists with status queued. If you sent an Idempotency-Key, the response is now stored for 24 hours, so a retry with the same key returns the original result instead of queuing a second copy.

One practical detail: while a message is still queued, GET /api/v2/messages/{id} currently returns 404. Do not treat that as "the message is lost." Give the worker a moment and look again, or rely on the webhook described below.

Sent

A worker picks up the job and delivers the message over SMTP to the recipient's mail server. Outbound mail is DKIM-signed with your domain's key; every sending domain on Koltrix gets its own 2048-bit RSA key. When the receiving server accepts the message, the status becomes sent and a message.sent webhook fires.

"Sent" means the next hop accepted responsibility for the message. It does not tell you whether it reached the inbox or the spam folder; no sending service can see that directly.

Bounced

If the receiving server returns a permanent rejection, the status becomes bounced, the recipient is automatically added to your suppression list so you do not keep mailing a dead address, and a message.bounced webhook fires. Its payload carries an error field with the reason.

Opens and clicks

Opens and clicks are reported as events rather than as message statuses. When the tracking pixel loads, a message.opened webhook fires; when a tracked link is followed, message.clicked fires. Treat opens as a rough signal: image proxies and privacy features in mail clients can load the pixel without a person reading the message.

What your application can observe

Message status

Once the worker has picked the message up, GET /api/v2/messages/{id} returns its status. There are exactly three values:

Status Meaning
queued Accepted by the API, waiting for the worker
sent The receiving server accepted the message
bounced Permanent rejection; recipient suppressed

Polling is fine for support lookups and for reconciling your own records, which matters for the reason below.

Webhooks

For anything automated, subscribe to webhooks. Koltrix sends four events:

Event Fires when Notes
message.sent The receiving server accepted the message No recipient field in the payload
message.bounced Permanent rejection; recipient auto-suppressed Includes error; no recipient field
message.opened Tracking pixel loaded Rough signal only
message.clicked A tracked link was followed No URL in the payload

Each delivery is an HTTPS POST with the event name in an X-Koltrix-Event header and a JSON body that includes the message_id. Because message.sent and message.bounced do not include the recipient, store the mapping from message id to recipient (and to whatever record triggered the email) when you receive the 202. Then every event can be joined back to your own data by message_id.

Every payload is signed. The X-Koltrix-Signature header carries sha256= followed by the hex HMAC-SHA256 of the raw request body, computed with your endpoint's secret. Verify it against the raw bytes, before your framework parses the JSON, and compare in constant time.

Each webhook is attempted once

Koltrix attempts each webhook delivery once, with an 8-second timeout. There is no retry or redelivery. If your endpoint is down, slow or returns an error at that moment, that event is not sent again.

That shapes how you should build the receiving side:

  • Respond fast. Verify the signature, record the event, return 200, and do the real work asynchronously. Anything slow risks the 8-second limit.
  • Keep the endpoint highly available. Deploys and restarts are the most common reason a single-attempt webhook is missed.
  • Reconcile by polling. For messages where the outcome matters, such as a password reset or an invoice, check GET /api/v2/messages/{id} if no message.sent or message.bounced event has arrived within a reasonable time. Status is the source of truth; webhooks are the fast path.

Putting it together

A robust integration looks like this:

  1. Generate an Idempotency-Key when you decide the email should exist, and store it.
  2. Call POST /api/v2/emails. On 202, store the returned id next to the recipient and the triggering record.
  3. Retry timeouts with the same key; a 200 with Idempotent-Replayed: true means it was already queued.
  4. On 429 with quota_exceeded, stop retrying, alert, and wait for resets_at or raise the limit.
  5. Receive webhooks, verify the signature, join on message_id, and acknowledge quickly.
  6. Periodically reconcile important messages by polling their status, since webhooks are not redelivered.
  7. On bounced, prompt the user to fix their address.

Key takeaways

  • 202 Accepted means queued, not delivered; SMTP delivery happens in a worker moments later.
  • Message status is one of queued, sent or bounced, and a lookup may return 404 while the message is still queued.
  • Koltrix sends four webhook events: message.sent, message.bounced, message.opened and message.clicked, each signed and attempted once with an 8-second timeout.
  • Store recipient and context against the message id, because sent and bounced payloads do not carry the recipient.
  • Quota limits return 429 with code: "quota_exceeded" and a resets_at time; the monthly window resets on the 1st, UTC.

Start with Koltrix

Your domain, one inbox, and an API that sends.

A team inbox where AI sorts and drafts (nothing is sent without your click), plus the transactional API and SMTP relay your product sends with. 7 days free, no card.

SharePost on XLinkedIn