Send test messages through local SMTP, SendGrid, Amazon SES, or Azure Communication Services and inspect the captured mail.
What Sqrzl does
Sqrzl accepts outbound mail requests and stores each message locally for inspection. It does not relay mail or deliver it to real inboxes. Start the emulator with the quickstart before connecting an SDK.
Choose an endpoint
| Transport | Local endpoint | Use it for |
|---|---|---|
| SMTP | localhost:2525 | Applications that send mail through an SMTP client. |
| SendGrid Mail Send v3 | http://localhost:9000 | SendGrid SDKs and requests to /v3/mail/send. |
| Amazon SES v2 | http://localhost:9000 | SES v2 SendEmail with simple content. |
| Azure Communication Services Email | http://localhost:9000 | ACS Email send and operation polling. |
The API providers share the HTTP listener with the storage APIs. Set the SDK endpoint or base URL to http://localhost:9000.
Connect an SDK
SMTP with Python
import smtplib
from email.message import EmailMessage
message = EmailMessage()
message["From"] = "sender@example.com"
message["To"] = "recipient@example.com"
message["Subject"] = "Local Sqrzl test"
message.set_content("This message is captured locally.")
with smtplib.SMTP("127.0.0.1", 2525, timeout=5) as client:
client.send_message(message)SMTP is plaintext and accepts strict angle-bracket envelope paths. Use a client that does not require SMTP AUTH or STARTTLS.
SendGrid
import sendgrid
from sendgrid.helpers.mail import Mail
client = sendgrid.SendGridAPIClient(api_key="local-key")
client.client.host = "http://localhost:9000"
message = Mail(
from_email="sender@example.com",
to_emails="recipient@example.com",
subject="Local Sqrzl test",
plain_text_content="This message is captured locally.",
)
response = client.send(message)If SQRZL_SENDGRID_API_KEY is set on Sqrzl, pass the same key to the SDK. If it is unset, the adapter does not require a SendGrid bearer token.
Amazon SES v2
import boto3
from botocore.config import Config
client = boto3.client(
"sesv2",
endpoint_url="http://localhost:9000",
aws_access_key_id="admin",
aws_secret_access_key="sqrzl-secret",
region_name="us-east-1",
config=Config(signature_version="s3v4"),
)
client.send_email(
FromEmailAddress="sender@example.com",
Destination={"ToAddresses": ["recipient@example.com"]},
Content={"Simple": {
"Subject": {"Data": "Local Sqrzl test"},
"Body": {"Text": {"Data": "This message is captured locally."}},
}},
)The credentials above match the checked-in Compose defaults. Use the values configured for your local instance if you changed them.
Azure Communication Services Email
import base64
from azure.communication.email import EmailClient
from azure.core.pipeline.transport import RequestsTransport
class LocalHttpTransport(RequestsTransport):
def send(self, request, **kwargs):
# The SDK signs its HTTPS URL; Sqrzl serves local HTTP.
if request.url.startswith("https://"):
request.url = "http://" + request.url.removeprefix("https://")
return super().send(request, **kwargs)
access_key = base64.b64encode(b"shared-secret").decode("ascii")
connection = (
"endpoint=http://localhost:9000;accesskey=" + access_key
)
client = EmailClient.from_connection_string(
connection,
transport=LocalHttpTransport(),
)
poller = client.begin_send({
"senderAddress": "sender@example.com",
"recipients": {"to": [{"address": "recipient@example.com"}]},
"content": {
"subject": "Local Sqrzl test",
"plainText": "This message is captured locally.",
},
})
poller.result(timeout=10)Configure SQRZL_ACS_CONNECTION_STRING on Sqrzl with the same base64 access key. This transport adapts the official SDK to the emulator's local HTTP endpoint after the SDK signs the request.
Configure authentication
| Setting | Effect |
|---|---|
SQRZL_ACCESS_KEY_ID and SQRZL_SECRET_ACCESS_KEY | Set both to enable shared provider and admin authentication. The default Compose values are admin and sqrzl-secret. |
SQRZL_SENDGRID_API_KEY | Require this bearer token on the SendGrid adapter when set. |
SQRZL_ACS_CONNECTION_STRING | Set endpoint=<url>;accesskey=<base64> to enable ACS HMAC authentication for Email and SMS. |
SES requests use SigV4. SMTP has no authentication. See configuration for the complete environment settings.
Inspect captured mail
Open http://localhost:9001, sign in if admin authentication is enabled, and choose Mailboxes. Sqrzl creates a mailbox for each recipient and shows the captured message content, headers, and attachments where supported.
All adapters capture locally. A successful provider-shaped response means the request was accepted by the emulator; it does not mean a remote mail server sent or delivered the message.
Supported behavior
- SMTP accepts a plaintext subset of EHLO/HELO, MAIL FROM, RCPT TO, DATA, RSET, NOOP, and QUIT. Recipient fan-out is validated and captured atomically.
- SendGrid supports selected personalizations, to/cc/bcc recipients, plain and HTML content, headers, reply-to fields, and base64 attachments. Its accepted request size is under 30 MB.
- SES v2 supports
Content.Simple, destinations, reply-to addresses, subject and body charset metadata, and selected custom headers. It does not support attachments. - ACS Email supports API versions
2023-03-01,2023-03-31, and2025-09-01, up to 50 combined to/cc/bcc recipients, plain or HTML content, and base64 regular or inline attachments up to 10 MB. Operation IDs and five-minute repeatability are supported.
SMTP AUTH, STARTTLS, relay and delivery, SES raw and template content, SendGrid templates and scheduling, and ACS delivery events are outside the supported scope. The provider-specific limits and rejected features are listed in the compatibility matrix.
Request and capture limits
SQRZL_MAX_REQUEST_BYTES defaults to 128 MiB per HTTP request body or SMTP DATA payload. Separately, each capture transaction has a conservative 64 MiB projected aggregate materialization limit. It accounts for fan-out copies, raw MIME, attachments, and stored records.
Oversized HTTP requests receive a provider-shaped 413; SMTP returns 552. Rejected requests do not leave partial mailbox captures. See configuration for the request-body limit.