# Fetch content

Fetch dynamic content from your backend.

```
POST https://api.croct.io/external/web/content
```

Retrieves the content of a [slot](/explanation/slot) on behalf of a specific user, applying [audience](/explanation/audience) targeting and [experiment](/explanation/experiment) assignment. The response reflects the active [experience](/explanation/experience/introduction) for that user.

This is the server-side variant, intended for calls originating from your backend. It authenticates with an [API key](/explanation/application/api-keys) and applies rate limits tuned for backend traffic. For calls from a browser or device, use the [client-side variant](/reference/api/service/content/endpoint/client/content) instead.

## Example

Here is an example of how to fetch dynamic content from a backend:

**JavaScript**

```js
const response = await fetch('https://api.croct.io/external/web/content', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Api-Key': '<API KEY>',
    'X-Client-Id': '<CLIENT ID>',
    'X-Token': '<USER TOKEN>',
    'X-Client-Ip': '192.0.2.10',
    'X-Client-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
    'X-Client-Library': 'Custom Client v1.0.0',
  },
  body: JSON.stringify({
    slotId: 'home-hero',
    version: '1',
    preferredLocale: 'en-us',
    includeSchema: true,
    context: {
      timeZone: 'America/New_York',
      page: {
        url: 'https://example.com/pricing',
        title: 'Pricing',
        referrer: 'https://google.com',
      },
      campaign: {
        name: 'summer-sale',
        source: 'google',
        medium: 'cpc',
      },
      attributes: {
        plan: 'pro',
      },
    },
  }),
});

const {content, metadata} = await response.json();
```

**cURL**

```bash
curl -X POST 'https://api.croct.io/external/web/content' \
  -H 'Content-Type: application/json' \
  -H 'X-Api-Key: <API KEY>' \
  -H 'X-Client-Id: <CLIENT ID>' \
  -H 'X-Token: <USER TOKEN>' \
  -H 'X-Client-Ip: 192.0.2.10' \
  -H 'X-Client-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36' \
  -H 'X-Client-Library: Custom Client v1.0.0' \
  -d '{
    "slotId": "home-hero",
    "version": "1",
    "preferredLocale": "en-us",
    "includeSchema": true,
    "context": {
      "timeZone": "America/New_York",
      "page": {
        "url": "https://example.com/pricing",
        "title": "Pricing",
        "referrer": "https://google.com"
      },
      "campaign": {
        "name": "summer-sale",
        "source": "google",
        "medium": "cpc"
      },
      "attributes": {
        "plan": "pro"
      }
    }
  }'
```

## Request headers

This endpoint accepts the following HTTP headers:

- `Content-Type`: `string`

  Must be `application/json`.

- `X-Api-Key`: `string`

  The [API key](/explanation/application/api-keys) to use for server-side requests.

  > **Keep your API key safe**
  >
  > Never use your API key in client-side code, only in server-side environments.

- `X-Client-Id`: `string` (optional)

  The ID of the client, in the form of a UUID.

  This must be a persistent identifier that uniquely identifies the browser or device across sessions.

- `X-Token`: `string` (optional)

  A base64-encoded [JSON Web Token (JWT)](https://jwt.io) that identifies the user.

  Pass this header to use the identified user's context.

  When the application enforces [signed tokens](/explanation/application/signed-tokens), the JWT must be signed using an [API key](/explanation/application/api-keys) with the **Issue user tokens** permission, or the request fails with an authorization error.

  Either `X-Client-Id` or [`X-Token`](#x-token-prop) must be present.

- `X-Client-Ip`: `string` (optional)

  The IP address of the client.

  Defaults to the IP of the incoming request. Passing a loopback address such as `127.0.0.1` also falls back to the request IP, which is useful for local development.

- `X-Client-Agent`: `string` (optional)

  The user agent of the client.

  Defaults to the `User-Agent` header of the incoming request.

- `X-Client-Library`: `string` (optional)

  The SDK name and version, such as `Croct SDK JS v0.20.0`.

  Used for usage statistics and feature switching to preserve backward compatibility across SDK versions.

## Parameters

This endpoint accepts the following JSON body parameters:

- `slotId`: `string`

  The ID of the [slot](/explanation/slot) to fetch, such as `hero-banner`.

- `version`: `string` (optional)

  The schema version of the slot to fetch, in `major` or `major.minor` format, such as `1` or `1.2`.

  Defaults to the latest version when omitted.

- `preferredLocale`: `string|null` (optional)

  The locale code of the content to fetch.

  The code consists of a two-part string that specifies the language and, optionally, the country. For example, `en` represents English, `en-us` stands for English (United States), and `pt-br` for Portuguese (Brazil). It is case-insensitive and supports both hyphens and underscores as separators to accommodate the different conventions used by browsers, libraries, and other systems.

  Falls back to the slot's default locale when the preferred locale is unsupported or omitted. Pass `null` to explicitly use the default.

- `previewToken`: `string` (optional)

  A base64-encoded [JSON Web Token (JWT)](https://jwt.io) that identifies the preview session.

  When previewing content, the platform generates a preview token and passes it to your application as a URL parameter. Forward this token through this field to enable preview mode.

  When provided, audience targeting is bypassed and the previewed variant is returned.

- `includeSchema`: `boolean` (optional) (default: )

  Whether to include the slot's [content schema](/reference/content/schema/introduction) in [`metadata.schema`](#metadata-schema-prop).

- `context`: `object` (optional)

  Information about the user context, such as the time zone, page, and marketing campaign.

  Used to evaluate [audience](/explanation/audience) targeting rules and [CQL](/reference/cql/introduction) expressions.

  - `timeZone`: `string` (optional)

    The time zone of the user, represented by an [IANA time zone ID](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones), such as `America/New_York`.

    Used to evaluate time-based targeting rules.

  - `page`: `object` (optional)

    Information about the page the user is currently viewing.

    - `url`: `string`

      The URL of the page, such as `https://www.example.com/products`.

      Required when `page` is provided.

    - `title`: `string` (optional)

      The title of the page, such as `Products`.

    - `referrer`: `string` (optional)

      The URL of the page that linked to the current page, such as `https://www.google.com`.

  - `campaign`: `object` (optional)

    Information about the marketing campaign that brought the user to the page.

    These properties are compatible with the [UTM parameters](https://en.wikipedia.org/wiki/UTM_parameters) widely used in digital marketing for tracking and analytics.

    - `name`: `string` (optional)

      A unique identifier for the campaign. It is usually a short string, such as `summer-sale` or `new-product-launch`.

    - `source`: `string` (optional)

      The advertiser, site, or publication generating the traffic. For example, `google`, `facebook`, or `newsletter`.

    - `medium`: `string` (optional)

      The advertising or marketing channel used to reach the user. For example, `email`, `video`, or `social`.

    - `content`: `string` (optional)

      An identification of the specific ad or content the user interacted with. For example, `main-banner` or `newsletter-cta`.

    - `term`: `string` (optional)

      The keyword or term that triggered the ad. For example, `running shoes` or `tennis racket`.

  - `attributes`: `object` (optional)

    Custom key-value pairs available in [CQL](/reference/cql/introduction) targeting expressions through the `context` variable.

    For example, suppose you pass the following attributes:

    ```json
    {"cities": ["New York", "San Francisco"]}
    ```

    You can then reference them in queries like:

    ```cql
    context's cities include location's cityName
    ```

    The following restrictions apply:

    - Up to 30 entries and 5 levels deep
    - Keys can be either numbers or non-empty strings with a maximum length of 50 characters
    - Values can be null, numbers, booleans, strings (up to 50 characters), or nested maps
    - Nested maps follow the same constraints for keys and values

## Response

The service can respond with either `200 OK` or `202 Accepted` status code.

### 200 OK

Returns the requested content in a JSON body with the following properties:

- `content`: `object`

  The resolved [slot](/explanation/slot) content.

  The shape is defined by the slot's content schema, plus a reserved `_component` field.

  - `_component`: `string|null`

    The component identifier and major version, such as `hero-banner@1`.

    Is `null` when no component is associated with the slot.

- `metadata`: `object`

  Metadata describing how the content was resolved.

  - `version`: `string`

    The resolved slot schema version.

  - `contentSource`: `string`

    The source of the returned content. The possible values are:

    | Value        | Description                                                                           |
    | ------------ | ------------------------------------------------------------------------------------- |
    | `slot`       | The slot's [default content](/explanation/content/slot-default-content) was returned. |
    | `experience` | An active [experience](/explanation/experience/introduction) matched the request.     |
    | `experiment` | An [experiment](/explanation/experiment) variant was assigned.                        |

  - `experience`: `object` (optional)

    Details about the matched [experience](/explanation/experience/introduction).

    This property is included when [`contentSource`](#metadata-contentsource-prop) is `experience` or `experiment`.

    - `experienceId`: `string`

      The ID of the matched [experience](/explanation/experience/introduction).

    - `audienceId`: `string`

      The ID of the [audience](/explanation/audience) the user matched.

    - `experiment`: `object` (optional)

      Details about the running [experiment](/explanation/experiment).

      This property is included when the experience has an active experiment.

      - `experimentId`: `string`

        The ID of the [experiment](/explanation/experiment).

      - `variantId`: `string`

        The ID of the assigned variant.

  - `schema`: [`Schema`](/reference/content/schema/introduction) (optional)

    The slot's content schema definition.

    Human-readable labels and descriptions are omitted from the returned schema for security reasons, since they may contain sensitive business information.

    This property is included only when the request set [`includeSchema`](#includeschema-prop) to `true`.

#### Example response

Here is an example of a JSON response from this endpoint:

```json
{
  "content": {
    "_component": "home-hero@1",
    "heading": "Personalized for Pro users",
    "cta": {
      "label": "Upgrade now",
      "link": "https://example.com/upgrade"
    }
  },
  "metadata": {
    "version": "1.0",
    "contentSource": "experience",
    "experience": {
      "experienceId": "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
      "audienceId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
      "experiment": {
        "experimentId": "6ba7b811-9dad-11d1-80b4-00c04fd430c8",
        "variantId": "6ba7b812-9dad-11d1-80b4-00c04fd430c8"
      }
    },
    "schema": {
      "root": {
        "type":"structure",
        "title":"Hero",
        "attributes": {
          "heading": {
            "position":0,
            "type": {
              "type":"text"
            }
          },
          "cta": {
            "position":1,
            "type": {
              "type":"structure",
              "attributes": {
                "label":{
                  "position":0,
                  "type":{
                    "type":"text"
                  }
                },
                "link": {
                  "position":1,
                  "type": {
                    "type":"text"
                  }
                }
              }
            }
          }
        }
      },
      "definitions": {}
    }
  }
}
```

### 202 Accepted

The request is accepted but the service is suspended and returns no content. Clients should treat this as a traffic control signal, not an error, and fall back to default behavior. The suspension reason is indicated by the `X-Suspension-Reason` header:

- `X-Suspension-Reason`: `string` (optional)

  The reason the API returned an empty response. The possible values are:

  | Value                                    | Description                                                                        |
  | ---------------------------------------- | ---------------------------------------------------------------------------------- |
  | `tracking_and_personalization_suspended` | The workspace has [suspended services](/explanation/workspace/service-suspension). |
