---
title: "media.view-request@1 · OpenVibe.Services"
canonical: "https://openvibe.services/docs/contracts/media.view-request"
updated: null
---

# media.view-request@1 · OpenVibe.Services

HTML: [media.view-request@1 · OpenVibe.Services](https://openvibe.services/docs/contracts/media.view-request)

# MediaViewRequest `media.view-request@1`

Generated at 2026-11-13 06:25:48 UTC from [`openvibe-contracts v0.419.0`](https://github.com/OpenVibers/OpenVibe.Contracts/tree/v0.419.0) and [`openvibe-sdk v0.161.0`](https://github.com/OpenVibers/OpenVibe.SDK/tree/v0.161.0).

- Version: 1.0.0
- Owner: media
- Visibility: first-party
- Status: active
- Compatibility: backward
- Schema: [`https://openvibe.network/contracts/media/view-request.v1.json`](/docs/contracts/media.view-request.json)

media.view-request@1: one view or watch heartbeat as a client sends it to OpenVibe.Media's POST /api/v1/views (the one view API for every content kind). ref names the item in its owner's id space: service is the service that owns it (media, live, network…), kind is that service's type (vod, clip, recording, stream, feed…), id is the item id. how is preview (a feed preview that has played), watch (a watch page) or live (a live stream). seconds is the cumulative number of seconds this session has watched the item — a retry or a seek sends the same or a larger number and is not counted twice; preview counts a view from 1 second, engaged is reached at 30 seconds or a third of of\_seconds (whichever is first) for a watch, or 60 seconds for a live stream. of\_seconds is the item's length when the client knows it. session is the anonymous session id the client keeps for a signed-out viewer (an opaque 8–128 character string); a signed-in viewer's Network token is used instead when sent. The server stores no IP address and no user agent with a view, dedupes one view per person or anonymous session per item per 30 minutes, never counts a declared bot or a crawler's user agent, accepts at most 20 events in one call (the batch is { events: \[request, …\] }) and rate-limits by session. A successful answer is a list of per-event results: accepted, counted, engaged and a reason when it was not counted.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `ref` | object | yes | The item being viewed. | - `no other fields` |
| `ref.service` | string | yes | The owning service: media, live, network, community… | - `pattern ^[a-z][a-z0-9-]{1,39}$` |
| `ref.kind` | string | yes | The owner's type: vod, clip, recording, stream, feed… | - `pattern ^[a-z][a-z0-9_]{1,39}$` |
| `ref.id` | string | yes | The item id in the owner's space, as a string. | - `minLength 1`<br>- `maxLength 200` |
| `ref.site` | string |  | Optional: the site the view happened on, when it differs from the owning service (the Feed counts a clip on network). Used only for Report's per-site numbers. | - `pattern ^[a-z][a-z0-9-]{0,39}$` |
| `ref.creator` | string |  | Optional: the creator's canonical subject (usr\_…) or channel username, when the caller knows it. Used only for Report's per-creator numbers; a Media item's owner is resolved by Media when absent. | - `maxLength 200` |
| `how` | enum | yes | preview: a silent preview in the feed; watch: a watch page; live: a live stream being watched now. | - `one of "preview", "watch", "live"` |
| `seconds` | number | yes | Cumulative seconds watched in this session for this item. Send the same number on a retry; send a larger one as playback continues. | - `minimum 0`<br>- `maximum 86400` |
| `of_seconds` | number |  | The item's length when the client knows it: engaged comes at min(30, of\_seconds / 3) for a watch. | - `maximum 86400` |
| `session` | string |  | The anonymous session id the client keeps for a signed-out viewer. Required when no Network user token is sent; never stored raw (Media keeps a keyed hash). | - `pattern ^[A-Za-z0-9._~-]{8,128}$` |

## Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

 Valid: preview

~~~~
{
  "ref": {
    "service": "media",
    "kind": "clip",
    "id": "7"
  },
  "how": "preview",
  "seconds": 2,
  "session": "anon_9f2c1e7a4b"
}
~~~~

Valid: watch

~~~~
{
  "ref": {
    "service": "media",
    "kind": "vod",
    "id": "42",
    "site": "network",
    "creator": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"
  },
  "how": "watch",
  "seconds": 34.5,
  "of_seconds": 601,
  "session": "anon_9f2c1e7a4b"
}
~~~~

Rejected: bad-how

~~~~
{
  "ref": {
    "service": "media",
    "kind": "vod",
    "id": "42"
  },
  "how": "scroll",
  "seconds": 3,
  "session": "anon_9f2c1e7a4b"
}
~~~~

Rejected: missing-ref

~~~~
{
  "how": "watch",
  "seconds": 3,
  "session": "anon_9f2c1e7a4b"
}
~~~~

## Validate

~~~~
const contracts = require('openvibe-contracts');
contracts.validate('media.view-request@1', value);   // { valid, errors: [{ path, message }] }
~~~~
