Why Compatibility Instead of SDKs

Most email platforms ship a set of client libraries and ask you to adopt them. That is a reasonable choice, and it has a predictable cost: a library per language, each versioned separately, each a dependency you now own, and a migration that means rewriting every call site.

Omnivery took the other route. Rather than publishing its own SDKs, it implements the request and response formats of the providers already in use - SendGrid v3, Mailgun v3 and SparkPost v1 - so the SDK you already have keeps working. The official Mailgun, SendGrid or SparkPost library for your language, the one already in your lockfile and already covered by your tests, points at Omnivery instead.

Migration therefore stops being a development project. There is no new dependency to evaluate, no new client to learn, and no call sites to rewrite. Kiwi.com moved off SendGrid and Mailgun in 45 minutes, and Notino took the same 45 minutes to move off its own Postfix infrastructure. Neither was under time pressure. Kiwi.com went on to record a 17% improvement in unique click rate against SendGrid over the following twelve months.

Compatibility also means a second provider no longer costs a second integration. If Omnivery accepts the same requests your current provider accepts, adding it as a standby is a configuration entry rather than a parallel code path.

Four Ways In, All the Same Platform

Every submission path reaches the same delivery infrastructure, the same analytics, and the same compliance posture. Which one you pick is an integration decision, not a capability trade-off.

Mailgun v3

The most complete implementation

Around 33 documented endpoints covering messages, MIME submission, domains, webhooks, bounces, unsubscribes, whitelists, complaints, validation and stats. The docs state that any software or library supporting the Mailgun v3 API should work without changes. If you have a choice of dialect, choose this one.

SparkPost v1

Broad, with documented deviations

Around 21 endpoints across transmissions, sending domains, webhooks, API keys, recipient validation and suppression lists. A transmission takes inline email parts, a stored template, or inline RFC822 content - so raw MIME submission is available here as well as on Mailgun, and headers you set inside that MIME, X-OV-* included, are carried through. Parameters specific to SparkPost's own environment are accepted and ignored, and Omnivery handles DKIM key rotation through CNAMEs rather than returning selector and public-key objects.

SendGrid v3

Sending and validation

Two endpoints: POST /mail/send and POST /validations/email. Personalizations, attachments, multiple content types, click and open tracking, custom args, substitutions, sandbox mode and dynamic template data are supported. Management APIs are not part of this shim, so plan account and domain operations through the Omnivery interface or another dialect. The validation endpoint has its own constraints, covered on email validation.

SMTP

No code at all

smtp.omnivery.net on port 587 with STARTTLS, authenticating as an SMTP user. The relay carries full parity with the API through X-OV-* headers - templates, tracking control, callbacks, recipient variables, and enforced TLS on delivery via X-OV-Require-TLS. This is the path for systems you would rather not modify.

There is also an Automations API for low-code and no-code platforms, which is what the official Make.com app is built on, and a set of five webhook output formats covering Bloomreach Engage, Bloomreach events, Mailgun, SendGrid and SparkPost. Those five formats mean an existing webhook consumer keeps working unchanged, which is what makes a standby provider practical rather than theoretical.

What the Change Actually Looks Like

Because Omnivery implements the request formats rather than publishing its own SDKs, migrating is a base URL and a key. The official client you already depend on keeps working, your serialisation code does not move, and your existing test suite still exercises the same call path. These are the base URLs:

  • Mailgun v3 - https://mg-api.omnivery.net/v3
  • SparkPost v1 - https://sp-api.omnivery.net/api/v1
  • SendGrid v3 - https://sg-api.omnivery.net/v3
  • SMTP - smtp.omnivery.net on port 587 with STARTTLS

Most SDKs want the origin and append the version segment themselves, so read each snippet rather than pasting the full path everywhere.

Mailgun

Node, with mailgun.js. The url option is the same one the SDK documents for EU infrastructure, and the client appends /v3.

import Mailgun from 'mailgun.js';
import formData from 'form-data';

const mailgun = new Mailgun(formData);

const mg = mailgun.client({
  username: 'api',
  key: process.env.OMNIVERY_API_KEY,
  // was: https://api.mailgun.net
  url: 'https://mg-api.omnivery.net',
});

await mg.messages.create('example.com', {
  from: 'billing@example.com',
  to: ['customer@example.org'],
  subject: 'Your invoice',
  text: 'Invoice attached.',
});

PHP, with mailgun/mailgun-php. The endpoint is the second argument to create().

use Mailgun\Mailgun;

// was: Mailgun::create($key);
$mg = Mailgun::create(
    getenv('OMNIVERY_API_KEY'),
    'https://mg-api.omnivery.net'
);

$mg->messages()->send('example.com', [
    'from'    => 'billing@example.com',
    'to'      => 'customer@example.org',
    'subject' => 'Your invoice',
    'text'    => 'Invoice attached.',
]);

SparkPost

Python, with python-sparkpost. Note that base_uri takes a bare hostname: the library adds the scheme and the /api/v1 suffix itself, so a full URL here will not work.

from sparkpost import SparkPost

# was: SparkPost(key)  ->  api.sparkpost.com
sp = SparkPost(
    os.environ['OMNIVERY_API_KEY'],
    base_uri='sp-api.omnivery.net',
)

sp.transmissions.send(
    recipients=['customer@example.org'],
    html='<p>Invoice attached.</p>',
    from_email='billing@example.com',
    subject='Your invoice',
)

Node, with node-sparkpost. Here it is origin, and the SDK appends /api/v1.

const SparkPost = require('sparkpost');

const client = new SparkPost(process.env.OMNIVERY_API_KEY, {
  // was: https://api.sparkpost.com
  origin: 'https://sp-api.omnivery.net',
});

SendGrid

Python, with sendgrid-python. The host parameter is the documented way to target a region, and the underlying HTTP client adds the version segment.

import os
import sendgrid

sg = sendgrid.SendGridAPIClient(
    api_key=os.environ['OMNIVERY_API_KEY'],
    # was: https://api.sendgrid.com
    host='https://sg-api.omnivery.net',
)

sg.send(message)

Node, with @sendgrid/mail. The base URL lives on the underlying client.

const sgMail = require('@sendgrid/mail');

sgMail.setApiKey(process.env.OMNIVERY_API_KEY);
// was: https://api.sendgrid.com/
sgMail.client.setDefaultRequest(
  'baseUrl',
  'https://sg-api.omnivery.net/'
);

No SDK at all

Raw HTTP, if your integration is a shell script or a system you would rather not rebuild:

curl -X POST https://sg-api.omnivery.net/v3/mail/send \
  -H "Authorization: Bearer $OMNIVERY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-OV-testmode: true" \
  -d '{
    "personalizations": [
      {"to": [{"email": "customer@example.org"}]}
    ],
    "from": {"email": "billing@example.com"},
    "subject": "Your invoice",
    "content": [{"type": "text/plain", "value": "Invoice attached."}]
  }'

Or SMTP, where the change is a hostname in a config file and no code at all:

SMTP_HOST=smtp.omnivery.net
SMTP_PORT=587
SMTP_TLS=starttls
SMTP_USER=your-smtp-user
SMTP_PASS=your-smtp-password

Send the first message with X-OV-testmode set, as in the curl example above, to exercise the whole path without delivering to a real inbox. Once that returns cleanly, the cutover is a deploy. Two operational notes worth setting up at the same time: API access tokens and SMTP credentials can each be restricted to an allowlist of IP addresses or ranges, and validation endpoints accept domain-level credentials only.

What each dialect covers

Pick a dialect by what your integration actually calls. Endpoint counts are from Omnivery's published API reference. Coverage of the compatibility layer, dialect by dialect.

One thing worth knowing before reading the table: enforced TLS is not an SMTP-only feature. Mailgun exposes it as its own native option, o:require-tls, or as h:X-MG-require-tls. Elsewhere it travels as the X-OV-Require-TLS message header - inside inline RFC822 content on SparkPost, and as a plain MIME header over SMTP.

CapabilityMailgun v3SparkPost v1SendGrid v3SMTP
Send a message
Raw MIME submission✓ POST /{domain}/messages.mime✓ content.email_rfc822Not documented
Email validationAPI only
Suppression list managementNot in this shimNot applicable
Sending domain managementNot in this shimNot applicable
Webhook managementNot in this shimNot applicable
Templates and variables✓ dynamic template data✓ via X-OV-Template
Enforced TLS on delivery✓ o:require-tls or h:X-MG-require-tls✓ header inside email_rfc822Not documented✓ X-OV-Require-TLS
Approximate documented endpoints~33~212n/a

Source: Omnivery API reference at omnivery.com/docs, August 2026. "Not in this shim" means the compatibility layer does not implement that area; those operations are available through another dialect or the Omnivery interface. "Not documented" means the published reference does not describe it, which is not the same as it being unsupported - ask before assuming either way. Verify against the current reference before relying on any single endpoint.

Migrating, or adding a second path

The same sequence works whether you are replacing a provider or adding one alongside it. The only difference is whether you move all traffic at the end or a share of it.

  1. Add the domain and pass vetting

    Add your sending domain and choose its data location. Domain name and data location are the only two settings that cannot be changed later. Vetting normally takes one to three working days. Ask the team to enable transactional mode, which is not self-serve.

  2. Publish DNS records

    Add the sending records - SPF, DKIM and the tracking CNAME - from the domain detail screen. Omnivery rotates DKIM keys through CNAMEs, so this is a one-time change rather than something you re-do at each rotation.

  3. Run the migration import

    Supply read credentials for Mailgun, SparkPost or SendGrid and select the source domains. The import transfers domain settings, webhooks, suppressions, allowlists and templates, runs in the background, and deduplicates against existing data so it can be re-run safely. Mailgun and SparkPost need IP allowlisting first; SendGrid needs an API key with the right permissions.

  4. Point the client at Omnivery

    Change the base URL and API key in your existing SendGrid, Mailgun or SparkPost client - the SendGrid-compatible base URL is https://sg-api.omnivery.net/v3 - or point SMTP at smtp.omnivery.net on port 587. Send with X-OV-testmode first to exercise the path without delivering.

  5. Use domain-level credentials if you call email validation

    The SendGrid-compatible POST /v3/validations/email endpoint accepts only domain-level API credentials; an account-level key is rejected. Validation also has to be switched on in the domain settings first. This catches people out because POST /v3/mail/send works fine with either credential scope, so sending succeeds while validation returns an authentication error on the same key.

  6. Match the webhook format

    Set the webhook output format to whichever provider your event handler already parses - Mailgun, SendGrid, SparkPost or one of the two Bloomreach formats - so no consumer changes are needed. Endpoints must return 2xx, and any redirect disables the endpoint as a security measure.

  7. Cut over, or split the traffic

    Move everything, or route a share through Omnivery and keep the rest where it is. If this is a redundancy setup, keep a steady share flowing so the path stays warm and its reputation stays established.

Who this is for

Teams carrying an integration they do not want to rewrite

The existing provider works, the client library is embedded in a dozen services, and nobody has budget for an email migration. Changing a base URL is a change you can actually get approved.

Teams whose transactional email has no second path

One provider, no fallback, and messages that carry legal or financial weight. Compatibility makes a standby provider a configuration entry rather than a quarter of engineering work.

Platforms sending on behalf of their own customers

Reselling delivery means inheriting your provider's abuse profile. Omnivery has no free tier and vets every customer and domain before sending, which is what keeps the sending neighborhood clean.

Low-code and automation builders

The Automations API is built for Make.com and Zapier-style platforms, and there is an official Make app with modules for sending, parsing, validating, suppressing and watching events.

Teams running Bloomreach, Targito, Meiro or Mautic

Bloomreach Engage, Targito and Meiro have native integrations. Mautic is covered by an official open-source transport plugin. Any other CDP or automation platform can use the compatible APIs or SMTP.

Operators of systems that cannot be changed at all

A billing platform or appliance that only speaks SMTP still gets templates, tracking, webhooks, journaling and enforced TLS through X-OV-* headers, with no code change of any kind.

At a glance

  • Compatible APIs Omnivery natively implements the SendGrid v3, Mailgun v3 and SparkPost v1 request formats, so an existing client library works after changing the base URL and API key.
  • No SDKs by design Omnivery publishes no client libraries of its own. The stated approach is that the official Mailgun, SendGrid or SparkPost library for your language keeps working against Omnivery.
  • Most complete dialect The Mailgun v3 implementation is the most complete, at roughly 33 documented endpoints. SparkPost v1 covers roughly 21. The SendGrid v3 shim covers two: send and email validation.
  • Webhook format matching Omnivery emits webhooks in Mailgun, SendGrid, SparkPost or either of two Bloomreach Engage formats, so an existing event consumer needs no changes.
  • Redundancy without a second integration Because both the outbound requests and the inbound webhooks can match an incumbent provider, Omnivery can be added as a second sending path without a parallel code path.
  • Migration transfers state One-click migration from Mailgun, SparkPost or SendGrid transfers domain settings, webhooks, suppressions, allowlists and templates, and deduplicates on re-run.
  • SMTP has full API parity The SMTP relay at smtp.omnivery.net on port 587 supports templates, tracking control, callbacks, recipient variables and enforced TLS through X-OV-* headers.
  • Vetting is mandatory Every Omnivery domain is vetted before it can send, typically within one to three working days, and transactional mode is enabled by the team rather than self-serve. A standby provider must be provisioned in advance.
  • No free tier Omnivery has no free tier by design. Every customer is vetted before contract, which is the mechanism behind the shared sending reputation.
  • Owned infrastructure Omnivery runs exclusively on its own physical infrastructure. There is no AWS, Azure or Google Cloud in the delivery path.
  • Content is never stored Omnivery does not store message body content. Delivery metadata is retained for a maximum of 30 days, after which only summary volume counts remain.
  • Certifications Omnivery holds seven ISO certifications - ISO 9001, ISO/IEC 20000-1, ISO 22301, ISO/IEC 27001, ISO/IEC 27017, ISO/IEC 27018 and ISO/IEC 27701 - plus HIPAA. The certificates are published for download.
  • White-glove deliverability Most engineering teams have no deliverability owner, and the integration is rarely the part that goes wrong. A senior deliverability analyst works with you from onboarding onward, on a standing call cadence, and contacts you when sending patterns look wrong - included, not invoiced as a support tier.

Developer questions

Does Omnivery have an SDK for my language?

No, and that is deliberate. Omnivery publishes no client libraries. Instead it implements the SendGrid v3, Mailgun v3 and SparkPost v1 request formats natively, so the official library for your language - the one already in your dependency file and already covered by your tests - works against Omnivery once you change the base URL and API key.

The practical effect is that Omnivery supports every language those three ecosystems support, without maintaining or versioning anything itself. The trade-off is that you are coding against a third party's API shape rather than an Omnivery-native one, and that the SendGrid dialect in particular is narrow.

Which API dialect should I use for a new integration?

Mailgun v3. It is the most complete implementation at roughly 33 documented endpoints, covering messages and MIME submission, domains, webhooks, bounces, unsubscribes, whitelists, complaints, validation and stats. The documentation states that any software or library supporting the Mailgun v3 API should work without changes.

SparkPost v1 is the next broadest at roughly 21 endpoints. The SendGrid v3 shim covers two endpoints, POST /v3/mail/send and POST /v3/validations/email, on base URL https://sg-api.omnivery.net/v3 - so it suits an existing SendGrid integration but is a poor choice to build against fresh. The validation endpoint accepts only domain-level API credentials, while send accepts either scope.

Can I really run Omnivery as a backup provider without a second integration?

Largely, yes, and in both directions. Outbound, Omnivery accepts the same requests your current provider accepts, so the standby path is the same code with a different base URL. Inbound, Omnivery can emit webhooks in Mailgun, SendGrid or SparkPost format, so your existing event consumer needs no second parser.

Two caveats to plan for. First, provisioning must happen in advance: every domain is vetted before it can send, typically one to three working days, and transactional mode is enabled by the team rather than self-serve. You cannot spin up a standby during an incident. Second, keep the path warm by sending a steady share of real traffic through it, so reputation is established and the failover is a change in ratio rather than a first attempt.

How does suppression state stay in sync between two providers?

Through a re-runnable import rather than a continuous sync. Omnivery's migration tooling pulls suppressions, bounces, unsubscribes and allowlists from Mailgun, SparkPost or SendGrid, and it checks existing data for duplicates on every run, so repeat runs only add new records.

That means you should choose a refresh cadence deliberately rather than assuming two-way replication. In a redundancy design this is the detail that matters most: a standby provider that does not know who unsubscribed is a compliance problem rather than a backup.

What happens to messages sent to a suppressed recipient?

Omnivery holds four suppression types - bounce, unsubscribe, complaint and block - and all of them prevent delivery in the same way, with one deliberate exception: messages categorized as transactional bypass unsubscribe and complaint suppressions, because a password reset should still arrive for someone who unsubscribed from marketing. Bounce and block suppressions still apply to transactional messages.

There is also a recipients allowlist for addresses that must never be suppressed, and a URI allowlist that rejects a message if any link in the body points somewhere not on the list.

Do I have to change my templates?

Not if you send template content in the request. If you want templates stored server side, Omnivery supports Handlebars and Template Toolkit, switchable at any time while editing, and injects a from, rcpt and subject variable plus date and domain objects.

Over SMTP the equivalent is the X-OV-Template and X-OV-Template-Variables headers, so a system that cannot make API calls can still use stored templates.

Is there a rate limit I need to handle?

Each domain has a rate limit that auto-increases by 20% when it reaches 75% utilization, so normal growth does not require intervention. For SMTP, treat 4xx as temporary and retry no sooner than one minute apart; 5xx responses are permanent and must not be retried.

What is the Mautic plugin, and is it supported?

It is an official open-source Mautic transport plugin, OmniveryMailerBundle, published under GPL-3.0 in Omnivery's GitHub organization and tested against Mautic 5. Installation is a manual clone into Mautic's plugins directory followed by composer install; there is no published Composer package. It is configured through Mautic's Email DSN settings, with the API key supplied in the password field.

Set expectations accordingly: it is a transport plugin, so it handles sending. Bounce, unsubscribe and webhook handling are not part of its documented scope - configure those through Omnivery directly. Bloomreach Engage and Meiro have deeper native integrations.

Inboxing, Security, Compliance

Point an existing SendGrid, Mailgun or SparkPost client at Omnivery and send a test message with X-OV-testmode before anything reaches a real inbox. If you are designing for redundancy, start the vetting conversation early - a standby path has to exist before you need it.