docs(campaigns): document insights storage and analytics
Covers the three things most likely to be broken by a well-meant change: insights are re-fetched and upserted rather than appended, breakdown rows duplicate the spend they break down and must be filtered out of aggregates, and both dashboard windows end yesterday because today is partial. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -234,6 +234,46 @@ raising a budget on a campaign that is awaiting approval returns `409`,
|
||||
because it would change the figure the approver is reviewing. Reject it back
|
||||
to draft, edit, and resubmit.
|
||||
|
||||
### Analytics
|
||||
|
||||
Insights are stored in `maskanx_campaign_insights` and read from there, so
|
||||
the dashboard stays fast and keeps working while Meta is rate-limiting. Only
|
||||
`POST /campaigns/{id}/insights/refresh` goes out to Meta.
|
||||
|
||||
| Route | Returns |
|
||||
| --- | --- |
|
||||
| `GET /campaigns/analytics/dashboard` | Yesterday and last-7-days spend, leads and cost per lead, plus the most and least efficient campaign |
|
||||
| `GET /campaigns/{id}/insights` | A daily series and the total for one campaign |
|
||||
| `GET /campaigns/{id}/insights?breakdown=age` | The same metrics split by one breakdown |
|
||||
| `POST /campaigns/{id}/insights/refresh` | Re-fetches from Meta now |
|
||||
|
||||
Three things about how this works are worth knowing before changing it:
|
||||
|
||||
**Insights are re-fetched, not appended.** Meta revises a day's figures for
|
||||
about 28 days as conversions are attributed late, so each sync re-reads a
|
||||
trailing window and upserts on
|
||||
`(level, object_id, date, breakdown_key, breakdown_value)`. Appending would
|
||||
double spend the first time a day was refreshed. Window:
|
||||
`MASKANX_INSIGHTS_WINDOW_DAYS`, default 28 — shortening it freezes late
|
||||
conversions out permanently.
|
||||
|
||||
**Breakdowns multiply rows, not columns.** The same spend appears in every
|
||||
breakdown's rows, so they are tagged with a `breakdown_key` and every
|
||||
aggregate filters to `breakdown_key = ''`. A query that summed across
|
||||
breakdowns would count the same spend once per age bracket.
|
||||
|
||||
**Both dashboard windows end yesterday.** Today's figures are partial;
|
||||
including them would make every "vs. last week" comparison read as a
|
||||
collapse each morning.
|
||||
|
||||
Costs are always computed from summed totals, never averaged from per-day
|
||||
costs — the average of daily costs per lead is not the cost per lead over the
|
||||
period. Cost fields are `null`, not `0`, when there is nothing to divide by.
|
||||
|
||||
Sync interval: `MASKANX_INSIGHTS_SYNC_SECONDS`, default 3600. `0` disables
|
||||
the loop; the dashboard then only updates when a campaign is refreshed by
|
||||
hand.
|
||||
|
||||
### Live smoke test
|
||||
|
||||
The unit suite runs entirely against a fake transport: it proves the code
|
||||
|
||||
Reference in New Issue
Block a user