Mail Gateway
Add a sending domain
Section titled “Add a sending domain”POST /v1/mail-gateway/domains · scope mailgateway:write · reversible · idempotent
Returns the DNS records you must publish in the domain’s zone. That is the point of this call: until they are published and SES sees them, the domain does not verify and you cannot send from it. Each record carries purpose, type, host, value, and status; purpose is the stable key for automating publication.
Verification is asynchronous and on the SES side: this call does not wait. Check the status with POST /v1/mail-gateway/domains/{domain}/verify.
It is idempotent: repeating it for an already-added domain reuses the same DKIM key pair and returns the same records, so a retry does not invalidate what is already published.
curl https://api.truo.cloud/v1/mail-gateway/domains \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN" \ -H "Content-Type: application/json" \ -d '{"domain":"example.com"}'truo mail-gateway domain add <domain>await truo.mailgateway.domains.create({"domain":"example.com"});truo_mailgateway({ "action": "domain_add"})operationId: mailgateway.domains.create
Remove a sending domain
Section titled “Remove a sending domain”DELETE /v1/mail-gateway/domains/{domain} · scope mailgateway:write · destructive — cannot be undone · idempotent
Cuts off sending from that domain: it leaves the SMTP policy and the keys. The DNS records remain published in your zone; removing them is up to you. Adding it back generates new DKIM keys, so the old TXT record stops working.
curl https://api.truo.cloud/v1/mail-gateway/domains/example.com \ -X DELETE \ -H "Authorization: Bearer $TRUO_TOKEN"truo mail-gateway domain remove example.comawait truo.mailgateway.domains.delete("example.com");truo_mailgateway({ "action": "domain_remove", "domain": "example.com"})operationId: mailgateway.domains.delete
List sending domains
Section titled “List sending domains”GET /v1/mail-gateway/domains · scope mailgateway:read
Each domain comes with its DNS records and the status of each one. You can only send from a verified domain.
curl https://api.truo.cloud/v1/mail-gateway/domains \ -H "Authorization: Bearer $TRUO_TOKEN"truo mail-gateway domain listawait truo.mailgateway.domains.list();truo_mailgateway({ "action": "domain_list"})operationId: mailgateway.domains.list
Check a domain’s verification
Section titled “Check a domain’s verification”POST /v1/mail-gateway/domains/{domain}/verify · scope mailgateway:write
A check, not a trigger. SES inspects public DNS on its own and at its own pace; this reads that result and updates the domain’s status and each record’s. Getting pending back is not an error: it means SES has not seen the records yet, either because they have not propagated or because they are not published.
The dkim and mail_from_mx records are the ones SES verifies. spf, mail_from_spf, and dmarc always stay info: they improve deliverability, but nothing checks them.
verified_at comes back null in this response even when the status is verified; the value is in GET /v1/mail-gateway/domains.
curl https://api.truo.cloud/v1/mail-gateway/domains/example.com/verify \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo mail-gateway domain verify example.comawait truo.mailgateway.domains.verify("example.com");truo_mailgateway({ "action": "domain_verify", "domain": "example.com"})operationId: mailgateway.domains.verify
Create a sending API key
Section titled “Create a sending API key”POST /v1/mail-gateway/keys · scope mailgateway:send · reversible · idempotent
Returns the full key in secret, exactly once: we store its hash, so there is no way to show it again. If it is lost, create another and revoke this one.
This key does not work against this API: it is used on the sending plane, POST {api_endpoint}/emails (currently https://mg.truo.cloud/v1/emails), with Authorization: Bearer mg_live_…. Sending deliberately does not go through api.truo.cloud: one extra hop on the mail path is one extra failure mode.
It requires mailgateway:send rather than mailgateway:write because issuing this credential is the ability to send on the account’s behalf, and it survives this API’s key being revoked.
curl https://api.truo.cloud/v1/mail-gateway/keys \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo mail-gateway key createawait truo.mailgateway.keys.create();truo_mailgateway({ "action": "key_create"})operationId: mailgateway.keys.create
Revoke a sending API key
Section titled “Revoke a sending API key”DELETE /v1/mail-gateway/keys/{key_id} · scope mailgateway:write · destructive — cannot be undone · idempotent
Takes effect almost immediately: the key leaves the gateway’s index. Whatever was already accepted is delivered. Revoking requires write rather than send on purpose: removing the account’s ability to send should not require the scope that grants the ability to send.
curl https://api.truo.cloud/v1/mail-gateway/keys/mgk3f9a2b1c4d5 \ -X DELETE \ -H "Authorization: Bearer $TRUO_TOKEN"truo mail-gateway key revoke mgk3f9a2b1c4d5await truo.mailgateway.keys.delete("mgk3f9a2b1c4d5");truo_mailgateway({ "action": "key_revoke", "keyId": "mgk3f9a2b1c4d5"})operationId: mailgateway.keys.delete
List sending API keys
Section titled “List sending API keys”GET /v1/mail-gateway/keys · scope mailgateway:read
Includes revoked keys, so history can be audited. secret is always null: we store the key’s hash and there is no way to recover it.
curl https://api.truo.cloud/v1/mail-gateway/keys \ -H "Authorization: Bearer $TRUO_TOKEN"truo mail-gateway key listawait truo.mailgateway.keys.list();truo_mailgateway({ "action": "key_list"})operationId: mailgateway.keys.list
List sent messages
Section titled “List sent messages”GET /v1/mail-gateway/messages · scope mailgateway:read
One element per message, newest first, with the aggregate status of the highest-severity event seen (bounced beats delivered). Retained for 90 days.
No total: the backend cannot know how many messages match without walking the entire history, and no /v1 collection publishes totals. Paginate with next_cursor.
curl https://api.truo.cloud/v1/mail-gateway/messages \ -H "Authorization: Bearer $TRUO_TOKEN"truo mail-gateway message listawait truo.mailgateway.messages.list();truo_mailgateway({ "action": "message_list"})operationId: mailgateway.messages.list
Get delivery and reputation metrics
Section titled “Get delivery and reputation metrics”GET /v1/mail-gateway/metrics · scope mailgateway:read
Delivery, open, bounce, and complaint rates for the range, with the daily series, send-to-delivery latency, and the per-domain breakdown. Rates are fractions (0–1), not percentages. bounce_rate_limit and complaint_rate_limit are the SES thresholds: crossing them suspends sending to protect the shared reputation.
curl https://api.truo.cloud/v1/mail-gateway/metrics \ -H "Authorization: Bearer $TRUO_TOKEN"truo mail-gateway metricsawait truo.mailgateway.metrics.get();truo_mailgateway({ "action": "metrics"})operationId: mailgateway.metrics.get
View the SMTP configuration
Section titled “View the SMTP configuration”GET /v1/mail-gateway/smtp · scope mailgateway:read
Host, port, username, and status, without the password. It is what you need to configure or review a mail client without handling the secret. For the password, use POST /v1/mail-gateway/smtp.
curl https://api.truo.cloud/v1/mail-gateway/smtp \ -H "Authorization: Bearer $TRUO_TOKEN"truo mail-gateway smtp getawait truo.mailgateway.smtp.get();truo_mailgateway({ "action": "smtp_get"})operationId: mailgateway.smtp.get
Reveal the SMTP password
Section titled “Reveal the SMTP password”POST /v1/mail-gateway/smtp · scope mailgateway:send
Returns the SMTP password in plaintext. It is a POST on purpose, even though it changes nothing. The backend exposes it in a GET, and a GET that returns a secret lands in browser history, in any proxy’s cache, and in yesterday’s curl. A POST forces a deliberate action, is not cacheable, and enters the audit log as a mutation — which is exactly how “who pulled the sending password, and when” has to be auditable.
The password is recoverable (stored encrypted, not hashed) because a mail server needs it whole on every connection. If it was compromised, no longer looking at it is not enough: rotate it with POST /v1/mail-gateway/smtp/rotate.
curl https://api.truo.cloud/v1/mail-gateway/smtp \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo mail-gateway smtp revealawait truo.mailgateway.smtp.reveal();truo_mailgateway({ "action": "smtp_reveal"})operationId: mailgateway.smtp.reveal
Rotate the SMTP credential
Section titled “Rotate the SMTP credential”POST /v1/mail-gateway/smtp/rotate · scope mailgateway:send · destructive — cannot be undone · idempotent
Issues a new username and password and returns both. The previous credential is deactivated, not deleted, so an application that still holds it in memory does not crash at the instant of rotation — but it will stop working, so update your systems. There is no going back: the old one cannot be reactivated from here.
curl https://api.truo.cloud/v1/mail-gateway/smtp/rotate \ -X POST \ -H "Authorization: Bearer $TRUO_TOKEN"truo mail-gateway smtp rotateawait truo.mailgateway.smtp.rotate();truo_mailgateway({ "action": "smtp_rotate"})operationId: mailgateway.smtp.rotate
Get the account’s Mail Gateway
Section titled “Get the account’s Mail Gateway”GET /v1/mail-gateway · scope mailgateway:read
It takes no id: there is one Mail Gateway per account. Returns the status, the month’s usage, and how many domains and keys exist; each has its own detail endpoint. If the account does not have the service, it returns 404.
curl https://api.truo.cloud/v1/mail-gateway \ -H "Authorization: Bearer $TRUO_TOKEN"truo mail-gateway getawait truo.mailgateway.tenant.get();truo_mailgateway({ "action": "get"})operationId: mailgateway.tenant.get
Get the current month’s usage
Section titled “Get the current month’s usage”GET /v1/mail-gateway/usage · scope mailgateway:read
Accepted and rejected sends for the current UTC calendar month. This is the number that gets billed. Rejected sends are not charged: they are the ones the gateway stopped before SES.
curl https://api.truo.cloud/v1/mail-gateway/usage \ -H "Authorization: Bearer $TRUO_TOKEN"truo mail-gateway usageawait truo.mailgateway.usage.get();truo_mailgateway({ "action": "usage"})operationId: mailgateway.usage.get