# Attribution Reports

> Save multi-touch attribution reports and compare channel, source, campaign, and term credit across six attribution models.

Source: https://docs.oursprivacy.com/docs/platform-api/attribution-reports

Attribution reports credit your marketing channels, sources, campaigns, and terms for up to five conversion outcomes. Every result includes credit under first touch, last touch, linear, U-shaped, J-shaped, and time decay models, so you can compare models without running the report again. The [Attribution Reports guide](https://docs.oursprivacy.com/docs/attribution/attribution-reports) covers building and reading these reports in the app.

Updates, duplicates, and deletes take the report's current `expectedRevision`, and a stale one returns `409` so you never overwrite a teammate's edit. A `PATCH` replaces any `reportContext` or `definition` you send in full, and `null` clears the description. To compute results, send a saved or draft `definition` with an inclusive date range of up to 92 days to the preview endpoint; nothing is saved. Structured query parameters are JSON-encoded.

Every number in a result opens the visitors behind it. Send the same `definition`, dates, `comparison`, and `interval` to the visitors endpoint with a `selection`: the outcome, period, row, touch role, and optionally one chart bucket. The list total matches the number you picked: a first-touch credit of 12 under unique counting opens exactly 12 visitors. Each visitor comes with their credited first and last touch, conversion time, sessions, credit, value, and recent session replays, so you can open their profile or watch what they did. The visitors endpoint requires an API key with the View Visitor permission as well as Analytics Suite access.

To see what moved, send the same input to the insights endpoint. It returns up to eight findings: sources, campaigns, and terms that are new to your account and already earning credit, the largest share-adjusted changes against your comparison period, channels that first and last touch credit very differently, and rows that went quiet. Each finding carries its baseline and sample size, and names the row it is about so you can find it in the preview result. Add a comparison to get every kind of finding; without one you get new values only.

## GET /rest/v1/attribution-reports

List saved attribution reports in stable ID order with case-insensitive name search. Defaults to 20 results; each request scans at most 100 reports. Search and unavailable or deleted entries can yield short pages. Continue with pagination.nextCursor while hasMore is true and preserve unavailableCount. Requires Analytics Suite access. Requires API-key scope or current OAuth user permission: web-analytics:view

Request URL: `GET https://app.oursprivacy.com/rest/v1/attribution-reports`

Authentication: apiKey or oauth2.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | integer | No | Maximum attribution reports to return. Defaults to 20; values above 100 are capped at 100. Each page scans at most 100 reports in stable ID order, so pages may be short even when more results exist. Maximum attribution reports to return. Defaults to 20; values above 100 are capped at 100. Each page scans at most 100 reports in stable ID order, so pages may be short even when more results exist. |
| `cursor` | query | string | No | Opaque pagination.nextCursor from the previous page, scoped to the account and search text. Continue while pagination.hasMore is true; changing the search requires starting without a cursor. Opaque pagination.nextCursor from the previous page, scoped to the account and search text. Continue while pagination.hasMore is true; changing the search requires starting without a cursor. |
| `search` | query | string | No | Case-insensitive substring search on attribution report names. Case-insensitive substring search on attribution report names. |

### Responses

- `200`: Saved attribution reports and list completeness
- `400`: Bad Request
- `401`: Unauthorized — missing, invalid, or expired API key or OAuth access token
- `403`: Forbidden — credential does not have the required current Ours permission
- `404`: Not Found
- `500`: Internal Server Error

#### `200` response body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `entities` | object[] | Yes |  |
| `entities[].id` | string (uuid) | Yes |  |
| `entities[].revision` | string (uuid) | Yes |  |
| `entities[].name` | string | Yes | Display name for the saved attribution report. |
| `entities[].description` | string \| null | No | Optional description; null clears it. |
| `entities[].reportContext` | object | Yes | Default UTC date range: a preset or inclusive from/to calendar dates. Replaces the saved context on PATCH. Results cover at most 92 inclusive days. |
| `entities[].reportContext.dateRange` | object | Yes |  |
| `entities[].definition` | object | Yes | Version 1 attribution definition: one to five conversion outcomes, each with one to ten unique event names, an optional filter, and an optional numeric event property summed as value. Set the credit model (default last_touch), counting unit (default unique), lookback window of 7, 14, 30, 60, or 90 days (default 30), an optional filter applied to every outcome, and the value currency (default USD). Outcome ids identify outcomes in results. Replaces the saved definition on PATCH. |
| `entities[].definition.version` | number | Yes | One of: `1`. |
| `entities[].definition.kind` | string | Yes | One of: `"attribution"`. |
| `entities[].definition.outcomes` | object[] | Yes |  |
| `entities[].definition.outcomes[].id` | string (uuid) | Yes |  |
| `entities[].definition.outcomes[].label` | string | Yes |  |
| `entities[].definition.outcomes[].eventNames` | string[] | Yes |  |
| `entities[].definition.outcomes[].filter` | object | No |  |
| `entities[].definition.outcomes[].filter.version` | number | Yes | One of: `1`. |
| `entities[].definition.outcomes[].filter.filter` | object | Yes |  |
| `entities[].definition.outcomes[].value` | object | No |  |
| `entities[].definition.outcomes[].value.property` | string | Yes | One of: `"event.properties"`. |
| `entities[].definition.outcomes[].value.type` | string | No | One of: `"number"`. |
| `entities[].definition.outcomes[].value.path` | string[] | Yes |  |
| `entities[].definition.model` | string | No | One of: `"first_touch"`, `"last_touch"`, `"linear"`, `"u_shaped"`, `"j_shaped"`, `"time_decay"`. |
| `entities[].definition.counting` | string | No | One of: `"unique"`, `"totals"`, `"sessions"`. |
| `entities[].definition.lookbackDays` | number | No |  |
| `entities[].definition.filter` | object | No |  |
| `entities[].definition.filter.version` | number | Yes | One of: `1`. |
| `entities[].definition.filter.filter` | object | Yes |  |
| `entities[].definition.currency` | string | No | One of: `"USD"`, `"CAD"`, `"GBP"`, `"EUR"`, `"AUD"`. |
| `entities[].createdAt` | string (date-time) | Yes |  |
| `entities[].updatedAt` | string (date-time) | Yes |  |
| `entities[].createdByUserId` | string | Yes |  |
| `entities[].updatedByUserId` | string | Yes |  |
| `pagination` | object | Yes |  |
| `pagination.nextCursor` | string \| null | Yes |  |
| `pagination.hasMore` | boolean | Yes |  |
| `unavailableCount` | integer | Yes | Number of attribution reports that could not be loaded. A positive count means this list is incomplete. |

Full schema: [OpenAPI specification](https://docs.oursprivacy.com/openapi-platform.json).

## POST /rest/v1/attribution-reports

Save an attribution report with a default date context and a definition of up to five conversion outcomes, the credit model, counting unit, lookback window, and currency. Use GET /rest/v1/attribution-reports/preview to compute results. Requires Analytics Suite access. Requires API-key scope or current OAuth user permission: web-analytics:write

Request URL: `POST https://app.oursprivacy.com/rest/v1/attribution-reports`

Authentication: apiKey or oauth2.

### Request body (`application/json`, required)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Yes | Display name for the saved attribution report. |
| `description` | string \| null | No | Optional description; null clears it. |
| `reportContext` | object | Yes | Default UTC date range: a preset or inclusive from/to calendar dates. Replaces the saved context on PATCH. Results cover at most 92 inclusive days. |
| `reportContext.dateRange` | object | Yes |  |
| `definition` | object | Yes | Version 1 attribution definition: one to five conversion outcomes, each with one to ten unique event names, an optional filter, and an optional numeric event property summed as value. Set the credit model (default last_touch), counting unit (default unique), lookback window of 7, 14, 30, 60, or 90 days (default 30), an optional filter applied to every outcome, and the value currency (default USD). Outcome ids identify outcomes in results. Replaces the saved definition on PATCH. |
| `definition.version` | number | Yes | One of: `1`. |
| `definition.kind` | string | Yes | One of: `"attribution"`. |
| `definition.outcomes` | object[] | Yes |  |
| `definition.outcomes[].id` | string (uuid) | Yes |  |
| `definition.outcomes[].label` | string | Yes |  |
| `definition.outcomes[].eventNames` | string[] | Yes |  |
| `definition.outcomes[].filter` | object | No |  |
| `definition.outcomes[].filter.version` | number | Yes | One of: `1`. |
| `definition.outcomes[].filter.filter` | object | Yes |  |
| `definition.outcomes[].value` | object | No |  |
| `definition.outcomes[].value.property` | string | Yes | One of: `"event.properties"`. |
| `definition.outcomes[].value.type` | string | No | One of: `"number"`. |
| `definition.outcomes[].value.path` | string[] | Yes |  |
| `definition.model` | string | No | One of: `"first_touch"`, `"last_touch"`, `"linear"`, `"u_shaped"`, `"j_shaped"`, `"time_decay"`. |
| `definition.counting` | string | No | One of: `"unique"`, `"totals"`, `"sessions"`. |
| `definition.lookbackDays` | number | No |  |
| `definition.filter` | object | No |  |
| `definition.filter.version` | number | Yes | One of: `1`. |
| `definition.filter.filter` | object | Yes |  |
| `definition.currency` | string | No | One of: `"USD"`, `"CAD"`, `"GBP"`, `"EUR"`, `"AUD"`. |

### Responses

- `200`: Created attribution report
- `400`: Bad Request
- `401`: Unauthorized — missing, invalid, or expired API key or OAuth access token
- `403`: Forbidden — credential does not have the required current Ours permission
- `404`: Not Found
- `500`: Internal Server Error

#### `200` response body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) | Yes |  |
| `revision` | string (uuid) | Yes |  |
| `name` | string | Yes | Display name for the saved attribution report. |
| `description` | string \| null | No | Optional description; null clears it. |
| `reportContext` | object | Yes | Default UTC date range: a preset or inclusive from/to calendar dates. Replaces the saved context on PATCH. Results cover at most 92 inclusive days. |
| `reportContext.dateRange` | object | Yes |  |
| `definition` | object | Yes | Version 1 attribution definition: one to five conversion outcomes, each with one to ten unique event names, an optional filter, and an optional numeric event property summed as value. Set the credit model (default last_touch), counting unit (default unique), lookback window of 7, 14, 30, 60, or 90 days (default 30), an optional filter applied to every outcome, and the value currency (default USD). Outcome ids identify outcomes in results. Replaces the saved definition on PATCH. |
| `definition.version` | number | Yes | One of: `1`. |
| `definition.kind` | string | Yes | One of: `"attribution"`. |
| `definition.outcomes` | object[] | Yes |  |
| `definition.outcomes[].id` | string (uuid) | Yes |  |
| `definition.outcomes[].label` | string | Yes |  |
| `definition.outcomes[].eventNames` | string[] | Yes |  |
| `definition.outcomes[].filter` | object | No |  |
| `definition.outcomes[].filter.version` | number | Yes | One of: `1`. |
| `definition.outcomes[].filter.filter` | object | Yes |  |
| `definition.outcomes[].value` | object | No |  |
| `definition.outcomes[].value.property` | string | Yes | One of: `"event.properties"`. |
| `definition.outcomes[].value.type` | string | No | One of: `"number"`. |
| `definition.outcomes[].value.path` | string[] | Yes |  |
| `definition.model` | string | No | One of: `"first_touch"`, `"last_touch"`, `"linear"`, `"u_shaped"`, `"j_shaped"`, `"time_decay"`. |
| `definition.counting` | string | No | One of: `"unique"`, `"totals"`, `"sessions"`. |
| `definition.lookbackDays` | number | No |  |
| `definition.filter` | object | No |  |
| `definition.filter.version` | number | Yes | One of: `1`. |
| `definition.filter.filter` | object | Yes |  |
| `definition.currency` | string | No | One of: `"USD"`, `"CAD"`, `"GBP"`, `"EUR"`, `"AUD"`. |
| `createdAt` | string (date-time) | Yes |  |
| `updatedAt` | string (date-time) | Yes |  |
| `createdByUserId` | string | Yes |  |
| `updatedByUserId` | string | Yes |  |

Full schema: [OpenAPI specification](https://docs.oursprivacy.com/openapi-platform.json).

## GET /rest/v1/attribution-reports/{id}

Get a saved attribution report including its revision, default date context, and attribution definition. Requires Analytics Suite access; unavailable reports return 404. Requires API-key scope or current OAuth user permission: web-analytics:view

Request URL: `GET https://app.oursprivacy.com/rest/v1/attribution-reports/{id}`

Authentication: apiKey or oauth2.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string (uuid) | Yes | Saved attribution report identifier returned by list_attribution_reports or create_attribution_report. Saved attribution report identifier returned by list_attribution_reports or create_attribution_report. |

### Responses

- `200`: Saved attribution report
- `400`: Bad Request
- `401`: Unauthorized — missing, invalid, or expired API key or OAuth access token
- `403`: Forbidden — credential does not have the required current Ours permission
- `404`: Not Found
- `500`: Internal Server Error

#### `200` response body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) | Yes |  |
| `revision` | string (uuid) | Yes |  |
| `name` | string | Yes | Display name for the saved attribution report. |
| `description` | string \| null | No | Optional description; null clears it. |
| `reportContext` | object | Yes | Default UTC date range: a preset or inclusive from/to calendar dates. Replaces the saved context on PATCH. Results cover at most 92 inclusive days. |
| `reportContext.dateRange` | object | Yes |  |
| `definition` | object | Yes | Version 1 attribution definition: one to five conversion outcomes, each with one to ten unique event names, an optional filter, and an optional numeric event property summed as value. Set the credit model (default last_touch), counting unit (default unique), lookback window of 7, 14, 30, 60, or 90 days (default 30), an optional filter applied to every outcome, and the value currency (default USD). Outcome ids identify outcomes in results. Replaces the saved definition on PATCH. |
| `definition.version` | number | Yes | One of: `1`. |
| `definition.kind` | string | Yes | One of: `"attribution"`. |
| `definition.outcomes` | object[] | Yes |  |
| `definition.outcomes[].id` | string (uuid) | Yes |  |
| `definition.outcomes[].label` | string | Yes |  |
| `definition.outcomes[].eventNames` | string[] | Yes |  |
| `definition.outcomes[].filter` | object | No |  |
| `definition.outcomes[].filter.version` | number | Yes | One of: `1`. |
| `definition.outcomes[].filter.filter` | object | Yes |  |
| `definition.outcomes[].value` | object | No |  |
| `definition.outcomes[].value.property` | string | Yes | One of: `"event.properties"`. |
| `definition.outcomes[].value.type` | string | No | One of: `"number"`. |
| `definition.outcomes[].value.path` | string[] | Yes |  |
| `definition.model` | string | No | One of: `"first_touch"`, `"last_touch"`, `"linear"`, `"u_shaped"`, `"j_shaped"`, `"time_decay"`. |
| `definition.counting` | string | No | One of: `"unique"`, `"totals"`, `"sessions"`. |
| `definition.lookbackDays` | number | No |  |
| `definition.filter` | object | No |  |
| `definition.filter.version` | number | Yes | One of: `1`. |
| `definition.filter.filter` | object | Yes |  |
| `definition.currency` | string | No | One of: `"USD"`, `"CAD"`, `"GBP"`, `"EUR"`, `"AUD"`. |
| `createdAt` | string (date-time) | Yes |  |
| `updatedAt` | string (date-time) | Yes |  |
| `createdByUserId` | string | Yes |  |
| `updatedByUserId` | string | Yes |  |

Full schema: [OpenAPI specification](https://docs.oursprivacy.com/openapi-platform.json).

## PATCH /rest/v1/attribution-reports/{id}

Update an attribution report using expectedRevision. Omitted fields remain unchanged; null clears the description. Supplied reportContext and definition replace the entire saved value. Requires Analytics Suite access. Requires API-key scope or current OAuth user permission: web-analytics:write

Request URL: `PATCH https://app.oursprivacy.com/rest/v1/attribution-reports/{id}`

Authentication: apiKey or oauth2.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string (uuid) | Yes | Saved attribution report identifier returned by list_attribution_reports or create_attribution_report. Saved attribution report identifier returned by list_attribution_reports or create_attribution_report. |

### Request body (`application/json`, required)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `expectedRevision` | string (uuid) | Yes | Current revision from get_attribution_report, list_attribution_reports, or the last write. A stale revision returns 409; reload before retrying. |
| `name` | string | No | Display name for the saved attribution report. |
| `description` | string \| null | No | Optional description; null clears it. |
| `reportContext` | object | No | Default UTC date range: a preset or inclusive from/to calendar dates. Replaces the saved context on PATCH. Results cover at most 92 inclusive days. |
| `reportContext.dateRange` | object | Yes |  |
| `definition` | object | No | Version 1 attribution definition: one to five conversion outcomes, each with one to ten unique event names, an optional filter, and an optional numeric event property summed as value. Set the credit model (default last_touch), counting unit (default unique), lookback window of 7, 14, 30, 60, or 90 days (default 30), an optional filter applied to every outcome, and the value currency (default USD). Outcome ids identify outcomes in results. Replaces the saved definition on PATCH. |
| `definition.version` | number | Yes | One of: `1`. |
| `definition.kind` | string | Yes | One of: `"attribution"`. |
| `definition.outcomes` | object[] | Yes |  |
| `definition.outcomes[].id` | string (uuid) | Yes |  |
| `definition.outcomes[].label` | string | Yes |  |
| `definition.outcomes[].eventNames` | string[] | Yes |  |
| `definition.outcomes[].filter` | object | No |  |
| `definition.outcomes[].filter.version` | number | Yes | One of: `1`. |
| `definition.outcomes[].filter.filter` | object | Yes |  |
| `definition.outcomes[].value` | object | No |  |
| `definition.outcomes[].value.property` | string | Yes | One of: `"event.properties"`. |
| `definition.outcomes[].value.type` | string | No | One of: `"number"`. |
| `definition.outcomes[].value.path` | string[] | Yes |  |
| `definition.model` | string | No | One of: `"first_touch"`, `"last_touch"`, `"linear"`, `"u_shaped"`, `"j_shaped"`, `"time_decay"`. |
| `definition.counting` | string | No | One of: `"unique"`, `"totals"`, `"sessions"`. |
| `definition.lookbackDays` | number | No |  |
| `definition.filter` | object | No |  |
| `definition.filter.version` | number | Yes | One of: `1`. |
| `definition.filter.filter` | object | Yes |  |
| `definition.currency` | string | No | One of: `"USD"`, `"CAD"`, `"GBP"`, `"EUR"`, `"AUD"`. |

### Responses

- `200`: Updated attribution report
- `400`: Bad Request
- `401`: Unauthorized — missing, invalid, or expired API key or OAuth access token
- `403`: Forbidden — credential does not have the required current Ours permission
- `404`: Not Found
- `409`: Attribution report revision conflict. Reload before retrying.
- `500`: Internal Server Error

#### `200` response body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) | Yes |  |
| `revision` | string (uuid) | Yes |  |
| `name` | string | Yes | Display name for the saved attribution report. |
| `description` | string \| null | No | Optional description; null clears it. |
| `reportContext` | object | Yes | Default UTC date range: a preset or inclusive from/to calendar dates. Replaces the saved context on PATCH. Results cover at most 92 inclusive days. |
| `reportContext.dateRange` | object | Yes |  |
| `definition` | object | Yes | Version 1 attribution definition: one to five conversion outcomes, each with one to ten unique event names, an optional filter, and an optional numeric event property summed as value. Set the credit model (default last_touch), counting unit (default unique), lookback window of 7, 14, 30, 60, or 90 days (default 30), an optional filter applied to every outcome, and the value currency (default USD). Outcome ids identify outcomes in results. Replaces the saved definition on PATCH. |
| `definition.version` | number | Yes | One of: `1`. |
| `definition.kind` | string | Yes | One of: `"attribution"`. |
| `definition.outcomes` | object[] | Yes |  |
| `definition.outcomes[].id` | string (uuid) | Yes |  |
| `definition.outcomes[].label` | string | Yes |  |
| `definition.outcomes[].eventNames` | string[] | Yes |  |
| `definition.outcomes[].filter` | object | No |  |
| `definition.outcomes[].filter.version` | number | Yes | One of: `1`. |
| `definition.outcomes[].filter.filter` | object | Yes |  |
| `definition.outcomes[].value` | object | No |  |
| `definition.outcomes[].value.property` | string | Yes | One of: `"event.properties"`. |
| `definition.outcomes[].value.type` | string | No | One of: `"number"`. |
| `definition.outcomes[].value.path` | string[] | Yes |  |
| `definition.model` | string | No | One of: `"first_touch"`, `"last_touch"`, `"linear"`, `"u_shaped"`, `"j_shaped"`, `"time_decay"`. |
| `definition.counting` | string | No | One of: `"unique"`, `"totals"`, `"sessions"`. |
| `definition.lookbackDays` | number | No |  |
| `definition.filter` | object | No |  |
| `definition.filter.version` | number | Yes | One of: `1`. |
| `definition.filter.filter` | object | Yes |  |
| `definition.currency` | string | No | One of: `"USD"`, `"CAD"`, `"GBP"`, `"EUR"`, `"AUD"`. |
| `createdAt` | string (date-time) | Yes |  |
| `updatedAt` | string (date-time) | Yes |  |
| `createdByUserId` | string | Yes |  |
| `updatedByUserId` | string | Yes |  |

Full schema: [OpenAPI specification](https://docs.oursprivacy.com/openapi-platform.json).

## DELETE /rest/v1/attribution-reports/{id}

Delete an attribution report using expectedRevision. Requires Analytics Suite access. Requires API-key scope or current OAuth user permission: web-analytics:write

Request URL: `DELETE https://app.oursprivacy.com/rest/v1/attribution-reports/{id}`

Authentication: apiKey or oauth2.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string (uuid) | Yes | Saved attribution report identifier returned by list_attribution_reports or create_attribution_report. Saved attribution report identifier returned by list_attribution_reports or create_attribution_report. |
| `expectedRevision` | query | string (uuid) | Yes | Current revision from get_attribution_report, list_attribution_reports, or the last write. A stale revision returns 409; reload before retrying. Current revision from get_attribution_report, list_attribution_reports, or the last write. A stale revision returns 409; reload before retrying. |

### Responses

- `200`: Deleted attribution report and revision
- `400`: Bad Request
- `401`: Unauthorized — missing, invalid, or expired API key or OAuth access token
- `403`: Forbidden — credential does not have the required current Ours permission
- `404`: Not Found
- `409`: Attribution report revision conflict. Reload before retrying.
- `500`: Internal Server Error

#### `200` response body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) | Yes |  |
| `deleted` | boolean | Yes | One of: `true`. |
| `deletedRevision` | string (uuid) | Yes |  |

Full schema: [OpenAPI specification](https://docs.oursprivacy.com/openapi-platform.json).

## POST /rest/v1/attribution-reports/{id}/duplicate

Duplicate an attribution report using the expectedRevision in the request body. Copies the description, date context, and definition under a new id with "(copy)" appended to the name. Requires Analytics Suite access. Requires API-key scope or current OAuth user permission: web-analytics:write, web-analytics:view

Request URL: `POST https://app.oursprivacy.com/rest/v1/attribution-reports/{id}/duplicate`

Authentication: apiKey or oauth2.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string (uuid) | Yes | Saved attribution report identifier returned by list_attribution_reports or create_attribution_report. Saved attribution report identifier returned by list_attribution_reports or create_attribution_report. |

### Request body (`application/json`, required)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `expectedRevision` | string (uuid) | Yes | Current revision from get_attribution_report, list_attribution_reports, or the last write. A stale revision returns 409; reload before retrying. |

### Responses

- `200`: Duplicated attribution report
- `400`: Bad Request
- `401`: Unauthorized — missing, invalid, or expired API key or OAuth access token
- `403`: Forbidden — credential does not have the required current Ours permission
- `404`: Not Found
- `409`: Attribution report revision conflict. Reload before retrying.
- `500`: Internal Server Error

#### `200` response body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string (uuid) | Yes |  |
| `revision` | string (uuid) | Yes |  |
| `name` | string | Yes | Display name for the saved attribution report. |
| `description` | string \| null | No | Optional description; null clears it. |
| `reportContext` | object | Yes | Default UTC date range: a preset or inclusive from/to calendar dates. Replaces the saved context on PATCH. Results cover at most 92 inclusive days. |
| `reportContext.dateRange` | object | Yes |  |
| `definition` | object | Yes | Version 1 attribution definition: one to five conversion outcomes, each with one to ten unique event names, an optional filter, and an optional numeric event property summed as value. Set the credit model (default last_touch), counting unit (default unique), lookback window of 7, 14, 30, 60, or 90 days (default 30), an optional filter applied to every outcome, and the value currency (default USD). Outcome ids identify outcomes in results. Replaces the saved definition on PATCH. |
| `definition.version` | number | Yes | One of: `1`. |
| `definition.kind` | string | Yes | One of: `"attribution"`. |
| `definition.outcomes` | object[] | Yes |  |
| `definition.outcomes[].id` | string (uuid) | Yes |  |
| `definition.outcomes[].label` | string | Yes |  |
| `definition.outcomes[].eventNames` | string[] | Yes |  |
| `definition.outcomes[].filter` | object | No |  |
| `definition.outcomes[].filter.version` | number | Yes | One of: `1`. |
| `definition.outcomes[].filter.filter` | object | Yes |  |
| `definition.outcomes[].value` | object | No |  |
| `definition.outcomes[].value.property` | string | Yes | One of: `"event.properties"`. |
| `definition.outcomes[].value.type` | string | No | One of: `"number"`. |
| `definition.outcomes[].value.path` | string[] | Yes |  |
| `definition.model` | string | No | One of: `"first_touch"`, `"last_touch"`, `"linear"`, `"u_shaped"`, `"j_shaped"`, `"time_decay"`. |
| `definition.counting` | string | No | One of: `"unique"`, `"totals"`, `"sessions"`. |
| `definition.lookbackDays` | number | No |  |
| `definition.filter` | object | No |  |
| `definition.filter.version` | number | Yes | One of: `1`. |
| `definition.filter.filter` | object | Yes |  |
| `definition.currency` | string | No | One of: `"USD"`, `"CAD"`, `"GBP"`, `"EUR"`, `"AUD"`. |
| `createdAt` | string (date-time) | Yes |  |
| `updatedAt` | string (date-time) | Yes |  |
| `createdByUserId` | string | Yes |  |
| `updatedByUserId` | string | Yes |  |

Full schema: [OpenAPI specification](https://docs.oursprivacy.com/openapi-platform.json).

## GET /rest/v1/attribution-reports/preview

Compute attribution credit for a saved or draft definition without saving it. JSON-encode input as {definition,from,to,comparison?,interval?}; dates are inclusive UTC calendar strings spanning at most 92 days. Results include a total row, channel, source/medium, campaign, and term breakdowns, and a time series, with credit under every model. Maximum 16384 bytes before URL encoding. Requires Analytics Suite access. Requires API-key scope or current OAuth user permission: web-analytics:view

Request URL: `GET https://app.oursprivacy.com/rest/v1/attribution-reports/preview`

Authentication: apiKey or oauth2.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `input` | query | string | Yes | Required input: definition, from, and to; comparison and interval are optional. JSON-encode this object in the REST query string; maximum 16384 bytes before URL encoding. Computing a result never saves or changes a report. Required input: definition, from, and to; comparison and interval are optional. JSON-encode this object in the REST query string; maximum 16384 bytes before URL encoding. Computing a result never saves or changes a report. |

### Responses

- `200`: Attribution result
- `400`: Invalid definition, filter, or date range.
- `401`: Unauthorized — missing, invalid, or expired API key or OAuth access token
- `403`: Forbidden — credential does not have the required current Ours permission
- `404`: Not Found
- `500`: Internal Server Error

#### `200` response body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `from` | string | Yes |  |
| `to` | string | Yes |  |
| `previousFrom` | string \| null | Yes | Inclusive comparison start date; null without a comparison. |
| `previousTo` | string \| null | Yes | Inclusive comparison end date; null without a comparison. |
| `interval` | string | Yes | One of: `"day"`, `"week"`. |
| `counting` | string | Yes | One of: `"unique"`, `"totals"`, `"sessions"`. |
| `channelRulesVersion` | integer | Yes |  |
| `rows` | object[] | Yes | The total row followed by channel, source/medium, campaign, and term breakdown rows, ordered by current-period credit under the selected model. |
| `rows[].grain` | string | Yes | One of: `"total"`, `"channel"`, `"source_medium"`, `"campaign"`, `"term"`. |
| `rows[].channel` | string \| null | Yes |  |
| `rows[].sourceMedium` | string \| null | Yes |  |
| `rows[].campaign` | string \| null | Yes |  |
| `rows[].term` | string \| null | Yes |  |
| `rows[].cells` | object[] | Yes | One cell per outcome and period, current period first, in outcome order. |
| `rows[].cells[].outcomeId` | string (uuid) | Yes |  |
| `rows[].cells[].period` | string | Yes | One of: `"current"`, `"previous"`. |
| `rows[].cells[].credit` | object | Yes | Credit under every model, keyed by model name, in the definition counting unit. Compare models without recomputing. |
| `rows[].cells[].credit.first_touch` | number | Yes |  |
| `rows[].cells[].credit.last_touch` | number | Yes |  |
| `rows[].cells[].credit.linear` | number | Yes |  |
| `rows[].cells[].credit.u_shaped` | number | Yes |  |
| `rows[].cells[].credit.j_shaped` | number | Yes |  |
| `rows[].cells[].credit.time_decay` | number | Yes |  |
| `rows[].cells[].value` | object \| null | Yes | Attributed value under every model, in the definition currency; null when the outcome has no value property. |
| `rows[].cells[].value.first_touch` | number | Yes |  |
| `rows[].cells[].value.last_touch` | number | Yes |  |
| `rows[].cells[].value.linear` | number | Yes |  |
| `rows[].cells[].value.u_shaped` | number | Yes |  |
| `rows[].cells[].value.j_shaped` | number | Yes |  |
| `rows[].cells[].value.time_decay` | number | Yes |  |
| `rows[].cells[].creditedUnits` | integer | Yes |  |
| `rows[].cells[].assistedVisitors` | integer | Yes |  |
| `rows[].cells[].medianDaysToConvert` | number \| null | Yes |  |
| `points` | object[] | Yes | Time-series credit per bucket, period, and outcome; channel is null for the all-channel series. |
| `points[].bucket` | string | Yes |  |
| `points[].period` | string | Yes | One of: `"current"`, `"previous"`. |
| `points[].outcomeId` | string (uuid) | Yes |  |
| `points[].channel` | string \| null | Yes |  |
| `points[].credit` | object | Yes | Credit under every model, keyed by model name, for this bucket. |
| `points[].credit.first_touch` | number | Yes |  |
| `points[].credit.last_touch` | number | Yes |  |
| `points[].credit.linear` | number | Yes |  |
| `points[].credit.u_shaped` | number | Yes |  |
| `points[].credit.j_shaped` | number | Yes |  |
| `points[].credit.time_decay` | number | Yes |  |
| `points[].value` | object \| null | Yes | Attributed value under every model for this bucket; null when the outcome has no value property. |
| `points[].value.first_touch` | number | Yes |  |
| `points[].value.last_touch` | number | Yes |  |
| `points[].value.linear` | number | Yes |  |
| `points[].value.u_shaped` | number | Yes |  |
| `points[].value.j_shaped` | number | Yes |  |
| `points[].value.time_decay` | number | Yes |  |
| `isTruncated` | boolean | Yes | True when more breakdown rows matched than the response returns; the total row is always complete. |
| `computedAt` | string (date-time) | Yes |  |

Full schema: [OpenAPI specification](https://docs.oursprivacy.com/openapi-platform.json).

## GET /rest/v1/attribution-reports/visitors

List the visitors behind one number in an attribution result: an outcome, period, row, touch role (first, last, any, or assist), and optional interval bucket. Send the same definition, dates, comparison, and interval that produced the result. Each visitor includes the credit and value they give that row, their credited first and last touch, conversion time, sessions in the lookback window, and up to three recent session replays. totalUnits equals the first- or last-touch credit for those roles and the row creditedUnits for any. Ordered newest conversion first; continue with pagination.nextCursor while hasMore is true. Requires Analytics Suite access and permission to view visitor data. Requires API-key scope or current OAuth user permission: web-analytics:view, report:view-visitor

Request URL: `GET https://app.oursprivacy.com/rest/v1/attribution-reports/visitors`

Authentication: apiKey or oauth2.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `input` | query | string | Yes | Required input: the same definition, from, to, and optional comparison and interval that produced the result, plus the selection to open; limit and cursor are optional. JSON-encode this object in the REST query string; maximum 16384 bytes before URL encoding. Required input: the same definition, from, to, and optional comparison and interval that produced the result, plus the selection to open; limit and cursor are optional. JSON-encode this object in the REST query string; maximum 16384 bytes before URL encoding. |

### Responses

- `200`: Visitors behind one attribution number
- `400`: Invalid definition, date range, selection, or cursor.
- `401`: Unauthorized — missing, invalid, or expired API key or OAuth access token
- `403`: Forbidden — credential does not have the required current Ours permission
- `404`: Not Found
- `500`: Internal Server Error

#### `200` response body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `entities` | object[] | Yes | Visitors ordered by latest matching conversion, newest first, then visitor id. |
| `entities[].visitorId` | string | Yes |  |
| `entities[].convertedAt` | string (date-time) | Yes | When the latest matching conversion happened (UTC). |
| `entities[].units` | integer | Yes | Matching conversions (totals counting) or converting sessions (sessions counting); always 1 under unique counting. |
| `entities[].credit` | number | Yes | Credit this visitor gives the selected row under the definition's model. |
| `entities[].value` | number \| null | Yes | Value this visitor credits to the selected row under the definition's model; null when the outcome has no value property. |
| `entities[].firstTouch` | object \| null | Yes | The credited first touch of the latest matching conversion; null when it had no eligible touch. |
| `entities[].firstTouch.at` | string (date-time) | Yes |  |
| `entities[].firstTouch.sessionId` | string | Yes |  |
| `entities[].firstTouch.channel` | string | Yes |  |
| `entities[].firstTouch.sourceMedium` | string | Yes |  |
| `entities[].firstTouch.campaign` | string | Yes |  |
| `entities[].firstTouch.term` | string | Yes |  |
| `entities[].lastTouch` | object \| null | Yes | The credited last touch of the latest matching conversion; null when it had no eligible touch. |
| `entities[].lastTouch.at` | string (date-time) | Yes |  |
| `entities[].lastTouch.sessionId` | string | Yes |  |
| `entities[].lastTouch.channel` | string | Yes |  |
| `entities[].lastTouch.sourceMedium` | string | Yes |  |
| `entities[].lastTouch.campaign` | string | Yes |  |
| `entities[].lastTouch.term` | string | Yes |  |
| `entities[].sessionCount` | integer | Yes | Sessions in the latest matching conversion's lookback window. |
| `entities[].latestReplays` | object[] | Yes | Up to three recorded sessions from the matching lookback windows, newest first, within session replay retention. |
| `entities[].latestReplays[].sessionId` | string | Yes |  |
| `entities[].latestReplays[].date` | string | Yes |  |
| `entities[].latestReplays[].startedAt` | string (date-time) | Yes |  |
| `pagination` | object | Yes |  |
| `pagination.nextCursor` | string \| null | Yes |  |
| `pagination.hasMore` | boolean | Yes |  |
| `totalCount` | integer | Yes | Visitors behind the selection. |
| `totalUnits` | integer | Yes | Counted units behind the selection. Equals the first- or last-touch credit for those roles and the row's creditedUnits for any. |

Full schema: [OpenAPI specification](https://docs.oursprivacy.com/openapi-platform.json).

## GET /rest/v1/attribution-reports/insights

Explain what moved in an attribution result: new sources, campaigns, and terms with credited conversions and no sessions in the prior 180 days; the largest share-adjusted credit changes against the comparison period; channels credited very differently by first and last touch; and rows that went quiet. Takes the same JSON input as the preview. Without a comparison only new values are returned. Returns at most 8 aggregate findings with their baselines and sample sizes, ranked by effect size and not tested for significance. Requires Analytics Suite access. Requires API-key scope or current OAuth user permission: web-analytics:view

Request URL: `GET https://app.oursprivacy.com/rest/v1/attribution-reports/insights`

Authentication: apiKey or oauth2.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `input` | query | string | Yes | Required input: definition, from, and to; comparison and interval are optional. Use the same input as the preview to explain what moved in that result. JSON-encode this object in the REST query string; maximum 16384 bytes before URL encoding. Nothing is saved. Required input: definition, from, and to; comparison and interval are optional. Use the same input as the preview to explain what moved in that result. JSON-encode this object in the REST query string; maximum 16384 bytes before URL encoding. Nothing is saved. |

### Responses

- `200`: What moved in an attribution result
- `400`: Invalid definition, filter, or date range.
- `401`: Unauthorized — missing, invalid, or expired API key or OAuth access token
- `403`: Forbidden — credential does not have the required current Ours permission
- `404`: Not Found
- `500`: Internal Server Error

#### `200` response body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `from` | string | Yes |  |
| `to` | string | Yes |  |
| `previousFrom` | string \| null | Yes | Inclusive comparison start date; null without a comparison. |
| `previousTo` | string \| null | Yes | Inclusive comparison end date; null without a comparison. |
| `counting` | string | Yes | One of: `"unique"`, `"totals"`, `"sessions"`. |
| `model` | string | Yes | One of: `"first_touch"`, `"last_touch"`, `"linear"`, `"u_shaped"`, `"j_shaped"`, `"time_decay"`. |
| `newValueLookbackDays` | integer | Yes | A value is new when it had no sessions in this many days before from. |
| `insights` | object[] | Yes | At most 8 findings, most notable first, one per row. Aggregates only; no visitor identifiers. |
| `insights[].id` | string | Yes | Stable identifier for the same input, made of the kind, outcome, and row. |
| `insights[].kind` | string | Yes | new_value: a source / medium, campaign, or term with credited conversions and no sessions in the lookback before the range. mover_up / mover_down: the largest share-adjusted credit changes against the comparison period. model_disagreement: a channel or source / medium credited at least twice as much by last touch as by first touch, or the reverse. went_quiet: a row that had credit in the comparison period and almost none now. One of: `"new_value"`, `"mover_up"`, `"mover_down"`, `"model_disagreement"`, `"went_quiet"`. |
| `insights[].evidence` | string | Yes | Always descriptive: findings are ranked by effect size above minimum volumes and are not tested for significance. One of: `"descriptive"`. |
| `insights[].outcomeId` | string (uuid) | Yes | Outcome id from the definition the numbers belong to. |
| `insights[].row` | object | Yes | The breakdown row the finding is about; matches a preview row by grain, channel, sourceMedium, campaign, and term. |
| `insights[].row.grain` | string | Yes | One of: `"channel"`, `"source_medium"`, `"campaign"`, `"term"`. |
| `insights[].row.channel` | string | Yes |  |
| `insights[].row.sourceMedium` | string \| null | Yes |  |
| `insights[].row.campaign` | string \| null | Yes |  |
| `insights[].row.term` | string \| null | Yes |  |
| `insights[].model` | string | Yes | Credit model of current. One of: `"first_touch"`, `"last_touch"`, `"linear"`, `"u_shaped"`, `"j_shaped"`, `"time_decay"`. |
| `insights[].current` | number | Yes | Credit in the current period under model, in the definition counting unit. |
| `insights[].baselineKind` | string | Yes | previous_period: baseline is the same model in the comparison period. other_model: baseline is baselineModel in the current period. lookback: baseline is the number of sessions with the value before the range, always 0. One of: `"previous_period"`, `"other_model"`, `"lookback"`. |
| `insights[].baselineModel` | string | Yes | One of: `"first_touch"`, `"last_touch"`, `"linear"`, `"u_shaped"`, `"j_shaped"`, `"time_decay"`. |
| `insights[].baseline` | number | Yes | The number current is compared with; see baselineKind. |
| `insights[].delta` | number | Yes | current minus baseline. |
| `insights[].change` | number \| null | Yes | Relative change from baseline; null when the baseline is 0. |
| `insights[].currentShare` | number \| null | Yes | The row's share of the outcome's current credit; null when the outcome has none. |
| `insights[].baselineShare` | number \| null | Yes | The row's share of the outcome's baseline credit; null for new values. |
| `insights[].outcomeChange` | number \| null | Yes | Relative change of the outcome's total credit, so a row's move can be read against the account's. |
| `insights[].sampleUnits` | integer | Yes | Counted units with a touch on this row in the current period. |
| `insights[].previousSampleUnits` | integer \| null | Yes | Counted units with a touch on this row in the comparison period; null without one. |
| `insights[].sampleSessions` | integer \| null | Yes | Sessions with the new value in the range; new_value only. |
| `insights[].firstSeen` | string \| null | Yes | First session date with the new value; new_value only. |
| `insights[].drivers` | string[] | Yes | Child rows that together explain most of a move, largest first. |
| `insights[].score` | number | Yes | Ranking score from 0 to 1, weighted by kind. |
| `computedAt` | string (date-time) | Yes |  |

Full schema: [OpenAPI specification](https://docs.oursprivacy.com/openapi-platform.json).

## Next Steps

* [Attribution API](https://docs.oursprivacy.com/docs/platform-api/attribution)
* [Dashboards API](https://docs.oursprivacy.com/docs/platform-api/dashboards)
* [Insights API](https://docs.oursprivacy.com/docs/platform-api/insights)
* [MCP server](https://docs.oursprivacy.com/docs/platform-mcp)
