Class: Webhooks::V1::SendgridController
- Inherits:
-
BaseController
- Object
- ActionController::API
- BaseController
- Webhooks::V1::SendgridController
- Defined in:
- app/controllers/webhooks/v1/sendgrid_controller.rb
Overview
SendGrid Event Webhook Controller
Reference: https://www.twilio.com/docs/sendgrid/for-developers/tracking-events/event
SendGrid sends batches of events in a single POST request as a JSON array.
Each event contains:
- email: recipient email address
- timestamp: Unix timestamp of event
- event: event type (processed, delivered, open, click, bounce, etc.)
- unique_id/sg_message_id: message identifier
- additional fields depending on event type
Security:
- Signature verification using ECDSA (when enabled)
- Reference: https://www.twilio.com/docs/sendgrid/for-developers/tracking-events/getting-started-event-webhook-security-features
Multiple SendGrid Subusers:
- Each subuser has its own webhook verification key
- URL includes ?u=<subuser_name> to identify which key to use
- Keys stored in credentials: sendgrid_api.<subuser_name>.webhook_verification_key
- Example: /webhooks/v1/sendgrid?u=warmlyyours_transaction
This controller ingests each event into WebhookLog for async processing,
providing full audit trail and retry capabilities.
Instance Method Summary collapse
-
#create ⇒ Object
POST /webhooks/v1/sendgrid?u=.
Instance Method Details
#create ⇒ Object
POST /webhooks/v1/sendgrid?u=
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'app/controllers/webhooks/v1/sendgrid_controller.rb', line 33 def create raw_payload = request.raw_post events = parse_events(raw_payload) if events.blank? Rails.logger.warn "[SendGrid Webhook] Empty or invalid payload received (subuser: #{sendgrid_subuser})" return head :ok # Return OK to prevent SendGrid from retrying empty payloads end Rails.logger.info "[SendGrid Webhook] Received #{events.size} event(s) from subuser: #{sendgrid_subuser}" # Ingest each event into WebhookLog # Sort by timestamp to process oldest first events.sort_by { |evt| evt['timestamp'] || 0 }.each do |event_data| ingest_event(event_data) end head :ok rescue JSON::ParserError => e Rails.logger.error "[SendGrid Webhook] Failed to parse JSON payload: #{e.}" head :bad_request rescue RedisClient::CannotConnectError => e Rails.logger.error "[SendGrid Webhook] Redis unavailable, returning 503 for retry: #{e.}" head :service_unavailable rescue StandardError => e Rails.logger.error "[SendGrid Webhook] Error processing webhook: #{e.}" head :internal_server_error end |