Documentation

SendGrid

Connect SendGrid for transactional email, marketing campaigns, and inbound delivery webhooks.

SendGrid powers email delivery in AppEngine — both transactional (order confirmations, password resets) and marketing broadcasts. The integration uses SendGrid's API key auth and posts events back via the Connect webhook handler.

Connect SendGrid

await fetch('/api/upstream/save-integration', {
  method: 'POST',
  headers: { orgid: ORG_ID, Authorization: `Bearer ${jwt}` },
  body: JSON.stringify({
    type: 'sendgrid-provider',
    name: 'SendGrid Production',
    credentials: { apiKey: 'SG.xxx' },
    publicConfig: {
      defaultFromEmail: '[email protected]',
      defaultFromName: 'Example Co',
      replyToEmail: '[email protected]',
    },
  }),
});

Required scopes on the API key: Mail Send, Tracking, Suppressions, Marketing (if running campaigns).

Domain authentication

Before sending production email, authenticate your domain in SendGrid (SPF + DKIM). The platform exposes the broadcast email-domain configurator that walks through DNS record creation:

POST/broadcast/email-domain/configureJWT

Without authentication, SendGrid sends mail but it's marked via sendgrid.net in clients, which hurts deliverability.

Sending email

await fetch('/api/upstream/call/sendgrid-provider/send', {
  method: 'POST',
  headers: { orgid: ORG_ID, Authorization: `Bearer ${jwt}` },
  body: JSON.stringify({
    to: '[email protected]',
    subject: 'Your order has shipped',
    html: '<p>Tracking: <a href="...">123</a></p>',
    text: 'Tracking: 123',
    from: '[email protected]',
    customArgs: { orderId: 'ord-12345' },
  }),
});

customArgs propagates through delivery, open, and click events — used to attribute engagement back to the originating record.

For broadcast campaigns, the Broadcast module handles segment expansion and rate limiting; you don't call send per recipient.

Webhooks

POST/connect/webhook/sendgridNo auth

SendGrid posts an array of events: processed, delivered, open, click, bounce, dropped, deferred, spam_report, unsubscribe, group_unsubscribe. The Connect module verifies the signature using the SendGrid public key (configured in your SendGrid Mail Settings → Event Webhook), then dispatches:

  • delivered → mark message delivered on the broadcast record
  • open → write email_opened activity on the recipient contact
  • click → write email_clicked activity
  • bounce → mark recipient bounced; future sends suppress
  • unsubscribe → mark recipient unsubscribed

Wire the webhook URL in SendGrid: https://api.appmint.io/connect/webhook/sendgrid?orgid=<your-org>.

Suppression lists

SendGrid maintains its own bounce, spam, and unsubscribe lists per API key. The platform reads them via:

await fetch('/api/upstream/call/sendgrid-provider/suppressions', {
  method: 'GET',
  headers: { orgid: ORG_ID, Authorization: `Bearer ${jwt}` },
});

Suppressed addresses are skipped at broadcast send time, regardless of audience targeting. To remove a suppression (after the customer asks to be re-subscribed):

await fetch('/api/upstream/call/sendgrid-provider/remove-suppression', {
  method: 'POST',
  headers: { orgid: ORG_ID, Authorization: `Bearer ${jwt}` },
  body: JSON.stringify({ email: '[email protected]', group: 'bounces' }),
});

Inbound parse

SendGrid's Inbound Parse can route emails to the platform — useful for support inboxes that accept replies as ticket comments. Configure the parse URL to:

https://api.appmint.io/connect/webhook/sendgrid/inbound?orgid=<your-org>

The platform parses the multipart body, extracts the original message ID from the In-Reply-To header, and posts it as a reply on the matching ticket.

Tracking pixels and click redirects

SendGrid handles open tracking and click rewriting natively. AppEngine adds an additional layer: the unified automation webhook at /connect/automation/:id/:stepId/:entity/:activity/ stamps engagement events into the CRM activity log. The two layers don't conflict — SendGrid's events feed deliverability, AppEngine's track per-record engagement for automation triggers.

Common quirks

  • Single Sender vs Domain Auth — Single Sender works for low-volume but is rate-limited and lower-deliverability. Always use domain auth in production.
  • Sandbox mode — append mail_settings.sandbox_mode.enable: true to a send to validate without sending. The platform doesn't expose this via Upstream; if you need it, call SendGrid directly with the same key.
  • Categories vs custom args — the connector uses customArgs consistently for record IDs. Don't put record IDs in SendGrid categories — they're rate-limited differently.

For redundancy, configure both SendGrid and Mailgun. The use-case routing layer can fall back to the secondary vendor if the primary is rate-limited or down.