For developers
Omnivery implements the SendGrid v3, Mailgun v3 and SparkPost v1 request formats natively. If your application already talks to one of those, it can talk to Omnivery by changing a base URL and an API key. That makes migration a configuration change rather than a development project - and it makes running Omnivery as a second, redundant provider possible without building a second integration.
Updated: July 2026
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.
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.
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.
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.
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.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.
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:
https://mg-api.omnivery.net/v3https://sp-api.omnivery.net/api/v1https://sg-api.omnivery.net/v3smtp.omnivery.net on port 587 with STARTTLSMost SDKs want the origin and append the version segment themselves, so read each snippet rather than pasting the full path everywhere.
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.',
]);
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',
});
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/'
);
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.
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.
| Capability | Mailgun v3 | SparkPost v1 | SendGrid v3 | SMTP |
|---|---|---|---|---|
| Send a message | ✓ | ✓ | ✓ | ✓ |
| Raw MIME submission | ✓ POST /{domain}/messages.mime | ✓ content.email_rfc822 | Not documented | ✓ |
| Email validation | ✓ | ✓ | ✓ | API only |
| Suppression list management | ✓ | ✓ | Not in this shim | Not applicable |
| Sending domain management | ✓ | ✓ | Not in this shim | Not applicable |
| Webhook management | ✓ | ✓ | Not in this shim | Not 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_rfc822 | Not documented | ✓ X-OV-Require-TLS |
| Approximate documented endpoints | ~33 | ~21 | 2 | n/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.
Transactional email is usually a single point of failure. Password resets, payment confirmations and fraud alerts all leave through one provider, and if that provider has a bad hour, there is no path for them to take. Most teams accept this, because the standard fix - integrating a second vendor - means a second client library, a second webhook parser, a second suppression store, and a second set of tests, for something you hope never to use.
API compatibility removes most of that cost, in both directions.
Because Omnivery accepts SendGrid v3, Mailgun v3 and SparkPost v1 requests natively, the standby path is the same code with a different base URL and key. No parallel client, no second serialisation format, no divergent template syntax to maintain.
Inbound is the half that usually gets missed. Omnivery can emit webhooks in Mailgun, SendGrid or SparkPost format, as well as two Bloomreach formats. So the event handler that already parses your current provider's delivery, bounce, open, click, unsubscribe and complaint callbacks does not need a second implementation either. Failing over does not mean losing event processing at exactly the moment you need visibility most.
A standby provider that does not know who has unsubscribed is a compliance problem, not a backup. Omnivery's migration tooling imports suppressions, bounces, unsubscribes and allowlists from Mailgun, SparkPost and SendGrid, and it checks existing data for duplicates on every run, so it can be re-run periodically to refresh that state without creating duplicates. This is a re-runnable import rather than a continuous two-way sync, so the cadence is a deliberate choice.
Set it up before you need it. Every Omnivery domain goes through vetting, typically one to three working days, and transactional mode is enabled by the team rather than self-serve. A standby provider therefore has to be provisioned in advance. You cannot stand one up during an incident. A vendor who says otherwise is describing a platform with no abuse controls.
Warm the path. A dormant sending identity is not a proven one. Sending a small, steady share of real traffic through the second provider keeps the route exercised, keeps reputation established, and means the failover you eventually perform is a change in ratio rather than a first attempt.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.