Skip to content

SendGrid Event Webhook Configuration

SendGrid Event Webhook Configuration

The SendGrid Event Webhook integration enables real-time tracking of email delivery events including opens, clicks, bounces, spam reports, and more. This integration automatically updates email message statuses and triggers internal webhooks when key events occur.

Overview

What is SendGrid Event Webhook?

SendGrid Event Webhook is a service that sends HTTP POST notifications to your API when email events occur. This allows you to:

  • Track delivery status in real-time
  • Monitor email engagement (opens, clicks)
  • Handle bounces and delivery failures
  • Detect spam reports and unsubscribes
  • Maintain accurate email status records

Supported Events

The following SendGrid events are supported and will update email message statuses:

EventEmail StatusTriggers Internal WebhookDescription
processedsentNoEmail has been processed and sent to SendGrid’s delivery queue (optional)
delivereddeliveredYesEmail was successfully delivered to the recipient’s mail server
openopenedYesRecipient opened the email
clickclickedYesRecipient clicked a link in the email
bouncebouncedYesEmail bounced (hard or soft bounce)
droppeddroppedNoEmail was dropped and not sent (invalid address, spam, etc.)
deferreddeferredNoDelivery was temporarily delayed
spamreportspam_reportNoRecipient marked email as spam
unsubscribeunsubscribeNoRecipient unsubscribed from emails

Note: The processed event is optional and provides early notification that an email entered the sending queue. While it updates the status to sent, it does not trigger internal webhooks since it doesn’t represent final delivery or engagement.

Configuration

Webhook Endpoint URL

The SendGrid Event Webhook endpoint is:

https://your-domain.com/api/sendgrid-inbound-parse-RaUDu49Mbkh2W7WrhU7XngdB3xEa3amD

Important Security Notes:

  • The path includes a security token (RaUDu49Mbkh2W7WrhU7XngdB3xEa3amD) that must not be changed
  • The endpoint uses both Basic Authentication and signature verification for security
  • Always use HTTPS - never HTTP

Step-by-Step Configuration in SendGrid

Follow these steps to configure the Event Webhook in your SendGrid account:

  1. Navigate to Event Webhook Settings

  2. Enter the Webhook URL

    • In the “HTTP POST URL” field, enter your webhook endpoint:
      https://your-domain.com/api/sendgrid-inbound-parse-RaUDu49Mbkh2W7WrhU7XngdB3xEa3amD
    • Replace your-domain.com with your actual domain (e.g., api.dev.pulsecrm.com)
  3. Enable HTTPS POST

    • Ensure “Enable HTTPS” is turned ON
    • This is required for secure transmission of event data
  4. Select Events to Track

    • Enable the following required events by checking their boxes:
      • ✓ Delivered
      • ✓ Opened
      • ✓ Clicked
      • ✓ Bounced
      • ✓ Dropped
      • ✓ Deferred
      • ✓ Spam Report
      • ✓ Unsubscribe
    • Optionally enable:
      • Processed (for early queue entry notification)
  5. Enable Signature Verification

    • Ensure “Signature Verification” is turned ON
    • This is automatically handled by the endpoint - no additional configuration needed
  6. Save Configuration

    • Click “Save” to activate the webhook
    • SendGrid will begin sending events to your endpoint immediately

Test the Configuration

After saving, SendGrid provides a “Test Your Integration” button that sends a sample event to verify the endpoint is working correctly.

Verification

Send a Test Email

To verify the integration is working properly, send a test email using the sendEmail function:

import { sendEmail } from "lib/sendgrid.ts";
import type { EmailEntityInfo } from "lib/sendgrid.ts";
// Prepare entity info to track this email
const entityInfo: EmailEntityInfo = {
accountName: "Test Account",
accountId: "account-123",
entityType: "lead", // or "application", "merchant", "ticket"
entityId: "lead-456",
};
// Send test email
await sendEmail(
{
to: "recipient@example.com",
subject: "Test Email for Event Tracking",
text: "This is a test email to verify SendGrid Event Webhook integration.",
html: "<p>This is a test email to verify SendGrid Event Webhook integration.</p>",
},
"PulseCRM",
entityInfo
);

Verify Email Message Records

After sending the test email, verify that records are created and updated:

  1. Check emailMessages Table

    • A new record should be created with the messageId from SendGrid
    • Initial status will be triggered
    • The entityType, entityId, and accountId should match your entityInfo
  2. Check emailMessageEvents Table

    • As events arrive from SendGrid, new event records are created
    • Each event includes the event type, timestamp, and relevant metadata
    • Multiple events for the same email will create multiple event records
  3. Monitor Status Updates

    • The emailMessages.status field updates automatically as higher-priority events arrive:
      • triggeredsent (if processed event enabled)
      • sentdelivered (when delivered)
      • deliveredopened (when recipient opens)
      • openedclicked (when recipient clicks a link)
    • Negative events (bounced, dropped, spam_report, unsubscribe) have the highest priority and will override positive statuses

Verify Internal Webhooks

For events that trigger internal webhooks (delivered, open, click, bounce), the webhook processing happens via:

lib/services/email-messages.service.ts
await triggerWebhooks(pulseEvent, accountId, webhookPayload, false);

The webhook payload includes:

  • messageId - SendGrid message ID
  • accountId - Account the email belongs to
  • entityType - Entity type (lead, application, etc.)
  • entityId - Entity ID
  • event - SendGrid event name
  • timestamp - When the event occurred
  • status - Current email status after processing the event
  • Additional metadata (IP, user agent, URL clicked, bounce reason, etc.)

Technical Implementation Reference

Webhook Endpoint

The webhook endpoint implementation is located at:

api/sendgrid-inbound-parse-RaUDu49Mbkh2W7WrhU7XngdB3xEa3amD/route.ts

Key features:

  • Basic Authentication: Validates the Authorization header
  • Signature Verification: Verifies SendGrid’s ECDSA signature using the public key
  • Event Processing: Routes events to processSendgridWebhookEvents for storage and webhook triggering
  • Dual Purpose: Handles both Event Webhook (JSON) and Inbound Parse (FormData) requests

Event Processing Service

Event processing logic is located at:

lib/services/email-messages.service.ts

Key functions:

  • processSendgridWebhookEvents(events) - Processes an array of events
  • processSendgridWebhookEvent(event) - Processes a single event with deduplication
  • Status priority system ensures correct final status (e.g., clicked overrides opened)
  • Duplicate event detection prevents multiple webhook triggers for the same event

Event Status Priority

Email statuses follow a priority system to ensure the most significant status is maintained:

PriorityStatusValue
10 (Highest)bounced, dropped, spam_report, unsubscribeDelivery failures
5clickedHighest engagement
4openedMedium engagement
3deliveredSuccessful delivery
2deferredTemporary delay
1sentProcessed for sending
0 (Lowest)triggeredInitial state

Once a higher-priority status is set, lower-priority events will not downgrade it. For example, if an email is bounced, subsequent delivered events (if any) will not change the status.

Troubleshooting

Events Not Arriving

  1. Verify webhook is enabled in SendGrid Mail Settings
  2. Check the webhook URL is correct and includes the security token
  3. Ensure HTTPS is enabled (HTTP will not work)
  4. Verify signature verification is enabled in SendGrid
  5. Check server logs for rejected requests or authentication failures

Email Status Not Updating

  1. Verify the message ID exists in the emailMessages table
  2. Check emailMessageEvents table to see if events are being recorded
  3. Review event priority - lower priority events don’t override higher ones
  4. Check for processing errors in application logs

Duplicate Events

  • Duplicate events are automatically detected using the sg_event_id field
  • The database has a unique constraint preventing duplicate event records
  • Internal webhooks are only triggered once per unique event

Internal Webhooks Not Triggering

  1. Verify the event type - only delivered, open, click, and bounce trigger internal webhooks
  2. Check webhook configuration for the account
  3. Review webhook logs for delivery failures
  4. Ensure it’s a new event - duplicate events don’t trigger webhooks

Security Considerations

Authentication Methods

The webhook endpoint uses two layers of security:

  1. Basic Authentication

    • Simple authentication check on incoming requests
    • Provides basic protection against unauthorized access
  2. Signature Verification

    • SendGrid signs each webhook request with an ECDSA signature
    • The endpoint verifies signatures using SendGrid’s public key
    • Prevents spoofed or tampered webhook requests

Best Practices

  • Never expose the security token in the webhook URL
  • Monitor failed authentication attempts in server logs
  • Regularly review email event logs for anomalies
  • Use environment-specific endpoints (dev, staging, production)
  • Keep SendGrid API keys secure and rotate them periodically

API Endpoints

Internal Endpoints (Automated)

  • POST /api/sendgrid-inbound-parse-RaUDu49Mbkh2W7WrhU7XngdB3xEa3amD - Webhook receiver (called by SendGrid)

Management Endpoints

  • Email message tracking is automatic - no manual API calls required
  • Query email messages and events through entity-specific endpoints (leads, applications, etc.)

Database Schema

Email Messages Table

{
id: number,
messageId: string, // SendGrid message ID
accountId: string,
entityType: EmailEntityType, // "lead", "application", "merchant", "ticket"
entityId: string,
toEmail: string,
fromEmail: string,
subject: string,
status: EmailStatus, // Current status based on events
createdAt: Date,
updatedAt: Date
}

Email Message Events Table

{
id: number,
emailMessageId: number, // Foreign key to emailMessages
sgEventId: string, // Unique SendGrid event ID (for deduplication)
event: string, // Event type: "delivered", "open", etc.
timestamp: Date, // When event occurred
ip: string, // Recipient IP (if available)
userAgent: string, // Recipient user agent
url: string, // Clicked URL (for click events)
reason: string, // Bounce/drop reason
bounceType: string, // "hard" or "soft"
rawPayload: JSON, // Full SendGrid event data
createdAt: Date
}

Unique constraint on sgEventId prevents duplicate event records.