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) + PDF (formatted report) — UPPERCASE literals. Constraints: 90-day max date range, cross-channel export only (returns 404 if ENABLE_CROSS_CHANNEL_ANALYTICS is off). Async via SQS EXPORT_UNIFIED_REPORT + job_result table tracks status; the finished file is downloaded in-app (the UI opens the download_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:

Date range + scopeTypical wait
7 days, 1 platform< 1 min
30 days, 3 platforms, CSV1-3 min
90 days, 5 platforms, PDF3-10 min

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_url directly 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: CSV or PDF (UPPERCASE literals — there is no mode field)
  • target_currency: 3-letter uppercase code
  • timezone: 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 / PDF
  • error: message (when failed)

CSV vs PDF — when to use which

Use PDFUse CSV
Client deliverableBI tool input
Monthly review meetingSpreadsheet analysis
Executive summaryAccounting reconciliation
Visual report with chartsRaw data manipulation
Polished branded outputQuick data dump

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,cpa

PDF

Standard sections:

  1. Cover page (workspace + date range + generated timestamp)
  2. Executive summary (KPI strip)
  3. Per-platform breakdown
  4. Channel mix donut
  5. Top campaigns
  6. Comparison vs previous period (if enabled)
  7. 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_id poll returns status: COMPLETED with a download_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:

  1. POST triggers SQS message → EXPORT_UNIFIED_REPORT worker type
  2. Worker runs query + formats output (CSV / PDF)
  3. Stores result file in S3 (or equivalent)
  4. Stores the file in S3 and generates a signed download_url
  5. Writes job_result row with status: 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 error field in job_result; common: timeout on very large datasets; reduce scope
  • Download didn't start: the file opens in-app when the poll returns COMPLETED with a download_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.