How to Send Your First Policy-Checked Email
2026-03-10
Most email APIs work like this: you POST a payload, the email goes out, and you hope for the best. If your agent sends something it shouldn't — wrong recipient, duplicate message, a suppressed contact — you find out after the damage is done.
Molted works differently. Every send request passes through a policy engine before anything leaves. If the send violates a rule — rate limit, suppression, cooldown, duplicate — it's blocked and you get a trace explaining why.
This tutorial walks you through the entire flow: creating an account, getting an API key, simulating a send, and sending your first real email. Every step takes a single API call.
Prerequisites
You need curl (or any HTTP client) and a terminal. That's it.
Step 1: Create an account
Sign up with an email and password. This creates your tenant, a default mailbox, and a default transactional template automatically.
curl -X POST https://api.molted.email/api/auth/sign-up/email \
-H 'Content-Type: application/json' \
-c cookies.txt \
-d '{"name": "My Agent", "email": "you@example.com", "password": "your-password"}'
The -c cookies.txt flag saves your session cookie. You'll need it for the next two calls.
Step 2: Get your API key
Create a Bearer token for all subsequent requests:
curl -X POST https://api.molted.email/v1/me/keys \
-H 'Content-Type: application/json' \
-b cookies.txt \
-d '{"label": "default"}'
Response:
{
"id": "uuid",
"key_prefix": "mm_live_...",
"raw_key": "mm_live_abc123...",
"label": "default",
"status": "active"
}
Save raw_key — it's only shown once.
Step 3: Get your tenant ID
Your tenant ID is required in every API call:
curl https://api.molted.email/v1/me/tenant \
-b cookies.txt
{
"id": "tenant-my-agent-a1b2c3d4",
"name": "My Agent",
"status": "active",
"billing_plan": "trial"
}
Save the id value. From here on, every request uses your Bearer token and tenant ID — no more cookies.
Step 4: Activate billing
New tenants start on the trial plan with sends blocked. Before you can send, you need to activate a billing plan:
curl -X POST https://api.molted.email/v1/billing/setup-link \
-H 'Authorization: Bearer mm_live_abc123...' \
-H 'Content-Type: application/json' \
-d '{"tenantId": "tenant-my-agent-a1b2c3d4", "plan": "starter"}'
{
"url": "https://checkout.stripe.com/...",
"plan": "starter"
}
Open the returned URL in your browser and complete the Stripe checkout. Once payment succeeds, sends are unblocked automatically.
On the Starter plan you get 1,000 sends per month, 200 per day, 50 per hour, and up to 3 mailboxes. Plans scale from there.
Step 5: Simulate a send
Before sending anything real, run a dry-run to see what the policy engine would do. This checks every rule — suppression, rate limits, cooldown, duplicates — without actually sending:
curl -X POST https://api.molted.email/v1/agent/simulate-send \
-H 'Authorization: Bearer mm_live_abc123...' \
-H 'Content-Type: application/json' \
-d '{
"tenantId": "tenant-my-agent-a1b2c3d4",
"recipientEmail": "alice@example.com",
"templateId": "_default",
"dedupeKey": "test-dry-run"
}'
If the send would be allowed:
{
"wouldAllow": true,
"simulation": true
}
If something would block it:
{
"wouldAllow": false,
"reason": "suppressed",
"suppressionInfo": {
"scope": "global",
"reasonCode": "hard_bounce"
},
"simulation": true
}
Nothing is persisted, nothing is sent. This is your preflight check — use it before every send in production, especially for batch operations.
Step 6: Send for real
Now send an actual email. The _default template was auto-created at signup, so you can use it immediately:
curl -X POST https://api.molted.email/v1/agent/request-send \
-H 'Authorization: Bearer mm_live_abc123...' \
-H 'Content-Type: application/json' \
-d '{
"tenantId": "tenant-my-agent-a1b2c3d4",
"recipientEmail": "alice@example.com",
"templateId": "_default",
"dedupeKey": "first-send-alice",
"payload": {
"subject": "Hello from Molted",
"html": "<p>This is my first policy-checked email.</p>"
},
"sendReason": "testing first send"
}'
The _default template accepts three variables: subject (required), html (required), and text (optional plaintext fallback). It passes them straight through — no extra formatting.
A successful send:
{
"requestId": "req_abc123",
"status": "queued",
"policyTrace": {
"decision": { "allow": true },
"auditEvents": [...]
}
}
Every response includes a policyTrace with the policy decision and a timeline of audit events. This is your audit trail.
What happens when a send is blocked
Try sending the exact same request again (same dedupeKey):
curl -X POST https://api.molted.email/v1/agent/request-send \
-H 'Authorization: Bearer mm_live_abc123...' \
-H 'Content-Type: application/json' \
-d '{
"tenantId": "tenant-my-agent-a1b2c3d4",
"recipientEmail": "alice@example.com",
"templateId": "_default",
"dedupeKey": "first-send-alice",
"payload": {
"subject": "Hello from Molted",
"html": "<p>This is my first policy-checked email.</p>"
},
"sendReason": "testing first send"
}'
{
"requestId": "req_def456",
"status": "blocked",
"reason": "duplicate",
"policyTrace": {
"decision": { "allow": false, "reason": "duplicate" },
"auditEvents": [...]
}
}
The response is still HTTP 200 — but status is "blocked" and the trace tells you exactly why. Your agent should always check the status field, not just the HTTP code.
Blocked sends return HTTP 200, not 4xx. Always check response.status in your agent's code.
This is the core idea behind Molted: the policy engine is not a side feature — it's in the critical path of every send. Your agent doesn't need to build its own rate limiting, deduplication, or suppression logic. The API handles it and tells you what happened.
The 14 policy rules
Every send is evaluated against these rules in order. If any rule fails, the send is blocked:
| Rule | What it checks |
|------|---------------|
| Tenant paused | Is the emergency pause active? |
| Template approval | Has the template been approved (if required)? |
| Template lint | Does the template pass validation? |
| Suppression list | Is the recipient on a suppression list? |
| Global DNC | Hard bounce, complaint, legal request, or role account? |
| Active opportunity | Does the contact have an active sales deal? |
| Duplicate | Has this dedupeKey been used before? |
| Cooldown | Same template to same recipient within 10 minutes? |
| Hourly rate limit | Have you hit your hourly quota? |
| Daily budget | Have you hit your daily quota? |
| Monthly budget | Have you hit your monthly quota? |
| Risk score | Is your daily risk score too high? |
| Negative signals | Too many bounces + complaints in 24 hours? |
| Lease conflict | Does another agent hold a lease on this contact? |
Your agent doesn't need to know about these rules to use the API. But when a send is blocked, the trace tells you which rule caught it — so you can decide what to do next (wait, skip, escalate).
A deeper dive into each rule is coming in a future post.
Next: send from a mailbox
The example above uses the default sender address. In production, you'll want to send from a specific mailbox — this scopes sends to a named inbox, enables threaded conversations, and gives you per-mailbox metrics in the portal.
Include mailboxId in your send request:
{
"tenantId": "tenant-my-agent-a1b2c3d4",
"recipientEmail": "alice@example.com",
"templateId": "_default",
"dedupeKey": "onboarding-alice-step1",
"payload": { "subject": "Welcome", "html": "..." },
"mailboxId": "mbx_abc123"
}
When you include a mailbox, Molted automatically creates a thread for the contact. If Alice replies, her response is linked to the same thread — visible in the portal and queryable via the API.
Check your remaining budget
Before a batch send, check how many sends you have left:
curl 'https://api.molted.email/v1/agent/budget?tenantId=tenant-my-agent-a1b2c3d4' \
-H 'Authorization: Bearer mm_live_abc123...'
{
"daily": { "used": 1, "limit": 200, "remaining": 199 },
"hourly": { "used": 1, "limit": 50, "remaining": 49 }
}
Build this into your agent's decision loop: check the budget, decide how many to send, then use batch simulate to pre-check the full list before committing.
That's the full flow — account to first send in six API calls, with policy enforcement at every step. No webhook configuration, no SMTP setup, no manual rate limiting.
Read the full API reference for everything else: batch sends, templates, journeys, and more. Or sign up and try it now.
Keep reading
- Why AI Agents Need Email Guardrails — the three failure modes this infrastructure prevents
- What Is Agent-Native Email? — the architecture behind policy-first email
- Safety & Policies — configure the 14 policy rules for your tenant
- Threaded Conversations — how mailbox-scoped sends enable full conversation tracking