Skip to content

Changelog

Customer-onboarding fixes

  • Self-service domain verificationPOST /domains/{name}/verify and POST /domains/{name}/register-subdomain are now open to customer tokens. Customers see only their own domains (filtered by partner_ref); master tokens keep the broader scope of verifying root domains not present locally.
  • Auto-provision of provider credentials on /activatePUT /domains/{name}/activate now bootstraps the per-tenant Mailgun SMTP credential when missing, closing the silent-bounce path where Postfix accepted the envelope and Mailgun later rejected with 5.7.1 Relaying denied. Idempotent; SES domains are skipped (single account-wide credential).
  • Domain owner transfer — new PUT /domains/{name}/owner (master only) reassigns a domain’s partner_ref atomically with both domains_used counter adjustments. Refuses to transfer to a partner with no active API key. Replaces the DELETE + recreate workaround.
  • POST /domains/ accepts partner_ref — master tokens can now attribute a domain to a tenant at creation time. Customer tokens always own what they create; the body field is ignored from a customer caller.
  • domains_used is now reliable — the counter is reconciled to the actual virtual_domains count on every API startup, curing legacy drift caused by master-on-behalf creates that previously inserted with partner_ref=NULL. Tenant quotas now enforce as documented.

Authentication discoverability

  • Master-only endpoints carry x-required-role: master in the OpenAPI schema, so SDKs can surface the requirement before runtime.
  • Unified 403 body for master-only routes: { "detail": "Master token required" }.

One-shot subdomain provisioning

  • POST /domains/{name}/register-subdomain is now fully self-contained. After a successful call, the subdomain can send outbound mail immediately using the parent’s DKIM identity — no additional provider-side setup required.
  • DELETE /domains/{name} on a subdomain cleans up every piece of provider-level configuration installed for it. Deleting and recreating a subdomain no longer leaves orphan state.

Odoo modules

  • emboux_core — Ticking Catch-All directly on the mailbox creation form now installs the catch-all alias immediately. Previously the form saved the flag locally but required a second toggle to dispatch the API call.
  • emboux_saas — Creating a subdomain under a wildcard parent always registers with the provider at creation time. Previously the provider registration was conditional on a sibling domain being SES-verified in local Odoo state.
  • The mailbox detail view now shows two SMTP configuration options for the Odoo Outgoing Mail Server: STARTTLS on 587 and SSL/TLS on 465 (recommended for most clients).

Anti-Spam & Reputation Protection

  • Suppression list: Global recipient blocklist fed by bounce/complaint webhooks. Checked before every outbound email — blocked recipients never reach the provider. New endpoints: GET /suppression, GET /suppression/check/{email}, DELETE /suppression/{email}
  • Mailgun webhook: POST /webhooks/mailgun — processes Mailgun bounce (permanent_fail) and complaint (complained) events with HMAC signature verification. Auto-configured on new domains via MAILGUN_WEBHOOK_URL
  • Guillotine thresholds lowered: bounce rate 3% → 0.5%, new complaint rate threshold at 0.1% (configurable via env vars)
  • Warm-up profiles: Progressive daily cap for new domains with three profiles — fast (1.8x/day), standard (1.1x/day, default), conservative (1.05x/day). Ends naturally when reaching the plan’s daily limit. Master-only field
  • Spike detection: Compares today’s volume against 7-day rolling average. Blocks if ratio exceeds spike_max_multiplier (default 5x). Daily history stored during reset cron
  • Hourly throttle: In-memory rate limiter caps each domain at ~1/6 of daily limit per hour (configurable via HOURLY_FRACTION). Prevents burning entire daily quota in minutes
  • Outbound response now includes warmup_profile and spike_max_multiplier fields
  • SES webhook refactored: shared _process_bounce_or_complaint() function handles both SES and Mailgun events with unified logic
  • Daily reset cron now saves outbound_sent_today into daily_history (last 7 days) before resetting, feeding spike detection

Mail Provider Abstraction & Multi-Provider Support

  • New generic endpoints: POST /domains/{name}/verify, GET /domains/{name}/verify-status, POST /domains/{name}/register-subdomain
  • Legacy /ses-verify and /ses-status endpoints kept as backward-compatible aliases
  • Support for multiple mail providers per domain (Mailgun, SES, future: local/OpenDKIM)
  • Verify a parent domain once and all subdomains inherit DKIM automatically
  • New domains verified immediately if parent is already verified
  • GET /api-keys/{token}/usage now accessible with client tokens (not just master)
  • Alias creation uses upsert — no more duplicate errors on re-sync

Wildcard Domains & DNS Verification

  • PUT /domains/{name}/parent — Set parent domain and wildcard mode (master only)
  • GET /domains/{name}/dns-check — Verify MX, SPF, SES TXT, and DKIM CNAME records
  • Auto-detection of subdomains: creating sub.parent.com auto-links to wildcard parent
  • Subdomains inherit SES verification, outbound tier, and enforcement from parent
  • Policy Daemon checks parent domain SES verification for subdomain inheritance

Security Hardening

  • Client tokens restricted from modifying outbound_tier, outbound_enforcement, outbound_status
  • All privileged endpoints audited: retention, transfer, ses-verify, parent — master only

SES Automation

  • Auto SES verification on domain creation (skipped for subdomains of verified parents)
  • Cron polling SES status every 2 hours with notification on verification success

Outbound Email Control

  • GET/PUT /domains/{name}/outbound — Outbound quota configuration (tier, limits, enforcement)
  • POST /domains/{name}/outbound/increment — Atomic counter increment for Policy Daemon
  • POST /webhooks/ses — SES bounce/complaint webhook with auto-guillotine at 3% bounce rate
  • POST /outbound/reset-daily — Daily counter reset (cron)
  • POST /outbound/reset-monthly — Monthly freeze + reset (cron)

SES Domain Verification

  • POST /domains/{name}/ses-verify — Initiate SES VerifyDomainIdentity + VerifyDomainDkim
  • GET /domains/{name}/ses-status — Check SES verification status, auto-update on success
  • Policy Daemon blocks outbound for unverified domains
  • Portal displays DKIM CNAME and verification TXT records

Suspension & Lifecycle Management

  • PUT /domains/{name}/suspend — Freeze mail delivery without data loss
  • PUT /domains/{name}/activate — Restore suspended domains

Storage & Quota Enforcement

  • GET/PUT /users/{email}/quota — Per-mailbox storage limits
  • GET/PUT /domains/{name}/retention — Email retention policy per domain
  • GET/PUT /domains/{name}/transfer — Monthly transfer limits per domain

Automation

  • Daily retention cron: automated purge of expired emails
  • Multiple API keys per partner with optional expiration dates

Multi-Tenant Architecture

  • Client API keys with per-partner scoping
  • Quota enforcement: domains, mailboxes per domain, aliases per mailbox
  • Tenant isolation — each key only sees its own resources
  • API key management endpoints (CRUD + usage)

Core CRUD

  • POST/GET/DELETE /domains/ — Domain management
  • POST/GET/PUT/DELETE /users/ — Mailbox management with secure password hashing
  • POST/GET/DELETE /aliases/ — Alias and catch-all management
  • GET /api-keys/{token}/usage — Delta sync with ?since= parameter

  • Initial API with domain, mailbox, and alias CRUD
  • Master token authentication
  • Health check endpoint