List inbound log events
One timeline of what happened to your inbound mail, newest first: accepted, routed, stored, forwarded, forward failed and rejected — filterable by type, domain and search.
The inbound log answers "what happened to that message?" without joining several endpoints. Each event carries the fields of its kind: accepted (the MX took the message: from, size_bytes, auth verdicts, attachments), routed (the route that matched — route_id null means no route matched and the message was kept by default), stored (kept for the poll API, with expires_at), forwarded / forward_failed (one forward attempt: url, status_code, duration_ms, attempt, terminal, manual, next_retry_at) and rejected (the MX refused a recipient or message: smtp_code, enhanced_code and reason). Rejections keep no sender details.
GET /v3/inbound/logs
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
range | string | Optional | Window back from now: 24h, 7d (default) or 30d. |
type | string | Optional | One event type or a comma-separated list: accepted, routed, stored, forwarded, forward_failed, rejected. counts ignore it. |
domain | string | Optional | Only events of this recipient domain (case-insensitive). |
q | string | Optional | Search: a recipient substring, an inbound message ID, an RFC Message-ID or a forward URL substring. |
cursor | string | Optional | The next_cursor of the previous page. A cursor only continues the walk it came from. |
limit | integer | Optional | Page size, 1–200. Default 100. |
Pages walk backwards in time: has_more says whether older events follow and next_cursor fetches them. counts gives the number of events per type over the whole window (domain and q applied, type and cursor ignored), so one page is enough for a summary strip. reason on rejections is one of inbound_off, domain_unverified, plan_not_allowed, quota_exceeded, too_large, content_rejected. A send_only key cannot read inbound mail. Limit: 60 calls per minute per key.
Responses
| Status | Description |
|---|---|
200 | OK. { items, has_more, next_cursor, counts }, newest first. |
400 | validation_error — invalid filters; invalid_cursor — the cursor is not a next_cursor value. |
401 | Missing or invalid API key. |
403 | insufficient_scope — a send_only key cannot read inbound mail. |
429 | Rate limit exceeded (60 calls per minute). |
Code examples
# Forward attempts of the last 24 hours, failures included
curl -G https://api.wemail.io/v3/inbound/logs \
-H "Authorization: Bearer afn_live_…" \
--data-urlencode "range=24h" \
--data-urlencode "type=forwarded,forward_failed" \
--data-urlencode "limit=50"const url = new URL("https://api.wemail.io/v3/inbound/logs");
url.search = new URLSearchParams({ range: "24h", type: "forwarded,forward_failed", limit: "50" }).toString();
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.WEMAIL_API_KEY}` } });
const { items, counts, has_more, next_cursor } = await res.json();
for (const e of items) console.log(e.at, e.type, e.recipient, e.url, e.status_code, e.attempt, e.terminal);
console.log(counts.forward_failed, "failed attempts in the window", has_more ? next_cursor : "");import os, requests
r = requests.get(
"https://api.wemail.io/v3/inbound/logs",
headers={"Authorization": f"Bearer {os.environ['WEMAIL_API_KEY']}"},
params={"range": "24h", "type": "forwarded,forward_failed", "limit": 50},
)
page = r.json()
for e in page["items"]:
print(e["at"], e["type"], e["recipient"], e["url"], e["status_code"], e["attempt"], e["terminal"])
print(page["counts"]["forward_failed"], "failed attempts in the window")client, _ := wemail.NewClient(os.Getenv("WEMAIL_API_KEY"))
ctx := context.WithValue(context.Background(), wemail.ContextAccessToken, os.Getenv("WEMAIL_API_KEY"))
page, _, err := client.API.InboundAPI.ListInboundLogs(ctx).Limit(50).Execute()
for _, e := range page.Items { fmt.Println(e.At, e.Type, e.Recipient) }{
"items": [
{
"id": "fwd_9c2d4e6f1a3b4c5d8e7f0a1b2c3d4e5f",
"type": "forward_failed",
"at": "2026-11-04T08:15:02.000Z",
"domain": "inbound.example.com",
"recipient": "orders@inbound.example.com",
"message_id": "6f1c2a1e-7d3b-4b7e-9f0e-2a4c8d9b1e55",
"from": null,
"size_bytes": null,
"auth": null,
"attachments": null,
"route_id": "0b7e4c2d-9a1f-4e6b-8c3d-5f2a1b9e7d40",
"route_name": "Orders → app",
"route_priority": 10,
"expression": "match_recipient("orders@inbound.example.com")",
"expires_at": null,
"url": "https://app.example.com/inbound",
"status_code": 503,
"duration_ms": 412,
"attempt": 2,
"terminal": false,
"manual": false,
"next_retry_at": "2026-11-04T08:25:02.000Z",
"smtp_code": null,
"enhanced_code": null,
"reason": null
},
{
"id": "acc_6f1c2a1e7d3b4b7e9f0e2a4c8d9b1e55",
"type": "accepted",
"at": "2026-11-04T08:14:58.000Z",
"domain": "inbound.example.com",
"recipient": "orders@inbound.example.com",
"message_id": "6f1c2a1e-7d3b-4b7e-9f0e-2a4c8d9b1e55",
"from": "customer@example.org",
"size_bytes": 48213,
"auth": { "spf": "pass", "dkim": "pass", "dmarc": "pass" },
"attachments": 1,
"route_id": null,
"route_name": null,
"route_priority": null,
"expression": null,
"expires_at": null,
"url": null,
"status_code": null,
"duration_ms": null,
"attempt": null,
"terminal": null,
"manual": null,
"next_retry_at": null,
"smtp_code": null,
"enhanced_code": null,
"reason": null
}
],
"has_more": true,
"next_cursor": "eyJhdCI6IjIwMjYtMTEtMDRUMDg6MTQ6NTguMDAwWiIsImlkIjoiYWNjXzZmMWMifQ",
"counts": { "accepted": 191, "routed": 188, "stored": 176, "forwarded": 185, "forward_failed": 6, "rejected": 3 }
}