Export PDF / CSV reports
POST /api/v1/analytics/export → 202 + job_id. Poll GET /api/v1/analytics/export/:id. CSV + PDF (UPPERCASE). 90-day max. Cross-channel-flag gated. Async via SQS, in-app download.
Written By Salvatore Sinigaglia
Last updated About 4 hours ago
POST /api/v1/analytics/export → 202 + job_id. Poll GET /api/v1/analytics/export/:id. CSV + PDF (UPPERCASE). 90-day max. Cross-channel-flag gated. Async via SQS, in-app download.
Export PDF / CSV reports
POST /api/v1/analytics/export (verified
apps/backend/src/routes/api/analytics/export.route.ts) → 202 Accepted +{job_id, status: "PENDING"}. Poll GET /api/v1/analytics/export/:job_id until complete. Formats:CSV(raw data) +ENABLE_CROSS_CHANNEL_ANALYTICSis off). Async via SQSEXPORT_UNIFIED_REPORT+job_resulttable tracks status; the finished file is downloaded in-app (the UI opens thedownload_url), not emailed.
Who is this for
Mediabuyers + admins producing reports for clients / leadership / external stakeholders. Anyone needing analytics data outside Wevion's UI (in BI tools, spreadsheets, presentations).
How to export
Step 1: Configure your view
Open /analytics in Cross-Channel mode → set date range, platforms, and target_currency. The export reflects this configuration. Note: the export endpoint is cross-channel-only — it requires ENABLE_CROSS_CHANNEL_ANALYTICS and takes no per-mode field.
Step 2: Click Export
Top-right toolbar → Export → pick format (CSV or PDF).
Step 3: Job submitted
UI shows a "Report queued" state. POST /api/v1/analytics/export runs → returns 202 + {job_id, status: "PENDING"}.
Step 4: Wait / poll
The export runs asynchronously via SQS. Typical times:
For Cross-Channel exports: 90-day hard limit (MAX_DATE_RANGE_DAYS = 90).
Step 5: In-app download
Once complete (status: COMPLETED):
- The export bar polls the job and, when done, opens the
download_urldirectly in the browser - The file downloads in-app — there is no email delivery for the analytics export
Endpoint
POST /api/v1/analytics/export (verified):
Body:
since,until:YYYY-MM-DD(max 90-day span)platforms: array (1-8 platform codes)format:CSVorPDF(UPPERCASE literals — there is nomodefield)target_currency: 3-letter uppercase codetimezone: optional
Returns 202 Accepted + {job_id, status: "PENDING"}. If ENABLE_CROSS_CHANNEL_ANALYTICS is off, returns 404 ("Cross-channel analytics are disabled").
GET /api/v1/analytics/export/:job_id (verified):
Returns the job_result row:
status:PENDING/RUNNING/COMPLETED/FAILED(UPPERCASE)download_url: URL to download (when completed)format:CSV/PDFerror: message (when failed)
CSV vs PDF — when to use which
PDF wins on presentation; CSV wins on data manipulation.
(Similar to Ads Manager am-116 + am-117 for row-level reports — Analytics export is the aggregated companion.)
What's in each format
CSV
- UTF-8 encoded
- Comma-separated
- One row per entity per date OR aggregated (configurable)
- Headers: include all selected metrics + dimensions
Example header for Cross-Channel:
date,platform,spend,impressions,clicks,ctr,cpc,cpm,conversions,roas,cpaStandard sections:
- Cover page (workspace + date range + generated timestamp)
- Executive summary (KPI strip)
- Per-platform breakdown
- Channel mix donut
- Top campaigns
- Comparison vs previous period (if enabled)
- Appendix (filters + date range exact + currency notes)
Workspace branding (logo, colors) applied if configured.
Subscription requirement
Export requires an active subscription (see acc-* billing):
- Workspaces without active subscription: export returns 403 with reason "subscription_required"
- Plan tier may limit: number of exports per month, scheduled exports, custom branding
Check your plan at /settings/team/billing.
90-day max date range
Hard constraint inherited from Cross-Channel:
- Date range cap: 90 days
- Larger ranges: request rejected with clear error
- Workaround: split into 2-3 calls, concatenate externally
The export pipeline is the unified cross-channel one (EXPORT_UNIFIED_REPORT SQS worker type); the 90-day cap comes from MAX_DATE_RANGE_DAYS in cross-channel-analytics.service.ts.
In-app delivery
Once the job completes:
- The
GET /export/:job_idpoll returnsstatus: COMPLETEDwith adownload_url - The export bar opens that URL, downloading the file in the browser
- The analytics export does not send an email; you get the file in-app
(Ads Manager reports have their own delivery model — see am-116.)
Tracking + history
Reports section in /analytics (or admin view) shows:
- All recent export jobs with status
- Per-job download link (if completed)
- Per-job error message (if failed)
- Filter by date / format
Async via SQS
Behind the scenes:
- POST triggers SQS message →
EXPORT_UNIFIED_REPORTworker type - Worker runs query + formats output (CSV / PDF)
- Stores result file in S3 (or equivalent)
- Stores the file in S3 and generates a signed
download_url - Writes
job_resultrow withstatus: COMPLETED+download_url(the UI polls and downloads it in-app — no email is sent)
Worker scaling: handles multiple concurrent exports; bounded by SQS in-flight cap + S3 throughput.
Common mistakes
- Date range > 90 days: rejected; split or use narrower window
- Exporting without active subscription: 403; check
/settings/team/billing - Expecting synchronous download: it's async — poll the job until
COMPLETED, then it downloads in-app - Expecting an email with the file: the analytics export downloads in-app; there is no email delivery
- Sharing the signed download URL: the S3 signed URL is time-limited; re-export if it expires
- Format mismatch for use case: PDF for spreadsheet analysis = painful; CSV for client presentation = unpolished
Common issues
- Job stuck pending > 10 min: SQS worker backlog; usually self-resolves; if persistent contact admin
- Job failed with no clear error: check
errorfield in job_result; common: timeout on very large datasets; reduce scope - Download didn't start: the file opens in-app when the poll returns
COMPLETEDwith adownload_url; if the tab was closed, re-poll the job or re-export - PDF missing branding: workspace branding not configured (see am-117 templates)
- CSV totals differ from on-screen: may be export aggregation vs view aggregation difference; verify scope params
FAQ
How do I export a report from Wevion analytics?
Configure your Cross-Channel view at /analytics by setting date range, platforms, and target_currency, then click Export in the top-right toolbar and choose CSV or PDF. The export reflects that configuration and runs asynchronously; the UI polls the job and downloads the finished file in-app when it's ready (no email is sent).
Why is my export asynchronous instead of an instant download?
Wevion runs exports as background jobs via the SQS EXPORT_UNIFIED_REPORT worker. Submitting returns 202 Accepted with a job_id and status: PENDING; you then poll GET /api/v1/analytics/export/:job_id until status is COMPLETED (statuses are UPPERCASE: PENDING/RUNNING/COMPLETED/FAILED). Typical waits range from under a minute for 7 days on one platform to 3-10 minutes for 90 days across five platforms with PDF.
Should I choose CSV or PDF?
Choose PDF for client deliverables, review meetings, executive summaries, and polished branded output with charts. Choose CSV for BI-tool input, spreadsheet analysis, accounting reconciliation, and raw data manipulation. In Wevion, PDF wins on presentation while CSV wins on data manipulation, so match the format to how the report will be used.
Why can't I export more than 90 days?
Wevion enforces a hard 90-day date-range cap (MAX_DATE_RANGE_DAYS = 90) inherited from Cross-Channel and applied to the unified export pipeline for both modes. Larger ranges are rejected with a clear error. The workaround is to split the range into two or three exports and concatenate them externally.
Why did my export return a 403 or subscription error?
Export requires an active subscription. Workspaces without one get a 403 with the reason subscription_required, and your plan tier may also limit the number of exports per month, scheduled exports, or custom branding. Check your plan at /settings/team/billing to confirm export access.