About 10 minutes
IMPORT DELIVERED PERFORMANCE.
Push what actually ran back into a plan, so planned versus delivered stops being a spreadsheet exercise.
Steps
1. Get a key
Workspace admins issue keys in the app at /app/api-keys. Start with a sandbox key: it is bound to a showroom workspace and can never touch live data. Keep the secret server-side.
export QN_API_KEY="qn_sandbox_…" curl "$QN_BASE/me" -H "Authorization: Bearer $QN_API_KEY"2. Shape the rows
One row per line item per day: impressions, clicks, conversions and spend. Up to 500 rows per call. Repeating a day for a line item overwrites it, so a re-run of yesterday is safe.
curl -X POST "$QN_BASE/imports/actuals" \ -H "Authorization: Bearer $QN_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "rows": [{ "line_item_id": "lit_…", "date": "2026-09-12", "impressions": 184200, "clicks": 1420, "conversions": 63, "spend": 2400.55, "currency": "AED" }] }'3. Read the verdict
The response counts rows written, rows skipped and rows rejected, with the reason per rejected row. Nothing is written silently.
4. Or let us pull it
If your ad server can post out, send the same rows to your inbound webhook endpoint as an actuals.reported event instead of running a job.
Done when
A 200 with written greater than zero, then the delivered column filling in on the plan.