Test Mode
Test your integration without sending real emails. Test mode API keys simulate the full email pipeline -- validation, queueing, and events -- without delivering anything.
Test API Keys
Test mode is activated by using an API key that starts with sk_test_ instead of sk_live_. Create test keys from the dashboard under Configuration → API keys.
import { PostStack } from '@poststack.dev/sdk';
// Use a test key -- no real emails will be sent
const poststack = new PostStack('sk_test_...');
// This email is validated and queued, but never delivered
const { id } = await poststack.emails.send({
from: 'you@yourdomain.com',
to: ['user@example.com'],
subject: 'Test Email',
html: '<p>This is a test.</p>',
});
// id is still returned, and events are simulatedHow Test Mode Works
Test mode behaves identically to live mode with these differences:
No actual delivery
Emails are validated and queued but never sent to the recipient's mail server. No SMTP connection is made.
Simulated events
PostStack automatically generates simulated delivery events (queued, sent, delivered) so you can test your webhook handlers and event processing.
Full validation
All request validation is applied -- domain verification checks, schema validation, permission checks, and rate limits all work normally.
No billing impact
Test emails are not counted toward your usage quota and do not incur any charges.
Test Mode with cURL
Simply use your test API key in the Authorization header:
curl -X POST https://api.poststack.dev/emails \
-H "Authorization: Bearer sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"from": "you@yourdomain.com",
"to": ["user@example.com"],
"subject": "Test Email",
"html": "<p>This is a test.</p>"
}'Test Mode with Python and Go
Every SDK treats a test key the same way — there is no flag to set. Read the key from the environment so staging and CI get an sk_test_ key and production gets an sk_live_ key without a code change.
import os
from poststack import PostStack
ps = PostStack(api_key=os.environ["POSTSTACK_API_KEY"]) # sk_test_... in CI
result = ps.emails.send({
"from": "you@yourdomain.com",
"to": ["user@example.com"],
"subject": "Test Email",
"html": "<p>This is a test.</p>",
})
assert result.get("test_mode") is Trueclient := poststack.NewClient(os.Getenv("POSTSTACK_API_KEY")) // sk_test_... in CI
res, err := client.Emails.Send(ctx, &poststack.SendEmailInput{
From: "you@yourdomain.com",
To: []string{"user@example.com"},
Subject: "Test Email",
Html: "<p>This is a test.</p>",
})Testing Webhooks
Test mode emails still trigger webhook deliveries with simulated events. This makes it easy to develop and test your webhook handler end-to-end:
// 1. Create a webhook (works with both test and live keys)
await poststack.webhooks.create({
url: 'https://yourdomain.com/webhooks/poststack',
events: ['email.delivered', 'email.bounced'],
});
// 2. Send a test email
await poststack.emails.send({
from: 'you@yourdomain.com',
to: ['user@example.com'],
subject: 'Test',
html: '<p>Testing webhooks</p>',
});
// 3. Your webhook endpoint receives simulated events:
// - email.sent
// - email.delivered
// Both carry "test_mode": true in their data object, so a handler can
// ignore them in production.Test Mode over SMTP
A test key works as an SMTP password exactly like a live one — authentication succeeds and the server answers 250 — but the message is simulated and never leaves PostStack. Because an SMTP client has no other channel to tell you, the acceptance line says so explicitly:
250 Accepted em_a2r19yq5nah5fx3ziepm3ehu in TEST MODE - simulated only, nothing was delivered. Use a live (sk_live_) API key to send real mail.If you are wiring PostStack into a framework or platform that only exposes SMTP settings (Supabase, Rails, Django, WordPress), point it at a sk_live_ key as soon as you want real deliveries.
Identifying Test Emails
Every email records how it was sent. Send responses carry test_mode: true (absent on a live send), email objects returned by emails.get() and emails.list() carry testMode, and the dashboard shows a Test mode badge in place of the delivery status.
To keep environments separate from the start, use a dedicated sk_test_ key per environment and filter on tags or domain_id when listing emails.
Test sends are excluded from billing usage — they never consume a send from your plan's monthly allowance. They are not excluded from analytics: a test send is recorded as a normal delivered email, so it appears in the emails list and counts towards the dashboard's sent/delivered/bounced totals, the timeseries, and the provider and geography breakdowns. If your test volume is large enough to distort those numbers, tag every test send (or route it through a dedicated test domain) and filter accordingly.
// Tag every test send so you can filter it out later
await poststack.emails.send({
from: 'you@yourdomain.com',
to: ['user@example.com'],
subject: 'Test Email',
html: '<p>This is a test.</p>',
tags: ['env:test'],
});
// Filter just your test sends:
const { data } = await poststack.emails.list({ tag: 'env:test' });Going Live Checklist
- Your sending domain is verified — test mode applies the same domain checks, so a send that passes with a test key will not fail on this point with a live key.
- Production reads an
sk_live_key from its secret store; no test key is baked into a build or container image. - Your webhook handler ignores (or logs separately) events whose data carries
test_mode: true. - Checks that assert
test_moderun only in CI and staging, never against production.