Skip to content

Mail Gateway

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.

Terminal window
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"}'

operationId: mailgateway.domains.create

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.

Terminal window
curl https://api.truo.cloud/v1/mail-gateway/domains/example.com \
-X DELETE \
-H "Authorization: Bearer $TRUO_TOKEN"

operationId: mailgateway.domains.delete

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.

Terminal window
curl https://api.truo.cloud/v1/mail-gateway/domains \
-H "Authorization: Bearer $TRUO_TOKEN"

operationId: mailgateway.domains.list

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.

Terminal window
curl https://api.truo.cloud/v1/mail-gateway/domains/example.com/verify \
-X POST \
-H "Authorization: Bearer $TRUO_TOKEN"

operationId: mailgateway.domains.verify

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.

Terminal window
curl https://api.truo.cloud/v1/mail-gateway/keys \
-X POST \
-H "Authorization: Bearer $TRUO_TOKEN"

operationId: mailgateway.keys.create

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.

Terminal window
curl https://api.truo.cloud/v1/mail-gateway/keys/mgk3f9a2b1c4d5 \
-X DELETE \
-H "Authorization: Bearer $TRUO_TOKEN"

operationId: mailgateway.keys.delete

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.

Terminal window
curl https://api.truo.cloud/v1/mail-gateway/keys \
-H "Authorization: Bearer $TRUO_TOKEN"

operationId: mailgateway.keys.list

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.

Terminal window
curl https://api.truo.cloud/v1/mail-gateway/messages \
-H "Authorization: Bearer $TRUO_TOKEN"

operationId: mailgateway.messages.list

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.

Terminal window
curl https://api.truo.cloud/v1/mail-gateway/metrics \
-H "Authorization: Bearer $TRUO_TOKEN"

operationId: mailgateway.metrics.get

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.

Terminal window
curl https://api.truo.cloud/v1/mail-gateway/smtp \
-H "Authorization: Bearer $TRUO_TOKEN"

operationId: mailgateway.smtp.get

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.

Terminal window
curl https://api.truo.cloud/v1/mail-gateway/smtp \
-X POST \
-H "Authorization: Bearer $TRUO_TOKEN"

operationId: mailgateway.smtp.reveal

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.

Terminal window
curl https://api.truo.cloud/v1/mail-gateway/smtp/rotate \
-X POST \
-H "Authorization: Bearer $TRUO_TOKEN"

operationId: mailgateway.smtp.rotate

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.

Terminal window
curl https://api.truo.cloud/v1/mail-gateway \
-H "Authorization: Bearer $TRUO_TOKEN"

operationId: mailgateway.tenant.get

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.

Terminal window
curl https://api.truo.cloud/v1/mail-gateway/usage \
-H "Authorization: Bearer $TRUO_TOKEN"

operationId: mailgateway.usage.get