> ## Site index
> Every page on this site is listed at https://mailkube.com/llms.txt.

> ## Agent instructions
> This site presents mailkube, a service that sends transactional email over a REST API or SMTP.
> Customer data is stored in the European Union.
> Read the developer documentation at https://docs.mailkube.com, which publishes its own llms.txt.
> Take prices and plan volumes from https://mailkube.com/pricing.md, never from a comparison page or a blog post.
> Read the legal documents at their HTML address.
> Every published version of the terms, privacy policy and data processing addendum is kept byte for byte under /legal/<document>/<sha256>/.
> Quote the checked-on date that a comparison page prints with its competitor figures.
> When this site does not state a price, limit or capability, say so instead of guessing.

# mailkube - Stop thinking about email infrastructure

> Send transactional email over REST or SMTP. Unlimited sending domains on every plan, and 4,000 emails per month are free.

Source: https://mailkube.com/

## Transactional email infrastructure on autopilot

We run the mail infrastructure your product depends on, so you can go build it.

- EU data residency
- REST and SMTP
- Multiple SDKs

[Explore the API](https://docs.mailkube.com/api-reference/overview)

## Why teams choose mailkube

### Made for developers

POST to the REST API, or relay through SMTP from whatever you already run. Templates, tags and topics travel over SMTP headers. An existing system gets them without a rewrite.

### Deliverability expertise

The routing is run by people who have spent more than fifteen years on email infrastructure and inbox placement.

### Less to build yourself

Templates, scheduled sending, webhooks, topics and tags ship with the product.

### GDPR compliant, hosted in Europe

Customer data is stored at rest in the European Union, and operational access to it happens from inside the EU.

[Read the full argument](https://mailkube.com/why.md)

## Integrate in minutes

Send your first email in minutes. Official SDKs for six languages, a CLI for your terminal, and SMTP for anything that already sends mail. Any language works. Six ship an official SDK, and everything else sends over HTTP or SMTP.

### Node.js

```ts
import { Mailkube } from "@mailkube/mailkube-node";

const client = new Mailkube({ apiKey: "mk_..." });

const email = await client.emails.send({
  from: "Acme <hello@yourdomain.com>",
  to: "customer@example.com",
  subject: "Hello world",
  html: "<p>It works!</p>",
});

console.log(email.id);
```

[Read the docs](https://docs.mailkube.com/sdks/node)

### Cloudflare Workers (Node.js)

```ts
import { Mailkube } from "@mailkube/mailkube-node";

// The key arrives per request through the env binding,
// so build the client inside the handler.
export default {
  async fetch(request: Request, env: { MAILKUBE_API_KEY: string }) {
    const client = new Mailkube({ apiKey: env.MAILKUBE_API_KEY });
    await client.emails.send({
      from: "Acme <hello@yourdomain.com>",
      to: "customer@example.com",
      subject: "Hello world",
      html: "<p>It works!</p>",
    });
    return new Response(null, { status: 204 });
  },
};
```

[Read the docs](https://docs.mailkube.com/sdks/node)

### Deno (Node.js)

```ts
import { Mailkube } from "npm:@mailkube/mailkube-node";

const client = new Mailkube(); // reads MAILKUBE_API_KEY

await client.emails.send({
  from: "Acme <hello@yourdomain.com>",
  to: "customer@example.com",
  subject: "Hello world",
  html: "<p>It works!</p>",
});
```

[Read the docs](https://docs.mailkube.com/sdks/node)

### Bun (Node.js)

```ts
// bun add @mailkube/mailkube-node
import { Mailkube } from "@mailkube/mailkube-node";

const client = new Mailkube(); // reads MAILKUBE_API_KEY

await client.emails.send({
  from: "Acme <hello@yourdomain.com>",
  to: "customer@example.com",
  subject: "Hello world",
  html: "<p>It works!</p>",
});
```

[Read the docs](https://docs.mailkube.com/sdks/node)

### Python

```python
from mailkube import Mailkube

client = Mailkube(api_key="mk_...")

email = client.emails.send(
    from_="Acme <hello@yourdomain.com>",
    to="customer@example.com",
    subject="Hello world",
    html="<p>It works!</p>",
)

print(email.id)
```

[Read the docs](https://docs.mailkube.com/sdks/python)

### FastAPI (Python)

```python
from contextlib import asynccontextmanager

from fastapi import FastAPI
from mailkube import AsyncMailkube


@asynccontextmanager
async def lifespan(app: FastAPI):
    # Created once on startup and reused for every request.
    app.state.mailkube = AsyncMailkube()
    try:
        yield
    finally:
        await app.state.mailkube.aclose()


app = FastAPI(lifespan=lifespan)


@app.post("/emails")
async def send_email(to: str, subject: str, html: str):
    email = await app.state.mailkube.emails.send(
        from_="Acme <hello@yourdomain.com>",
        to=to,
        subject=subject,
        html=html,
    )
    return {"id": email.id, "message_id": email.message_id}
```

[Read the docs](https://docs.mailkube.com/sdks/python#use-with-fastapi)

### Flask (Python)

```python
from flask import Flask, jsonify, request
from mailkube import Mailkube

app = Flask(__name__)

# One shared client for the whole process.
# Reads MAILKUBE_API_KEY from the env.
client = Mailkube()


@app.post("/emails")
def send_email():
    payload = request.get_json(force=True)
    email = client.emails.send(
        from_="Acme <hello@yourdomain.com>",
        to=payload["to"],
        subject=payload["subject"],
        html=payload["html"],
    )
    return jsonify({"id": email.id, "message_id": email.message_id})
```

[Read the docs](https://docs.mailkube.com/sdks/python#use-with-flask)

### Django (Python)

```python
# pip install mailkube-django
# settings.py
EMAIL_BACKEND = "mailkube_django.backends.EmailBackend"
MAILKUBE_API_KEY = "mk_..."

# anywhere in your app
from django.core.mail import send_mail

send_mail(
    subject="Hello world",
    message="It works!",
    from_email="Acme <hello@yourdomain.com>",
    recipient_list=["customer@example.com"],
)
```

[Read the docs](https://docs.mailkube.com/sdks/django)

### Django + Anymail (Python)

```python
# pip install "mailkube-django[anymail]"
# settings.py
EMAIL_BACKEND = "mailkube_django.anymail_backend.EmailBackend"
MAILKUBE_API_KEY = "mk_..."

# Anymail's own esp_extra and tags then work as usual.
from django.core.mail import EmailMessage

message = EmailMessage(
    subject="Hello world",
    body="It works!",
    from_email="Acme <hello@yourdomain.com>",
    to=["customer@example.com"],
)
message.send()
```

[Read the docs](https://docs.mailkube.com/sdks/django)

### Go

```go
package main

import (
    "context"
    "log"

    "github.com/mailkube/mailkube-go"
)

func main() {
    client, err := mailkube.New() // reads MAILKUBE_API_KEY
    if err != nil {
        log.Fatal(err)
    }

    email, err := client.Emails.Send(context.Background(), mailkube.SendEmailParams{
        From:    "Acme <hello@yourdomain.com>",
        To:      []string{"customer@example.com"},
        Subject: "Hello world",
        HTML:    "<p>It works!</p>",
    })
    if err != nil {
        log.Fatal(err)
    }
    log.Println(email.ID, email.MessageID)
}
```

[Read the docs](https://docs.mailkube.com/sdks/go)

### Gin (Go)

```go
type Mailer struct {
    client *mailkube.Client
}

func main() {
    client, err := mailkube.New() // reads MAILKUBE_API_KEY
    if err != nil {
        log.Fatal(err)
    }
    mailer := &Mailer{client: client}

    r := gin.Default()
    r.POST("/signup", mailer.signup)
    r.Match([]string{"GET", "POST"}, "/webhooks", gin.WrapH(webhookHandler()))
    log.Fatal(r.Run(":8080"))
}
```

[Read the docs](https://docs.mailkube.com/sdks/go#use-with-gin)

### Echo (Go)

```go
client, err := mailkube.New() // reads MAILKUBE_API_KEY
if err != nil {
    log.Fatal(err)
}
h := &handler{client: client}

e := echo.New()
e.Use(middleware.Recover())
e.POST("/receipts", h.sendReceipt)
e.Any("/webhooks", echo.WrapHandler(webhookHandler()))
log.Fatal(e.Start(":8080"))
```

[Read the docs](https://docs.mailkube.com/sdks/go#use-with-echo)

### Fiber (Go)

```go
client, err := mailkube.New() // reads MAILKUBE_API_KEY
if err != nil {
    log.Fatal(err)
}

app := fiber.New()
app.Post("/signup", signup(client))
app.All("/webhooks", adaptor.HTTPHandler(webhookHandler()))
log.Fatal(app.Listen(":8080"))
```

[Read the docs](https://docs.mailkube.com/sdks/go#use-with-fiber)

### PHP

```php
<?php

require 'vendor/autoload.php';

use Mailkube\Client;

$client = new Client(); // reads MAILKUBE_API_KEY

$email = $client->emails->send(
    from: 'Acme <hello@yourdomain.com>',
    to: 'customer@example.com',
    subject: 'Hello world',
    html: '<p>It works!</p>',
);

echo $email->id, ' ', $email->messageId;
```

[Read the docs](https://docs.mailkube.com/sdks/php)

### Laravel (PHP)

```php
// composer require mailkube/mailkube-laravel
//
// config/mail.php
//   'mailers' => ['mailkube' => ['transport' => 'mailkube']],
//
// .env
//   MAIL_MAILER=mailkube
//   MAILKUBE_API_KEY=mk_...

use Illuminate\Support\Facades\Mail;

Mail::to('customer@example.com')->send(new OrderShipped($order));
```

[Read the docs](https://docs.mailkube.com/sdks/laravel)

### Ruby

```ruby
require "mailkube"

client = Mailkube.new # reads MAILKUBE_API_KEY

email = client.emails.send(
  from: "Acme <hello@yourdomain.com>",
  to: "customer@example.com",
  subject: "Hello world",
  html: "<p>It works!</p>"
)

puts email.id
```

[Read the docs](https://docs.mailkube.com/sdks/ruby)

### Rails (Ruby)

```ruby
# Gemfile
gem "mailkube-rails"

# config/environments/production.rb
config.action_mailer.delivery_method = :mailkube
config.action_mailer.mailkube_settings = {
  api_key: ENV.fetch("MAILKUBE_API_KEY")
}

# Every ActionMailer delivery now goes through mailkube.
OrderMailer.with(order: order).shipped.deliver_later
```

[Read the docs](https://docs.mailkube.com/sdks/rails)

### Java

```java
import com.mailkube.MailkubeClient;
import com.mailkube.model.Email;
import com.mailkube.model.SendEmailParams;
import java.util.List;

// reads MAILKUBE_API_KEY
try (MailkubeClient client = MailkubeClient.builder().build()) {
    Email email = client.emails().send(
        SendEmailParams.builder(
                "Acme <hello@yourdomain.com>",
                List.of("customer@example.com"),
                "Hello world")
            .html("<p>It works!</p>")
            .build());

    System.out.println(email.id());
}
```

[Read the docs](https://docs.mailkube.com/sdks/java)

### Spring Boot (Java)

```java
// build.gradle.kts
//   implementation("com.mailkube:mailkube-spring-boot:X.Y.Z")
// application.properties
//   mailkube.api-key=mk_...

@Service
class OrderService {

    private final MailSender mail;

    OrderService(MailSender mail) {
        this.mail = mail;
    }

    void confirm(Order order) {
        SimpleMailMessage message = new SimpleMailMessage();
        message.setFrom("Acme <hello@yourdomain.com>");
        message.setTo(order.customerEmail());
        message.setSubject("Your order shipped");
        message.setText("It is on its way.");
        mail.send(message);
    }
}
```

[Read the docs](https://docs.mailkube.com/sdks/spring-boot)

### cURL (REST)

```bash
curl -X POST https://api.mailkube.com/mta/v1/emails \
  -H "Authorization: Bearer mk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Acme <hello@yourdomain.com>",
    "to": "customer@example.com",
    "subject": "Hello world",
    "html": "<p>It works!</p>"
  }'
```

[Read the docs](https://docs.mailkube.com/api-reference/overview)

### CLI

```bash
mailkube emails send \
  --from "Acme <hello@yourdomain.com>" \
  --to customer@example.com \
  --subject "Your invoice" \
  --html "<p>Thanks for your order.</p>"
```

[Read the docs](https://docs.mailkube.com/cli/send)

### SMTP

```bash
SMTP_HOST=smtp.mailkube.com
SMTP_PORT=587
SMTP_SECURITY=STARTTLS
SMTP_USERNAME=<your-smtp-credential-username>
SMTP_PASSWORD=<your-smtp-credential-password>
```

[Read the docs](https://docs.mailkube.com/smtp-reference/connection)

## Everything around the send

These five sections follow one message, from the draft to the delivery report.

### Templates ready to send, or your own

Copy a ready-made template, or open the editor on a blank page. Send its id and a few values, and mailkube renders the message. A wording change stops needing a deploy.

#### Versioned, never overwritten

Every save appends a version. Pin one, or send latest and restore an earlier snapshot when a change goes wrong.

#### Variables with a declared schema

Declare what a template expects. A send missing a required variable is rejected at validation.

[Templates](https://mailkube.com/features/templates.md)

### Topics and tags on every send

Classify a send with a topic, and label it with your own tags. Both follow the message, so you can slice your sending by either one.

#### Topics classify the traffic

A topic is the mailing list a send belongs to: a newsletter, product updates, receipts. Set it on REST or in an SMTP header.

#### Tags carry your own labels

Attach up to twenty tags of your own metadata per message. They travel into the logs, the dashboards and the webhook payload.

[Topics](https://docs.mailkube.com/docs/topics), [Tags](https://docs.mailkube.com/docs/tags)

### Emails that can wait

Add a timestamp to a normal send and mailkube transmits it at that instant. An appointment reminder, a nightly digest, a receipt that should not arrive at 3am.

#### Cancel or move it before it goes

List, retrieve, reschedule or cancel any scheduled message before its due time, one at a time or a whole batch at once.

#### One batch id, many sends

Give related messages the same batch id, then move or cancel the whole group in one call. mailkube is not a campaign builder; a batch is a label for one send.

[Scheduled sending](https://docs.mailkube.com/docs/scheduled-emails)

### Know what happened to every email

Receive a signed event at your endpoint when a message is delivered, bounces, is opened or is marked as spam. Your application never polls.

#### Signed with your endpoint secret

Verify every delivery with your endpoint secret. The SDKs ship the verifier.

#### Retried until you answer

A failed delivery is retried with backoff for up to 48 hours. Deduplicate on the delivery id, which stays stable across retries.

[Webhooks](https://docs.mailkube.com/docs/webhooks)

### Send from your terminal or your agent

Send mail, manage scheduled sends and stream webhook events from one binary. It carries a skill file, so a coding agent drives it correctly instead of guessing at flags.

#### See the message before it goes

Dry-run any send. It prints the exact request that would have gone out, and transmits nothing.

#### Webhook events in your terminal

Watch deliveries arrive as they happen while you build the handler. Add a forward flag to re-post each one into your local application.

[CLI](https://docs.mailkube.com/cli), [Agent skill](https://docs.mailkube.com/cli/agent-skill)

## Pricing

[See every plan](https://mailkube.com/pricing.md)

## Send your first email today

Start free, no card. Point an existing SMTP client at us, or install an SDK and make one POST.

[Read the docs](https://docs.mailkube.com/docs/quickstart)

