Guides

    About 10 minutes

    IMPORT DELIVERED PERFORMANCE.

    Push what actually ran back into a plan, so planned versus delivered stops being a spreadsheet exercise.

    actuals:write

    Steps

    1. 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. 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. 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. 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.