From SendGrid

What an import brings over from SendGrid, how it maps onto coss.com, and how to switch your code.

Start an import from the dashboard's Import page (see the overview) with a SendGrid API key. A restricted key works: give it read access to everything you want to import. If the key can't read something, the import shows that kind as unavailable instead of failing. Keys of EU regional subusers are recognized and read from SendGrid's EU API.

What's imported

Not imported: segments, Single Sends and automations, reserved contact fields other than the names (address, phone and so on), blocks (they're temporary), link branding, subusers, API keys, inactive template versions and webhook signing secrets.

Contacts come out of SendGrid's contact export, which SendGrid prepares in the background. The import waits for it, which can take a few minutes for large lists. Each list's members come from an export of their own.

Suppressions

Global unsubscribes suppress every email, transactional included, as they did in SendGrid.

Unsubscribe groups

Each unsubscribe group becomes a topic that everyone gets until they opt out (opt_in by default), shown in the preference center. Its key is made from its name in lower case (Weekly digest becomes weekly_digest); the report lists each group's key. Where you passed asm.groupId, pass the key as the send's category.

Templates

Dynamic templates are Handlebars. The import converts them to coss.com's template language, using each version's test data to tell text from numbers and lists from objects:

A template that converts exactly is published. These are left in the draft as they were, for you to rewrite before you publish it: other helpers (formatDate, equals, notEquals, greaterThan, lessThan, and, or, length and your own), sub-expressions, @index, @first and @last, partials, loops inside loops, ~ whitespace control (removed: check the spacing it trimmed, such as inside a link), {{#with}} (its fields become separate variables), lists of plain values (each item becomes an object with a value field) and {{#if}} over a number (coss.com treats 0 as true). The other Sender_* tags are removed: coss.com prints the whole postal address as {{{mailing_address}}}. The unsubscribe and address placeholders are filled on marketing sends.

Legacy templates are always imported as drafts. <%body%> and <%subject%> become {{{body}}} and {{subject}}, but their substitution tags (like -name-) use delimiters each sender chose, so replace them with variables yourself.

A template SendGrid generated the plain-text part for is imported without one: coss.com generates it from the HTML when it sends.

Webhooks

Each event webhook becomes an endpoint subscribed to the coss.com events its enabled events map to:

An Inbound Parse URL becomes an endpoint for email.received. That event carries the message's details, not the whole message (fetch it through the API), and mail reaches it once you set up receiving for the hostname.

The payloads differ from SendGrid's, and requests are signed with the endpoint's new secret, so update your handler before you enable an endpoint (see verifying requests).

Domains and DNS

coss.com's records (DKIM CNAMEs and the send subdomain for bounces) don't collide with SendGrid's (em…, s1._domainkey, s2._domainkey). Add them and keep sending through SendGrid while they verify.

Switch your code

A dynamic template send in @sendgrid/mail:

await sgMail.send({
  from: "hello@acme.com",
  to: "ada@example.com",
  templateId: "d-2c214ac919e84170b1b8d5e9e7da6a72",
  dynamicTemplateData: { user: { name: "Ada" }, total: 42 },
  asm: { groupId: 12345 },
});

On coss.com, the template keeps its id as its alias, nested data uses the renamed variables, and the group becomes a category. coss.com returns errors instead of throwing them:

const { ,  } = await ..({
  : "hello@acme.com",
  : "ada@example.com",
  : "d-2c214ac919e84170b1b8d5e9e7da6a72",
  : { : "Ada", : 42 },
  : "weekly_digest",
});

A send with its own content takes subject, html and text, and reply_to, cc, bcc, attachments and scheduled_at like any send.