# Mailvina > Mailvina is an email sending platform for developers: a REST API and SMTP relay for transactional and marketing email, with domain authentication (DKIM, SPF, DMARC), webhooks, open/click tracking, suppression lists, broadcasts, templates, analytics and IP warm-up. API base URL: https://api.mailvina.com. Authenticate with `Authorization: Bearer ` (keys start with `mv_`). Every page below is available as Markdown. The full documentation in one file: https://mailvina.com/llms-full.txt ## Getting started - [Introduction](https://mailvina.com/docs.md): What Mailvina is, how sending works end to end, and where to start. - [Quickstart](https://mailvina.com/docs/quickstart.md): Send your first email in under five minutes, before verifying a domain. - [Authentication](https://mailvina.com/docs/authentication.md): Authenticate API requests with a bearer API key, and choose the right key permission. ## Guides - [Sending email](https://mailvina.com/docs/send-email.md): Send, batch, schedule and cancel emails; attachments, tags, headers, idempotency and test addresses. - [Domains](https://mailvina.com/docs/domains.md): Add and verify a sending domain with DKIM, SPF and MX records, set up a custom tracking subdomain, and choose TLS. - [API keys](https://mailvina.com/docs/api-keys.md): Create, scope and rotate API keys. - [SMTP relay](https://mailvina.com/docs/smtp.md): Send through Mailvina from any app or framework that speaks SMTP. - [Webhooks](https://mailvina.com/docs/webhooks.md): Receive signed delivery and engagement events, verify signatures and handle retries. - [Templates](https://mailvina.com/docs/templates.md): Store reusable subjects and HTML with variables, then send by ID or alias. - [Broadcasts](https://mailvina.com/docs/broadcasts.md): Send one message to every subscribed contact in an audience, now or on a schedule. - [Audiences & contacts](https://mailvina.com/docs/audiences.md): Organise recipients into audiences for broadcasts, and keep subscription status in sync. - [Suppression list](https://mailvina.com/docs/suppressions.md): How hard bounces and spam complaints are suppressed automatically, and how to manage the list. - [Open & click tracking](https://mailvina.com/docs/tracking.md): How the open pixel and link rewriting work, their limits, and custom tracking subdomains. ## Deliverability - [IP warm-up](https://mailvina.com/docs/ip-warmup.md): How Mailvina warms up new sending IPs safely and automatically. - [Deliverability](https://mailvina.com/docs/deliverability.md): Authentication, bounces, complaints and reputation: what Mailvina does and what you should do. - [Analytics & logs](https://mailvina.com/docs/analytics.md): Delivery metrics, per-email event logs, and the metrics API. ## Reference - [Limits & errors](https://mailvina.com/docs/limits-and-errors.md): Rate limits, sending quotas, the error format and every error code. ## API reference - [Emails](https://mailvina.com/docs/api-reference/emails.md): Send single and batch emails, and retrieve, list, reschedule or cancel them. - [Domains](https://mailvina.com/docs/api-reference/domains.md): Add, verify, configure and remove sending domains. - [API keys](https://mailvina.com/docs/api-reference/api-keys.md): Create, list and delete API keys. Requires a `full` access key. - [Audiences & contacts](https://mailvina.com/docs/api-reference/audiences.md): Manage audiences and the contacts in them. - [Broadcasts](https://mailvina.com/docs/api-reference/broadcasts.md): Create, update, send and delete broadcasts. - [Templates](https://mailvina.com/docs/api-reference/templates.md): Create and manage reusable email templates. Look them up by ID or alias. - [Webhooks](https://mailvina.com/docs/api-reference/webhooks.md): Create and manage webhook endpoints. - [Suppressions](https://mailvina.com/docs/api-reference/suppressions.md): List, add and remove suppressed addresses. - [Metrics](https://mailvina.com/docs/api-reference/metrics.md): Aggregate sending metrics for your team. --- # Introduction > What Mailvina is, how sending works end to end, and where to start. Source: https://mailvina.com/docs · Base URL: https://api.mailvina.com Mailvina is an email platform for developers. Send transactional and marketing email through a REST API or SMTP. Mailvina queues, signs and delivers every message, then tells you what happened through webhooks, logs and analytics. ## How it works 1. **Add a domain** and publish its DKIM, SPF and MX records. One click on Cloudflare. See [Domains](https://mailvina.com/docs/domains). 2. **Create an API key** in the dashboard. See [API keys](https://mailvina.com/docs/api-keys). 3. **Send** with `POST /emails` or the [SMTP relay](https://mailvina.com/docs/smtp). Messages enter a durable queue and are delivered straight to the recipient’s mail server. 4. **Track** each email from `sent` to `delivered`, `opened` and `clicked`, or `bounced` and `complained`, using [webhooks](https://mailvina.com/docs/webhooks) or the dashboard. ## Base URL ``` https://api.mailvina.com ``` All requests use HTTPS and JSON. Authenticate with `Authorization: Bearer `. See [Authentication](https://mailvina.com/docs/authentication). ## What’s included | Area | What you get | | --- | --- | | Sending | [Email API](https://mailvina.com/docs/send-email) with batch, scheduling, attachments, templates and idempotency; [SMTP relay](https://mailvina.com/docs/smtp) | | Marketing | [Broadcasts](https://mailvina.com/docs/broadcasts) to [audiences](https://mailvina.com/docs/audiences) with one-click unsubscribe | | Visibility | [Webhooks](https://mailvina.com/docs/webhooks), [open & click tracking](https://mailvina.com/docs/tracking), [analytics](https://mailvina.com/docs/analytics) | | Deliverability | DKIM signing, aligned SPF, automatic [suppression list](https://mailvina.com/docs/suppressions), [IP warm-up](https://mailvina.com/docs/ip-warmup) | > **Note:** Using an AI assistant? Every docs page is available as Markdown: add `.md` to the URL, or use [/llms.txt](https://mailvina.com/llms.txt) and [/llms-full.txt](https://mailvina.com/llms-full.txt). --- # Quickstart > Send your first email in under five minutes, before verifying a domain. Source: https://mailvina.com/docs/quickstart · Base URL: https://api.mailvina.com · API reference: https://mailvina.com/docs/api-reference/emails#send-email You can send right away with the sandbox sender `onboarding@mailvina.com`. It delivers only to members of your team and to the reserved [test addresses](https://mailvina.com/docs/send-email#test-addresses). ## 1. Create an API key Open [Dashboard → API keys](https://mailvina.com/dashboard/api-keys), click **Create API key** and copy the token. It starts with `mv_` and is shown only once. Store it as an environment variable: ```bash export MAILVINA_API_KEY=mv_xxxxxxxxx ``` ## 2. Send an email cURL: ```bash curl -X POST 'https://api.mailvina.com/emails' \ -H "Authorization: Bearer $MAILVINA_API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "from": "Mailvina ", "to": ["delivered@mailvina.com"], "subject": "Hello world", "html": "

It works!

" }' ``` Node.js: ```js const res = await fetch('https://api.mailvina.com/emails', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ from: 'Mailvina ', to: ['delivered@mailvina.com'], subject: 'Hello world', html: '

It works!

', }), }); console.log(await res.json()); // { id: '…' } ``` Python: ```python import os, requests res = requests.post( "https://api.mailvina.com/emails", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "from": "Mailvina ", "to": ["delivered@mailvina.com"], "subject": "Hello world", "html": "

It works!

", }, ) print(res.json()) ``` PHP: ```php true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . getenv('MAILVINA_API_KEY'), 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => json_encode([ 'from' => 'Mailvina ', 'to' => ['delivered@mailvina.com'], 'subject' => 'Hello world', 'html' => '

It works!

', ]), ]); echo curl_exec($ch); ``` ```json { "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" } ``` ## 3. See what happened Open [Dashboard → Emails](https://mailvina.com/dashboard/emails) to see the event timeline, or fetch it with [`GET /emails/:id`](https://mailvina.com/docs/api-reference/emails#retrieve-email). ## Next steps - [Verify your domain](https://mailvina.com/docs/domains) to send to anyone - [Set up webhooks](https://mailvina.com/docs/webhooks) to react to bounces and complaints - [Use templates](https://mailvina.com/docs/templates) to keep HTML out of your code --- # Authentication > Authenticate API requests with a bearer API key, and choose the right key permission. Source: https://mailvina.com/docs/authentication · Base URL: https://api.mailvina.com · API reference: https://mailvina.com/docs/api-reference/api-keys Every API request must include your API key in the `Authorization` header: ``` Authorization: Bearer mv_xxxxxxxxxxxxxxxx ``` Requests without a valid key get `401 missing_api_key` or `401 invalid_api_key`. ## Key permissions | Permission | Can do | | --- | --- | | `full` | Everything: send, and manage domains, audiences, templates, broadcasts, webhooks and API keys. | | `sending_access` | Only `POST /emails` and `POST /emails/batch`. Use this in application servers. | A key can also be limited to one domain with `domain_id`. Sending from any other domain then fails with `403 restricted_api_key`. ## Keeping keys safe - Store keys in environment variables or a secrets manager, never in source control. - Never call the API from a browser or mobile app: the key would be public. - Use one key per environment, so you can revoke one without affecting the others. --- # Sending email > Send, batch, schedule and cancel emails; attachments, tags, headers, idempotency and test addresses. Source: https://mailvina.com/docs/send-email · Base URL: https://api.mailvina.com · API reference: https://mailvina.com/docs/api-reference/emails ## Basic send cURL: ```bash curl -X POST 'https://api.mailvina.com/emails' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "from": "Acme ", "to": ["ada@example.com"], "subject": "Welcome to Acme", "html": "

Hi Ada!

" }' ``` Node.js: ```js await fetch('https://api.mailvina.com/emails', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ from: 'Acme ', to: ['ada@example.com'], subject: 'Welcome to Acme', html: '

Hi Ada!

' }), }); ``` The response is `{ "id": "…" }`. The email is queued immediately; delivery usually takes a few seconds. ## Test addresses These reserved recipients simulate outcomes without sending anything. They work with any sender, including the sandbox. | Address | Outcome | | --- | --- | | `delivered@mailvina.com` | `email.delivered` | | `bounced@mailvina.com` | `email.bounced` (permanent) | | `complained@mailvina.com` | Delivered, then `email.complained` | | `delayed@mailvina.com` | `email.delivery_delayed`, then delivered on retry | | `suppressed@mailvina.com` | `email.suppressed` | ## Batch sending Send up to 100 different emails in one request with `POST /emails/batch`. The body is an array of email objects; the response lists IDs in the same order. Validation stops at the first invalid email. ## Scheduling Pass `scheduled_at` as an ISO 8601 timestamp up to 30 days ahead. Reschedule with `PATCH /emails/:id`, or cancel with `POST /emails/:id/cancel` while it is still `scheduled` or `queued`. ```json { "from": "Acme ", "to": "ada@example.com", "subject": "Your trial ends tomorrow", "html": "

…

", "scheduled_at": "2026-12-01T09:00:00Z" } ``` ## Attachments Base64-encode file contents. The total size of an email, attachments included, can be up to 40 MB. ```json { "attachments": [ { "filename": "invoice.pdf", "content": "JVBERi0xLjcK…", "content_type": "application/pdf" } ] } ``` ## Idempotency Send an `Idempotency-Key` header (any unique string, such as an order ID). Repeating the request with the same key within 24 hours returns the original email ID instead of sending a duplicate. For batches, each item gets `:`. ## Tags and custom headers `tags` are `{ name, value }` pairs echoed in every webhook for that email, so you can tie events back to your own records. `headers` adds custom MIME headers. ## Email lifecycle | Status | Meaning | | --- | --- | | `scheduled` | Waiting for `scheduled_at` | | `queued` | Accepted and waiting for a sending slot | | `sending` | Being delivered | | `deferred` | Temporary failure; retrying with backoff for up to 48 hours | | `delivered` | Accepted by the recipient’s mail server | | `bounced` | Permanently rejected | | `suppressed` | Skipped because the address is on your suppression list | | `failed` | Could not be sent (e.g. domain suspended) | | `canceled` | Canceled before sending | --- # Domains > Add and verify a sending domain with DKIM, SPF and MX records, set up a custom tracking subdomain, and choose TLS. Source: https://mailvina.com/docs/domains · Base URL: https://api.mailvina.com · API reference: https://mailvina.com/docs/api-reference/domains To send to anyone, verify the domain in your `from` address. Use a subdomain such as `mail.example.com` so marketing or transactional reputation stays separate from your main domain. ## Records | Type | Name | Value | Purpose | | --- | --- | --- | --- | | TXT | `mailvina._domainkey.mail.example.com` | `p=MIIBIjAN…` | DKIM public key. Signs every email. | | TXT | `send.mail.example.com` | `v=spf1 include:spf.mailvina.com ~all` | SPF for the return-path. | | MX | `send.mail.example.com` | `feedback-smtp.mailvina.com` (priority 10) | Routes bounces and complaints back to Mailvina. | | TXT | `_dmarc.mail.example.com` | `v=DMARC1; p=none;` | Recommended. Tells inboxes how to treat unauthenticated mail. | Exact values are shown in the dashboard and returned by `GET /domains/:id`. > **Note:** Most DNS providers want the name **relative to the zone**. For `mail.example.com` in the `example.com` zone, enter `mailvina._domainkey.mail` and `send.mail`. The dashboard shows the right names for your zone, including public suffixes like `us.cc` or `co.uk`. ## One-click setup If your domain is on Cloudflare, click **Auto configure** on the domain page. You create a DNS-scoped Cloudflare token from a pre-filled link and paste it once. Mailvina adds every record and verifies the domain. It never overwrites an existing DMARC policy and doesn’t store the token. ## Add and verify with the API ```bash curl -X POST 'https://api.mailvina.com/domains' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "name": "mail.example.com" }' ``` Publish the records, then call `POST /domains/:id/verify`. Pending domains are also re-checked every five minutes. DNS changes usually appear within minutes but can take up to 72 hours. ## Custom tracking subdomain By default, tracked links and the open pixel use Mailvina’s domain. On the domain’s **Configuration** tab you can add a subdomain such as `links.mail.example.com` so links match your brand, which also helps deliverability: 1. Choose the subdomain (default `links`) and which tracking to enable. 2. Add a `CNAME` from that subdomain to `track.mailvina.com`. 3. Mailvina detects the record, issues an HTTPS certificate and switches new emails to it automatically. > **Warning:** On Cloudflare, set the tracking record to **DNS only** (grey cloud). With the proxy on, the certificate can’t be issued, and the dashboard tells you to turn it off. ## TLS **Opportunistic** (default) always tries an encrypted connection to the recipient’s server and falls back to plain text if the server doesn’t support TLS. **Enforced** only delivers over TLS and bounces the email otherwise. Change it on the Configuration tab or with `PATCH /domains/:id`. ## Domain status | Status | Meaning | | --- | --- | | `pending` | Records not found yet | | `partially_verified` | Some required records found | | `verified` | Ready to send | | `failed` | Records were removed or are wrong; sending is blocked | --- # API keys > Create, scope and rotate API keys. Source: https://mailvina.com/docs/api-keys · Base URL: https://api.mailvina.com · API reference: https://mailvina.com/docs/api-reference/api-keys Create keys in [the dashboard](https://mailvina.com/dashboard/api-keys) or with `POST /api-keys`. The token is shown only once; Mailvina stores only a SHA-256 hash of it. ## Scoping - `permission: "sending_access"` limits a key to sending email. - `domain_id` limits a key to one sending domain. ## Rotating a key 1. Create a new key. 2. Deploy it to your application. 3. Delete the old key with `DELETE /api-keys/:id`. It stops working immediately. The dashboard shows when each key was last used, which helps find keys that are safe to remove. --- # SMTP relay > Send through Mailvina from any app or framework that speaks SMTP. Source: https://mailvina.com/docs/smtp · Base URL: https://api.mailvina.com Use SMTP when your software can’t call an HTTP API, such as WordPress, Laravel, Django or a CMS. The password is an API key. | Setting | Value | | --- | --- | | Host | `smtp.mailvina.com` | | Port | `587` (STARTTLS) or `465` (implicit TLS) | | Username | `mailvina` (any value works) | | Password | Your API key (`mv_…`) | Nodemailer: ```js import nodemailer from 'nodemailer'; const transport = nodemailer.createTransport({ host: 'smtp.mailvina.com', port: 587, auth: { user: 'mailvina', pass: process.env.MAILVINA_API_KEY }, }); await transport.sendMail({ from: 'Acme ', to: 'ada@example.com', subject: 'Hi', html: '

Hello

', }); ``` Laravel .env: ```bash MAIL_MAILER=smtp MAIL_HOST=smtp.mailvina.com MAIL_PORT=587 MAIL_USERNAME=mailvina MAIL_PASSWORD=mv_xxxxxxxxx MAIL_ENCRYPTION=tls ``` Django: ```python EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" EMAIL_HOST = "smtp.mailvina.com" EMAIL_PORT = 587 EMAIL_USE_TLS = True EMAIL_HOST_USER = "mailvina" EMAIL_HOST_PASSWORD = os.environ["MAILVINA_API_KEY"] ``` SMTP mail goes through the same queue, suppression list, tracking, quotas and webhooks as the API. `Bcc` recipients are supported. --- # Webhooks > Receive signed delivery and engagement events, verify signatures and handle retries. Source: https://mailvina.com/docs/webhooks · Base URL: https://api.mailvina.com · API reference: https://mailvina.com/docs/api-reference/webhooks Register an HTTPS endpoint and choose which events it receives. Each event is delivered as a JSON `POST`. ## Events | Event | When | | --- | --- | | `email.sent` | Accepted by Mailvina and queued | | `email.scheduled` | Accepted for future delivery | | `email.delivered` | Accepted by the recipient’s mail server | | `email.delivery_delayed` | Temporary failure; Mailvina is retrying | | `email.bounced` | Permanently rejected (the address is suppressed) | | `email.complained` | The recipient marked it as spam | | `email.opened` | The tracking pixel loaded | | `email.clicked` | A tracked link was clicked | | `email.suppressed` | Skipped because the address is suppressed | | `email.failed` | Could not be sent | | `domain.created` · `domain.updated` · `domain.deleted` | Domain changes, including verification | | `contact.created` · `contact.updated` · `contact.deleted` | Audience contact changes | ## Payload ```json { "type": "email.bounced", "created_at": "2026-11-14T10:00:00.000Z", "data": { "email_id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794", "from": "Acme ", "to": [ "ada@example.com" ], "subject": "Welcome", "recipient": "ada@example.com", "tags": [ { "name": "category", "value": "welcome" } ], "bounced": { "type": "permanent", "message": "550 5.1.1 Mailbox does not exist" } } } ``` ## Verify signatures Every request has `mailvina-id`, `mailvina-timestamp` and `mailvina-signature` headers. The signature is `v1,` followed by the base64 HMAC-SHA256 of `{id}.{timestamp}.{raw body}`, keyed with your signing secret (base64-decoded, without the `whsec_` prefix). Always use the raw request body, not re-serialized JSON. Node.js: ```js import crypto from 'node:crypto'; export function verify(rawBody, headers, secret) { const id = headers['mailvina-id']; const ts = headers['mailvina-timestamp']; if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; // replay protection const key = Buffer.from(secret.replace('whsec_', ''), 'base64'); const expected = 'v1,' + crypto.createHmac('sha256', key).update(`${id}.${ts}.${rawBody}`).digest('base64'); const got = headers['mailvina-signature'] || ''; return got.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(got)); } ``` Python: ```python import base64, hashlib, hmac, time def verify(raw_body: bytes, headers, secret: str) -> bool: msg_id, ts = headers["mailvina-id"], headers["mailvina-timestamp"] if abs(time.time() - int(ts)) > 300: return False # replay protection key = base64.b64decode(secret.removeprefix("whsec_")) signed = f"{msg_id}.{ts}.".encode() + raw_body expected = "v1," + base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode() return hmac.compare_digest(expected, headers.get("mailvina-signature", "")) ``` ## Retries Respond with any `2xx` within 10 seconds. Failed deliveries are retried after 30 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours and 10 hours. Events can arrive more than once or out of order, so make your handler idempotent (for example, keyed on `mailvina-id`). --- # Templates > Store reusable subjects and HTML with variables, then send by ID or alias. Source: https://mailvina.com/docs/templates · Base URL: https://api.mailvina.com · API reference: https://mailvina.com/docs/api-reference/templates A template holds a subject, HTML and optional text, plus a default sender. Variables are filled at send time. | Syntax | Output | | --- | --- | | `{{name}}` | HTML-escaped value | | `{{name\|there}}` | HTML-escaped value, or `there` when missing | | `{{{name}}}` | Raw value (only for trusted HTML) | | `{{{name\|there}}}` | Raw value, or `there` when missing | ```bash curl -X POST 'https://api.mailvina.com/templates' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "name": "Welcome", "alias": "welcome-email", "subject": "Welcome, {{name|friend}}!", "html": "

Hi {{name|there}}

" }' ``` Send with a template by passing its ID or alias. `subject` and `html` come from the template unless you override them: ```json { "from": "Acme ", "to": "ada@example.com", "template": { "id": "welcome-email", "variables": { "name": "Ada" } } } ``` --- # Broadcasts > Send one message to every subscribed contact in an audience, now or on a schedule. Source: https://mailvina.com/docs/broadcasts · Base URL: https://api.mailvina.com · API reference: https://mailvina.com/docs/api-reference/broadcasts Broadcasts are for newsletters and announcements. Create a draft, then send it immediately or schedule it. ```bash curl -X POST 'https://api.mailvina.com/broadcasts' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "name": "November update", "audience_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf", "from": "Acme ", "subject": "What’s new, {{{FIRST_NAME|friend}}}", "html": "

Big news…

", "send": true }' ``` ## Personalization Use `{{{FIRST_NAME}}}`, `{{{LAST_NAME}}}`, `{{{EMAIL}}}` and `{{{MAILVINA_UNSUBSCRIBE_URL}}}`, with optional fallbacks like `{{{FIRST_NAME|there}}}`. ## Unsubscribes Every broadcast email has `List-Unsubscribe` and `List-Unsubscribe-Post` headers (one-click unsubscribe, required by Gmail and Yahoo). If your HTML has no unsubscribe link, a footer link is added. Unsubscribed contacts and suppressed addresses are skipped automatically. --- # Audiences & contacts > Organise recipients into audiences for broadcasts, and keep subscription status in sync. Source: https://mailvina.com/docs/audiences · Base URL: https://api.mailvina.com · API reference: https://mailvina.com/docs/api-reference/audiences An audience is a list of contacts. A contact has an email, an optional first and last name, and an `unsubscribed` flag. ```bash curl -X POST 'https://api.mailvina.com/audiences/78261eea-8f8b-4381-83c6-79fa7120f1cf/contacts' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "email": "ada@example.com", "first_name": "Ada", "last_name": "Lovelace" }' ``` You can also paste or upload a CSV (`email,first_name,last_name`) in the dashboard. Contacts can be looked up and updated by ID or by email address. --- # Suppression list > How hard bounces and spam complaints are suppressed automatically, and how to manage the list. Source: https://mailvina.com/docs/suppressions · Base URL: https://api.mailvina.com · API reference: https://mailvina.com/docs/api-reference/suppressions Addresses that hard-bounce or complain are added to your team’s suppression list automatically. Later sends to them are skipped and reported as `email.suppressed`. This protects your sender reputation: repeatedly mailing invalid addresses is one of the fastest ways to land in spam. | Reason | Added when | | --- | --- | | `bounce` | A permanent (5xx) bounce | | `complaint` | A spam complaint from a feedback loop | | `manual` | You added it from the dashboard or API | Remove an address only if you are sure it is valid again, for example after the recipient fixed their mailbox. --- # Open & click tracking > How the open pixel and link rewriting work, their limits, and custom tracking subdomains. Source: https://mailvina.com/docs/tracking · Base URL: https://api.mailvina.com · API reference: https://mailvina.com/docs/api-reference/domains#update-domain Tracking is configured per domain. With **click tracking**, `http(s)` links are rewritten to pass through a signed redirect. With **open tracking**, a 1×1 transparent image is added. Both create events and webhooks. - Open counts are approximate. Apple Mail Privacy Protection and some security scanners load images automatically (over-counting); clients that block images under-count. - Unsubscribe links are never rewritten. - For sensitive transactional mail such as password resets, consider turning tracking off. Use a [custom tracking subdomain](https://mailvina.com/docs/domains#custom-tracking-subdomain) so tracked links show your own domain. --- # IP warm-up > How Mailvina warms up new sending IPs safely and automatically. Source: https://mailvina.com/docs/ip-warmup · Base URL: https://api.mailvina.com Mailbox providers have no history for a new IP address. Sending too much too soon gets mail throttled or blocked. Warm-up raises the daily limit step by step so providers learn to trust the IP. ## Default schedule | Day | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 14 | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | | Emails / day | 50 | 100 | 500 | 1k | 2.5k | 5k | 10k | 20k | 40k | 100k | ## How Mailvina manages it - **Adaptive:** the schedule moves to the next day only if you used at least half of the previous day’s allowance. - **Queue-aware:** mail above the daily cap stays queued and continues after the daily reset (UTC midnight). Nothing is dropped. - **Protected:** an IP pauses automatically when, over 24 hours and at least 100 sends, bounces exceed 8% or complaints exceed 0.3%. - **Graduation:** after the last step the IP becomes `active` with no cap. ## Tips - Send to your most engaged recipients first. - Keep daily volume steady. - Remove bounced and long-unengaged addresses before you start. Dedicated IPs are available on the Scale and Enterprise plans. --- # Deliverability > Authentication, bounces, complaints and reputation: what Mailvina does and what you should do. Source: https://mailvina.com/docs/deliverability · Base URL: https://api.mailvina.com ## Authentication Every message is DKIM-signed with your domain’s 2048-bit key. The return-path is `bounce+@send.`, so SPF aligns with your From domain, which is what DMARC checks. Add a DMARC record and you are compliant with Gmail’s and Yahoo’s bulk-sender rules. ## Bounces A **hard bounce** (5xx) is final: `email.bounced` fires and the address is suppressed. A **soft failure** (4xx or network error) emits `email.delivery_delayed` and is retried after 1 min, 5 min, 15 min, 1 h, 2 h, 4 h, 8 h and then every 12 h, for up to 48 hours. ## Complaints Spam reports sent by mailbox providers’ feedback loops arrive at Mailvina’s inbound server and produce `email.complained`. The address is suppressed. ## Reputation The dashboard scores your team, each domain and each sending IP from 0 to 100. Keep the bounce rate under 4% and the complaint rate under 0.1%. Teams above 10% bounces or 0.5% complaints over at least 100 sends are paused automatically to protect everyone on shared IPs. --- # Analytics & logs > Delivery metrics, per-email event logs, and the metrics API. Source: https://mailvina.com/docs/analytics · Base URL: https://api.mailvina.com · API reference: https://mailvina.com/docs/api-reference/metrics The [Metrics](https://mailvina.com/dashboard/metrics) page shows delivery, bounce, complaint, open and click rates over time. The [Emails](https://mailvina.com/dashboard/emails) page lists every message with its full event timeline, and can be filtered and exported to CSV. To pull the same numbers into your own dashboards, use `GET /metrics?days=30`. Email bodies are kept for 1 day on Free, 7 days on Pro, 30 days on Scale and 90 days on Enterprise (at least 3 days in every case). Events and metrics are kept longer. --- # Limits & errors > Rate limits, sending quotas, the error format and every error code. Source: https://mailvina.com/docs/limits-and-errors · Base URL: https://api.mailvina.com ## Rate limit 10 requests per second per API key. Above that you get `429 rate_limit_exceeded` with a `Retry-After: 1` header. Use `POST /emails/batch` to send many emails in one request. ## Quotas Monthly and daily sending quotas depend on your plan. Exceeding them returns `429 monthly_quota_exceeded` or `429 daily_quota_exceeded`. Your team admins get an email at 80% of the monthly quota. ## Error format ```json { "statusCode": 422, "name": "validation_error", "message": "Missing `to` field." } ``` | Status | name | Meaning | | --- | --- | --- | | 400 | `invalid_json` | The body is not valid JSON | | 401 | `missing_api_key` · `invalid_api_key` | Authentication failed | | 403 | `validation_error` · `restricted_api_key` · `sending_paused` · `domain_suspended` | Domain not verified, key scope, paused team or suspended domain | | 404 | `not_found` | Unknown resource or path | | 422 | `validation_error` | Invalid parameters; `message` says which | | 429 | `rate_limit_exceeded` · `daily_quota_exceeded` · `monthly_quota_exceeded` | Slow down, or upgrade your plan | | 500 | `internal_server_error` | Something went wrong on our side; retry with backoff | --- # Emails API > Send single and batch emails, and retrieve, list, reschedule or cancel them. Source: https://mailvina.com/docs/api-reference/emails · Base URL: https://api.mailvina.com · Guide: https://mailvina.com/docs/send-email ## Send email Sends an email. It is queued immediately and delivered within seconds, or at `scheduled_at`. `POST /emails` **Headers** | Name | Type | Required | Description | | --- | --- | --- | --- | | `Idempotency-Key` | `string` | no | Optional. Repeating a request with the same key within 24 hours returns the original email instead of sending again. | **Body parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `from` | `string` | yes | Sender, e.g. `"Acme "`. The domain must be verified (or use the sandbox sender). | | `to` | `string \| string[]` | yes | Recipient addresses. Up to 50 recipients across `to`, `cc` and `bcc`. | | `subject` | `string` | yes | Subject line. Optional when `template` provides one. | | `html` | `string` | no | HTML body. Either `html`, `text` or `template` is required. | | `text` | `string` | no | Plain-text body. Generated from `html` when omitted. | | `cc` | `string \| string[]` | no | Carbon-copy recipients. | | `bcc` | `string \| string[]` | no | Blind carbon-copy recipients. | | `reply_to` | `string \| string[]` | no | Reply-To address(es). | | `headers` | `object` | no | Custom MIME headers, e.g. `{ "X-Entity-Ref-ID": "123" }`. | | `attachments` | `object[]` | no | `{ filename, content, content_type }` with `content` base64-encoded. 40 MB per email in total. | | `tags` | `object[]` | no | `[{ name, value }]` pairs. Echoed in webhooks so you can correlate events. | | `scheduled_at` | `string` | no | ISO 8601 timestamp up to 30 days ahead. The email is created with status `scheduled`. | | `template` | `object` | no | `{ id, variables }`. `id` is a template ID or alias; its subject and HTML are used when you omit them. | **Request** ```bash curl -X POST 'https://api.mailvina.com/emails' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "from": "Acme ", "to": [ "ada@example.com" ], "subject": "Welcome to Acme", "html": "

Hi Ada!

", "tags": [ { "name": "category", "value": "welcome" } ] }' ``` ```js const res = await fetch('https://api.mailvina.com/emails', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "from": "Acme ", "to": [ "ada@example.com" ], "subject": "Welcome to Acme", "html": "

Hi Ada!

", "tags": [ { "name": "category", "value": "welcome" } ] }), }); const data = await res.json(); ``` ```python import os, requests res = requests.post( "https://api.mailvina.com/emails", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "from": "Acme ", "to": [ "ada@example.com" ], "subject": "Welcome to Acme", "html": "

Hi Ada!

", "tags": [ { "name": "category", "value": "welcome" } ] }, ) print(res.json()) ``` **Response** ```json { "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" } ``` ## Send batch emails Sends up to 100 different emails in one request. The body is an array of email objects with the same fields as **Send email**. `POST /emails/batch` Emails are created in order and validation stops at the first invalid one. `scheduled_at` and `attachments` are supported per email. **Request** ```bash curl -X POST 'https://api.mailvina.com/emails/batch' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '[ { "from": "Acme ", "to": [ "ada@example.com" ], "subject": "Hi Ada", "html": "

…

" }, { "from": "Acme ", "to": [ "alan@example.com" ], "subject": "Hi Alan", "html": "

…

" } ]' ``` ```js const res = await fetch('https://api.mailvina.com/emails/batch', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify([ { "from": "Acme ", "to": [ "ada@example.com" ], "subject": "Hi Ada", "html": "

…

" }, { "from": "Acme ", "to": [ "alan@example.com" ], "subject": "Hi Alan", "html": "

…

" } ]), }); const data = await res.json(); ``` ```python import os, requests res = requests.post( "https://api.mailvina.com/emails/batch", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json=[ { "from": "Acme ", "to": [ "ada@example.com" ], "subject": "Hi Ada", "html": "

…

" }, { "from": "Acme ", "to": [ "alan@example.com" ], "subject": "Hi Alan", "html": "

…

" } ], ) print(res.json()) ``` **Response** ```json { "data": [ { "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" }, { "id": "0c3a1b7e-2f4d-4b8a-9e61-5d2f8c7a9b10" } ] } ``` ## Retrieve email Returns one email with its current status and every event that happened to it. `GET /emails/:id` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The email ID. | **Request** ```bash curl -X GET 'https://api.mailvina.com/emails/:id' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/emails/:id', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/emails/:id", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "email", "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794", "from": "Acme ", "to": [ "ada@example.com" ], "cc": [], "bcc": [], "reply_to": [], "subject": "Welcome to Acme", "html": "

Hi Ada!

", "text": "Hi Ada!", "tags": [ { "name": "category", "value": "welcome" } ], "last_event": "delivered", "status": "delivered", "scheduled_at": null, "created_at": "2026-11-14T10:00:00.000Z", "broadcast_id": null, "events": [ { "type": "email.sent", "recipient": null, "created_at": "2026-11-14T10:00:00.000Z", "data": null }, { "type": "email.delivered", "recipient": "ada@example.com", "created_at": "2026-11-14T10:00:00.000Z", "data": { "response": "250 2.0.0 OK" } } ] } ``` ## List emails Lists your team’s emails, newest first. `GET /emails` **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `limit` | `number` | no | Results per page, 1–100. Default 20. | | `offset` | `number` | no | Number of results to skip. Default 0. | **Request** ```bash curl -X GET 'https://api.mailvina.com/emails?limit=20' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/emails?limit=20', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/emails?limit=20", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "list", "data": [ { "object": "email", "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794", "from": "Acme ", "to": [ "ada@example.com" ], "cc": [], "bcc": [], "reply_to": [], "subject": "Welcome to Acme", "html": "

Hi Ada!

", "text": "Hi Ada!", "tags": [ { "name": "category", "value": "welcome" } ], "last_event": "delivered", "status": "delivered", "scheduled_at": null, "created_at": "2026-11-14T10:00:00.000Z", "broadcast_id": null } ], "has_more": false } ``` ## Update email Reschedules an email that is still `scheduled`. `PATCH /emails/:id` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The email ID. | **Body parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `scheduled_at` | `string` | yes | New ISO 8601 send time. | **Request** ```bash curl -X PATCH 'https://api.mailvina.com/emails/:id' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "scheduled_at": "2026-12-01T09:00:00Z" }' ``` ```js const res = await fetch('https://api.mailvina.com/emails/:id', { method: 'PATCH', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "scheduled_at": "2026-12-01T09:00:00Z" }), }); const data = await res.json(); ``` ```python import os, requests res = requests.patch( "https://api.mailvina.com/emails/:id", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "scheduled_at": "2026-12-01T09:00:00Z" }, ) print(res.json()) ``` **Response** ```json { "object": "email", "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" } ``` ## Cancel email Cancels an email that is `scheduled` or `queued`. Emails already being delivered can’t be canceled. `POST /emails/:id/cancel` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The email ID. | **Request** ```bash curl -X POST 'https://api.mailvina.com/emails/:id/cancel' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/emails/:id/cancel', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.post( "https://api.mailvina.com/emails/:id/cancel", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "email", "id": "49a3999c-0ce1-4ea6-ab68-afcd6dc2e794" } ``` --- # Domains API > Add, verify, configure and remove sending domains. Source: https://mailvina.com/docs/api-reference/domains · Base URL: https://api.mailvina.com · Guide: https://mailvina.com/docs/domains ## Create domain Adds a domain and generates its DKIM key. The response includes the DNS records to publish. `POST /domains` **Body parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `name` | `string` | yes | Domain or subdomain, e.g. `mail.example.com`. | **Request** ```bash curl -X POST 'https://api.mailvina.com/domains' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "name": "mail.acme.com" }' ``` ```js const res = await fetch('https://api.mailvina.com/domains', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "name": "mail.acme.com" }), }); const data = await res.json(); ``` ```python import os, requests res = requests.post( "https://api.mailvina.com/domains", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "name": "mail.acme.com" }, ) print(res.json()) ``` **Response** ```json { "object": "domain", "id": "4dd369bc-aa82-4ff3-97de-514ae3000ee0", "name": "mail.acme.com", "status": "pending", "created_at": "2026-11-14T10:00:00.000Z", "open_tracking": true, "click_tracking": true, "tls": "opportunistic", "tracking_domain": null, "records": [ { "record": "DKIM", "type": "TXT", "name": "mailvina._domainkey.mail.acme.com", "value": "p=MIIBIjANBgkqh…", "status": "verified", "optional": false }, { "record": "SPF", "type": "TXT", "name": "send.mail.acme.com", "value": "v=spf1 include:spf.mailvina.com ~all", "status": "verified", "optional": false }, { "record": "MX", "type": "MX", "name": "send.mail.acme.com", "value": "feedback-smtp.mailvina.com", "priority": 10, "status": "verified", "optional": false }, { "record": "DMARC", "type": "TXT", "name": "_dmarc.mail.acme.com", "value": "v=DMARC1; p=none; rua=mailto:dmarc@mail.acme.com", "status": "not_started", "optional": true } ] } ``` ## List domains Lists all of your team’s domains with their records and status. `GET /domains` **Request** ```bash curl -X GET 'https://api.mailvina.com/domains' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/domains', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/domains", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "list", "data": [ { "object": "domain", "id": "4dd369bc-aa82-4ff3-97de-514ae3000ee0", "name": "mail.acme.com", "status": "verified", "created_at": "2026-11-14T10:00:00.000Z", "open_tracking": true, "click_tracking": true, "tls": "opportunistic", "tracking_domain": { "name": "links.mail.acme.com", "status": "active", "record": { "type": "CNAME", "name": "links.mail.acme.com", "value": "track.mailvina.com" } }, "records": [ { "record": "DKIM", "type": "TXT", "name": "mailvina._domainkey.mail.acme.com", "value": "p=MIIBIjANBgkqh…", "status": "verified", "optional": false }, { "record": "SPF", "type": "TXT", "name": "send.mail.acme.com", "value": "v=spf1 include:spf.mailvina.com ~all", "status": "verified", "optional": false }, { "record": "MX", "type": "MX", "name": "send.mail.acme.com", "value": "feedback-smtp.mailvina.com", "priority": 10, "status": "verified", "optional": false }, { "record": "DMARC", "type": "TXT", "name": "_dmarc.mail.acme.com", "value": "v=DMARC1; p=none; rua=mailto:dmarc@mail.acme.com", "status": "not_started", "optional": true } ] } ] } ``` ## Retrieve domain Returns one domain, its DNS records with their verification status, and its tracking subdomain. `GET /domains/:id` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The domain ID. | **Request** ```bash curl -X GET 'https://api.mailvina.com/domains/:id' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/domains/:id', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/domains/:id", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "domain", "id": "4dd369bc-aa82-4ff3-97de-514ae3000ee0", "name": "mail.acme.com", "status": "verified", "created_at": "2026-11-14T10:00:00.000Z", "open_tracking": true, "click_tracking": true, "tls": "opportunistic", "tracking_domain": { "name": "links.mail.acme.com", "status": "active", "record": { "type": "CNAME", "name": "links.mail.acme.com", "value": "track.mailvina.com" } }, "records": [ { "record": "DKIM", "type": "TXT", "name": "mailvina._domainkey.mail.acme.com", "value": "p=MIIBIjANBgkqh…", "status": "verified", "optional": false }, { "record": "SPF", "type": "TXT", "name": "send.mail.acme.com", "value": "v=spf1 include:spf.mailvina.com ~all", "status": "verified", "optional": false }, { "record": "MX", "type": "MX", "name": "send.mail.acme.com", "value": "feedback-smtp.mailvina.com", "priority": 10, "status": "verified", "optional": false }, { "record": "DMARC", "type": "TXT", "name": "_dmarc.mail.acme.com", "value": "v=DMARC1; p=none; rua=mailto:dmarc@mail.acme.com", "status": "not_started", "optional": true } ] } ``` ## Verify domain Checks DNS now instead of waiting for the next automatic check (every five minutes). `POST /domains/:id/verify` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The domain ID. | **Request** ```bash curl -X POST 'https://api.mailvina.com/domains/:id/verify' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/domains/:id/verify', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.post( "https://api.mailvina.com/domains/:id/verify", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "domain", "id": "4dd369bc-aa82-4ff3-97de-514ae3000ee0", "name": "mail.acme.com", "status": "verified", "created_at": "2026-11-14T10:00:00.000Z", "open_tracking": true, "click_tracking": true, "tls": "opportunistic", "tracking_domain": { "name": "links.mail.acme.com", "status": "active", "record": { "type": "CNAME", "name": "links.mail.acme.com", "value": "track.mailvina.com" } }, "records": [ { "record": "DKIM", "type": "TXT", "name": "mailvina._domainkey.mail.acme.com", "value": "p=MIIBIjANBgkqh…", "status": "verified", "optional": false }, { "record": "SPF", "type": "TXT", "name": "send.mail.acme.com", "value": "v=spf1 include:spf.mailvina.com ~all", "status": "verified", "optional": false }, { "record": "MX", "type": "MX", "name": "send.mail.acme.com", "value": "feedback-smtp.mailvina.com", "priority": 10, "status": "verified", "optional": false }, { "record": "DMARC", "type": "TXT", "name": "_dmarc.mail.acme.com", "value": "v=DMARC1; p=none; rua=mailto:dmarc@mail.acme.com", "status": "not_started", "optional": true } ] } ``` ## Update domain Changes tracking and TLS settings. Applies to emails sent after the change. `PATCH /domains/:id` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The domain ID. | **Body parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `open_tracking` | `boolean` | no | Add the open-tracking pixel. | | `click_tracking` | `boolean` | no | Rewrite links through the click-tracking redirect. | | `tls` | `'opportunistic' \| 'enforced'` | no | `enforced` bounces mail to servers without TLS. | **Request** ```bash curl -X PATCH 'https://api.mailvina.com/domains/:id' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "open_tracking": false, "click_tracking": true, "tls": "enforced" }' ``` ```js const res = await fetch('https://api.mailvina.com/domains/:id', { method: 'PATCH', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "open_tracking": false, "click_tracking": true, "tls": "enforced" }), }); const data = await res.json(); ``` ```python import os, requests res = requests.patch( "https://api.mailvina.com/domains/:id", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "open_tracking": False, "click_tracking": True, "tls": "enforced" }, ) print(res.json()) ``` **Response** ```json { "object": "domain", "id": "4dd369bc-aa82-4ff3-97de-514ae3000ee0", "name": "mail.acme.com", "status": "verified", "created_at": "2026-11-14T10:00:00.000Z", "open_tracking": false, "click_tracking": true, "tls": "enforced", "tracking_domain": { "name": "links.mail.acme.com", "status": "active", "record": { "type": "CNAME", "name": "links.mail.acme.com", "value": "track.mailvina.com" } }, "records": [ { "record": "DKIM", "type": "TXT", "name": "mailvina._domainkey.mail.acme.com", "value": "p=MIIBIjANBgkqh…", "status": "verified", "optional": false }, { "record": "SPF", "type": "TXT", "name": "send.mail.acme.com", "value": "v=spf1 include:spf.mailvina.com ~all", "status": "verified", "optional": false }, { "record": "MX", "type": "MX", "name": "send.mail.acme.com", "value": "feedback-smtp.mailvina.com", "priority": 10, "status": "verified", "optional": false }, { "record": "DMARC", "type": "TXT", "name": "_dmarc.mail.acme.com", "value": "v=DMARC1; p=none; rua=mailto:dmarc@mail.acme.com", "status": "not_started", "optional": true } ] } ``` ## Delete domain Removes the domain, its DKIM key and its tracking subdomain. DNS records at your provider are not touched. `DELETE /domains/:id` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The domain ID. | **Request** ```bash curl -X DELETE 'https://api.mailvina.com/domains/:id' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/domains/:id', { method: 'DELETE', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.delete( "https://api.mailvina.com/domains/:id", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "domain", "id": "4dd369bc-aa82-4ff3-97de-514ae3000ee0", "deleted": true } ``` --- # API keys API > Create, list and delete API keys. Requires a `full` access key. Source: https://mailvina.com/docs/api-reference/api-keys · Base URL: https://api.mailvina.com · Guide: https://mailvina.com/docs/api-keys ## Create API key Creates a key. The `token` is returned only in this response, so store it securely. `POST /api-keys` **Body parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `name` | `string` | yes | A name to recognise the key, e.g. `Production`. | | `permission` | `'full' \| 'sending_access'` | no | Default `full`. | | `domain_id` | `string` | no | Limit the key to one sending domain. | **Request** ```bash curl -X POST 'https://api.mailvina.com/api-keys' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "name": "Production", "permission": "sending_access" }' ``` ```js const res = await fetch('https://api.mailvina.com/api-keys', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "name": "Production", "permission": "sending_access" }), }); const data = await res.json(); ``` ```python import os, requests res = requests.post( "https://api.mailvina.com/api-keys", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "name": "Production", "permission": "sending_access" }, ) print(res.json()) ``` **Response** ```json { "id": "b6d24b8e-af0b-4c3c-be0c-359bbd97381e", "token": "mv_123456789_abcdefghijklmnop" } ``` ## List API keys Lists active keys. Tokens are never returned again. `GET /api-keys` **Request** ```bash curl -X GET 'https://api.mailvina.com/api-keys' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/api-keys', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/api-keys", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "list", "data": [ { "id": "b6d24b8e-af0b-4c3c-be0c-359bbd97381e", "name": "Production", "permission": "sending_access", "created_at": "2026-11-14T10:00:00.000Z", "last_used_at": "2026-11-14T10:00:00.000Z" } ] } ``` ## Delete API key Revokes a key. Requests using it fail immediately. `DELETE /api-keys/:id` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The API key ID. | **Request** ```bash curl -X DELETE 'https://api.mailvina.com/api-keys/:id' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/api-keys/:id', { method: 'DELETE', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.delete( "https://api.mailvina.com/api-keys/:id", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "id": "b6d24b8e-af0b-4c3c-be0c-359bbd97381e", "revoked": true } ``` --- # Audiences & contacts API > Manage audiences and the contacts in them. Source: https://mailvina.com/docs/api-reference/audiences · Base URL: https://api.mailvina.com · Guide: https://mailvina.com/docs/audiences ## Create audience Creates an empty audience. `POST /audiences` **Body parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `name` | `string` | yes | e.g. `Newsletter`. | **Request** ```bash curl -X POST 'https://api.mailvina.com/audiences' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "name": "Newsletter" }' ``` ```js const res = await fetch('https://api.mailvina.com/audiences', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "name": "Newsletter" }), }); const data = await res.json(); ``` ```python import os, requests res = requests.post( "https://api.mailvina.com/audiences", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "name": "Newsletter" }, ) print(res.json()) ``` **Response** ```json { "object": "audience", "id": "78261eea-8f8b-4381-83c6-79fa7120f1cf", "name": "Newsletter" } ``` ## List audiences Lists all audiences. `GET /audiences` **Request** ```bash curl -X GET 'https://api.mailvina.com/audiences' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/audiences', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/audiences", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "list", "data": [ { "object": "audience", "id": "78261eea-8f8b-4381-83c6-79fa7120f1cf", "name": "Newsletter", "created_at": "2026-11-14T10:00:00.000Z" } ] } ``` ## Retrieve audience Returns one audience. `GET /audiences/:id` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The audience ID. | **Request** ```bash curl -X GET 'https://api.mailvina.com/audiences/:id' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/audiences/:id', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/audiences/:id", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "audience", "id": "78261eea-8f8b-4381-83c6-79fa7120f1cf", "name": "Newsletter", "created_at": "2026-11-14T10:00:00.000Z" } ``` ## Delete audience Deletes the audience and all of its contacts. `DELETE /audiences/:id` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The audience ID. | **Request** ```bash curl -X DELETE 'https://api.mailvina.com/audiences/:id' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/audiences/:id', { method: 'DELETE', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.delete( "https://api.mailvina.com/audiences/:id", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "audience", "id": "78261eea-8f8b-4381-83c6-79fa7120f1cf", "deleted": true } ``` ## Create contact Adds a contact to an audience. `POST /audiences/:audience_id/contacts` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `audience_id` | `string` | yes | The audience ID. | **Body parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `email` | `string` | yes | Contact email. | | `first_name` | `string` | no | Used by `{{{FIRST_NAME}}}`. | | `last_name` | `string` | no | Used by `{{{LAST_NAME}}}`. | | `unsubscribed` | `boolean` | no | Default `false`. | **Request** ```bash curl -X POST 'https://api.mailvina.com/audiences/:audience_id/contacts' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "email": "ada@example.com", "first_name": "Ada", "last_name": "Lovelace" }' ``` ```js const res = await fetch('https://api.mailvina.com/audiences/:audience_id/contacts', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "email": "ada@example.com", "first_name": "Ada", "last_name": "Lovelace" }), }); const data = await res.json(); ``` ```python import os, requests res = requests.post( "https://api.mailvina.com/audiences/:audience_id/contacts", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "email": "ada@example.com", "first_name": "Ada", "last_name": "Lovelace" }, ) print(res.json()) ``` **Response** ```json { "object": "contact", "id": "e169aa45-1ecf-4183-9955-b1499d5701d3" } ``` ## List contacts Lists contacts, newest first. `GET /audiences/:audience_id/contacts` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `audience_id` | `string` | yes | The audience ID. | **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `limit` | `number` | no | 1–200. Default 50. | | `offset` | `number` | no | Default 0. | **Request** ```bash curl -X GET 'https://api.mailvina.com/audiences/:audience_id/contacts?limit=50' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/audiences/:audience_id/contacts?limit=50', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/audiences/:audience_id/contacts?limit=50", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "list", "data": [ { "object": "contact", "id": "e169aa45-1ecf-4183-9955-b1499d5701d3", "email": "ada@example.com", "first_name": "Ada", "last_name": "Lovelace", "unsubscribed": false, "created_at": "2026-11-14T10:00:00.000Z" } ] } ``` ## Retrieve contact Returns one contact, looked up by ID or email. `GET /audiences/:audience_id/contacts/:id_or_email` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `audience_id` | `string` | yes | The audience ID. | | `id_or_email` | `string` | yes | Contact ID or URL-encoded email. | **Request** ```bash curl -X GET 'https://api.mailvina.com/audiences/:audience_id/contacts/:id_or_email' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/audiences/:audience_id/contacts/:id_or_email', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/audiences/:audience_id/contacts/:id_or_email", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "contact", "id": "e169aa45-1ecf-4183-9955-b1499d5701d3", "email": "ada@example.com", "first_name": "Ada", "last_name": "Lovelace", "unsubscribed": false, "created_at": "2026-11-14T10:00:00.000Z" } ``` ## Update contact Updates a contact. Set `unsubscribed: true` to stop broadcasts to it. `PATCH /audiences/:audience_id/contacts/:id_or_email` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `audience_id` | `string` | yes | The audience ID. | | `id_or_email` | `string` | yes | Contact ID or URL-encoded email. | **Body parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `first_name` | `string` | no | | | `last_name` | `string` | no | | | `unsubscribed` | `boolean` | no | | **Request** ```bash curl -X PATCH 'https://api.mailvina.com/audiences/:audience_id/contacts/:id_or_email' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "unsubscribed": true }' ``` ```js const res = await fetch('https://api.mailvina.com/audiences/:audience_id/contacts/:id_or_email', { method: 'PATCH', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "unsubscribed": true }), }); const data = await res.json(); ``` ```python import os, requests res = requests.patch( "https://api.mailvina.com/audiences/:audience_id/contacts/:id_or_email", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "unsubscribed": True }, ) print(res.json()) ``` **Response** ```json { "object": "contact", "id": "e169aa45-1ecf-4183-9955-b1499d5701d3" } ``` ## Delete contact Removes a contact from the audience. `DELETE /audiences/:audience_id/contacts/:id_or_email` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `audience_id` | `string` | yes | The audience ID. | | `id_or_email` | `string` | yes | Contact ID or URL-encoded email. | **Request** ```bash curl -X DELETE 'https://api.mailvina.com/audiences/:audience_id/contacts/:id_or_email' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/audiences/:audience_id/contacts/:id_or_email', { method: 'DELETE', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.delete( "https://api.mailvina.com/audiences/:audience_id/contacts/:id_or_email", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "contact", "id": "e169aa45-1ecf-4183-9955-b1499d5701d3", "deleted": true } ``` --- # Broadcasts API > Create, update, send and delete broadcasts. Source: https://mailvina.com/docs/api-reference/broadcasts · Base URL: https://api.mailvina.com · Guide: https://mailvina.com/docs/broadcasts ## Create broadcast Creates a draft broadcast. Pass `send: true` to send it right away (or at `scheduled_at`). `POST /broadcasts` **Body parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `audience_id` | `string` | yes | Audience to send to. | | `from` | `string` | yes | Verified sender. | | `subject` | `string` | yes | Supports contact variables. | | `html` | `string` | yes | Supports contact variables and `{{{MAILVINA_UNSUBSCRIBE_URL}}}`. | | `text` | `string` | no | Plain-text version. | | `name` | `string` | no | Internal name. | | `reply_to` | `string` | no | Reply-To address. | | `send` | `boolean` | no | Send after creating. | | `scheduled_at` | `string` | no | ISO 8601; used with `send`. | **Request** ```bash curl -X POST 'https://api.mailvina.com/broadcasts' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "name": "November update", "audience_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf", "from": "Acme ", "subject": "What’s new", "html": "

Big news…

" }' ``` ```js const res = await fetch('https://api.mailvina.com/broadcasts', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "name": "November update", "audience_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf", "from": "Acme ", "subject": "What’s new", "html": "

Big news…

" }), }); const data = await res.json(); ``` ```python import os, requests res = requests.post( "https://api.mailvina.com/broadcasts", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "name": "November update", "audience_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf", "from": "Acme ", "subject": "What’s new", "html": "

Big news…

" }, ) print(res.json()) ``` **Response** ```json { "id": "559ac32e-9ef5-46fb-82a1-b76b840c0f7b" } ``` ## List broadcasts Lists broadcasts, newest first. `GET /broadcasts` **Request** ```bash curl -X GET 'https://api.mailvina.com/broadcasts' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/broadcasts', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/broadcasts", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "list", "data": [ { "object": "broadcast", "id": "559ac32e-9ef5-46fb-82a1-b76b840c0f7b", "name": "November update", "audience_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf", "from": "Acme ", "subject": "What’s new", "status": "sent", "total": 1240, "created_at": "2026-11-14T10:00:00.000Z", "sent_at": "2026-11-14T10:00:00.000Z" } ] } ``` ## Retrieve broadcast Returns one broadcast. `GET /broadcasts/:id` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The broadcast ID. | **Request** ```bash curl -X GET 'https://api.mailvina.com/broadcasts/:id' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/broadcasts/:id', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/broadcasts/:id", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "broadcast", "id": "559ac32e-9ef5-46fb-82a1-b76b840c0f7b", "name": "November update", "status": "draft", "total": 0 } ``` ## Update broadcast Updates a draft. Accepts the same fields as **Create broadcast**. `PATCH /broadcasts/:id` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The broadcast ID. | **Request** ```bash curl -X PATCH 'https://api.mailvina.com/broadcasts/:id' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "subject": "What’s new this month" }' ``` ```js const res = await fetch('https://api.mailvina.com/broadcasts/:id', { method: 'PATCH', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "subject": "What’s new this month" }), }); const data = await res.json(); ``` ```python import os, requests res = requests.patch( "https://api.mailvina.com/broadcasts/:id", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "subject": "What’s new this month" }, ) print(res.json()) ``` **Response** ```json { "id": "559ac32e-9ef5-46fb-82a1-b76b840c0f7b" } ``` ## Send broadcast Sends a draft now, or schedules it. `POST /broadcasts/:id/send` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The broadcast ID. | **Body parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `scheduled_at` | `string` | no | ISO 8601. Omit to send now. | **Request** ```bash curl -X POST 'https://api.mailvina.com/broadcasts/:id/send' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "scheduled_at": "2026-12-01T09:00:00Z" }' ``` ```js const res = await fetch('https://api.mailvina.com/broadcasts/:id/send', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "scheduled_at": "2026-12-01T09:00:00Z" }), }); const data = await res.json(); ``` ```python import os, requests res = requests.post( "https://api.mailvina.com/broadcasts/:id/send", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "scheduled_at": "2026-12-01T09:00:00Z" }, ) print(res.json()) ``` **Response** ```json { "id": "559ac32e-9ef5-46fb-82a1-b76b840c0f7b", "status": "scheduled", "total": 1240 } ``` ## Delete broadcast Deletes a draft. Sent broadcasts are kept. `DELETE /broadcasts/:id` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The broadcast ID. | **Request** ```bash curl -X DELETE 'https://api.mailvina.com/broadcasts/:id' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/broadcasts/:id', { method: 'DELETE', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.delete( "https://api.mailvina.com/broadcasts/:id", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "id": "559ac32e-9ef5-46fb-82a1-b76b840c0f7b", "deleted": true } ``` --- # Templates API > Create and manage reusable email templates. Look them up by ID or alias. Source: https://mailvina.com/docs/api-reference/templates · Base URL: https://api.mailvina.com · Guide: https://mailvina.com/docs/templates ## Create template Creates a template. `POST /templates` **Body parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `name` | `string` | yes | Internal name. | | `alias` | `string` | no | Stable identifier you can send with, e.g. `welcome-email`. | | `subject` | `string` | no | Supports variables. | | `html` | `string` | no | Supports variables. | | `text` | `string` | no | Plain-text version. | | `from` | `string` | no | Default sender. | | `reply_to` | `string` | no | Default Reply-To. | **Request** ```bash curl -X POST 'https://api.mailvina.com/templates' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "name": "Welcome", "alias": "welcome-email", "subject": "Welcome, {{name|friend}}!", "html": "

Hi {{name|there}}

" }' ``` ```js const res = await fetch('https://api.mailvina.com/templates', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "name": "Welcome", "alias": "welcome-email", "subject": "Welcome, {{name|friend}}!", "html": "

Hi {{name|there}}

" }), }); const data = await res.json(); ``` ```python import os, requests res = requests.post( "https://api.mailvina.com/templates", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "name": "Welcome", "alias": "welcome-email", "subject": "Welcome, {{name|friend}}!", "html": "

Hi {{name|there}}

" }, ) print(res.json()) ``` **Response** ```json { "object": "template", "id": "f3b9756c-f4f4-44da-bc00-9f7903c8a83f" } ``` ## List templates Lists templates, most recently updated first. `GET /templates` **Request** ```bash curl -X GET 'https://api.mailvina.com/templates' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/templates', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/templates", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "list", "data": [ { "object": "template", "id": "f3b9756c-f4f4-44da-bc00-9f7903c8a83f", "name": "Welcome", "alias": "welcome-email", "subject": "Welcome, {{name|friend}}!", "created_at": "2026-11-14T10:00:00.000Z", "updated_at": "2026-11-14T10:00:00.000Z" } ] } ``` ## Retrieve template Returns one template. `GET /templates/:id_or_alias` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id_or_alias` | `string` | yes | Template ID or alias. | **Request** ```bash curl -X GET 'https://api.mailvina.com/templates/:id_or_alias' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/templates/:id_or_alias', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/templates/:id_or_alias", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "template", "id": "f3b9756c-f4f4-44da-bc00-9f7903c8a83f", "name": "Welcome", "alias": "welcome-email", "subject": "Welcome, {{name|friend}}!", "html": "

Hi {{name|there}}

" } ``` ## Update template Updates any field of a template. `PATCH /templates/:id_or_alias` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id_or_alias` | `string` | yes | Template ID or alias. | **Request** ```bash curl -X PATCH 'https://api.mailvina.com/templates/:id_or_alias' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "subject": "Welcome aboard, {{name|friend}}!" }' ``` ```js const res = await fetch('https://api.mailvina.com/templates/:id_or_alias', { method: 'PATCH', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "subject": "Welcome aboard, {{name|friend}}!" }), }); const data = await res.json(); ``` ```python import os, requests res = requests.patch( "https://api.mailvina.com/templates/:id_or_alias", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "subject": "Welcome aboard, {{name|friend}}!" }, ) print(res.json()) ``` **Response** ```json { "object": "template", "id": "f3b9756c-f4f4-44da-bc00-9f7903c8a83f" } ``` ## Delete template Deletes a template. `DELETE /templates/:id_or_alias` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id_or_alias` | `string` | yes | Template ID or alias. | **Request** ```bash curl -X DELETE 'https://api.mailvina.com/templates/:id_or_alias' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/templates/:id_or_alias', { method: 'DELETE', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.delete( "https://api.mailvina.com/templates/:id_or_alias", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "template", "id": "f3b9756c-f4f4-44da-bc00-9f7903c8a83f", "deleted": true } ``` --- # Webhooks API > Create and manage webhook endpoints. Source: https://mailvina.com/docs/api-reference/webhooks · Base URL: https://api.mailvina.com · Guide: https://mailvina.com/docs/webhooks ## Create webhook Registers an endpoint. The signing `secret` is returned only in this response. `POST /webhooks` **Body parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `url` | `string` | yes | Public HTTPS URL. | | `events` | `string[]` | no | Event types to send. Empty or omitted means all events. | **Request** ```bash curl -X POST 'https://api.mailvina.com/webhooks' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "url": "https://acme.com/webhooks/mailvina", "events": [ "email.delivered", "email.bounced", "email.complained" ] }' ``` ```js const res = await fetch('https://api.mailvina.com/webhooks', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "url": "https://acme.com/webhooks/mailvina", "events": [ "email.delivered", "email.bounced", "email.complained" ] }), }); const data = await res.json(); ``` ```python import os, requests res = requests.post( "https://api.mailvina.com/webhooks", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "url": "https://acme.com/webhooks/mailvina", "events": [ "email.delivered", "email.bounced", "email.complained" ] }, ) print(res.json()) ``` **Response** ```json { "object": "webhook", "id": "a1f9c1d2-7d3e-4b8f-9a0c-2e5b6d7f8a90", "secret": "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw" } ``` ## List webhooks Lists your endpoints. `GET /webhooks` **Request** ```bash curl -X GET 'https://api.mailvina.com/webhooks' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/webhooks', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/webhooks", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "list", "data": [ { "object": "webhook", "id": "a1f9c1d2-7d3e-4b8f-9a0c-2e5b6d7f8a90", "url": "https://acme.com/webhooks/mailvina", "events": [ "email.delivered", "email.bounced" ], "active": true, "created_at": "2026-11-14T10:00:00.000Z" } ] } ``` ## Retrieve webhook Returns one endpoint. `GET /webhooks/:id` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The webhook ID. | **Request** ```bash curl -X GET 'https://api.mailvina.com/webhooks/:id' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/webhooks/:id', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/webhooks/:id", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "webhook", "id": "a1f9c1d2-7d3e-4b8f-9a0c-2e5b6d7f8a90", "url": "https://acme.com/webhooks/mailvina", "events": [], "active": true, "created_at": "2026-11-14T10:00:00.000Z" } ``` ## Update webhook Changes the URL or events, or pauses the endpoint. `PATCH /webhooks/:id` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The webhook ID. | **Body parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `url` | `string` | no | New URL. | | `events` | `string[]` | no | New event list. | | `active` | `boolean` | no | `false` pauses deliveries. | **Request** ```bash curl -X PATCH 'https://api.mailvina.com/webhooks/:id' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "active": false }' ``` ```js const res = await fetch('https://api.mailvina.com/webhooks/:id', { method: 'PATCH', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "active": false }), }); const data = await res.json(); ``` ```python import os, requests res = requests.patch( "https://api.mailvina.com/webhooks/:id", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "active": False }, ) print(res.json()) ``` **Response** ```json { "object": "webhook", "id": "a1f9c1d2-7d3e-4b8f-9a0c-2e5b6d7f8a90" } ``` ## Delete webhook Removes the endpoint. `DELETE /webhooks/:id` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `id` | `string` | yes | The webhook ID. | **Request** ```bash curl -X DELETE 'https://api.mailvina.com/webhooks/:id' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/webhooks/:id', { method: 'DELETE', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.delete( "https://api.mailvina.com/webhooks/:id", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "webhook", "id": "a1f9c1d2-7d3e-4b8f-9a0c-2e5b6d7f8a90", "deleted": true } ``` --- # Suppressions API > List, add and remove suppressed addresses. Source: https://mailvina.com/docs/api-reference/suppressions · Base URL: https://api.mailvina.com · Guide: https://mailvina.com/docs/suppressions ## List suppressions Lists up to 500 suppressed addresses, newest first. `GET /suppressions` **Request** ```bash curl -X GET 'https://api.mailvina.com/suppressions' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/suppressions', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/suppressions", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "object": "list", "data": [ { "email": "old@example.com", "reason": "bounce", "created_at": "2026-11-14T10:00:00.000Z" } ] } ``` ## Add suppression Suppresses an address so it never receives email from your team. `POST /suppressions` **Body parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `email` | `string` | yes | Address to suppress. | **Request** ```bash curl -X POST 'https://api.mailvina.com/suppressions' \ -H 'Authorization: Bearer mv_xxxxxxxxx' \ -H 'Content-Type: application/json' \ -d '{ "email": "no-mail@example.com" }' ``` ```js const res = await fetch('https://api.mailvina.com/suppressions', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "email": "no-mail@example.com" }), }); const data = await res.json(); ``` ```python import os, requests res = requests.post( "https://api.mailvina.com/suppressions", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, json={ "email": "no-mail@example.com" }, ) print(res.json()) ``` **Response** ```json { "email": "no-mail@example.com" } ``` ## Remove suppression Removes an address from the list so it can receive email again. `DELETE /suppressions/:email` **Path parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `email` | `string` | yes | URL-encoded address. | **Request** ```bash curl -X DELETE 'https://api.mailvina.com/suppressions/:email' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/suppressions/:email', { method: 'DELETE', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.delete( "https://api.mailvina.com/suppressions/:email", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "deleted": true } ``` --- # Metrics API > Aggregate sending metrics for your team. Source: https://mailvina.com/docs/api-reference/metrics · Base URL: https://api.mailvina.com · Guide: https://mailvina.com/docs/analytics ## Get metrics Returns totals and rates for the period, plus one row per day. `GET /metrics` **Query parameters** | Name | Type | Required | Description | | --- | --- | --- | --- | | `days` | `number` | no | 1–90. Default 7. | **Request** ```bash curl -X GET 'https://api.mailvina.com/metrics?days=30' \ -H 'Authorization: Bearer mv_xxxxxxxxx' ``` ```js const res = await fetch('https://api.mailvina.com/metrics?days=30', { method: 'GET', headers: { Authorization: `Bearer ${process.env.MAILVINA_API_KEY}`, }, }); const data = await res.json(); ``` ```python import os, requests res = requests.get( "https://api.mailvina.com/metrics?days=30", headers={"Authorization": f"Bearer {os.environ['MAILVINA_API_KEY']}"}, ) print(res.json()) ``` **Response** ```json { "summary": { "total": 1200, "delivered": 1180, "bounced": 12, "complained": 1, "opened": 640, "clicked": 210, "queued": 0, "failed": 0, "deliveryRate": 98.99, "bounceRate": 1.01, "complaintRate": 0.08, "openRate": 54.24, "clickRate": 17.8 }, "daily": [ { "date": "2026-11-14", "total": 100, "delivered": 98, "bounced": 2, "complained": 0, "opened": 51, "clicked": 17 } ] } ```