Ship, observe, debug.
The message log is your audit trail and your debugging surface. Plus attachments, batch send patterns, and the right-to-be-forgotten deletion endpoint.
1
The message log
Every message Mail sends gets a row in the workspace-scoped message log. Outbound only (inbound messages live under the destination mailbox). Listable, filterable, exportable.
curl 'https://api.mail.productcraft.co/v1/workspaces/<ws>/messages?template=welcome&status=delivered&limit=20' \
-H 'authorization: Bearer pcft_live_...'{
"data": [
{
"id": "msg_a1b2...",
"to": "ada@example.com",
"from": "noreply@acme.com",
"template": "welcome",
"subject": "Welcome to Acme, Ada!",
"status": "delivered",
"sent_at": "2026-05-11T11:38:42Z",
"delivered_at": "2026-05-11T11:38:44Z",
"metadata": { "user_id": "...", "trigger": "signup" }
}
],
"pagination": { "next_cursor": "...", "has_more": true }
}Query params: template, status (queued / sent / delivered / bounced / complained / deferred / suppressed), since, until, to, from_mailbox_id.
2
Per-message detail
curl https://api.mail.productcraft.co/v1/workspaces/<ws>/messages/msg_a1b2... \
-H 'authorization: Bearer pcft_live_...'Returns the structured shape plus a timeline of state transitions (queued → sent → delivered, or queued → sent → bounced). For the actual rendered HTML/text body of a message, the body is gated separately:
curl https://api.mail.productcraft.co/v1/workspaces/<ws>/messages/msg_a1b2.../body \
-H 'authorization: Bearer pcft_live_...'Why a separate endpoint? Message bodies often contain PII — reset links with one-time codes, billing amounts, account details. The mail.message.body.read permission is distinct from mail.message.read so you can grant message-list access to support without exposing message contents.
3
Message timeline
A consolidated view of every event for a single message — useful for support debugging 'why didn't my customer get this?'
{
"message_id": "msg_a1b2...",
"events": [
{ "kind": "queued", "at": "2026-05-11T11:38:42Z" },
{ "kind": "sent", "at": "2026-05-11T11:38:43Z", "smtp_relay": "shared" },
{ "kind": "delivered", "at": "2026-05-11T11:38:44Z", "remote_mx": "gmail-smtp-in.l.google.com" },
{ "kind": "opened", "at": "2026-05-11T11:42:01Z", "user_agent": "..." }
]
}4
Export the message log
For compliance + analytics, the CSV export endpoint is the efficient path:
curl -o messages.csv \
'https://api.mail.productcraft.co/v1/workspaces/<ws>/messages/export?since=2026-05-01T00:00:00Z' \
-H 'authorization: Bearer pcft_live_...'Throttled — one export per minute per workspace. For large workspaces with millions of messages, exports paginate internally and the response includes a continuationURL.
5
Attachments
Inline base64 in the send call. Up to 10 attachments per message; total size under 10 MB.
curl https://api.mail.productcraft.co/v1/workspaces/<ws>/templates/invoice/send \
-H 'authorization: Bearer pcft_live_...' \
-H 'content-type: application/json' \
-d '{
"to": "ada@example.com",
"from_mailbox_id": "<billing-mailbox-id>",
"data": { "name": "Ada", "amount": "$29" },
"attachments": [
{
"filename": "invoice-2026-05.pdf",
"content_type": "application/pdf",
"content": "<base64-encoded-bytes>"
}
]
}'Big PDFs hit the size limit fast. For invoices over a few hundred KB, host the file on your own CDN and link to it in the email body instead. Better UX too — recipient opens on phone, sees the link, doesn't have to download to view.
6
Right-to-be-forgotten deletion
GDPR / CCPA workflow — a recipient asks you to delete every record of them. Mail exposes a workspace-scoped bulk delete by email address.
curl -X DELETE \
'https://api.mail.productcraft.co/v1/workspaces/<ws>/messages?recipient=ada@example.com' \
-H 'authorization: Bearer pcft_live_...'Irreversible. Removes every outbound message log row for that recipient address from this workspace. Audit-log entry is preserved (action + actor + timestamp + count); the actual message content + metadata is purged.
Doesn't touch the suppression list — the recipient stays suppressed (you don't want a bounced address to start bouncing again). Remove from suppression separately if needed.
7
Debugging guide
When a customer reports “I didn't get the email,” walk through these in order:
- Is the address suppressed?
GET /suppression?email=.... If yes, reason + timestamp tells you why. Resolve with them (correct the address) or remove from suppression with their consent. - Did we send?
GET /messages?to=ada@example.com&template=welcome. If no row, your application never called send. Bug in your code. - Did delivery succeed? Inspect the message timeline.
deliveredmeans receiver accepted; the email is in their world.bouncedmeans receiver rejected with a reason — share the diagnostic with the customer. - If delivered but recipient says missing — it's in their spam folder or their corporate filter quarantined it. Tell them to check spam; have them whitelist your domain.
- If “deferred” for hours — receiver throttling. Patient retries usually clear it; if it deferred for >6h, the receiver almost certainly tarpit-ed you. Worth opening a support ticket if it persists.
What's next
You've finished the series
From here:
- API reference for every endpoint with full request/response schemas.
- Auth guide 07 for the Mail-integrated verification + reset shortcut.
- Platform docs if you want to automate Mail setup (mint PAK, create mailboxes, deploy templates from CI).