# Schedules


Schedules turn a one-off audience into a recurring one. A schedule clones the
definition of a completed [audience]({{< relref "/api/v2/audiences" >}}) at
creation time and, on every cycle, rebuilds that audience over a rolling data
window - optionally re-exporting the result to one of your endpoint
connections, exactly like a one-off
[activation]({{< relref "/api/v2/activations" >}}). This is the same Scheduler
your team uses in the Audience Manager (Analyses - Scheduler).

Schedules are a gated capability: they require additional permissions which
need to be approved by your Account Manager.

All schedule endpoints are authenticated and JSON-only. Send `Authorization:
Bearer <token>` and `Accept: application/json` on every call (plus
`Content-Type: application/json` on the POSTs). Reads use the read rate bucket
(120 requests/min per caller); writes use the write bucket (30 requests/min per
caller). Every endpoint is scoped to your own company; a schedule belonging to
another company is reported as not found.

## How schedules work

**Cycles.** Each cycle the platform creates a fresh audience named
`<schedule name> - #<cycle number>` from the stored definition, restamped to
that cycle's data window. Origin data is weekly, so an Origin dataset's window
is widened to the whole Monday-to-Sunday weeks it touches, the same widening
Create Audience applies. A
[cross purchase]({{< relref "/api/v2/audiences" >}}#crosspurchase) block is
not restamped: it keeps the dates it was built with, so every cycle reports
purchases over the same window. The produced audiences appear in
[List Audiences]({{< relref "/api/v2/audiences" >}}#get-apiv2analysesaudiencesindex)
and follow the normal audience lifecycle (`104 Completed` is the only
success). When the schedule carries an activation block, the export is created
automatically once the cycle's audience completes, but only when that audience
passes the activation checks for the schedule's pricing model and your
organization is under its monthly data-scan limit. Otherwise the cycle is not
exported, and the schedule read does not say so.

**Data-scan limit.** A cycle that falls while your organization is over its
monthly data-scan limit is skipped: no audience is built, nothing is exported,
and the schedule's owner gets a notification. The `Custom Date` ending counts a
skipped cycle as one of its runs, so the schedule makes fewer runs. The
`Recurrences` ending does not count it, so the schedule still makes every run
and finishes later.

**Data windows.** `recurrence.window_type` picks how the window is derived each
cycle - fetch the valid values from
[Get Schedule Windows]({{< relref "/api/v2/common" >}}#get-apiv2analysesreferencecommonschedule-windows).
Rolling windows (for example `Last 7 Days`, `Last 30 Days`, `Custom`) end three
days before the run time, allowing signal deliveries to settle; calendar
windows (`Last Full Month`, `Last 3 Full Months`) cover the full prior calendar
months. The `Custom` window uses your own day count via
`recurrence.window_days`.

**Endings.** `recurrence.ending.type` picks when the recurrence stops - fetch
the valid values from
[Get Schedule Endings]({{< relref "/api/v2/common" >}}#get-apiv2analysesreferencecommonschedule-endings).
A schedule can run forever, stop after a fixed number of recurrences, or stop
at a date. When the ending condition is met the schedule becomes `Fulfilled`.

**Schedule status.** A schedule is `Active` (running), `Deactivated` (paused
via [Deactivate](#post-apiv2analysesschedulesdeactivate)) or `Fulfilled` (its
ending condition was met). This status is the schedule's own lifecycle - it is
distinct from the status of the audiences the schedule produces.

**Settings are fixed at creation.** A schedule's definition snapshot,
recurrence and activation settings cannot be edited afterwards - delete the
schedule and create a new one to change them. Deleting the source audience
later does not affect the schedule (the definition is a copy).

**Credentials.** Unlike
[Create Activation]({{< relref "/api/v2/activations" >}}#post-apiv2analysesactivationscreate),
a schedule never accepts credentials: it would have to retain them for the
life of the recurrence. Each scheduled export authenticates with the
credentials stored on the referenced endpoint connection at the moment it
runs - keep them current in the console.

## Create Schedule {#post-apiv2analysesschedulescreate}

`POST /api/v2/analyses/schedules/create`

Creates a schedule from an existing completed audience owned by your company
and arms the first run. Omit `activation` for a refresh-only schedule (the
audience is rebuilt every cycle but nothing is exported).

**Auth:** bearer token + `Accept: application/json` + `Content-Type:
application/json`. Rate limit: write bucket (30 requests/min). Optional:
`Idempotency-Key` header (see [Audiences]({{< relref "/api/v2/audiences" >}}#idempotency)).

### Body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | **Yes** | Schedule name (letters, numbers, spaces, hyphens and underscores; max 255). Cycle audiences are named `<name> - #<cycle>`. |
| `audience_id` | integer | **Yes** | The audience to make recurring. Must be owned by your company and completed. Its definition is snapshotted server-side. |
| `project_id` | integer | No | Project folder to file the schedule under (see [Projects]({{< relref "/api/v2/projects" >}})). |
| `recurrence` | object | **Yes** | The cadence, window and ending settings below. |
| `recurrence.start` | string | **Yes** | First run time, `Y-m-d H:i:s`, interpreted in `recurrence.timezone`. Must be in the future. |
| `recurrence.timezone` | string | **Yes** | IANA timezone identifier the recurrence runs in (for example `America/New_York`). |
| `recurrence.frequency` | string | **Yes** | Cadence value - fetch valid values from [Get Schedule Frequencies]({{< relref "/api/v2/common" >}}#get-apiv2analysesreferencecommonschedule-frequencies). |
| `recurrence.window_type` | integer | **Yes** | Data window id - fetch valid values from [Get Schedule Windows]({{< relref "/api/v2/common" >}}#get-apiv2analysesreferencecommonschedule-windows). |
| `recurrence.window_days` | integer | Conditional | The day count for the `Custom` window (1-365). Required with that window, not accepted with any other. |
| `recurrence.ending.type` | integer | **Yes** | Ending id - fetch valid values from [Get Schedule Endings]({{< relref "/api/v2/common" >}}#get-apiv2analysesreferencecommonschedule-endings). |
| `recurrence.ending.after_recurrences` | integer | Conditional | Number of cycles to run. Required with the `Recurrences` ending, not accepted with any other. |
| `recurrence.ending.end_date` | string | Conditional | The date the recurrence ends, `Y-m-d`, on or after the date of `recurrence.start`. Required with the `Custom Date` ending, not accepted with any other. It is turned into a cycle count when the schedule is created: the cycle at `recurrence.start` plus every later cycle that starts by 00:00 on this date. `daily`, `weekly`, and `bi-weekly` cycles are 1, 7, and 14 days apart: weekly from `2026-10-15 06:00:00` to `2027-01-01` runs 12 cycles. `monthly` cycles are one calendar month apart, on the start's day of the month, or on the last day of a shorter month and on that day from then on: monthly from `2026-10-15 06:00:00` to `2027-10-15` runs 12 cycles, the last on `2027-09-15`. |
| `activation` | object | No | Auto-export settings for every cycle. Omit for a refresh-only schedule. |
| `activation.endpoint_connection_id` | integer | **Yes**, with `activation` | The endpoint connection to export to - fetch yours from [Get Endpoint Connections]({{< relref "/api/v2/common" >}}#get-apiv2analysesreferencecommonendpoint-connections). The partner and its input definitions are resolved from the connection. |
| `activation.pricing_model_id` | integer | **Yes**, with `activation` | A pricing model available for the connection's partner - fetch from [Get Pricing Models]({{< relref "/api/v2/common" >}}#get-apiv2analysesreferencecommonpricing-models). |
| `activation.audience_inputs` | array | No | Partner account-detail values, index-matched to the connection's `partner.inputs` definitions (same contract as [Create Activation]({{< relref "/api/v2/activations" >}}#post-apiv2analysesactivationscreate)). |
| `activation.datastreams` | array | No | The partner outputs to enable: `{ id, status, compression, inputs, service_account, visualizing_status }` per stream. Per-stream `inputs` fall back to `audience_inputs`. |
| `activation.limit` | integer | No | Maximum devices to export per cycle. Omit or `0` to export the full audience. |
| `activation.price` | number | No | The export cost figure recorded on each activation. |
| `activation.freq_limit` | boolean | No | Enable the visit-frequency range filter on each export. |
| `activation.freq_min` | integer | No | Minimum visit frequency (with `freq_limit`). |
| `activation.freq_max` | integer | No | Maximum visit frequency (with `freq_limit`). |
| `activation.compression` | string | No | Output compression. Defaults to `gzip`. |

`activation.credentials` is not accepted (see
[How schedules work](#how-schedules-work)); `activation.partner_id`,
`activation.partner_name` and `activation.pricing_model` are server-resolved
from the connection and not accepted either.

{{< tabs >}}

  {{< tab name="cURL" >}}
  ```bash
  curl -X POST "https://console.intuizi.com/api/v2/analyses/schedules/create" \
    -H "Authorization: Bearer <YOUR_TOKEN>" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json" \
    -H "Idempotency-Key: <UNIQUE_KEY>" \
    -d '{
      "name": "Weekly Coffee Refresh",
      "audience_id": 8891,
      "project_id": 12,
      "recurrence": {
        "start": "2026-08-01 09:00:00",
        "timezone": "America/New_York",
        "frequency": "weekly",
        "window_type": 4,
        "ending": { "type": 2, "after_recurrences": 12 }
      },
      "activation": {
        "endpoint_connection_id": 12,
        "pricing_model_id": 3,
        "audience_inputs": ["acme-advertiser"],
        "datastreams": [
          { "id": 7, "status": true, "compression": "gzip" }
        ],
        "price": 1250.00
      }
    }'
  ```
  {{< /tab >}}

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

  res = requests.post(
      "https://console.intuizi.com/api/v2/analyses/schedules/create",
      headers={
          "Authorization": "Bearer <YOUR_TOKEN>",
          "Content-Type": "application/json",
          "Accept": "application/json",
          "Idempotency-Key": "<UNIQUE_KEY>",
      },
      json={
          "name": "Weekly Coffee Refresh",
          "audience_id": 8891,
          "project_id": 12,
          "recurrence": {
              "start": "2026-08-01 09:00:00",
              "timezone": "America/New_York",
              "frequency": "weekly",
              "window_type": 4,
              "ending": {"type": 2, "after_recurrences": 12},
          },
          "activation": {
              "endpoint_connection_id": 12,
              "pricing_model_id": 3,
              "audience_inputs": ["acme-advertiser"],
              "datastreams": [{"id": 7, "status": True, "compression": "gzip"}],
              "price": 1250.00,
          },
      },
  )
  schedule_id = res.json()["data"][0]["id"]
  ```
  {{< /tab >}}

  {{< tab name="JavaScript" >}}
  ```javascript
  const res = await fetch(
    "https://console.intuizi.com/api/v2/analyses/schedules/create",
    {
      method: "POST",
      headers: {
        Authorization: "Bearer <YOUR_TOKEN>",
        "Content-Type": "application/json",
        Accept: "application/json",
        "Idempotency-Key": "<UNIQUE_KEY>",
      },
      body: JSON.stringify({
        name: "Weekly Coffee Refresh",
        audience_id: 8891,
        project_id: 12,
        recurrence: {
          start: "2026-08-01 09:00:00",
          timezone: "America/New_York",
          frequency: "weekly",
          window_type: 4,
          ending: { type: 2, after_recurrences: 12 },
        },
        activation: {
          endpoint_connection_id: 12,
          pricing_model_id: 3,
          audience_inputs: ["acme-advertiser"],
          datastreams: [{ id: 7, status: true, compression: "gzip" }],
          price: 1250.0,
        },
      }),
    }
  );
  const scheduleId = (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/schedules/create', [
          'name' => 'Weekly Coffee Refresh',
          'audience_id' => 8891,
          'project_id' => 12,
          'recurrence' => [
              'start' => '2026-08-01 09:00:00',
              'timezone' => 'America/New_York',
              'frequency' => 'weekly',
              'window_type' => 4,
              'ending' => ['type' => 2, 'after_recurrences' => 12],
          ],
          'activation' => [
              'endpoint_connection_id' => 12,
              'pricing_model_id' => 3,
              'audience_inputs' => ['acme-advertiser'],
              'datastreams' => [['id' => 7, 'status' => true, 'compression' => 'gzip']],
              'price' => 1250.00,
          ],
      ]);
  $scheduleId = $res->json('data.0.id');
  ```
  {{< /tab >}}

{{< /tabs >}}

### Response

`recurrence.cycles` is the live cycle bookkeeping: `done` counts completed
cycles, `available` is how many the ending allows, `last_run` / `next_run`
carry the previous and upcoming data windows (`m/d/Y` dates) and the upcoming
run time. `last_run` is `null` before the first cycle; `next_run` is `null`
once the schedule is fulfilled.

```json
{
  "status": "success",
  "code": 201,
  "message": "Resource created successfully.",
  "data": [
    {
      "id": 42,
      "name": "Weekly Coffee Refresh",
      "status": { "id": 1, "name": "Active" },
      "source_audience": { "id": 8891, "name": "Coffee visitors L30" },
      "project": { "id": 12, "name": "Retail 2026" },
      "created_by": { "name": "Jane Doe", "email": "jane@acme.com" },
      "recurrence": {
        "start": "2026-08-01 09:00:00",
        "timezone": "America/New_York",
        "frequency": "weekly",
        "window": { "id": 4, "name": "Last 30 Days" },
        "window_days": null,
        "ending": { "type": 2, "after_recurrences": 12, "end_date": null },
        "cycles": {
          "done": 0,
          "available": 12,
          "last_run": null,
          "next_run": {
            "time": "08/01/2026 09:00",
            "date_start": "06/30/2026",
            "date_end": "07/29/2026"
          }
        }
      },
      "activation": {
        "enabled": true,
        "partner": { "name": "The Trade Desk", "description": "Programmatic advertising platform" },
        "pricing_model": { "name": "CPM", "price": 1.5 },
        "datastreams": [ { "name": "S3", "compression": "gzip" } ]
      },
      "created_at": "2026-07-21 10:00:00",
      "updated_at": "2026-07-21 10:00:00"
    }
  ]
}
```

## List Schedules {#get-apiv2analysesschedulesindex}

`GET /api/v2/analyses/schedules/index`

Lists the schedules owned by your company, most recent first, paginated. Each
item has the same shape as the [create response](#response).

**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 schedule name (case-insensitive contains). |

### Response

```json
{
  "status": "success",
  "code": 200,
  "message": "Resources fetched successfully.",
  "data": {
    "items": [
      { "id": 42, "name": "Weekly Coffee Refresh", "status": { "id": 1, "name": "Active" } }
    ],
    "pagination": { "current_page": 1, "per_page": 25, "total": 1, "last_page": 1 }
  }
}
```

## Get Schedule {#get-apiv2analysesschedulesid}

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

Fetches one schedule owned by your company, in the same shape as the
[create response](#response).

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

### Path parameters

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

## Activate Schedule {#post-apiv2analysesschedulesactivate}

`POST /api/v2/analyses/schedules/activate`

Sets the schedule `Active` and, when its recurrence is no longer armed (a
deactivation outlasted the pending run), re-arms it: the next run is rolled
forward from now on the stored cadence, with the data window restamped to
match. A schedule whose ending condition has already been met is not re-armed.
Responds with the updated schedule in the [create response](#response) shape.

**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 schedule id to activate. Must be owned by your company. |

## Deactivate Schedule {#post-apiv2analysesschedulesdeactivate}

`POST /api/v2/analyses/schedules/deactivate`

Pauses the schedule: upcoming cycles are skipped until it is
[activated](#post-apiv2analysesschedulesactivate) again. Audiences and
activations already produced are untouched, and a cycle already building when
you deactivate still completes and, with an activation block, is still
exported. Responds with the updated schedule
in the [create response](#response) shape.

**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 schedule id to deactivate. Must be owned by your company. |

## Delete Schedule {#post-apiv2analysesschedulesdelete-by-id}

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

Deletes one schedule owned by your company. The recurrence stops; audiences
and activations already produced by the schedule keep working exactly as
before. To change a schedule's settings, delete it and create a new one.

**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 schedule id to delete. Must be owned by your company. |

### Response

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