Report a campaign

GET/api/v1/campaigns/{campaign}/report
Requires anorganization API keywith the scopecampaigns:read

Parameters

NameInTypeDescription
campaignrequiredpathstringThe campaign identifier.

Request

GET/api/v1/campaigns/{campaign}/report
curl -X GET 'https://app.mailyte.com/api/v1/campaigns/01JBT8XQ2M9WYC3K4F6R7S8T9V/report' \
  -H 'Authorization: Bearer mk_live_YOUR_API_KEY'
The key names its own organization, so no X-Organization-ID header is needed.

Response

Success.

  • dataobject
    • objectstringcampaign_report
    • campaign_idstring
    • statestringdraft | scheduled | sending | paused | sent | canceled | unknown

      The campaign's lifecycle state, from the same map `campaign.state` uses.

    • timelineobject

      Engagement per bucket from the send start to the last observed event. BOTH members are null for a campaign that never started — an empty `points` array beside a confident bucket size would read as "we measured and nothing happened", which is a different fact from "there is no window to measure over". Same cut as the stats counts, from the same column.

      • bucketstringhour | day

        `hour` while the window is short enough to read that way (72 hours), `day` after. Null when there is no window.

      • pointsarray<object>

        Null when the campaign never started; never `[]` in that case.

        • atstring

          The bucket boundary as an INSTANT, ISO 8601 UTC. The underlying value is a bucket label with no timezone and no `T`; publishing it unchanged meant every date parser read it as local time. `null` means UNKNOWN — the stored bucket label was empty or did not parse against this row's `bucket` format, so we could not say which instant it is. It never means "no time": the row's counts are still real, they just cannot be placed on the axis. Drop such a point rather than guessing where it goes.

        • deliveredinteger
        • openedinteger
        • clickedinteger
    • linksarray<object>

      Clicks grouped by DESTINATION, most-clicked first — a per-destination table, not a list of click events. At most 50 rows; no campaign at today's volumes comes near that. No ratio is published: you have both inputs.

      • urlstring

        Can be the literal string `(unknown link)`: click events from before the tracking pipeline echoed the original URL are grouped under it rather than dropped, so `total_clicks` still sums to `engagement.clicked`. It is a display sentinel in a data field and a known wart.

      • total_clicksinteger

        Every click on this destination.

      • unique_clickersinteger

        Distinct recipients who clicked it. Five clicks from one person is not five interested readers, which is why both numbers are kept.

Returned inside the standard envelope.

Errors

StatusWhen
401The API key is missing, unknown, revoked or expired. All four answer identically, on purpose: distinguishing them would confirm which keys exist.
403The key is valid but may not do this: it lacks the required scope, its IP allowlist does not include you, or this endpoint does not accept API keys.
404No such resource in this organization.
422The request was understood but the values were not acceptable.
429Too many requests, or the organization has spent its sending allowance. `Retry-After` says how long to wait.

Every status, with what causes it and what to do, is on the error reference.