---
title: 'For Developers'
url: 'https://omnivery.com/solutions/developers'
markdown: 'https://omnivery.com/solutions/developers.md'
lang: en
date: '2026-08-11'
description: 'Omnivery speaks SendGrid v3, Mailgun v3 and SparkPost v1 natively, so you can migrate by swapping an endpoint - or add it as a redundant second provider.'
---

For developers

# Change the endpoint, not the integration

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

[Read the API reference](https://omnivery.com/docs)[Talk to an engineer](mailto:sales@omnivery.com)

## 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](https://omnivery.com/products/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.

 | 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.

## Running Omnivery as Your Second Provider

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.

### Outbound: the same requests

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: the same webhooks

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.

### Suppression state

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.

### Two things to plan for

**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.

## 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](https://omnivery.com/documents/omnivery_iso_certificates.pdf).
- **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](https://omnivery.com/solutions/deliverability) 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 increases by 20% when it reaches 75% utilization and delivery metrics support it, 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.

 [Read the API reference](https://omnivery.com/docs)[Talk to an engineer](mailto:sales@omnivery.com)

---

## Navigation

- Parent: [Solutions](https://omnivery.com/solutions.md)
- Previous: [EU Transactional Email Provider](https://omnivery.com/solutions/eu-transactional-email.md)
- Next: [E-commerce](https://omnivery.com/solutions/ecommerce.md)
