Guushu Studio / Notes

Cloudflare Email Sending activity log shows only failures: 3 checks

Cloudflare Email Sending activity log shows only failures while mail arrives? Query the same GraphQL dataset by status and sendingDomain. 3 checks, 1 query.

By · · updated

A Worker sent mail through Cloudflare Email Service, the messages were arriving, and the sender had checked the raw sources to be sure. The Cloudflare Email Sending activity log still listed only failures, and Analytics showed 0 successes and a 100% failure rate. I answered that thread on Cloudflare Community on Sep 24.

You don't have to take the dashboard's word for it, or anyone's guess. The Activity log reads a GraphQL dataset you can query yourself, and the answer tells you which of three problems you have.

Does the Activity log have its own data?

No. The Email Service logs page says the Activity log surfaces the same per-event data as the emailSendingAdaptive and emailRoutingAdaptive GraphQL datasets. The analytics page says the dashboard charts are queried from the GraphQL Analytics API. If successful sends were recorded, a direct query finds them.

That turns a vague "the dashboard looks wrong" into a yes or no question. Either the rows exist and the dashboard is dropping them, or they were never written for the zone you're looking at.

The GraphQL query, by status and sendingDomain

The Email Service metrics and analytics page has a ready-made query called Email sending operations. It groups by date and status. I add sendingDomain, which is one of the documented dimensions and the one that matters here:

query EmailSendingByStatus($zoneTag: string!, $start: Date!, $end: Date!) {
  viewer {
    zones(filter: { zoneTag: $zoneTag }) {
      emailSendingAdaptiveGroups(
        filter: { date_geq: $start, date_leq: $end }
        limit: 10000
        orderBy: [date_DESC]
      ) {
        count
        dimensions { date status sendingDomain }
      }
    }
  }
}

Three details from the same page that trip people up. The token needs Analytics Read. zoneTag is the zone ID, not the account ID, because these are zone-level datasets. Data is kept for 31 days, so a date range older than that returns nothing.

To run it from a terminal, save the query as q.graphql:

jq -n --rawfile q q.graphql \
  --arg z "$ZONE_ID" --arg s 2026-09-01 --arg e 2026-09-30 \
  '{query: $q, variables: {zoneTag: $z, start: $s, end: $e}}' \
| curl -s https://api.cloudflare.com/client/v4/graphql \
    -H "Authorization: Bearer $CF_TOKEN" -H "Content-Type: application/json" -d @- \
| jq '.data.viewer.zones[0].emailSendingAdaptiveGroups'

Reading the result: rows, an empty list, or only failures

delivered rows come back. The data exists and the dashboard isn't showing it. That's a clean report for support: the query, the output, and a screenshot of the log for the same range.

An empty list comes back. That isn't an error, and it doesn't prove the dashboard is broken. When I ran this on my own zone for September, emailSendingAdaptiveGroups returned [], because I don't send through Email Service. The routing version of the same query returned 30 rows from the same zone. Empty means nothing was recorded under this zone ID.

Only failure statuses come back. Then the log is probably right and the mail you saw arrive went some other way, or through another zone. Keep reading.

Is the From domain on a different zone?

It's the first thing I'd check when the list is empty or short. These datasets are per zone, so a Worker whose From address is on another zone should log its successes under that zone. The docs don't say this in one sentence, so treat it as my inference from the zone-level design, not a documented rule.

The cheap test is in the dashboard. Go to Compute, Email Service, Email Sending, and pick the account-wide view instead of one domain before opening Analytics. If the missing sends show up there, the zone was the problem, not the log.

Delivery failed and Failed are different problems

Expand one failure before changing anything. The logs page lists five sending statuses, and two of them sound alike:

StatusMeaning per the docsWhere to look
SentAccepted and queuedWait, or check for a later event
DeliveredRecipient server accepted itNothing to fix
Delivery failedBounced, hard or softRecipient address, their mail server response
RejectedRecipient is on your suppression listSuppressions page
FailedConfiguration or authentication problemSending domain setup, DNS, the Worker binding

A log full of Delivery failed while mail is landing would be odd. A log full of Failed points at the sending domain's own setup. The docs also have a Delivery failure analysis query that groups by errorCause and sendingDomain, and the per-event emailSendingAdaptive dataset adds errorDetail.

What I'd hand support

If delivered rows exist and the dashboard shows 100% failed, the sender is right that something is off on Cloudflare's side. The report that gets a quick answer is short: the zone ID, the date range, the GraphQL output with status and sendingDomain, and the Activity log screenshot for the same range.

The receiving side works the same way. Its dataset is emailRoutingAdaptiveGroups, and a blank routing log has its own diagnosis, which I wrote up in Cloudflare Email Routing stopped working. If you're only receiving, the Cloudflare Email Routing support address setup is the simpler place to start.

The shared inbox I'm sketching sits on the receiving side of Email Service, one support@ that two people can answer. It's a waitlist page today, not a product: guard.guushu.com/inbox.