Sendgrid API

vv3 Base URL: https://sg-api.omnivery.net/v3

Omnivery API allows you to send messages, retrieve delivery stats, check and configure your sending domains and more in multiple formats. This documentation covers the SendGrid v3 compatible API of Omnivery.

This API follows the SendGrid (v3) API as closely as possible, so existing software or libraries supporting SendGrid (v3) can be used to send email by changing the endpoint and API token, keeping in mind the differences below.

Differences from SendGrid

Endpoints

  • Available: POST /mail/send, POST /mail/batch, the scheduled sends API (/user/scheduled_sends: cancel, list, get), POST /validations/email, GET /scopes, GET /user/account, GET /user/profile and /api_keys (list, create, get, delete). Other SendGrid endpoints (for example suppressions, stats, sender authentication, templates, event webhook settings, Marketing Campaigns and subusers) are not. Manage these in the Omnivery app.

API keys

  • POST /mail/send and POST /validations/email require a sending (domain) API key; the primary (account) API key is not accepted. The sender address must belong to the key's domain.
  • GET /user/account, GET /user/profile and /api_keys require the primary (account) API key. GET /scopes takes either; the scopes it returns depend on the key's role.
  • An API key belongs to one sending domain, so POST /api_keys needs a domain, and the key list holds the domain keys only.
  • The on-behalf-of header is not supported. To act on a sub-account, use that sub-account's own API key.

Sending

  • Each personalization is sent as a separate message. If a personalization is rejected, the personalizations before it may already have been sent, so check the error before retrying the whole request.
  • subject is required, at the top level or on every personalization, also with template_id: an Omnivery template has no subject of its own (SendGrid can take it from the template).
  • template_id refers to a template stored in Omnivery.
  • These fields are not supported and are ignored: categories, reply_to_list, personalizations[].headers, asm, ip_pool_name, mail_settings other than sandbox_mode, tracking_settings.subscription_tracking, tracking_settings.ganalytics, tracking_settings.click_tracking.enable_text and tracking_settings.open_tracking.substitution_tag.
  • Attachments with disposition: inline and a content_id are sent as regular attachments; inline (cid:) images are not supported.
  • send_at must be less than 72 hours in the future.
  • Scheduled sends can be cancelled, not paused: the scheduled messages of a batch are deleted from the queue, so status: pause is rejected with 400, there is nothing to resume, and DELETE /user/scheduled_sends/{batch_id} (which removes a pause or cancellation) always answers 400. Each cancelled message produces a soft-bounce event (554 5.7.1 [internal] Campaign cancelled) and a bounce notification to the sender, and the recipient is not added to a suppression list (SendGrid's cancelled messages are silently discarded). Cancelling is asynchronous (201 means it is queued, 503 that it could not be queued). The 10-minute cut-off applies to the earliest send_at of a batch with messages at different times, and PATCH also cancels a batch that has no status yet. The on-behalf-of header and the limit of 100 batches per call (a call takes one batch) do not apply.
  • batch_id must come from POST /mail/batch and be one of your own batches that can still be cancelled; an unknown, expired or foreign ID is rejected with 400. A batch can be cancelled until 10 minutes before the earliest send_at of its messages, so a batch ID stops being valid then. A cancelled batch accepts no more messages.
  • With mail_settings.sandbox_mode.enable set to true the request returns 200 OK and nothing is delivered, as in SendGrid. The difference: the message is not stopped at the API. It is sent in test mode (like Mailgun o:testmode), and our MTA accepts and discards it.
  • Besides SendGrid's own reserved headers, Sender, Return-Path, DomainKey-Signature, ARC-*, Authentication-Results, MIME-Version and our system headers X-OV-Ident, X-OV-Submission and X-OV-APIFormat are refused in headers.
  • A successful request returns 202 Accepted without an X-Message-Id header.
  • The maximum message size, including attachments, is 10 MB unless a different limit is set for your domain.
  • A rejected message returns HTTP status code 400 with {"errors": [{"message", "field", "help"}]}, as SendGrid does. field names the request field when the error is about one (for example from or personalizations.0.to) and is null otherwise; help is always null. This covers a missing from or recipient, a disallowed attachment and a rejection of the content by the mail server. A message over the size limit returns 413.

Email validation

  • The response is returned as a flat object, as documented below, rather than wrapped in result.

Event webhook

  • Event webhooks in SendGrid format are set up in the Omnivery app. Signed event webhooks (X-Twilio-Email-Event-Webhook-Signature) are not supported.
  • Temporary delivery failures are reported with the event name blocked, not deferred.

This REST API requires all requests to be authenticated using the Authorization header with your access token as a Bearer token (Authorization: Bearer <access token>). API tokens can be found in the UI under Credentials menu in API Keys tab (for domain specific/sending tokens) or under API keys menu. For security reasons we strongly suggest using domain specific API keys rather than account API keys whenever possible.

All data to the API endpoint must be posted in JSON format.

Authentication

  • API Key: HTTP bearer

Enter your credentials below to use the Try it feature:

Mail

POST /mail/batch Generate a batch ID ▼

Generates a batch ID that groups scheduled messages. Pass it as batch_id to POST /mail/send, then cancel the whole batch with POST /user/scheduled_sends.

A batch ID can be used for 72 hours. Once a message is scheduled with it, the batch can be cancelled until 10 minutes before the earliest send_at of its messages; after that the ID is no longer valid.

Requires a sending (domain) API key. A batch belongs to that key's domain and cannot be used by another key.

Responses

201

Created

{
  "batch_id": "0b0c3c0e-5a0e-4f43-8f56-0a1f9d4a7c11"
}
401

Unauthorized

{
  "errors": [
    {
      "message": "Unauthorized"
    }
  ]
}

Code Samples

curl -X POST 'https://sg-api.omnivery.net/v3/mail/batch' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("POST", "https://sg-api.omnivery.net/v3/mail/batch", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/mail/batch', {
  method: 'POST',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/mail/batch');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.post(
    'https://sg-api.omnivery.net/v3/mail/batch',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
)
print(response.json())
Response

                            
POST /mail/send Mail Send ▼

This endpoint allows you to send email using SendGrid v3 compatible API

  • Top level parameters are referred to as "global".
  • Individual fields within the personalizations array will override any other global, or "message level", parameters that are defined outside of personalizations.

This endpoint requires use of domain level API credentials and can not be used with account level API key.

Scheduled delivery

Set send_at to a unix timestamp to hold the message and deliver it at that time. A global send_at applies to every personalization; a send_at inside a personalization overrides it for that personalization only.

  • The time must be less than 72 hours in the future. A later time is rejected with HTTP status code 400.
  • A time that is now or already past sends the message immediately.
  • A value that is not an integer unix timestamp is rejected with HTTP status code 400.
  • Every send_at in the request is checked before anything is sent, so one invalid value rejects the whole request. The error's field names the offending value, e.g. send_at or personalizations.1.send_at (0-based).

Parameters

NameInTypeRequiredDescription
Accept header string No
Content-Type header string No

Request Body application/json

FieldTypeRequiredDescription
attachments array No

An array of attachment objects to be included in the message.

FieldTypeRequiredDescription
content string Yes

The Base64 encoded content of the attachment.

disposition string No

The attachment's content-disposition, specifying how you would like the attachment to be displayed. For example, "inline" results in the attached file are displayed automatically within the message while "attachment" results in the attached file require some action to be taken before it is displayed, such as opening or downloading the file.

filename string Yes

The attachment's filename.

type string Yes

The MIME type of the content you are attaching (e.g., "text/plain" or "text/html").

batch_id string No

A batch ID generated with POST /mail/batch. The scheduled messages of a batch can be cancelled with POST /user/scheduled_sends. It must be one of your own batches that can still be cancelled; otherwise the request is rejected with HTTP status code 400 and the field batch_id.

callback_url string No
categories array No

Not supported: categories are ignored (see Differences from SendGrid).

content array No

An array where you can specify the content of your email. Required unless a template_id is set. You can include multiple MIME types of content, but you must specify at least one MIME type. To include more than one MIME type, add another object to the array containing the type and value parameters.

FieldTypeRequiredDescription
type string Yes

The MIME type of the content you are including in your email (e.g., "text/plain" or "text/html").

value string Yes

The actual content of the specified MIME type that you are including in your email.

custom_args object No

Custom values are key/value pairs attached to the message. Key/value pairs must be strings. Substitutions will not be made on custom arguments, so any string that is entered into this parameter will be assumed to be the custom argument that you would like to be used. Custom arguments defined in personalizations will override arguments set on global level.

FieldTypeRequiredDescription
id string No
from object Yes

Email address object with the email address and full name of the sender (from) or recipient (to, cc, bcc or reply-tp).

FieldTypeRequiredDescription
email string Yes

The email address used to send or deliver the message.

name string No

A name or title associated with the email address.

headers object No

An object of custom header names and values to add to every message. Reserved headers are refused with HTTP status code 400 in the SendGrid error format (field is headers.<name>): SendGrid's own reserved headers To, From, Subject, Reply-To, Cc, Bcc, Content-Type, Content-Transfer-Encoding, DKIM-Signature, Received, x-sg-id and x-sg-eid, and also Sender, Return-Path, DomainKey-Signature, ARC-*, Authentication-Results, MIME-Version, X-OV-Ident, X-OV-Submission and X-OV-APIFormat.

mail_settings object No

Only sandbox_mode is supported; other mail settings are ignored (see Differences from SendGrid).

FieldTypeRequiredDescription
sandbox_mode object No

Set enable to true to test a request without delivering it. The request returns 200 OK; the message is sent in test mode and discarded by the MTA.

FieldTypeRequiredDescription
enable boolean No
personalizations array Yes

An array of messages and their metadata. Each object within personalizations can be thought of as an envelope - it defines who should receive an individual message and how that message should be handled.

FieldTypeRequiredDescription
bcc array No

An array of recipients who will receive a blind carbon copy of your email. Each object in this array must contain the recipient's email address. Each object in the array may optionally contain the recipient's name.

FieldTypeRequiredDescription
email string Yes

The email address used to send or deliver the message.

name string No

A name or title associated with the email address.

cc array No

An array of recipients who will receive a copy of your email. Each object in this array must contain the recipient's email address. Each object in the array may optionally contain the recipient's name.

FieldTypeRequiredDescription
email string Yes

The email address used to send or deliver the message.

name string No

A name or title associated with the email address.

custom_args object No

Custom values are key/value pairs attached to the message. Key/value pairs must be strings. Substitutions will not be made on custom arguments, so any string that is entered into this parameter will be assumed to be the custom argument that you would like to be used. Custom arguments defined in personalizations will override arguments set on global level.

FieldTypeRequiredDescription
id string No
dynamic_template_data object No

Dynamic template data are variables used in Templates. Unlike custom_args the template data will not be stored and passed back in the webhook. This field is a collection of key/value pairs following the pattern "variable_name":"value to insert".

from object No

Email address object with the email address and full name of the sender (from) or recipient (to, cc, bcc or reply-tp).

FieldTypeRequiredDescription
email string Yes

The email address used to send or deliver the message.

name string No

A name or title associated with the email address.

send_at integer No

A unix timestamp allowing you to specify when the email for this personalization should be delivered. Overrides the global send_at for this personalization only. Must be less than 72 hours in the future.

subject string No

The subject of your email. When set the subject from the personalizations object takes precedence over the subject set in global parameters.

substitutions object No

An object of key/value pairs following the pattern "substitution_tag":"value to substitute".

FieldTypeRequiredDescription
key string No
template_id string No

The template to use for this personalization. Overrides the global template_id.

to array Yes

An array of recipients who will receive this email. Each object in this array must contain the recipient's email address. Each object in the array may optionally contain the recipient's name.

FieldTypeRequiredDescription
email string Yes

The email address used to send or deliver the message.

name string No

A name or title associated with the email address.

reply_to object No

Email address object with the email address and full name of the sender (from) or recipient (to, cc, bcc or reply-tp).

FieldTypeRequiredDescription
email string Yes

The email address used to send or deliver the message.

name string No

A name or title associated with the email address.

send_at integer No

A unix timestamp allowing you to specify when you want your email to be delivered. Applies to every personalization that does not set its own send_at. Must be less than 72 hours in the future; a time that is now or already past sends the message immediately.

subject string No

The subject of your email. Required unless every personalization sets its own subject, also when a template_id is used.

substitutions object No

An object of key/value pairs following the pattern "substitution_tag":"value to substitute".

FieldTypeRequiredDescription
key string No
template_id string No

The ID of a template stored in Omnivery. When it is set, content is not required. A subject is still required (see Differences from SendGrid).

tracking_settings object No

A collection of different settings that you can use to override domain defaults.

FieldTypeRequiredDescription
click_tracking object No

Allows you to set specific click tracking overriding the domain's default setting.

FieldTypeRequiredDescription
enable boolean No
enable_text boolean No

Not supported, ignored.

open_tracking object No

Allows you to set specific open tracking overriding the domain's default setting.

FieldTypeRequiredDescription
enable boolean No
substitution_tag string No

Not supported, ignored.

Responses

200

OK: sandbox mode (mail_settings.sandbox_mode.enable: true). The message is sent in test mode and not delivered. Empty body.

202

Accepted. Empty body.

400

Bad Request

413

Payload Too Large - the message, including attachments, is over the size limit

Code Samples

curl -X POST 'https://sg-api.omnivery.net/v3/mail/send' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -d '{"attachments": [], "batch_id": 0b0c3c0e-5a0e-4f43-8f56-0a1f9d4a7c11, "callback_url": "string", "categories": [], "content": [], "custom_args": {"id": "string"}, "from": {"email": "string", "name": "string"}, "headers": map[X-Accept-Language:en], "mail_settings": {"sandbox_mode": {"enable": false}}, "personalizations": [], "reply_to": {"email": "string", "name": "string"}, "send_at": 1.6172604e+09, "subject": "string", "substitutions": {"key": "string"}, "template_id": "string", "tracking_settings": {"click_tracking": {"enable": false, "enable_text": false}, "open_tracking": {"enable": false, "substitution_tag": "string"}}}'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("POST", "https://sg-api.omnivery.net/v3/mail/send", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/mail/send', {
  method: 'POST',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
  body: JSON.stringify({"attachments": [], "batch_id": 0b0c3c0e-5a0e-4f43-8f56-0a1f9d4a7c11, "callback_url": "string", "categories": [], "content": [], "custom_args": {"id": "string"}, "from": {"email": "string", "name": "string"}, "headers": map[X-Accept-Language:en], "mail_settings": {"sandbox_mode": {"enable": false}}, "personalizations": [], "reply_to": {"email": "string", "name": "string"}, "send_at": 1.6172604e+09, "subject": "string", "substitutions": {"key": "string"}, "template_id": "string", "tracking_settings": {"click_tracking": {"enable": false, "enable_text": false}, "open_tracking": {"enable": false, "substitution_tag": "string"}}})
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/mail/send');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"attachments": [], "batch_id": 0b0c3c0e-5a0e-4f43-8f56-0a1f9d4a7c11, "callback_url": "string", "categories": [], "content": [], "custom_args": {"id": "string"}, "from": {"email": "string", "name": "string"}, "headers": map[X-Accept-Language:en], "mail_settings": {"sandbox_mode": {"enable": false}}, "personalizations": [], "reply_to": {"email": "string", "name": "string"}, "send_at": 1.6172604e+09, "subject": "string", "substitutions": {"key": "string"}, "template_id": "string", "tracking_settings": {"click_tracking": {"enable": false, "enable_text": false}, "open_tracking": {"enable": false, "substitution_tag": "string"}}}');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.post(
    'https://sg-api.omnivery.net/v3/mail/send',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
    json={"attachments": [], "batch_id": 0b0c3c0e-5a0e-4f43-8f56-0a1f9d4a7c11, "callback_url": "string", "categories": [], "content": [], "custom_args": {"id": "string"}, "from": {"email": "string", "name": "string"}, "headers": map[X-Accept-Language:en], "mail_settings": {"sandbox_mode": {"enable": false}}, "personalizations": [], "reply_to": {"email": "string", "name": "string"}, "send_at": 1.6172604e+09, "subject": "string", "substitutions": {"key": "string"}, "template_id": "string", "tracking_settings": {"click_tracking": {"enable": false, "enable_text": false}, "open_tracking": {"enable": false, "substitution_tag": "string"}}}
)
print(response.json())
Response

                            

Address

POST /validations/email Validate Email Address ▼

Email Address Validation provides real-time detailed information on the validity of email addresses. You can integrate this validation process into your platform's signup form and customize the best use of email address validation for your use case.

Email validation must be enabled in the domain settings requires use of domain level API credentials.

Parameters

NameInTypeRequiredDescription
Accept header string No
Content-Type header string No

Request Body application/json

FieldTypeRequiredDescription
email string Yes

Email address to validate

Responses

200

OK

Code Samples

curl -X POST 'https://sg-api.omnivery.net/v3/validations/email' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -d '{"email": "string"}'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("POST", "https://sg-api.omnivery.net/v3/validations/email", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/validations/email', {
  method: 'POST',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
  body: JSON.stringify({"email": "string"})
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/validations/email');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"email": "string"}');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.post(
    'https://sg-api.omnivery.net/v3/validations/email',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
    json={"email": "string"}
)
print(response.json())
Response

                            

Scheduled sends

GET /user/scheduled_sends Retrieve all cancelled scheduled sends ▼

Returns the cancelled batches of the key's domain.

Requires a sending (domain) API key. A batch belongs to the domain of the key that generated it; another key gets 404.

Differences from SendGrid: there are no paused batches. A cancelled batch is listed until its ID expires (10 minutes before the earliest send_at of its messages).

Responses

200

OK

[
  {
    "batch_id": "0b0c3c0e-5a0e-4f43-8f56-0a1f9d4a7c11",
    "status": "cancel"
  }
]
401

Unauthorized

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "Unauthorized"
    }
  ]
}

Code Samples

curl -X GET 'https://sg-api.omnivery.net/v3/user/scheduled_sends' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("GET", "https://sg-api.omnivery.net/v3/user/scheduled_sends", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/user/scheduled_sends', {
  method: 'GET',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/user/scheduled_sends');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.get(
    'https://sg-api.omnivery.net/v3/user/scheduled_sends',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
)
print(response.json())
Response

                            
POST /user/scheduled_sends Cancel a scheduled send ▼

Cancels the scheduled messages of a batch (see POST /mail/batch): they are deleted and never delivered.

Requires a sending (domain) API key. A batch belongs to the domain of the key that generated it; another key gets 404.

The cancellation is queued and carried out in the background; 201 means it is queued, and 503 that it could not be queued (nothing changed, try again). A batch can be cancelled until 10 minutes before the earliest send_at of its messages; after that its ID is no longer valid and the call returns 404.

Differences from SendGrid: only the status cancel exists. pause is rejected with 400, because the messages are deleted and cannot be resumed.

Request Body application/json

FieldTypeRequiredDescription
batch_id string Yes
status string Yes

Responses

201

Created

{
  "batch_id": "0b0c3c0e-5a0e-4f43-8f56-0a1f9d4a7c11",
  "status": "cancel"
}
400

Invalid request - a missing or invalid batch_id or status, the status pause, or a batch that is already cancelled

{
  "errors": [
    {
      "field": "status",
      "help": null,
      "message": "The status pause is not supported, it must be cancel"
    }
  ]
}
401

Unauthorized

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "Unauthorized"
    }
  ]
}
404

Batch not found - unknown, expired, or not a batch of this key

{
  "errors": [
    {
      "field": "batch_id",
      "help": null,
      "message": "Batch not found"
    }
  ]
}
503

The cancellation could not be queued, try again

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "Unable to queue the cancellation, please try again"
    }
  ]
}

Code Samples

curl -X POST 'https://sg-api.omnivery.net/v3/user/scheduled_sends' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -d '{"batch_id": "string", "status": "string"}'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("POST", "https://sg-api.omnivery.net/v3/user/scheduled_sends", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/user/scheduled_sends', {
  method: 'POST',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
  body: JSON.stringify({"batch_id": "string", "status": "string"})
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/user/scheduled_sends');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"batch_id": "string", "status": "string"}');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.post(
    'https://sg-api.omnivery.net/v3/user/scheduled_sends',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
    json={"batch_id": "string", "status": "string"}
)
print(response.json())
Response

                            
GET /user/scheduled_sends/{batch_id} Retrieve a scheduled send ▼

Returns the status of a cancelled batch.

Requires a sending (domain) API key. A batch belongs to the domain of the key that generated it; another key gets 404.

Responses

200

OK

[
  {
    "batch_id": "0b0c3c0e-5a0e-4f43-8f56-0a1f9d4a7c11",
    "status": "cancel"
  }
]
401

Unauthorized

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "Unauthorized"
    }
  ]
}
404

Batch not found, not cancelled, or not a batch of this key

{
  "errors": [
    {
      "field": "batch_id",
      "help": null,
      "message": "Batch not found"
    }
  ]
}

Code Samples

curl -X GET 'https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("GET", "https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}', {
  method: 'GET',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.get(
    'https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
)
print(response.json())
Response

                            
PATCH /user/scheduled_sends/{batch_id} Update a scheduled send ▼

Cancels the scheduled messages of the batch, like POST /user/scheduled_sends.

Requires a sending (domain) API key. A batch belongs to the domain of the key that generated it; another key gets 404.

Differences from SendGrid: only the status cancel exists, and it also applies to a batch that has no status yet. pause is rejected with 400; a batch that is already cancelled returns 400.

Request Body application/json

FieldTypeRequiredDescription
status string Yes

Responses

204

No Content - the cancellation is queued

400

Invalid status, or the batch is already cancelled

{
  "errors": [
    {
      "field": "batch_id",
      "help": null,
      "message": "The scheduled send already has a status"
    }
  ]
}
401

Unauthorized

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "Unauthorized"
    }
  ]
}
404

Batch not found - unknown, expired, or not a batch of this key

{
  "errors": [
    {
      "field": "batch_id",
      "help": null,
      "message": "Batch not found"
    }
  ]
}
503

The cancellation could not be queued, try again

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "Unable to queue the cancellation, please try again"
    }
  ]
}

Code Samples

curl -X PATCH 'https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -d '{"status": "string"}'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("PATCH", "https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}', {
  method: 'PATCH',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
  body: JSON.stringify({"status": "string"})
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"status": "string"}');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.patch(
    'https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
    json={"status": "string"}
)
print(response.json())
Response

                            
DELETE /user/scheduled_sends/{batch_id} Delete a cancellation or pause from a scheduled send ▼

Not supported: a cancelled batch has had its messages deleted, so the cancellation cannot be undone. The call answers 400 for a cancelled batch and 404 for any other.

Requires a sending (domain) API key. A batch belongs to the domain of the key that generated it; another key gets 404.

Responses

400

A cancellation cannot be removed

{
  "errors": [
    {
      "field": "batch_id",
      "help": null,
      "message": "A cancellation cannot be removed: the messages of the batch have been deleted"
    }
  ]
}
401

Unauthorized

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "Unauthorized"
    }
  ]
}
404

Batch not found, not cancelled, or not a batch of this key

{
  "errors": [
    {
      "field": "batch_id",
      "help": null,
      "message": "Batch not found"
    }
  ]
}

Code Samples

curl -X DELETE 'https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("DELETE", "https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}', {
  method: 'DELETE',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.delete(
    'https://sg-api.omnivery.net/v3/user/scheduled_sends/{batch_id}',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
)
print(response.json())
Response

                            

Account

GET /scopes Retrieve a list of scopes for which this user has access ▼

Returns the scopes of the API key used for the request. Libraries and plugins call it to check that a key may send mail.

  • A sending (domain) API key returns ["mail.send"].
  • A primary (account) API key returns user.account.read, user.profile.read, api_keys.create, api_keys.read and api_keys.delete.

Differences from SendGrid: the scopes are fixed by the key's role (there are no custom scope sets), and email validation is not advertised as a scope although a sending key can use it.

Responses

200

OK

{
  "scopes": [
    "mail.send"
  ]
}
401

Unauthorized

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "Unauthorized"
    }
  ]
}

Code Samples

curl -X GET 'https://sg-api.omnivery.net/v3/scopes' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("GET", "https://sg-api.omnivery.net/v3/scopes", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/scopes', {
  method: 'GET',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/scopes');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.get(
    'https://sg-api.omnivery.net/v3/scopes',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
)
print(response.json())
Response

                            
GET /user/account Get a user's account information ▼

Returns {"type": "paid"} or {"type": "free"} for the account of the API key. Primary (account) API key only.

Differences from SendGrid: reputation is not returned, Omnivery has no account-level reputation score (reputation is reported per domain in the app).

Responses

200

OK

{
  "type": "paid"
}
401

Unauthorized - the key is missing or not allowed to use this endpoint

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "Unauthorized - this endpoint requires a \"primary\" API credential role"
    }
  ]
}

Code Samples

curl -X GET 'https://sg-api.omnivery.net/v3/user/account' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("GET", "https://sg-api.omnivery.net/v3/user/account", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/user/account', {
  method: 'GET',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/user/account');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.get(
    'https://sg-api.omnivery.net/v3/user/account',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
)
print(response.json())
Response

                            
GET /user/profile Get a user's profile ▼

Returns the profile of the account of the API key, with SendGrid's fields. Primary (account) API key only.

Differences from SendGrid: first_name, last_name, address2, state and website are not stored by Omnivery and are returned as empty strings; country is the ISO 3166-1 alpha-2 code. zip is always a string.

Responses

200

OK

{
  "address": "Main 1",
  "address2": "",
  "city": "Praha",
  "company": "Alpha s.r.o.",
  "country": "CZ",
  "first_name": "",
  "last_name": "",
  "phone": "+420111222333",
  "state": "",
  "website": "",
  "zip": "01008"
}
401

Unauthorized - the key is missing or not allowed to use this endpoint

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "Unauthorized - this endpoint requires a \"primary\" API credential role"
    }
  ]
}

Code Samples

curl -X GET 'https://sg-api.omnivery.net/v3/user/profile' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("GET", "https://sg-api.omnivery.net/v3/user/profile", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/user/profile', {
  method: 'GET',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/user/profile');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.get(
    'https://sg-api.omnivery.net/v3/user/profile',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
)
print(response.json())
Response

                            

API Keys

GET /api_keys Retrieve all API Keys belonging to the authenticated user ▼

Lists the sending API keys of the account's domains as {"result": [{"api_key_id", "name"}]}. Primary (account) API key only. The key values are never returned.

Differences from SendGrid: an Omnivery API key belongs to one sending domain, so the list holds the domain (sending) keys; account (primary) keys and webhook signing keys are not listed. api_key_id is the key's numeric id as a string.

Responses

200

OK

{
  "result": [
    {
      "api_key_id": "1234",
      "name": "Shop mailer"
    }
  ]
}
401

Unauthorized - the key is missing or not allowed to use this endpoint

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "Unauthorized - this endpoint requires a \"primary\" API credential role"
    }
  ]
}

Code Samples

curl -X GET 'https://sg-api.omnivery.net/v3/api_keys' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("GET", "https://sg-api.omnivery.net/v3/api_keys", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/api_keys', {
  method: 'GET',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/api_keys');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.get(
    'https://sg-api.omnivery.net/v3/api_keys',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
)
print(response.json())
Response

                            
POST /api_keys Create API keys ▼

Creates a sending API key and returns it. The api_key is shown only in this response. Primary (account) API key only.

Differences from SendGrid: a key belongs to one sending domain of your account, so the request must carry domain. scopes is accepted and ignored: the key is a sending key with the scope mail.send.

Request Body application/json

FieldTypeRequiredDescription
domain string Yes

Sending domain of your account the key is bound to. Omnivery-specific, required.

name string Yes

Name of the key

scopes array No

Accepted and ignored.

Responses

201

Created

{
  "api_key": "0123456789abcdef0123456789abcdef",
  "api_key_id": "1234",
  "name": "Shop mailer",
  "scopes": [
    "mail.send"
  ]
}
400

Bad Request - name or domain is missing, or the domain is not available

{
  "errors": [
    {
      "field": "domain",
      "help": null,
      "message": "domain is required: an Omnivery API key belongs to one sending domain"
    }
  ]
}
401

Unauthorized - the key is missing or not allowed to use this endpoint

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "Unauthorized - this endpoint requires a \"primary\" API credential role"
    }
  ]
}

Code Samples

curl -X POST 'https://sg-api.omnivery.net/v3/api_keys' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -d '{"domain": "string", "name": "string", "scopes": []}'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("POST", "https://sg-api.omnivery.net/v3/api_keys", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/api_keys', {
  method: 'POST',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
  body: JSON.stringify({"domain": "string", "name": "string", "scopes": []})
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/api_keys');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"domain": "string", "name": "string", "scopes": []}');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.post(
    'https://sg-api.omnivery.net/v3/api_keys',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
    json={"domain": "string", "name": "string", "scopes": []}
)
print(response.json())
Response

                            
GET /api_keys/{api_key_id} Retrieve an existing API Key ▼

Returns the key as {"result": [{"api_key_id", "name", "scopes"}]}: result is an array, as in SendGrid. Primary (account) API key only. A key of another account, an account (primary) key or an unknown id answers 404.

Responses

200

OK

{
  "result": [
    {
      "api_key_id": "1234",
      "name": "Shop mailer",
      "scopes": [
        "mail.send"
      ]
    }
  ]
}
401

Unauthorized - the key is missing or not allowed to use this endpoint

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "Unauthorized - this endpoint requires a \"primary\" API credential role"
    }
  ]
}
404

Not Found

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "API key not found"
    }
  ]
}

Code Samples

curl -X GET 'https://sg-api.omnivery.net/v3/api_keys/{api_key_id}' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("GET", "https://sg-api.omnivery.net/v3/api_keys/{api_key_id}", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/api_keys/{api_key_id}', {
  method: 'GET',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/api_keys/{api_key_id}');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.get(
    'https://sg-api.omnivery.net/v3/api_keys/{api_key_id}',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
)
print(response.json())
Response

                            
DELETE /api_keys/{api_key_id} Delete API keys ▼

Deletes the key. Primary (account) API key only. A key of another account, an account (primary) key or an unknown id answers 404 and nothing is deleted.

Differences from SendGrid: updating a key (PUT / PATCH) is not available.

Responses

204

No Content

401

Unauthorized - the key is missing or not allowed to use this endpoint

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "Unauthorized - this endpoint requires a \"primary\" API credential role"
    }
  ]
}
404

Not Found

{
  "errors": [
    {
      "field": null,
      "help": null,
      "message": "API key not found"
    }
  ]
}

Code Samples

curl -X DELETE 'https://sg-api.omnivery.net/v3/api_keys/{api_key_id}' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN'
package main

import (
    "fmt"
    "net/http"
)

func main() {
    req, _ := http.NewRequest("DELETE", "https://sg-api.omnivery.net/v3/api_keys/{api_key_id}", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN")
    resp, _ := http.DefaultClient.Do(req)
    fmt.Println(resp.Status)
}
const response = await fetch('https://sg-api.omnivery.net/v3/api_keys/{api_key_id}', {
  method: 'DELETE',
  headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
});
const data = await response.json();
<?php
$ch = curl_init('https://sg-api.omnivery.net/v3/api_keys/{api_key_id}');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer YOUR_TOKEN']);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import requests

response = requests.delete(
    'https://sg-api.omnivery.net/v3/api_keys/{api_key_id}',
    headers={'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN'},
)
print(response.json())
Response