# Activations


Activate (export) a completed audience to an endpoint connection, then list, read
and delete activations - and preview the frequency filter an activation would
apply before you create it. Activation is asynchronous: the create call returns
immediately with a new id, and you poll the get endpoint until its datastreams
report their output.

All activation endpoints are authenticated and JSON-only. Send `Authorization:
Bearer <token>` and `Accept: application/json` on every call (plus `Content-Type:
application/json` on the POST create). Reads use the read rate bucket (120
requests/min per caller); create and delete use the write bucket (30 requests/min
per caller).

## Create Activation {#post-apiv2analysesactivationscreate}

`POST /api/v2/analyses/activations/create`

Creates an activation that exports a custom audience owned by your company to a
company-scoped endpoint connection. The audience must be `104` Completed and
hold at least **500 unique devices** - the audience read's
`is_activation_allowed` field tells you eligibility up front. When it is `false`,
`eligibility.reasons[0].message` tells you why (device floor, Affinity device
coverage, retention) and the activation endpoint returns `422` with the same
message. The export is queued asynchronously; poll
[`GET /api/v2/analyses/activations/{id}`](#get-apiv2analysesactivationsid) for its
status and datastream output.

**Auth:** bearer token + `Accept: application/json` + `Content-Type:
application/json`. Rate limit: write bucket (30 requests/min). Optional:
`Idempotency-Key` header (see [below](#idempotency)).

### Body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `audience_id` | integer | Yes | The audience to export. Must be owned by your company. |
| `endpoint_connection_id` | integer | Yes | The destination endpoint connection. Must be owned by your company. Resolves the partner, pricing and datastreams server-side. |
| `pricing_model_id` | integer | Yes | The pricing model id for the activation. |
| `description` | string | No | A label for the activation (max 255 chars). |
| `project_id` | integer | No | A [project](/api/v2/projects) id owned by your company to file the activation under. |
| `credentials` | object | No | Caller-supplied destination credentials (see the [credentials note](#caller-supplied-credentials)). Write-only and sensitive. |
| `audience_inputs` | array | Per partner definitions | Partner account-detail values, **index-matched** to the connection's `partner.inputs` definitions from [Get Endpoint Connections]({{< relref "/api/v2/common" >}}#get-apiv2analysesreferencecommonendpoint-connections). Inputs flagged `required` must be supplied for every enabled datastream (here or per stream). |
| `datastreams` | object[] | No | The partner outputs to enable. Each entry is `{ id, status, compression, inputs, service_account, visualizing_status }`. |
| `datastreams[].inputs` | array | Per partner definitions | Per-stream partner input values, index-matched to `partner.inputs` exactly like `audience_inputs` (per-stream values win). |
| `datastreams[].service_account` | object | No | The decoded service-account JSON key. Only accepted when the partner defines a `file`-type input (GCP-style destinations); otherwise rejected. Write-only and sensitive. |
| `datastreams[].visualizing_status` | boolean | No | Whether this stream also feeds visualization. |
| `datastreams[].id` | integer | Required with `datastreams` | The datastream id (re-resolved server-side through your company's permitted set). An enabled stream must apply to the audience's dataset types - each item of [Get Datastreams]({{< relref "/api/v2/common" >}}#get-apiv2analysesreferencecommondatastreams) lists its `dataset_types`, and a lookalike audience counts as `cohorts`. A mismatch is a `422` naming the stream; it is not silently dropped, because a stream run against the wrong dataset produces a broken file rather than an empty one. |
| `datastreams[].status` | boolean | No | Whether to enable this datastream. |
| `datastreams[].compression` | string | No | Compression for this datastream. |
| `freq_limit` | boolean | With `freq_min` / `freq_max` | Enable the frequency filter. The export applies `freq_min` / `freq_max` only when this is `true`, so bounds sent without an explicit `freq_limit` are rejected - a filter is never applied implicitly. Must be `true` when `filter_hash` is sent. |
| `freq_min` | integer | With `filter_hash` | Lowest distinct visit-day count to export (inclusive). Preview the resulting count first with [Preview Activation](#get-apiv2analysesactivationspreview). |
| `freq_max` | integer | With `filter_hash` | Highest distinct visit-day count to export (inclusive). |
| `filter_hash` | string | No | The `filter_hash` returned by [Preview Activation](#get-apiv2analysesactivationspreview) for the same `audience_id`, `freq_min` and `freq_max`. Recomputed server-side from the audience as stored now; a different range, or an audience rebuilt since the preview, is rejected - so the export applies exactly what was previewed. Echoed on the activation read. |
| `limit` | integer | No | Result cap. |
| `distance_limit` | boolean | No | Enable the distance limit. |
| `distance` | number | No | Distance value. |
| `compression` | string | No | Top-level compression. |
| `price` | number | No | Price override. |
| `notification` | boolean | No | Whether to notify on completion. |

{{< callout type="warning" >}}
`partner_id`, `partner_name` and `pricing_model` are **prohibited** in the body.
The partner, pricing and datastreams are always derived server-side from the
company-scoped `endpoint_connection_id`. Sending any of these prohibited fields is
rejected with `422`.
{{< /callout >}}

#### Caller-supplied credentials

`credentials` is an optional, **write-only and sensitive** map of string keys to
simple values (strings, numbers or booleans). It is never logged, never echoed,
and never returned in any response.

- When **present**, the caller-supplied credentials **override** the endpoint
  connection's stored credentials for this export.
- When **absent**, the server falls back to the connection's stored,
  server-decrypted credentials.
- If the connection has **no** stored credentials **and** the caller supplies
  none, the activation is rejected with `422` (there is nothing to authenticate
  the export with).

{{< tabs >}}

  {{< tab name="cURL" >}}
  ```bash
  curl -X POST "https://console.intuizi.com/api/v2/analyses/activations/create" \
    -H "Authorization: Bearer <YOUR_TOKEN>" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Idempotency-Key: <UNIQUE_KEY>" \
    -d '{
      "audience_id": 88,
      "endpoint_connection_id": 12,
      "pricing_model_id": 3,
      "description": "Q1 retail export",
      "datastreams": [
        { "id": 7, "status": true }
      ]
    }'
  ```
  {{< /tab >}}

  {{< tab name="Python" >}}
  ```python
  import requests

  res = requests.post(
      "https://console.intuizi.com/api/v2/analyses/activations/create",
      headers={
          "Authorization": "Bearer <YOUR_TOKEN>",
          "Content-Type": "application/json",
          "Accept": "application/json",
          "Idempotency-Key": "<UNIQUE_KEY>",
      },
      json={
          "audience_id": 88,
          "endpoint_connection_id": 12,
          "pricing_model_id": 3,
          "description": "Q1 retail export",
          "datastreams": [{"id": 7, "status": True}],
      },
  )
  activation_id = res.json()["data"][0]["id"]
  ```
  {{< /tab >}}

  {{< tab name="JavaScript" >}}
  ```javascript
  const res = await fetch(
    "https://console.intuizi.com/api/v2/analyses/activations/create",
    {
      method: "POST",
      headers: {
        Authorization: "Bearer <YOUR_TOKEN>",
        "Content-Type": "application/json",
        Accept: "application/json",
        "Idempotency-Key": "<UNIQUE_KEY>",
      },
      body: JSON.stringify({
        audience_id: 88,
        endpoint_connection_id: 12,
        pricing_model_id: 3,
        description: "Q1 retail export",
        datastreams: [{ id: 7, status: true }],
      }),
    }
  );
  const activationId = (await res.json()).data[0].id;
  ```
  {{< /tab >}}

  {{< tab name="PHP" >}}
  ```php
  $res = Http::withToken('<YOUR_TOKEN>')->acceptJson()
      ->withHeaders(['Idempotency-Key' => '<UNIQUE_KEY>'])
      ->post('https://console.intuizi.com/api/v2/analyses/activations/create', [
          'audience_id' => 88,
          'endpoint_connection_id' => 12,
          'pricing_model_id' => 3,
          'description' => 'Q1 retail export',
          'datastreams' => [['id' => 7, 'status' => true]],
      ]);
  $activationId = $res->json('data.0.id');
  ```
  {{< /tab >}}

{{< /tabs >}}

### Response

The response never includes the endpoint JSON, credentials, result links or
device identifiers. `created_by`, `partner` and `pricing_model` are always
objects (empty `{}` when they cannot be resolved). `filters` echoes the
frequency filter the export applies (`freq_min` / `freq_max` only apply when
`freq_limit` is `true`) and `filter_hash` the preview hash the activation was
created with (`null` when none was sent).

```json
{
  "status": "success",
  "code": 201,
  "message": "Resource created successfully.",
  "data": [
    {
      "id": 501,
      "description": "Q1 retail export",
      "status": { "id": 100, "name": "Initiating" },
      "audience": { "id": 88, "name": "Coffee Buyers NYC" },
      "created_by": { "name": "Jane Doe", "email": "jane.doe@acme.example" },
      "project": { "id": 4, "name": "Retail 2025" },
      "partner": { "name": "Acme DSP", "description": "Programmatic DSP" },
      "pricing_model": { "name": "CPM", "price": 2.5 },
      "datastreams": [],
      "filters": { "freq_limit": false, "freq_min": null, "freq_max": null },
      "filter_hash": null,
      "created_at": "2025-02-01 12:00:00",
      "updated_at": "2025-02-01 12:00:00"
    }
  ]
}
```

### Idempotency

The optional `Idempotency-Key` header makes a create safe to retry. The key is
**caller-generated**: any unique string up to 255 characters (a UUID v4 is
typical), one per logical create - reuse it only when retrying that same
request. The first
request with a given key runs normally and the successful (2xx) response is cached
per caller. A retry with the **same key and the same body** replays the stored
response verbatim with an `Idempotency-Replayed: true` header, so no duplicate
activation is created. A retry with the **same key but a different body** returns
`409 Conflict`. See [Idempotency](/concepts/idempotency) for the full replay
semantics and the eight endpoints that honor the header.

## Preview Activation {#get-apiv2analysesactivationspreview}

`GET /api/v2/analyses/activations/preview`

Read-only dry run of the frequency filter an activation would apply to a
Completed audience. It returns the exact device count the Audience Manager
shows as **Limit Audience** for the same Freq. Range: the audience's stored
frequency histogram (distinct visit days -> devices) summed over the inclusive
`[freq_min, freq_max]` range, which is the same predicate the export applies.
Alongside the count you get the histogram, its valid bounds, the audience total,
the read-only recency window, the audience eligibility and a canonical
`filter_hash`. Nothing is created, queued, exported, scheduled or billed - call
it once per candidate range.

The audience must be `104` Completed and built with a frequency analysis -
**Visitation Frequency**, **Apps Frequency** or **Web Frequency** in the
Audience Manager, or the matching `analyses` key on
[Create Audience](/api/v2/audiences#frequency-analysis).
Any other audience - a day-part frequency analysis, a lookalike or cohort, an
audience built without a frequency analysis - is refused; the filter is never
approximated. For a worked agent session see
[Preview, then Activate](/mcp/preview-and-activate).

Recency is not an input: the date window is fixed by the audience definition
and echoed read-only as `recency`. The preview accepts exactly the three
parameters below; any other parameter is rejected.

**Auth:** bearer token + `Accept: application/json`. Rate limit: read bucket
(120 requests/min).

### Query parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `audience_id` | integer | Yes | A Completed audience owned by your company, built with exactly one frequency analysis (see [Frequency analyses](/api/v2/audiences#frequency-analysis)). |
| `freq_min` | integer | Yes | Lowest distinct visit-day count to include (inclusive). Must be at least `frequency_bounds.min` - `1` for a "1+" range, `2` for "2+". |
| `freq_max` | integer | Yes | Highest distinct visit-day count to include (inclusive). Must not exceed `frequency_bounds.max`; use that bound for an open-ended "N+" range. |

{{< tabs >}}

  {{< tab name="cURL" >}}
  ```bash
  curl "https://console.intuizi.com/api/v2/analyses/activations/preview?audience_id=88&freq_min=2&freq_max=5" \
    -H "Authorization: Bearer <YOUR_TOKEN>" \
    -H "Accept: application/json"
  ```
  {{< /tab >}}

  {{< tab name="Python" >}}
  ```python
  import requests

  res = requests.get(
      "https://console.intuizi.com/api/v2/analyses/activations/preview",
      params={"audience_id": 88, "freq_min": 2, "freq_max": 5},
      headers={"Authorization": "Bearer <YOUR_TOKEN>", "Accept": "application/json"},
  )
  preview = res.json()["data"][0]
  filtered_count, filter_hash = preview["filtered_count"], preview["filter_hash"]
  ```
  {{< /tab >}}

  {{< tab name="JavaScript" >}}
  ```javascript
  const params = new URLSearchParams({ audience_id: 88, freq_min: 2, freq_max: 5 });
  const res = await fetch(
    `https://console.intuizi.com/api/v2/analyses/activations/preview?${params}`,
    { headers: { Authorization: "Bearer <YOUR_TOKEN>", Accept: "application/json" } }
  );
  const preview = (await res.json()).data[0];
  const { filtered_count, filter_hash } = preview;
  ```
  {{< /tab >}}

  {{< tab name="PHP" >}}
  ```php
  $res = Http::withToken('<YOUR_TOKEN>')->acceptJson()
      ->get('https://console.intuizi.com/api/v2/analyses/activations/preview', [
          'audience_id' => 88,
          'freq_min' => 2,
          'freq_max' => 5,
      ]);
  $preview = $res->json('data.0');
  ```
  {{< /tab >}}

{{< /tabs >}}

### Response

`filtered_count` is exact. `source_count` is the audience total shown on the
audience itself (an approximate distinct count, about 2.3% standard error) and
`histogram_total` is the exact sum of the histogram, so the two can differ
slightly; a `[1, frequency_bounds.max]` range returns `histogram_total`.
`as_of` is when the audience's stored results were last updated.

To activate exactly what was previewed, send the same `audience_id`, `freq_min`
and `freq_max` with `freq_limit: true` and the returned `filter_hash` to
[Create Activation](#post-apiv2analysesactivationscreate); the activation read
then echoes both.

```json
{
  "status": "success",
  "code": 200,
  "message": "Resource fetched successfully.",
  "data": [
    {
      "audience": {
        "id": 88,
        "name": "Coffee Buyers NYC",
        "status": { "id": 104, "name": "Completed" },
        "is_activation_allowed": true,
        "eligibility": {
          "allowed": true,
          "reasons": [],
          "metrics": { "unique_eids": 5100, "unique_scids": null, "eid_scid_ratio": null, "is_affinity": false }
        }
      },
      "dataset_type": "POI",
      "analysis_type": "frequency",
      "recency": [
        { "dataset_type": "POI", "start_date": "06/01/2026", "end_date": "07/31/2026" }
      ],
      "source_count": 5100,
      "histogram_total": 5000,
      "filtered_count": 2000,
      "frequency_bounds": { "min": 1, "max": 5 },
      "histogram": [
        { "index": 1, "counts": 3000 },
        { "index": 2, "counts": 1200 },
        { "index": 3, "counts": 500 },
        { "index": 5, "counts": 300 }
      ],
      "applied_filters": { "freq_limit": true, "freq_min": 2, "freq_max": 5 },
      "filter_hash": "sha256:4f9d0c7e1b2a8d3f6e5c4b3a2918f7e6d5c4b3a291807f6e5d4c3b2a19180706",
      "method": "histogram_sum",
      "as_of": "2026-08-01 06:12:44",
      "limitations": [
        "filtered_count is the exact number of devices whose distinct visit-day count inside the audience date window falls within [freq_min, freq_max] (both inclusive) - the same figure the Audience Manager shows as Limit Audience for that Freq. Range, and the same predicate the export applies.",
        "source_count is the audience total shown on the audience itself (an approximate distinct count, about 2.3% standard error); histogram_total is the exact sum of the frequency histogram, so the two can differ slightly. A [1, max] range returns histogram_total."
      ]
    }
  ]
}
```

## List Activations {#get-apiv2analysesactivationsindex}

`GET /api/v2/analyses/activations/index`

Lists the activations owned by your company, most recent first, paginated.

**Auth:** bearer token + `Accept: application/json`. Rate limit: read bucket (120
requests/min).

### Query parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `per_page` | integer | No | Items per page. Defaults to 25, capped at 100. |
| `page` | integer | No | Page number (standard pagination). |
| `search` | string | No | Optional free-text filter on the activation description (case-insensitive contains). |

{{< tabs >}}

  {{< tab name="cURL" >}}
  ```bash
  curl "https://console.intuizi.com/api/v2/analyses/activations/index?per_page=25" \
    -H "Authorization: Bearer <YOUR_TOKEN>" \
    -H "Accept: application/json"
  ```
  {{< /tab >}}

  {{< tab name="Python" >}}
  ```python
  import requests

  res = requests.get(
      "https://console.intuizi.com/api/v2/analyses/activations/index",
      headers={"Authorization": "Bearer <YOUR_TOKEN>", "Accept": "application/json"},
      params={"per_page": 25},
  )
  page = res.json()["data"]
  ```
  {{< /tab >}}

  {{< tab name="JavaScript" >}}
  ```javascript
  const res = await fetch(
    "https://console.intuizi.com/api/v2/analyses/activations/index?per_page=25",
    { headers: { Authorization: "Bearer <YOUR_TOKEN>", Accept: "application/json" } }
  );
  const page = (await res.json()).data;
  ```
  {{< /tab >}}

  {{< tab name="PHP" >}}
  ```php
  $res = Http::withToken('<YOUR_TOKEN>')->acceptJson()
      ->get('https://console.intuizi.com/api/v2/analyses/activations/index', ['per_page' => 25]);
  $page = $res->json('data');
  ```
  {{< /tab >}}

{{< /tabs >}}

### Response

```json
{
  "status": "success",
  "code": 200,
  "message": "Resources fetched successfully.",
  "data": {
    "items": [
      {
        "id": 501,
        "description": "Q1 retail export",
        "status": { "id": 104, "name": "Completed" },
        "audience": { "id": 88, "name": "Coffee Buyers NYC" },
        "created_by": { "name": "Jane Doe", "email": "jane.doe@acme.example" },
        "project": { "id": 4, "name": "Retail 2025" },
        "partner": { "name": "Acme DSP", "description": "Programmatic DSP" },
        "pricing_model": { "name": "CPM", "price": 2.5 },
        "datastreams": [
          { "name": "match_file", "status": "success", "results": { "uri": "s3://bucket/path/output.csv" } }
        ],
        "created_at": "2025-02-01 12:00:00",
        "updated_at": "2025-02-02 09:30:00"
      }
    ],
    "pagination": {
      "current_page": 1,
      "per_page": 25,
      "total": 1,
      "last_page": 1
    }
  }
}
```

## Get Activation {#get-apiv2analysesactivationsid}

`GET /api/v2/analyses/activations/{id}`

Retrieves a single activation by id.

The same read is also available at `GET /api/v2/my-data/activations/{id}` for
backward compatibility; both return the identical response shape.

**Auth:** bearer token + `Accept: application/json`. Rate limit: read bucket (120
requests/min).

### Path parameters

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | integer | Yes | The activation id to fetch. |

{{< tabs >}}

  {{< tab name="cURL" >}}
  ```bash
  curl "https://console.intuizi.com/api/v2/analyses/activations/501" \
    -H "Authorization: Bearer <YOUR_TOKEN>" \
    -H "Accept: application/json"
  ```
  {{< /tab >}}

  {{< tab name="Python" >}}
  ```python
  import requests

  res = requests.get(
      "https://console.intuizi.com/api/v2/analyses/activations/501",
      headers={"Authorization": "Bearer <YOUR_TOKEN>", "Accept": "application/json"},
  )
  activation = res.json()["data"][0]
  ```
  {{< /tab >}}

  {{< tab name="JavaScript" >}}
  ```javascript
  const res = await fetch(
    "https://console.intuizi.com/api/v2/analyses/activations/501",
    { headers: { Authorization: "Bearer <YOUR_TOKEN>", Accept: "application/json" } }
  );
  const activation = (await res.json()).data[0];
  ```
  {{< /tab >}}

  {{< tab name="PHP" >}}
  ```php
  $res = Http::withToken('<YOUR_TOKEN>')->acceptJson()
      ->get('https://console.intuizi.com/api/v2/analyses/activations/501');
  $activation = $res->json('data.0');
  ```
  {{< /tab >}}

{{< /tabs >}}

### Response

Each entry in `datastreams[]` carries a `name`, a `status`, an always-object
`results` (`{ "uri": "s3://..." }` or `{ "gs://..." }` when an output file is
available, else `{}`), and an `error` string only when the datastream failed.
The `name` is the stream's internal slug, not the name
[Get Datastreams]({{< relref "/api/v2/common" >}}#get-apiv2analysesreferencecommondatastreams)
prints. The `status` is `success` for a delivered stream, `failed` when it
failed, or `skipped` when it cannot run on the audience's data, and the last
two carry an `error`.
Output URIs are normalized to their `s3://` or `gs://` form. Storage locations
and URLs are removed from the `error`, and an error passed through from the query
engine or cloud storage is cut to its first sentence, which states the reason.

```json
{
  "status": "success",
  "code": 200,
  "message": "Resource fetched successfully.",
  "data": [
    {
      "id": 501,
      "description": "Q1 retail export",
      "status": { "id": 104, "name": "Completed" },
      "audience": { "id": 88, "name": "Coffee Buyers NYC" },
      "created_by": { "name": "Jane Doe", "email": "jane.doe@acme.example" },
      "project": { "id": 4, "name": "Retail 2025" },
      "partner": { "name": "Acme DSP", "description": "Programmatic DSP" },
      "pricing_model": { "name": "CPM", "price": 2.5 },
      "datastreams": [
        {
          "name": "match_file",
          "status": "success",
          "results": { "uri": "s3://bucket/path/output.csv" }
        }
      ],
      "filters": { "freq_limit": true, "freq_min": 2, "freq_max": 5 },
      "filter_hash": "sha256:4f9d0c7e1b2a8d3f6e5c4b3a2918f7e6d5c4b3a291807f6e5d4c3b2a19180706",
      "created_at": "2025-02-01 12:00:00",
      "updated_at": "2025-02-02 09:30:00"
    }
  ]
}
```

## Delete Activation {#post-apiv2analysesactivationsdelete-by-id}

`POST /api/v2/analyses/activations/delete-by-id`

Soft-deletes an activation.

**Auth:** bearer token + `Accept: application/json` + `Content-Type:
application/json`. Rate limit: write bucket (30 requests/min).

### Body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | integer | Yes | The activation id to delete. Must be owned by your company. |

{{< tabs >}}

  {{< tab name="cURL" >}}
  ```bash
  curl -X POST "https://console.intuizi.com/api/v2/analyses/activations/delete-by-id" \
    -H "Authorization: Bearer <YOUR_TOKEN>" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -d '{ "id": 501 }'
  ```
  {{< /tab >}}

  {{< tab name="Python" >}}
  ```python
  import requests

  res = requests.post(
      "https://console.intuizi.com/api/v2/analyses/activations/delete-by-id",
      headers={
          "Authorization": "Bearer <YOUR_TOKEN>",
          "Content-Type": "application/json",
          "Accept": "application/json",
      },
      json={"id": 501},
  )
  ```
  {{< /tab >}}

  {{< tab name="JavaScript" >}}
  ```javascript
  const res = await fetch(
    "https://console.intuizi.com/api/v2/analyses/activations/delete-by-id",
    {
      method: "POST",
      headers: {
        Authorization: "Bearer <YOUR_TOKEN>",
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      body: JSON.stringify({ id: 501 }),
    }
  );
  ```
  {{< /tab >}}

  {{< tab name="PHP" >}}
  ```php
  $res = Http::withToken('<YOUR_TOKEN>')->acceptJson()
      ->post('https://console.intuizi.com/api/v2/analyses/activations/delete-by-id', ['id' => 501]);
  ```
  {{< /tab >}}

{{< /tabs >}}

### Response

```json
{
  "status": "success",
  "code": 200,
  "message": "Resource deleted successfully.",
  "data": []
}
```

