> For the complete documentation index, see [llms.txt](https://docs.mediarithmics.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.mediarithmics.io/setup-and-monitor/collect-and-manage-your-data/collect-data-in-real-time/server-side-events.md).

# Server-side events

Send activities, profiles, and consent from your own servers with the tracking API.

## Your goal

Send activities from your backend rather than from a browser - because the data lives there, because you want to avoid client-side loss, or because there is no browser involved at all.

## Expected outcome

Activities, profile updates, and user choices arriving from your servers, identified as reliably as your systems allow.

{% hint style="warning" %}
Choose the [authentication method](/setup-and-monitor/manage-the-platform/manage-api-access.md) that matches where the calling code runs. Long-term API tokens are for server-to-server calls; code running on a user's device must use signature authentication instead.
{% endhint %}

## Send an activity

```
POST https://api.mediarithmics.com/v1/datamarts/:datamartId/user_activities
```

With `Content-Type: application/json`, and a valid [UserActivity](/setup-and-monitor/collect-and-manage-your-data/collect-data-in-real-time.md#the-useractivity-object) object as the body.

Identification is done through the `$user_identifiers` property. **Use as many identifiers as are available in your environment at the time of capture** - this is what determines how well the platform can consolidate the user.

```json
{
  "$ts": 3489009384393,
  "$type": "APP_VISIT",
  "$session_status": "IN_SESSION",
  "$user_identifiers": [
    {
      "$type": "USER_ACCOUNT",
      "$compartment_id": "<COMPARTMENT_ID-1>",
      "$user_account_id": "<ACCOUNT_ID-1>"
    },
    {
      "$type": "USER_ACCOUNT",
      "$compartment_id": "<COMPARTMENT_ID-1>",
      "$user_account_id": "<ACCOUNT_ID-2>"
    },
    {
      "$type": "USER_AGENT",
      "$user_agent_id": "<USER_AGENT_ID>"
    },
    {
      "$type": "USER_EMAIL",
      "$hash": "<USER_EMAIL_HASH>",
      "$email": "<USER_EMAIL>"
    }
  ],
  "$app_id": "1023",
  "$events": [
    {
      "$ts": 3489009384393,
      "$event_name": "$app_open",
      "$properties": {}
    }
  ]
}
```

{% hint style="info" %}
Identifier properties at the activity root - `$user_agent_id`, `$user_account_id` with `$compartment_id`, and `$email_hash` - still work but are **legacy**. Prefer `$user_identifiers`.
{% endhint %}

## Create or update a profile

There are two ways, and which one you pick depends on whether you want the update recorded as an event.

{% tabs %}
{% tab title="Inside an activity" %}
Register an activity containing a `$set_user_profile_properties` event. Use `$user_account_id` and `$compartment_id` **inside `$properties`** to identify the profile to update.

```json
{
  "$ts": 3489009384393,
  "$type": "APP_VISIT",
  "$session_status": "IN_SESSION",
  "$user_identifiers": [
    {
      "$type": "USER_ACCOUNT",
      "$compartment_id": "<COMPARTMENT_ID>",
      "$user_account_id": "<ACCOUNT_ID>"
    }
  ],
  "$app_id": "1023",
  "$events": [
    {
      "$ts": 1679588413000,
      "$event_name": "$set_user_profile_properties",
      "$properties": {
        "$compartment_id": "<COMPARTMENT_ID>",
        "$user_account_id": "<ACCOUNT_ID>",
        "gender": "Male",
        "zipcode": "78000"
      }
    }
  ]
}
```

Any identifier available on the UserPoint can be used in the activity object.

Use this when you want the profile update to exist as an event you can retrieve later.
{% endtab %}

{% tab title="On the profile endpoint" %}

```
PUT https://api.mediarithmics.com/v1/datamarts/:datamartId/user_points/:userPointSelector/user_profiles/compartment_id=:compartmentId/user_account_id=:userAccountId
```

The body is a [UserProfile](/setup-and-monitor/collect-and-manage-your-data/design-your-data-model-and-schema/user-identity/user-identifiers/user-account.md#when-importing-profiles) object:

```json
{
  "$compartment_id": ":compartment_id",
  "$user_account_id": ":user_account_id",
  "gender": "female",
  "zipcode": "75001"
}
```

`$compartment_id` and `$user_account_id` in the payload are not mandatory here, because they are already given as parameters.

Prefer this method if you can integrate several API endpoints and you do **not** need the profile update tracked as an event.

{% hint style="warning" %}
Two different pairs of values appear in the same URL and are easy to confuse:

* `<COMPARTMENT_ID>` and `<USER_ACCOUNT_ID>` in the **selector** choose the **UserPoint**.
* `:compartmentId` and `:userAccountId` at the end choose the **profile to update**.
  {% endhint %}

The `update_strategy` query parameter controls how the update is applied - `PARTIAL_UPDATE`, `PARTIAL_DELETE`, or `FORCE_REPLACE`. See [Import profiles](/setup-and-monitor/collect-and-manage-your-data/import-data-in-batch/import-profiles.md) for what each one does. The older `force_replace` and `merge_objects` parameters are legacy; use `update_strategy` instead.
{% endtab %}
{% endtabs %}

## The UserPoint selector

Endpoints that act on a specific user take a selector, and any identifier can be used:

```
# By UserPoint ID
/v1/datamarts/<DATAMART_ID>/user_points/<USER_POINT_ID>/...
/v1/datamarts/<DATAMART_ID>/user_points/user_point_id=<USER_POINT_ID>/...

# By user agent ID
/v1/datamarts/<DATAMART_ID>/user_points/user_agent_id=<USER_AGENT_ID>/...

# By account ID and compartment
/v1/datamarts/<DATAMART_ID>/user_points/compartment_id=<COMPARTMENT_ID>,user_account_id=<USER_ACCOUNT_ID>/...

# By email hash
/v1/datamarts/<DATAMART_ID>/user_points/email_hash=<EMAIL_HASH>/...
```

## Create or update a user choice

Also two ways.

{% tabs %}
{% tab title="Inside an activity" %}
Register an activity containing a `$set_user_choice` event. This is the method to use when you want real-time tracking but cannot use the JavaScript tag - in a mobile application, for instance.

```json
{
    "$user_account_id": "<your_user_account_id>",
    "$compartment_id": "<your_compartment_id>",
    "$type": "SITE_VISIT",
    "$site_id": "<your_site_id>",
    "$session_status": "NO_SESSION",
    "$ts": 1572947762,
    "$events": [
        {
            "$event_name": "$set_user_choice",
            "$ts": 1572948120,
            "$properties": {
                "$processing_id": "<your_processing_id>",
                "$choice_acceptance_value": true,
                "<your_custom_field>": "<your_custom_field_value>"
            }
        }
    ]
}
```

`$processing_id` and `$choice_acceptance_value` are both mandatory.

{% hint style="warning" %}
These events go through the [processing pipeline](/setup-and-monitor/collect-and-manage-your-data/collect-data-in-real-time.md#the-processing-pipeline) before being stored as a UserChoice. Make sure no [activity analyzer](/setup-and-monitor/collect-and-manage-your-data/collect-data-in-real-time/activity-analyzers.md) removes them on the way.
{% endhint %}
{% endtab %}

{% tab title="On the choices endpoint" %}

```
PUT https://api.mediarithmics.com/v1/datamarts/:datamartId/user_points/:userSelector/user_choices/processing_id=:processingId
```

```json
{
    "$choice_ts": 1573135588140,
    "$choice_acceptance_value": true,
    "<your_custom_field>": "<your_custom_field_value>"
}
```

`$choice_ts` and `$choice_acceptance_value` are mandatory.

Reading choices back:

* `GET .../user_points/:userSelector/user_choices/processing_id=:processingId` lists the choices. The processing ID is optional - omit it to list all of them.
* `GET .../user_points/:userSelector/user_choices/processing_id=:processingId/change_log` returns the history of a choice.

Prefer this method if you can integrate several API endpoints.
{% endtab %}
{% endtabs %}

See [Manage consent & Processing activities](/setup-and-monitor/ensure-privacy-and-compliance/manage-consent-and-processing-activities.md) for what a processing activity is and how choices are enforced.

## Verify

* The `API` step shows events in ingestion monitoring. See [Monitor data ingestion](/setup-and-monitor/operate-and-monitor/monitor-data-ingestion.md).
* A test call produces an activity on the expected UserPoint, with the identifiers you sent.
* A profile update is visible on the profile you targeted, in the compartment you targeted.
* A user choice is readable back through the choices endpoint.

## Troubleshooting

| Symptom                                       | Likely cause                                                                                       |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| A new UserPoint per call                      | The identifiers you send are not stable, or the registry or compartment referenced does not exist. |
| The profile update lands on the wrong profile | The selector and the profile parameters were confused - see the warning above.                     |
| A user choice never appears                   | An activity analyzer removed the `$set_user_choice` event before storage.                          |
| The activity is rejected                      | The body is not a valid UserActivity, or it exceeds the 8,000-character limit.                     |

## Related pages

<table data-view="cards" data-search="false"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Manage API access</strong></td><td>Tokens, signature keys, and response formats.</td><td><a href="/setup-and-monitor/manage-the-platform/manage-api-access.md">Manage API access</a></td></tr><tr><td><strong>Import data in batch</strong></td><td>The right tool for mass volumes.</td><td><a href="/setup-and-monitor/collect-and-manage-your-data/import-data-in-batch.md">Import data in batch</a></td></tr><tr><td><strong>Import user consent</strong></td><td>Loading choices in bulk.</td><td><a href="/setup-and-monitor/collect-and-manage-your-data/import-data-in-batch/import-user-consent.md">Import user consent</a></td></tr></tbody></table>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.mediarithmics.io/setup-and-monitor/collect-and-manage-your-data/collect-data-in-real-time/server-side-events.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
