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

# Direct API Deployment

> Deploy Permutive without an SDK using direct API calls for server-side, CTV, and non-O&O environments

A Direct API deployment lets you integrate Permutive by making API calls directly, rather than deploying one of our [SDKs](/sdks/web/javascript-sdk/overview). This approach is suited to environments where deploying an SDK is not feasible — for example, certain CTV or hybrid TV setups with technical constraints, non-owned-and-operated (non-O\&O) properties where you don't control the application environment, or custom server-side architectures.

<Info>
  If your environment supports one of our [SDKs](/sdks/web/javascript-sdk/overview), that is the recommended approach — the SDK handles identity, event collection, caching, and activation delivery automatically. Use a Direct API deployment only when an SDK is not feasible.
</Info>

## Deployment Flow

A Direct API deployment involves two Permutive APIs, each serving a different purpose:

| API                                                           | Purpose                                                   | Requires consent | Requires user identity |
| ------------------------------------------------------------- | --------------------------------------------------------- | ---------------- | ---------------------- |
| [Custom Cohort Segmentation (CCS) API](/api/ccs/introduction) | Collect events and retrieve **behavioral** cohort signals | Yes              | Yes                    |
| [Contextual API](/api/contextual/introduction)                | Retrieve **contextual** cohort signals based on content   | No               | No                     |

Your application orchestrates these APIs, merges their results, and passes the combined signals to your ad server for activation.

```mermaid theme={"dark"}
flowchart TD
    A{User consented?} -->|Yes| B["CCS API<br/>(events + identity)"]
    A -->|No| C["Contextual API<br/>(content URL)"]
    B --> D[Behavioral cohort signals]
    C --> E[Contextual cohort signals]
    B -.->|Also call| C
    D --> F[Merge signals]
    E --> F
    F --> G[Ad server / SSP]
```

The sections below walk through each step in order.

## Step 1: Manage Consent

Your application is responsible for enforcing consent before collecting any user data.

* **CCS API** — only call this for users who have given appropriate consent under your applicable privacy framework (GDPR, CCPA, etc.). The API processes all events it receives; there is no server-side consent gate.
* **Contextual API** — this can be called for **all users**, regardless of consent status, because it evaluates content properties rather than user behavior.

<Warning>
  Do not send events to the CCS API for users who have not consented. Unlike the SDK, which can be configured with `consentRequired` to gate data collection, the API has no built-in consent check.
</Warning>

This means every user session follows one of two paths:

| User consent status | APIs to call             | Signals available       |
| ------------------- | ------------------------ | ----------------------- |
| Consented           | CCS API + Contextual API | Behavioral + Contextual |
| Not consented       | Contextual API only      | Contextual only         |

## Step 2: Manage Identity

For consented users, you need a stable identifier to send with CCS API requests. Your application owns the identity lifecycle — generating, persisting, and passing identifiers consistently.

### Choosing a User Identifier

Permutive supports three types of user identifier. They differ in **scope** — that is, how broadly the identifier can link a user across different contexts. We recommend using the broadest-scope identifier available in your environment.

| # | User identifier                                                            | Scope             | Links a user across...                       |
| - | -------------------------------------------------------------------------- | ----------------- | -------------------------------------------- |
| 1 | Authenticated first-party ID (e.g., hashed email, internal login ID)       | Broadest          | Multiple devices and environments            |
| 2 | Device ID (e.g., IDFA, AAID, CTV device identifiers, Alexa Advertising ID) | Device-level      | Multiple applications on the same device     |
| 3 | Generated Permutive ID (a Version 4 UUID your application generates)       | Application-level | Sessions within the current application only |

<Info>
  A Direct API deployment does not require third-party identifiers such as cookies, universal IDs, or authenticated logins. A device ID or a self-generated Permutive ID is sufficient for real-time, cross-session behavioral segmentation. This means your deployment is not constrained by identity availability — even in environments where traditional identifiers are limited (e.g., iOS, CTV).
</Info>

If more than one identifier is available, you can pass them all in a single request — Permutive will use the highest-priority identifier and retain the others for identity resolution.

### Sending the User Identifier

#### Authenticated First-Party ID

Pass as an `alias`, with the tag matching a user identifier configured in your Permutive workspace (e.g., `email_sha256`, `internal_id`):

```json theme={"dark"}
{
  "alias": {
    "tag": "email_sha256",
    "id": "a1b2c3d4e5f6..."
  },
  "events": [...]
}
```

#### Device ID

Pass as an `alias`, with the tag matching the device identifier type (e.g., `idfa`, `aaid`, `tifa`, `alexa_advertising_id`):

```json theme={"dark"}
{
  "alias": {
    "tag": "idfa",
    "id": "6D92078A-8246-4BA4-AE5B-76104861E7DC"
  },
  "events": [...]
}
```

#### Generated Permutive ID

For users without an authenticated ID or accessible device ID, your application can generate a Version 4 UUID on-device, persist it in local storage, and reuse it across sessions. Pass it directly as `user_id`:

```json theme={"dark"}
{
  "user_id": "2008c38f-dece-4570-976d-87593ed001c3",
  "events": [...]
}
```

#### Multiple Identifiers

If you have more than one identifier available, pass them in `aliases` with priorities (lower number = higher priority). Ordering by scope maximizes the chance of linking the user across sessions, devices, and environments:

```json theme={"dark"}
{
  "aliases": [
    { "tag": "email_sha256", "id": "a1b2c3d4e5f6...", "priority": 0 },
    { "tag": "idfa", "id": "6D92078A-8246-4BA4-AE5B-76104861E7DC", "priority": 1 }
  ],
  "events": [...]
}
```

### Key Considerations

* **Consistency** — always use the same identifier for the same user. Inconsistent identifiers create duplicate profiles and fragment cohort history.
* **Uniqueness** - make sure the identifiers are unique for each individual user. If multiple users share the same identity then all behavior from those users will be shared between them.
* **Persistence** — a generated Permutive ID must be stored in durable storage (e.g., device local storage) so it survives across sessions.
* **Workspace configuration** — user identifiers must be configured in your Permutive workspace before use. You can do this in the Permutive dashboard — see [Configuring identifiers](/products/signals/identity/identity-graph#configuring-identifiers-in-the-dashboard).
* **Identity resolution** — when multiple identifiers map to different Permutive user IDs, the highest-priority identifier (lowest number) wins and all future activity resolves to that user ID. Historical state under other user IDs is not retroactively merged into the winning profile — so consistent priority ordering across all platforms is important from the start. See [Identity concepts](/concepts/identity) and the [Identify API reference](/api/identity/identify) for the full resolution model.

## Step 3: Collect Data via the CCS API

For consented users, send events to the [CCS API](/api/ccs/introduction) to collect behavioral data and trigger real-time cohort evaluation. Each request contains a user identity and one or more events.

### Event Structure

Each event requires a `name`, `time`, and `properties` object that matches the schema defined in your Permutive workspace.

| Field        | Type   | Required | Description                                                                                              |
| ------------ | ------ | -------- | -------------------------------------------------------------------------------------------------------- |
| `name`       | string | Yes      | The event name as defined in your workspace (e.g., `"Videoview"`, `"AudioPlay"`)                         |
| `time`       | string | Yes      | ISO 8601 timestamp of when the event occurred                                                            |
| `view_id`    | UUID   | No       | Groups events within a single content engagement (e.g., watching a TV program or listening to a podcast) |
| `session_id` | UUID   | No       | Groups events within a single session, across multiple content engagements                               |
| `properties` | object | Yes      | Event properties matching your workspace schema                                                          |

<Info>
  Each request can include up to **10 events**. If you need to send more, split them across multiple requests. Events should be sent in chronological order.
</Info>

If a property is not applicable or not available for a given environment (e.g., `bluetooth_audio_active` on a web client), **omit it entirely** rather than sending it as `null` or an empty value.

### Session and View IDs

* **`session_id`** — generate a new UUID at the start of each user session and reuse it for all events within that session, which may span multiple content engagements. Define what a "session" means for your application (e.g., a 30-minute inactivity timeout).
* **`view_id`** — generate a new UUID for each content engagement (e.g., a video watched or a podcast listened to), and include it on every event that occurs within that engagement.

<Info>
  These IDs are required for creating cohorts which target using 'this session' and 'the current page' time windows.
</Info>

### Play and Completion Events

For any given content engagement, we recommend tracking **two events**:

* A **play event** when playback starts (e.g., `Videoview`, `AudioPlay`) — captures content metadata
* A **completion event** when playback ends or the user stops (e.g., `VideoCompletion`, `AudioCompletion`) — captures engagement metrics such as time spent and percentage completed

Both events share the same `view_id` to link them together. The exact event names and property schemas are configured in your Permutive workspace during onboarding.

<Tabs>
  <Tab title="Video">
    **1. Play event** — sent when the video starts playing:

    ```json theme={"dark"}
    {
      "name": "Videoview",
      "time": "2024-01-15T14:00:00Z",
      "view_id": "a2b3c4d5-e6f7-8901-2345-6789abcdef01",
      "session_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "properties": {
        "video_id": "vid_456",
        "title": "Highlights: Premier League Week 20",
        "categories": ["sports", "football"],
        "duration_seconds": 600
      }
    }
    ```

    **2. Completion event** — sent when the video ends or the user stops watching:

    ```json theme={"dark"}
    {
      "name": "VideoCompletion",
      "time": "2024-01-15T14:07:30Z",
      "view_id": "a2b3c4d5-e6f7-8901-2345-6789abcdef01",
      "session_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "properties": {
        "video_id": "vid_456",
        "title": "Highlights: Premier League Week 20",
        "categories": ["sports", "football"],
        "duration_seconds": 600,
        "time_spent_seconds": 450,
        "percentage_watched": 0.75
      }
    }
    ```
  </Tab>

  <Tab title="Audio">
    **1. Play event** — sent when the audio starts playing:

    ```json theme={"dark"}
    {
      "name": "AudioPlay",
      "time": "2024-01-15T08:15:00Z",
      "view_id": "b3c4d5e6-f789-0123-4567-89abcdef0123",
      "session_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "properties": {
        "podcast_id": "pod_789",
        "title": "Morning News Briefing",
        "categories": ["news", "current-affairs"],
        "duration_seconds": 1800
      }
    }
    ```

    **2. Completion event** — sent when the audio ends or the user stops listening:

    ```json theme={"dark"}
    {
      "name": "AudioCompletion",
      "time": "2024-01-15T08:37:30Z",
      "view_id": "b3c4d5e6-f789-0123-4567-89abcdef0123",
      "session_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "properties": {
        "podcast_id": "pod_789",
        "title": "Morning News Briefing",
        "categories": ["news", "current-affairs"],
        "duration_seconds": 1800,
        "time_spent_seconds": 1350,
        "percentage_listened": 0.75
      }
    }
    ```
  </Tab>
</Tabs>

<Warning>
  Cohort rules often depend on engagement data from the completion event. If you skip it, cohorts that use conditions like "listened for more than 30 seconds" or "watched at least 50%" will not evaluate correctly.
</Warning>

### Engagement Metrics

Your application is responsible for measuring engagement and including it on the completion event. Two metrics are typically captured:

| Metric     | Description                                                                                                                                                  |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Time spent | Total seconds the user spent engaging with the content                                                                                                       |
| Completion | Proportion of the content consumed (0–1). For video and audio, this is the percentage watched or listened to. For article web pages, it is the scroll depth. |

## Step 4: Retrieve Behavioral Cohort Signals

The CCS API response contains the user's updated cohort membership and, if requested, activations grouped by ad platform.

### Response Format

```json theme={"dark"}
{
  "user_id": "2008c38f-dece-4570-976d-87593ed001c3",
  "cohorts": ["12345", "67890", "11111"],
  "activations": {
    "freewheel": ["12345", "67890"],
    "adswizz_keyvalue": ["12345"],
    "dfp": ["12345", "67890", "11111"]
  }
}
```

* **`user_id`** — the Permutive user ID assigned to this user.
* **`cohorts`** — all cohort IDs the user currently belongs to.
* **`activations`** — cohorts grouped by the ad platform they are activated for. Only included if `activations=true` is set as a query parameter. The keys (e.g., `freewheel`, `adswizz_keyvalue`, `dfp`) correspond to activation platforms configured in your Permutive dashboard.

<Info>
  Always request activations by adding `?activations=true` to your API call if you intend to use cohorts for ad targeting.
</Info>

<Warning>
  When attaching cohorts to an ad request, use the per-platform `activations` object — not the top-level `cohorts` array. The `cohorts` array includes every cohort the user belongs to, many of which may not be relevant to (or activated for) the specific ad platform you are calling. Using the per-platform `activations` keeps your ad request focused on cohorts that have been explicitly activated for that platform.
</Warning>

### Caching

Call the CCS API on every content view to keep cohort membership up to date. However, cache the most recent response as a fallback:

1. **Cache each response** — store the latest cohorts and activations in a location accessible to your ad-serving logic (server-side session, in-memory cache, or device storage).
2. **Use cached values as fallback** — if the API call fails or is too slow, use cached cohorts from the previous response.
3. **Set a TTL** — a long TTL (e.g., 30 days or longer) is reasonable since the cached data is a short list of cohort IDs.

<Warning>
  Do not skip API calls and rely solely on cached data. Cohort membership updates in real time based on user behavior, and stale data reduces targeting accuracy.
</Warning>

## Step 5: Retrieve Contextual Cohort Signals

The [Contextual API](/api/contextual/introduction) returns cohort signals based on the content being viewed, rather than the user's behavior. This means it:

* Does **not** require user identity
* Does **not** require consent
* Should be called for **every content view**, regardless of consent status

The Contextual API takes a content URL and the same content properties you send in your CCS API events — **minus any user-related data** — and returns contextual cohort membership.

### Example Request

```bash theme={"dark"}
curl -X POST "https://api.permutive.com/ctx/v1/segment?k=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "<string>",
    "page_properties": {
      "client": {
        // Client properties
      },
      "context": {
        // Context properties
      },
      "audio": {
        // Audio properties (or video, article, etc. — matching your workspace schema)
      }
    }
  }'
```

Mirror the `properties` object from your CCS API event, omitting any user-related fields. This ensures the Contextual API classifies the same content the user is engaging with. See the [Contextual API reference](/api/contextual/introduction) for full request and response details.

### The `url` Field

The Contextual API requires a `url` field. Any URL scheme is accepted, so in audio, video, or other non-web environments, construct a URL that uniquely and deterministically identifies the active content.

**Recommendations:**

* **Use the public web URL where one exists** (e.g., `https://example.com/shows/morning-news/`). If the content has a web presence that drives traffic, Permutive may already have URL-based affinity scores associated with it, improving contextual classification.
* **Use an app-schema URL for in-app content** (e.g., `myapp://podcast/1234`) when no public web URL applies.
* **Construct a deterministic URL for environments without a natural URL** (e.g., smart speakers). Build one from a stable content identifier, such as `smartspeaker://station/abc123`.

The same piece of content should always resolve to the same URL — consistency is what allows Permutive to build and match contextual signals reliably.

<Info>
  Contextual cohorts are configured separately in the Permutive dashboard. If your workspace uses contextual cohorts, this API call is essential for maximizing signal coverage — especially for unconsented users where it is the only source of cohort signals.
</Info>

## Step 6: Merge Signals and Activate

Before passing cohort signals to your ad server, merge the behavioral activations (from the CCS API) with the contextual activations (from the Contextual API) into a single set of key-values.

```
Behavioral activations (CCS API):        {"freewheel": ["12345", "67890"]}
Contextual activations (Contextual API): {"freewheel": ["99999", "88888"]}

Merged activations:                      {"freewheel": ["12345", "67890", "99999", "88888"]}
```

For unconsented users, you will only have contextual activations — pass those directly.

<Info>
  In some cases it may make sense to send behavioral and contextual cohorts as **separate** key-values (e.g., `permutive` and `prmtvctx`) rather than merging them into one. This can enable separate reporting and targeting on each signal type in your ad platform. Discuss with your Customer Success Manager to decide on the right approach for your ad stack.
</Info>

### Attaching Signals to Ad Requests

Once merged (or split, depending on your approach), append the signals to your ad request as key-values. The exact format depends on your ad platform — we recommend working with [Technical Services](mailto:technical-services@permutive.com) during setup to confirm the correct key-value structure for each platform you are activating against.

## End-to-End Example

The following sequence diagram shows a complete content engagement — from playback start through to completion — illustrating how the CCS API, Contextual API, and ad server interact in practice.

```mermaid theme={"dark"}
sequenceDiagram
    autonumber
    participant App as Client App<br/>(CTV / server-side)
    participant CCS as CCS API
    participant CTX as Contextual API
    participant Ads as Ad Server

    Note over App: Preconditions<br/>consent granted, session_id and view_id generated,<br/>tifa available from device

    rect rgba(100, 160, 255, 0.15)
    Note over App,Ads: ▶ Playback starts

    par Behavioral (identity + consent required)
        App->>+CCS: POST /ccs/v1/segmentation?activations=true<br/>identifier: tifa = a1b2c3d4-...<br/>event: Videoview
        CCS-->>-App: user_id, cohorts,<br/>activations per platform
    and Contextual (no identity, no consent)
        App->>+CTX: POST /ctx/v1/segment<br/>url + content properties
        CTX-->>-App: contextual activations
    end

    Note over App: Cache responses (TTL ~24h)<br/>Merge activations per platform<br/>freewheel → behavioral + contextual

    App->>+Ads: Ad request with merged key-values
    Ads-->>-App: Ad creative
    end

    Note over App,Ads: ...video/TV content continues playing...

    rect rgba(255, 160, 100, 0.15)
    Note over App,Ads: ⏹ Playback ends — 450s of 600s watched (75%)

    App->>+CCS: POST /ccs/v1/segmentation?activations=true<br/>SAME identifier, view_id, session_id<br/>event: VideoCompletion + engagement metrics
    CCS-->>-App: Updated cohorts<br/>(feeds the NEXT ad opportunity)
    end
```

### Request and Response Payloads

Expand each step below to see the corresponding request or response body. Step numbers match the sequence diagram above.

<AccordionGroup>
  <Accordion title="Step 3 — Videoview: CCS API request body">
    The play event sent when playback starts, including the user identifier and content metadata:

    ```json theme={"dark"}
    {
      "aliases": [
        { "tag": "tifa", "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "priority": 0 }
      ],
      "events": [{
        "name": "Videoview",
        "time": "2024-01-15T14:00:00Z",
        "view_id": "a2b3c4d5-e6f7-8901-2345-6789abcdef01",
        "session_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
        "properties": {
          "video_id": "vid_456",
          "title": "Highlights: Premier League Week 20",
          "categories": ["sports", "football"],
          "duration_seconds": 600
        }
      }]
    }
    ```
  </Accordion>

  <Accordion title="Step 4 — CCS API response">
    The response includes the user's cohort membership and activations grouped by ad platform:

    ```json theme={"dark"}
    {
      "user_id": "2008c38f-dece-4570-976d-87593ed001c3",
      "cohorts": ["12345", "67890", "11111"],
      "activations": {
        "freewheel": ["12345", "67890"],
        "dfp": ["12345", "67890", "11111"]
      }
    }
    ```
  </Accordion>

  <Accordion title="Step 5 — Contextual API request body">
    Mirrors the content properties from the CCS API event, with a URL and no user-related data:

    ```json theme={"dark"}
    {
      "url": "https://example.com/shows/pl-highlights/",
      "page_properties": {
        "video": {
          "video_id": "vid_456",
          "title": "Highlights: Premier League Week 20",
          "categories": ["sports", "football"],
          "duration_seconds": 600
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="Step 6 — Contextual API response">
    Contextual cohort activations based on the content, independent of user identity:

    ```json theme={"dark"}
    {
      "activations": {
        "freewheel": ["99999"],
        "dfp": ["88888"]
      }
    }
    ```
  </Accordion>

  <Accordion title="Step 10 — VideoCompletion: CCS API request body">
    The completion event sent when playback ends. Note the **same** user identifier, `view_id`, and `session_id` as the play event, with added engagement metrics:

    ```json theme={"dark"}
    {
      "aliases": [
        { "tag": "tifa", "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "priority": 0 }
      ],
      "events": [{
        "name": "VideoCompletion",
        "time": "2024-01-15T14:07:30Z",
        "view_id": "a2b3c4d5-e6f7-8901-2345-6789abcdef01",
        "session_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
        "properties": {
          "video_id": "vid_456",
          "title": "Highlights: Premier League Week 20",
          "categories": ["sports", "football"],
          "duration_seconds": 600,
          "time_spent_seconds": 450,
          "percentage_watched": 0.75
        }
      }]
    }
    ```
  </Accordion>
</AccordionGroup>

## Feature Comparison: Direct API vs SDK

| Capability                                   | SDK                                                                                                                                            | Direct API                                                                  |
| -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| Event tracking                               | Simple — helper functions in our Web and CTV SDKs make event tracking straightforward                                                          | Manual — events must be sent explicitly via API                             |
| Engagement tracking (time spent, completion) | Automatic — the SDK measures engagement and manages state                                                                                      | Manual — your application must measure and track engagement                 |
| Identity management                          | Partially managed — the SDK generates a Permutive ID and collects device IDs where configured                                                  | Manual — your application must generate, persist, and send user identifiers |
| Classification Modeled Cohorts               | Supported                                                                                                                                      | Unsupported                                                                 |
| Cohort evaluation lookback                   | Supported — the SDK stores event history on-device, so newly created cohorts can be evaluated against the user's historical events             | Unsupported                                                                 |
| Cohort caching                               | Automatic — cohorts are cached in local/device storage                                                                                         | Manual — your application must cache API responses                          |
| Activation to ad platforms                   | Fully automatic on Web for supported platforms; on Mobile and CTV, cohorts are exposed via SDK APIs for your application to attach to ad calls | Manual — extract activations from the response and attach them to ad calls  |
| Latency                                      | Real-time — on-device segmentation with minimal network dependency                                                                             | Near real-time — dependent on the API round-trip                            |

## Verification and Troubleshooting

### Verifying Your Integration

After implementing a Direct API deployment, verify each step:

1. **Identity** — send a request and confirm the response returns a stable `user_id`. Send a second request with the same user identifier and confirm the same `user_id` is returned.
2. **Event collection** — create a test cohort in the Permutive dashboard with simple rules (e.g., "any AudioPlay event") and confirm the user enters it after sending a matching event.
3. **Activations** — with `activations=true`, check that the response includes the expected platforms and cohort IDs.
4. **Contextual signals** — call the Contextual API with a known URL and confirm contextual cohorts are returned.
5. **Signal merging** — verify that both behavioral and contextual signals appear in your ad requests.
6. **Consent gating** — confirm that no CCS API calls are made for unconsented users, and that contextual signals still flow.

### Common Issues

<AccordionGroup>
  <Accordion title="Event schema mismatch" icon="triangle-exclamation">
    **Problem:** Events are being sent but the user is not entering expected cohorts.

    **Cause:** The event name or property structure does not match the schema defined in your Permutive workspace.

    **Solutions:**

    * Verify the event `name` matches exactly (case-sensitive) what is configured in your workspace
    * Check that `properties` match the expected schema — missing required fields or incorrect types will cause the event to be ignored
    * Use the Permutive dashboard to inspect the event schema and compare it with what your application is sending
    * Ensure nested properties follow the correct structure (e.g., `audio.categories` should be an array, not a string)
    * For debugging, add `synchronous-validation=true` as a query parameter to your API call — this forces the CCS API to validate the event schema synchronously and return any schema mismatch errors in the response (by default, validation is asynchronous and schema errors return a `200` response). **Use this only for testing** — synchronous validation has performance implications and should not be enabled in production.
  </Accordion>

  <Accordion title="Duplicate or fragmented users" icon="user">
    **Problem:** The same real user appears as multiple users in Permutive, or cohort history is fragmented across sessions.

    **Cause:** Inconsistent identifiers are being sent across requests, or the identifier is not being persisted between sessions.

    **Solutions:**

    * Ensure you are using the same user identifier for the same user across all requests
    * If passing a tagged identifier, ensure both the tag and value are identical across all touchpoints
    * If you are generating a Permutive ID on-device, check that it is being read from durable storage on subsequent sessions and not regenerated
    * Verify that the user identifier is configured in your Permutive workspace
  </Accordion>

  <Accordion title="Incorrect API key" icon="key">
    **Problem:** API requests return `401 Unauthorized` or `403 Forbidden`.

    **Solutions:**

    * Find your API key in the [Permutive Dashboard](https://dash.permutive.com) under Settings
    * Provide the key as a query parameter (`?k=YOUR_API_KEY`) or header (`X-Api-Key: YOUR_API_KEY`)
    * Ensure you are using the key for the correct workspace
    * Check that the key has not been rotated or revoked
  </Accordion>

  <Accordion title="Empty cohorts in response" icon="circle-question">
    **Problem:** The API returns successfully but `cohorts` is always an empty array.

    **Solutions:**

    * Check that cohorts are configured and active in your Permutive dashboard
    * Verify that the events you are sending match the rules defined in your cohorts
    * Ensure the event properties contain the values that your cohort rules are looking for
    * Allow a few requests to build up user history — some cohorts require multiple events or sessions to qualify
  </Accordion>

  <Accordion title="Activations missing from response" icon="bullhorn">
    **Problem:** The response includes `cohorts` but no `activations`.

    **Solutions:**

    * Ensure you are passing `activations=true` as a query parameter
    * Check that cohorts are activated for at least one platform in the Permutive dashboard
    * A cohort must be explicitly activated for a platform to appear in the activations map
  </Accordion>

  <Accordion title="High latency or timeouts" icon="clock">
    **Problem:** API requests are slow or timing out, delaying ad requests.

    **Solutions:**

    * Implement a timeout on your API call (e.g., 200-500ms) and fall back to cached cohorts
    * Send API requests as early as possible in the page/screen lifecycle
    * Contact [Support](mailto:support@permutive.com) if latency persists
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="CCS API Reference" icon="code" href="/api/ccs/introduction">
    Full request and response schemas for behavioral cohort segmentation
  </Card>

  <Card title="Contextual API Reference" icon="bullseye" href="/api/contextual/introduction">
    Request and response schemas for contextual cohort signals
  </Card>

  <Card title="Identity Management" icon="user" href="/products/signals/identity/identity-graph">
    Learn more about Permutive's identity resolution
  </Card>

  <Card title="SDK Deployment" icon="browser" href="/sdks/web/javascript-sdk/overview">
    Consider an SDK if your environment supports it
  </Card>
</CardGroup>
