> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apostra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Upload measurement records

> Batch-upload per-geo outcome records for the learning engine

`POST /api/v2/buyer/advertisers/{advertiserId}/measurement-records`

Uploads outcome measurement records (sales, brand lift, etc.) directly to the learning engine. Each record carries a value for one outcome type, geo, and time window. Up to 5,000 records per call. Records upsert on `(account, advertiser, outcomeType, source, geo, time window)`.

## Request

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.apostra.com/api/v2/buyer/advertisers/12345/measurement-records \
    -H "Authorization: Bearer $SCOPE3_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "records": [
        {
          "outcomeType": "incremental_revenue",
          "geo": "US-CA",
          "timeWindowStart": "2026-03-01",
          "timeWindowEnd": "2026-03-07",
          "value": 8450.75,
          "baselineValue": 7100.00,
          "confidenceInterval": 0.92,
          "source": "acme_incr_rev",
          "lagDays": 7
        }
      ]
    }'
  ```
</CodeGroup>

## Parameters

| Field | Type | Required | Notes |
| - | - | - | - |
| `advertiserId` | string | Yes | Path parameter — the owning advertiser |
| `records` | array | Yes | 1–5000 records |
| `records[].outcomeType` | string | Yes | Outcome being measured (e.g. `incremental_revenue`) |
| `records[].geo` | string | Yes | Geo identifier (e.g. `US-CA`) |
| `records[].timeWindowStart` | string | Yes | Date `YYYY-MM-DD` |
| `records[].timeWindowEnd` | string | Yes | Date `YYYY-MM-DD` |
| `records[].value` | number | Yes | Measured value |
| `records[].source` | string | Yes | Source key (matches a measurement source) |
| `records[].baselineValue` | number | No | Counterfactual baseline |
| `records[].confidenceInterval` | number | No | Confidence on the value |
| `records[].lagDays` | integer | No | Reporting lag in days |

## Response

```json theme={null}
{
  "recordsUpserted": 1,
  "records": [
    {
      "id": "mr_001",
      "advertiserId": "12345",
      "outcomeType": "incremental_revenue",
      "geo": "US-CA",
      "timeWindowStart": "2026-03-01",
      "timeWindowEnd": "2026-03-07",
      "value": 8450.75,
      "baselineValue": 7100.00,
      "confidenceInterval": 0.92,
      "source": "acme_incr_rev",
      "receivedAt": "2026-04-19T08:00:00Z",
      "lagDays": 7
    }
  ]
}
```

`recordsUpserted` is the number of records inserted or updated (records upsert on `(account, advertiser, outcomeType, source, geo, time window)`, so re-uploading the same key updates in place). `records[]` echoes the persisted rows.

## Errors

* `400 VALIDATION_ERROR` — empty `records`, more than 5,000 records, a missing required field, or a malformed `timeWindowStart`/`timeWindowEnd` date.
* `404 NOT_FOUND` — `advertiserId` does not exist or is not visible to the authenticated account.

See [Errors](/v2/reference/errors) for the full error contract.

## Related

<CardGroup cols={2}>
  <Card title="Measurement tasks" href="/v2/buyer/measurement/tasks" icon="list-check">
    All measurement operations
  </Card>

  <Card title="Measurement overview" href="/v2/guides/measurement-incrementality" icon="chart-line">
    Pipeline, belief state, and concepts
  </Card>

  <Card title="List measurement records" href="/v2/buyer/measurement/tasks/list-measurement-records" icon="list">
    Query uploaded records
  </Card>

  <Card title="Upload context records" href="/v2/buyer/measurement/tasks/upload-context-records" icon="cloud">
    Add market conditions to partial out
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.