Migrate from Brevo to Unitpost

Easy migrationContacts with their email blocklist state, transactional blocked contacts and transactional templates all import; what you rebuild sits outside those lists (segments, SMS consent, automations).

BrevoUnitpost
ContactsImports
Unsubscribes and consentImports
Lists and segmentsPartly imports
TopicsRebuild

What comes with you

To leave Brevo, paste a Brevo API key into Unitpost's Migration Assistant. It imports every contact with its attributes and email blocklist state, lists as static segments, transactional blocked contacts as suppressions, transactional templates as drafts, both webhook types switched off, and domains where Brevo's API exposes them. Segments, SMS consent, automations, campaign history, CRM and landing pages stay at Brevo, and `{{ params.x }}` placeholders need rewriting.

DataResultWhat happens
ContactsImportsEvery contact imports with FIRSTNAME and LASTNAME as its name and other attributes as custom fields; address objects and multiple-choice values are flattened into separate fields.
Unsubscribes and consentImportsA contact Brevo has blocklisted for email (emailBlacklisted) imports unsubscribed, which stops marketing here. Transactional contacts that unsubscribed import as unsubscribe suppressions.
Suppressions (bounces and complaints)ImportsTransactional blocked contacts import with their reason: hard bounces, spam complaints and admin blocks. Admin blocks stop every send here, transactional included.
Lists and segmentsPartly importsLists become static segments with their members, and list folders are flattened. Brevo segments are not read; rebuild them as dynamic segments.
TemplatesPartly importsTransactional templates import subject and HTML as drafts in code mode; text versions are not read. {{ params.x }} and {{ contact.X }} stay as written, and the preview warns about them.
Sending domainsPartly importsDomains and their sender addresses are recreated as verifying with new DKIM keys when Brevo's domains API answers for your account; when it does not, the preview says so and you add them by hand.
WebhooksPartly importsTransactional and marketing webhooks import switched off with a new signing secret. request, uniqueOpened and listAddition are not mapped and are listed in the preview.
TopicsRebuildBrevo has no topics. Create them here if you want per-category opt-outs on campaigns.
API keysRebuildAPI keys and SMTP keys stay at Brevo. Create Unitpost API keys instead.
SMS contacts and consentRebuildThe SMS attribute arrives as a text custom field, not as the contact's SMS number, and smsBlacklisted is not read. Re-record SMS consent before texting anyone.
Email campaignsRebuildNot read. Recreate drafts and scheduled campaigns as Unitpost campaigns on a segment.
Send history and analyticsStays behindEmail logs, campaign reports and statistics stay at Brevo.
AutomationsStays behindBrevo automations stay; Unitpost automations are not launched yet.
CRM, landing pages, chat and WhatsAppStays behindUnitpost has no equivalent; keep Brevo for them if you use them.

Before you start

  • A Brevo API key (xkeysib-…) from Settings → SMTP & API. Keys created with the MCP option work too.
  • If Brevo blocks unknown IPs for API keys (Security → Authorised IPs), choose Deactivate for API keys before connecting, or Brevo rejects even a valid key.
  • A Unitpost API key with emails:send, created in API keys.
  • A second Unitpost key for the migration steps, with templates:write and webhooks:manage, saved as UNITPOST_MIGRATION_KEY. Keep it out of your app and revoke it once the move is done.
  • Access to your DNS host.
  • If you text contacts: SMS on Unitpost is on paid plans only, and marketing texts need consent recorded per contact.

The cut-over plan

Brevo keeps sending until the last step, so there is no gap in delivery.

  1. Run the Migration Assistant

    Paste the key in Settings → Migration. Suppressions are written before contacts, so blocked and blocklisted addresses land unsubscribed. Check the domains line in the preview: if Brevo's domains API is not enabled for your account, add the domains by hand in step 2.

  2. Verify your domains next to Brevo

    Open Domains (add any the importer could not read) and publish the records shown: MX and SPF on unitpost.<your domain> and a DKIM TXT under _domainkey. They do not replace Brevo's records, so both providers stay authenticated. Keep your single _dmarc record.

  3. Rewrite template variables and publish

    {{ params.order_id }} becomes {{order_id}}, filled from template.variables. {{ contact.FIRSTNAME }} and {{ contact.LASTNAME }} become {{first_name}} and {{last_name}}, which fill from the recipient's contact. Set receipts to transactional (imports land as marketing, which adds a footer) and publish with PATCH /api/v1/email/templates/{id}.

  4. Switch the API call

    sender becomes a from string, to takes addresses, htmlContent becomes html, and templateId with params becomes template: { id, variables }. The api-key header becomes Authorization: Bearer, and sends accept an Idempotency-Key.

    send.ts
    // Before: Brevo
    await fetch("https://api.brevo.com/v3/smtp/email", {
      method: "POST",
      headers: { "api-key": process.env.BREVO_API_KEY, "content-type": "application/json" },
      body: JSON.stringify({
        sender: { name: "Acme", email: "hi@acme.com" },
        to: [{ email: "user@example.com" }],
        templateId: 8,
        params: { order_id: "A-1009" },
      }),
    });
    
    // After: Unitpost
    import { Unitpost } from "unitpost";
    const unitpost = new Unitpost(process.env.UNITPOST_API_KEY);
    const { data, error } = await unitpost.email.send({
      from: "Acme <hi@acme.com>",
      to: "user@example.com",
      template: { id: "tmpl_123", variables: { order_id: "A-1009" } },
    });
  5. Re-key and enable webhooks

    Brevo protects webhooks with credentials in the URL, a bearer token or an IP allowlist; Unitpost signs each body with a whsec_ secret in the svix headers (svix-id, svix-timestamp, svix-signature). Rotate the secret in Webhooks to see it, add verification, add request events by hand if you used them, then set status to enabled.

  6. Re-record SMS consent

    Brevo's SMS opt-outs do not import. Before any marketing text, record each contact's consent where they gave it (POST /api/v1/contacts/{id}/sms-consent) or ask them to confirm with POST /api/v1/contacts/{id}/sms-opt-in-request. A phone number on a contact is not consent.

  7. Send tests

    Send each template with POST /api/v1/email/templates/{id}/test-send, and fire POST /api/v1/webhooks/{id}/test at each endpoint.

  8. Move traffic in stages

    Move transactional mail first, watching email.bounced and email.complained, then campaigns. Brevo stays authenticated, so rolling back is a config change.

  9. Cancel Brevo last

    Finish the checks below, then downgrade or close the account.

Name changes

Same ideas, new names

BrevoUnitpostNote
Contact listSegment (static)
SegmentSegment (dynamic)Rebuilt by hand.
List folderNo equivalentFlattened on import.
Contact attributeContact field
Email blocklisted (emailBlacklisted)Unsubscribed contact
Transactional blocked contactSuppression
paramsTemplate variables
Email campaignCampaign
AutomationAutomationsNot launched yet.

API calls

Brevo APIUnitpost APINote
POST /v3/smtp/emailPOST /api/v1/email
templateId + paramstemplate: { id, variables }
api-key headerAuthorization: Bearer <api key>
POST /v3/contactsPOST /api/v1/contacts
PUT /v3/contacts/{identifier}PATCH /api/v1/contacts/{id or email}
POST /v3/contacts/importPOST /api/v1/contacts/importsUp to 5,000 rows per request.
GET /v3/contacts/segmentsGET /api/v1/segments
GET /v3/smtp/blockedContactsGET /api/v1/suppressions
POST /v3/emailCampaignsPOST /api/v1/email/campaigns
POST /v3/webhooksPOST /api/v1/webhooks

SMTP settings

Brevo SMTPUnitpost SMTPNote
smtp-relay.brevo.comsmtp.unitpost.com
Login: the SMTP login from SMTP & API settingsUsername unitpost
Password: an SMTP key (not the API key)Password: a Unitpost API key with emails:send
587, 2525 (encryption optional)587, 2587, 25 (STARTTLS)TLS 1.2 or later is required before login.
465 (SSL or TLS)465, 2465 (implicit TLS)

Webhook events

Brevo eventUnitpost eventNote
sentemail.sent
requestemail.sentBrevo's other name for sent; not mapped by the importer.
deliveredemail.delivered
softBounce, deferredemail.delivery_delayed
hardBounceemail.bounced
spamemail.complained
openedemail.opened
uniqueOpenedemail.openedNot mapped by the importer.
clickemail.clicked
blocked, invalidemail.failedA send our suppression list stops is email.suppressed.
unsubscribed, contactUpdatedcontact.updated
contactDeletedcontact.deleted
listAdditionsegment.joinedNot mapped; bulk imports do not fire it.

Before you cancel Brevo

  • Export contacts and segments as files, including SMS numbers and SMS opt-outs, which do not import.
  • Export the campaign reports and email logs you want to keep; none of them move.
  • Pull the transactional blocked contacts one last time and add new ones here.
  • Delete Brevo's DKIM and verification records once no mail goes through Brevo. Keep _dmarc and Unitpost's records.
  • Turn the IP allowlist back on if you switched it off for the migration, then revoke the API key you used.

Facts about Brevo checked 2026-11-02. Providers change their APIs and plans; check their docs before you rely on a detail.

Questions about leaving Brevo

Brevo's free plan sends more. Why switch?

It does: 300 emails a day, about 9,000 a month, against Unitpost's 5,000 a month. If free volume decides it, keep Brevo. Unitpost is for teams whose main interface is code: one REST API, SDKs, SMTP and an MCP server on the same send rules.

Do my Brevo automations come over?

No. They stay in Brevo, and Unitpost automations are not launched yet, so trigger those emails from your code until they are.

Will my SMS opt-outs come with me?

No. Only email consent imports. Phone numbers arrive as a text field, and you record SMS consent per contact before any marketing text.

Brevo rejected the connection with a valid key. Why?

Brevo's authorised IPs setting refuses API calls from unlisted addresses even with a valid key. Choose Deactivate for API keys under Security → Authorised IPs, connect, then turn it back on.

Can Unitpost replace Brevo entirely?

Only for email and SMS. Unitpost has no CRM, landing pages, live chat or WhatsApp, so teams using Brevo as their whole marketing suite keep parts of it.

Leaving Brevo?

Preview the migration on the free tier

Verify a domain, paste a read-only key, and see what would come across before anything is written.