MOLTED EMAIL

Policy Simulation

Dry-run sends against the policy engine without actually sending email.

Policy simulation lets you test whether a send would be allowed or blocked — without actually sending the email. Use it to pre-check sends, validate batch campaigns, and inspect remaining quotas.

Simulate a single send

POST https://api.molted.email/v1/agent/simulate-send
CLI (shorthand)
molted send simulate --to user@example.com --subject "Hello" --body "Hi Alice!"
CLI (template)
molted send simulate \
  --to user@example.com \
  --template campaign-march \
  --dedupe-key "march-user@example.com" \
  --payload '{"name": "Alice"}'
curl
curl -X POST https://api.molted.email/v1/agent/simulate-send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "tenant_abc123",
    "recipientEmail": "user@example.com",
    "templateId": "campaign-march",
    "dedupeKey": "march-user@example.com",
    "payload": { "name": "Alice" }
  }'
FieldRequiredDescription
tenantIdyesTenant identifier (injected automatically by CLI)
recipientEmailyesRecipient email address
templateIdyesTemplate to validate against
dedupeKeynoDeduplication key (checks duplicate policy)
mailboxIdnoSpecific mailbox to resolve sender from
payloadnoTemplate variables -- passed to the policy engine for validation

Response

Allowed
{
  "wouldAllow": true,
  "simulation": true
}
Blocked
{
  "wouldAllow": false,
  "reason": "cooldown",
  "cooldownExpiresAt": "2026-03-01T12:10:00Z",
  "simulation": true
}
Suppressed
{
  "wouldAllow": false,
  "reason": "suppressed",
  "suppressionInfo": {
    "scope": "tenant",
    "reasonCode": "hard_bounce"
  },
  "simulation": true
}
Billing blocked (trial not activated)
{
  "wouldAllow": false,
  "reason": "trial_not_activated",
  "simulation": true,
  "policyContext": {
    "isBillingBlocked": true,
    "billingPlan": "trial"
  }
}

The simulation now checks billing status in addition to policy rules. If your account is on a trial or expired plan, the simulation correctly reports that real sends would be blocked. This prevents false positives where policy checks pass but actual sends fail due to billing.

The response uses the same reason values as real sends — see Errors & Policy Blocks for the full list.

Simulate a batch

Dry-run policy evaluation for up to 500 recipients.

POST https://api.molted.email/v1/agent/simulate-batch
curl
curl -X POST https://api.molted.email/v1/agent/simulate-batch \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "tenant_abc123",
    "requests": [
      { "recipientEmail": "user1@example.com", "templateId": "campaign-march", "dedupeKey": "march-user1" },
      { "recipientEmail": "user2@example.com", "templateId": "campaign-march", "dedupeKey": "march-user2" }
    ]
  }'

Check remaining quotas

GET https://api.molted.email/v1/agent/budget
curl
curl "https://api.molted.email/v1/agent/budget?tenantId=tenant_abc123" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response

Response
{
  "tenantId": "tenant_abc123",
  "monthly": { "used": 450, "limit": 1000, "remaining": 550 },
  "daily": { "used": 23, "limit": 10000, "remaining": 9977 },
  "hourly": { "used": 5, "limit": 1000, "remaining": 995 },
  "negativeSignals": { "count": 2, "budget": 50, "remaining": 48 },
  "timestamp": "2026-04-12T12:00:00.000Z"
}

Important notes

  • No side effects — simulations do not increment counters, create send records, or trigger delivery. They are read-only.
  • Billing included — simulations check your billing status and will return trial_not_activated or subscription_expired if sends would be blocked. The policyContext includes isBillingBlocked and billingPlan fields.
  • Time-of-check vs. time-of-use — a simulation result reflects the policy state at the moment of the check. By the time you submit the actual send, conditions may have changed (e.g., another agent may have consumed budget). Treat simulation results as guidance, not a guarantee.