SDKs and OpenAPI
Official libraries for seven languages, Laravel and Rails. Every one covers the whole API and behaves the same way: it retries safely, walks every page of a list, raises typed errors and verifies webhook signatures.
Libraries
Node.js
npm install zapsmtpTypeScript types for every request and response; ESM and CommonJS; no dependencies.
Node.js 18+, Bun, Deno
GitHubnpmPython
pip install zapsmtpSync and async clients, typed models, httpx underneath.
Python 3.9+
GitHubPyPIPHP
composer require zapsmtp/zapsmtp-phpPSR-18 HTTP client, Guzzle by default; typed models with array access.
PHP 8.1+
GitHubPackagistLaravel
composer require zapsmtp/zapsmtp-laravelA mail transport for your Mailables, a facade for the API and verified webhook events.
Laravel 10, 11, 12
GitHubPackagistGo
go get github.com/zapsmtp/zapsmtp-goStandard library only; context on every call; range over every page.
Go 1.23+
GitHubpkg.go.devRuby
gem install zapsmtpStandard library only; an ActionMailer delivery method for Rails.
Ruby 3.0+
GitHubRubyGemsJava
implementation("com.zapsmtp:zapsmtp-java:0.1.0")Builders for every request, immutable models, java.net.http underneath.
Java 11+
GitHubMaven CentralSend your first email
Create a test key in the dashboard (zap_test_…) and put it in ZAP_API_KEY. Test keys deliver nothing, and the simulator address acts out a delivery.
import { ZapSMTP } from 'zapsmtp';
const zap = new ZapSMTP(); // reads ZAP_API_KEY
const { id } = await zap.emails.send({
from: 'Acme <hello@acme.com>',
to: 'delivered@simulator.zapsmtp.xyz',
subject: 'Hello from ZapSMTP',
html: '<p>It works.</p>',
});
console.log(id);from zapsmtp import ZapSMTP
zap = ZapSMTP() # reads ZAP_API_KEY
email = zap.emails.send(
from_="Acme <hello@acme.com>", # from is a Python keyword
to="delivered@simulator.zapsmtp.xyz",
subject="Hello from ZapSMTP",
html="<p>It works.</p>",
)
print(email.id)<?php
require 'vendor/autoload.php';
$zap = new \ZapSMTP\Client(); // reads ZAP_API_KEY
$email = $zap->emails->send([
'from' => 'Acme <hello@acme.com>',
'to' => 'delivered@simulator.zapsmtp.xyz',
'subject' => 'Hello from ZapSMTP',
'html' => '<p>It works.</p>',
]);
echo $email->id;package main
import (
"context"
"fmt"
"log"
"github.com/zapsmtp/zapsmtp-go"
)
func main() {
client := zapsmtp.NewClient("") // "" reads ZAP_API_KEY
email, err := client.Emails.Send(context.Background(), &zapsmtp.SendEmailParams{
From: zapsmtp.String("Acme <hello@acme.com>"),
To: []string{"delivered@simulator.zapsmtp.xyz"},
Subject: zapsmtp.String("Hello from ZapSMTP"),
HTML: zapsmtp.String("<p>It works.</p>"),
})
if err != nil {
log.Fatal(err)
}
fmt.Println(email.ID)
}require "zapsmtp"
zap = ZapSMTP::Client.new # reads ZAP_API_KEY
email = zap.emails.send(
from: "Acme <hello@acme.com>",
to: "delivered@simulator.zapsmtp.xyz",
subject: "Hello from ZapSMTP",
html: "<p>It works.</p>"
)
puts email.idimport com.zapsmtp.ZapSMTP;
import com.zapsmtp.params.SendEmailParams;
ZapSMTP zap = ZapSMTP.fromEnv(); // reads ZAP_API_KEY
var email = zap.emails().send(SendEmailParams.builder()
.from("Acme <hello@acme.com>")
.to("delivered@simulator.zapsmtp.xyz")
.subject("Hello from ZapSMTP")
.html("<p>It works.</p>")
.build());
System.out.println(email.getId());using ZapSMTP;
var zap = new ZapSMTPClient(); // reads ZAP_API_KEY
var email = await zap.Emails.SendAsync(new SendEmailParams
{
From = "Acme <hello@acme.com>",
To = new[] { "delivered@simulator.zapsmtp.xyz" },
Subject = "Hello from ZapSMTP",
Html = "<p>It works.</p>",
});
Console.WriteLine(email.Id);What every SDK does
- The whole API. Every endpoint in the reference, with types for its parameters and response. The libraries are generated from the same description as these pages, so a new endpoint reaches every language at once.
- Safe retries. Rate limits (
429), server errors and lost connections are retried twice by default, waiting asRetry-Aftersays or backing off exponentially. Sends always carry anIdempotency-Key, so a retried send never goes out twice; pass your own key to tie a send to your order or event. - Every page. List methods return one page; each SDK can also iterate every item of every page.
- Typed errors. Authentication, permission, not found, conflict, validation, rate limit, server and connection errors are separate classes carrying the status, the error
codeand the request id. See Errors, limits & paging. - Webhook verification. One call checks the
Zap-Signatureheader in constant time, rejects signatures older than five minutes and accepts both secrets during a rotation. - Configuration. The key comes from
ZAP_API_KEYand the base URL fromZAP_BASE_URLwhen not passed in code. Timeouts, retries and extra headers can be set per client or per request.
Verify a webhook
Pass the body exactly as it arrived, before parsing it. The call returns the event or throws. The payloads are described in Webhook events.
import { verifyWebhook } from 'zapsmtp';
// the raw body, before any JSON parsing
const event = verifyWebhook(rawBody, req.headers, process.env.ZAP_WEBHOOK_SECRET);import os
from zapsmtp import verify_webhook
event = verify_webhook(request.get_data(), request.headers, os.environ["ZAP_WEBHOOK_SECRET"])$event = \ZapSMTP\Webhook::verify(
file_get_contents('php://input'),
getallheaders(),
getenv('ZAP_WEBHOOK_SECRET'),
);body, _ := io.ReadAll(r.Body)
event, err := zapsmtp.VerifyWebhook(body, r.Header.Get("Zap-Signature"), os.Getenv("ZAP_WEBHOOK_SECRET"))event = ZapSMTP::Webhook.verify(request.raw_post, request.headers, ENV.fetch("ZAP_WEBHOOK_SECRET"))import com.zapsmtp.WebhookEvent;
import com.zapsmtp.Webhooks;
WebhookEvent event = Webhooks.verify(rawBody, headers, System.getenv("ZAP_WEBHOOK_SECRET"));var body = await new StreamReader(Request.Body).ReadToEndAsync();
var evt = Webhooks.Verify(body, Request.Headers["Zap-Signature"].ToString(),
Environment.GetEnvironmentVariable("ZAP_WEBHOOK_SECRET"));Laravel and Rails
Laravel. Set MAIL_MAILER=zapsmtp and your Mailables go through the API: attachments, inline images, tags and metadata included. The package also gives a ZapSMTP facade for the whole API and a webhook route that checks signatures and fires Laravel events.
Rails. Set config.action_mailer.delivery_method = :zapsmtp and Action Mailer sends through the API. Pass tags, metadata or a template with mail(zapsmtp: { tags: [...] }). The gem includes a Rack middleware that verifies webhooks.
OpenAPI
The whole API as an OpenAPI 3.1 description: every operation with its parameters, request and response schemas and documented errors. Import it into Postman or Insomnia, or generate a client for a language without an SDK (Rust, Elixir, Dart, Swift) with OpenAPI Generator. Each operation's x-sdk-method names the SDK method that calls it.
Its repository: github.com/ZapSMTP/zapsmtp-openapi.
Other clients
Any SMTP library works too: SMTP relay. Moving from another provider: Migrating to ZapSMTP.