---
title: "Receive SendGrid delivery-status events for outreaches"
method: POST
path: "/outreach/sendgrid/webhook/email-status"
tags: ["Send Grid Webhook Resource"]
---

# Receive SendGrid delivery-status events for outreaches

`POST /outreach/sendgrid/webhook/email-status`

Accepts a batch of email delivery-status events (delivered, bounced, opened, clicked, dropped, deferred, etc.) posted by SendGrid's Event Webhook and applies each one to the matching outreach email record. Configure this URL in SendGrid's Event Webhook settings for outreach sender identities; use the sibling POST /outreach/sendgrid/webhook/incoming-parse endpoint instead for inbound reply emails. The request body must be a non-empty JSON array of event objects; each event should carry outreach context (`tenantId`/`emailId`/`outreachId`) as top-level fields or nested under a `custom_args` object, and falls back to matching by `sg_message_id` alone when that context is absent. This call returns 200 immediately after starting processing: the delivery-status updates and the best-effort audit copy (raw payload to Cloud Storage plus a WebhookRawBlob record) all happen asynchronously in the background after the response is sent, so a 200 does not guarantee any individual event was actually applied. A body that is not a JSON array, an empty array, or an array with no event carrying a usable `sg_message_id`/`event` pair is not treated as an error - it still returns 200 and applies nothing. This is a public endpoint that does not require authentication; no tenant-id header is used.

## Headers

- `tenant-id` string, required

## Request body

- SendGridEvent[]
  - `email` string — Recipient email address the event applies to.
  - `timestamp` string — Unix epoch seconds, as a string, at which SendGrid recorded the event; see getEventTime() to convert to an Instant.
  - `event` string — SendGrid event type, e.g. delivered, open, click, bounce, dropped, deferred, spamreport, unsubscribe, group_unsubscribe, group_resubscribe.
  - `category` string — Category tag attached to the originating send, if one was set.
  - `sgMessageId` string — SendGrid's message identifier for the originating send, delivered on the wire as sg_message_id; used to correlate this event back to the outreach email that generated it.
  - `smtp-id` string — The smtp-id header of the originating message, if present.
  - `response` string — Raw SMTP response text from the receiving mail server; present on delivered, bounce, and deferred events.
  - `attempt` string — Delivery attempt number, as a string; present on deferred events.
  - `status` string — SMTP status/response code; present on bounce and deferred events.
  - `reason` string — Human-readable reason for the event; present on bounce, dropped, and deferred events.
  - `type` string — Bounce classification, e.g. bounce vs blocked; present on bounce events.
  - `eventTime` string, date-time

## Response `200`

Processing was started (or intentionally skipped for an empty/invalid batch). The JSON body is a bare string, not an object: "Processing N email status update events" when at least one event carried a usable sg_message_id/event pair (updates continue in the background); "No events to process" when the body was not a non-empty JSON array; or "No valid events to process" when it was an array but no event carried a usable sg_message_id/event pair. None of these are errors.

- string

## Other responses

- `400` — Bad Request
- `401` — Returned only when the event-webhook public key is configured and the request's signature/timestamp headers (X-Twilio-Email-Event-Webhook-Signature/-Timestamp, or their X-SendGrid-Webhook-* equivalents) are missing or fail verification against that key; skipped entirely when no key is configured. Resend the webhook with a valid signature from the configured SendGrid key pair.
- `500` — Returned when the body could not be parsed as JSON or an unexpected failure occurred while starting processing. The JSON body is a bare string: "Error processing webhook: <cause>". Configure SendGrid to retry this webhook on 5xx.

---

[API](https://skmtc.net/certifyos/apis/certify-api-layer.md) · [All operations](https://skmtc.net/certifyos/apis/certify-api-layer/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/certifyos/certify-api-layer/revisions/563848e0ecc0/schema)
