FeedItem feed.item@1
Generated at from
openvibe-contracts v0.419.0 and
openvibe-sdk v0.161.0.
- Version
- 1.0.0
- Owner
- network
- Visibility
- public
- Status
- active
- Compatibility
- backward
- Schema
https://openvibe.network/contracts/feed/item.v1.json
feed.item@1: one card of OpenVibe's one feed, served by OpenVibe.Network (GET /api/v1/feed and GET /api/frame/feed; OpenVibe.Community's Pulse reads the same feed). Every card names the service it came from, a stable id, its own link, and only public or follower-visible activity. Kinds: post, pic, clip, video, live, stream, rundown, quest, member, chat, event, wiki, update, work, deal, news, report, poll, game, meme, paste, episode, space, avatar, banner, follow, site.
Fields
| Field | Type | Required | Description | Constraints |
|---|---|---|---|---|
id | string | yes | Stable across feeds and restarts: f:<service>:<type>:<id> for a source item and t:<site>:<url> for a trending card. |
|
kind | enum | yes | What happened. live is a channel on air now (a go-live that ended is stream); stream is a past broadcast, including a watched-together summary; work is one grouped row of new jobs per hour; report is a number with its trend. |
|
url | string | yes | The thing itself: the post, stream, job board, report page or wiki page. |
|
title | string | yes |
| |
text | string | yes | The source's first 280 characters, on one line; empty when it has none. |
|
at | string | null | yes | When it happened; null only for a trending card. |
|
starts_at | string | When a scheduled event begins; present only on event cards. |
| |
site | string | yes | The source service: live, community, media-hub, pics, games, quest, chat, today, work, deals, news, report, wiki, blog, tools, network… |
|
site_name | string | yes |
| |
actor | object | null | yes | Who did it; null for AI, system, aggregate and trending cards. | |
subject | string | null | yes | The acting person's subject, when one is known; null for AI, system and aggregate cards. The Following filter and For you ranking read it. |
|
media | object | null | yes | The card's first picture, as a Media thumbnail; null when it has none. | |
counts | object | yes | The numbers the source knows; each present only when it knows it. |
|
counts.views | integer |
| ||
counts.likes | integer |
| ||
counts.comments | integer |
| ||
counts.watchers | integer | People who watched a stream (peak for a past broadcast). |
| |
counts.people | integer | A count a report card is about (people online, jobs, stories). |
| |
live_now | boolean | Present on live and stream cards: true only while the channel is on air. | ||
trend | object | Present on report cards: which way the number moved and by how much. |
| |
trend.direction | enum | yes |
| |
trend.pct | number |
| ||
trend.previous | integer |
| ||
group | object | A run of the same kind folded into one card (two or more items). |
| |
group.count | integer | yes |
| |
group.items | array of object | yes |
| |
group.items[].id | string | yes | ||
group.items[].url | string | yes |
| |
group.items[].title | string | yes | ||
group.items[].media | object | null | yes | ||
label | enum | Present only when the card needs one. |
| |
visibility | enum | Present only for an item returned to a follower or its author; never public. |
|
Examples
From the contract's own test fixtures: valid ones validate, rejected ones must fail.
Valid: avatar
{
"id": "f:example:avatar:1",
"kind": "avatar",
"url": "https://openvibe.network/@alex",
"title": "Alex changed their picture",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "network",
"site_name": "Network",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {}
}Valid: banner
{
"id": "f:example:banner:1",
"kind": "banner",
"url": "https://openvibe.network/@alex",
"title": "Alex changed their banner",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "network",
"site_name": "Network",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {}
}Valid: chat
{
"id": "f:example:chat:1",
"kind": "chat",
"url": "https://openvibe.chat/r/feed",
"title": "A busy hour in #feed",
"text": "28 messages",
"at": "2026-11-12T09:00:00.000Z",
"site": "chat",
"site_name": "Chat",
"actor": null,
"subject": null,
"media": null,
"counts": {
"people": 9
}
}Valid: clip
{
"id": "f:example:clip:1",
"kind": "clip",
"url": "https://openvibe.live/clip/c1",
"title": "A clip",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "live",
"site_name": "Live",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": {
"thumb": "https://openvibe.media/t/clip.jpg",
"ratio": 1.777,
"length": 42
},
"counts": {}
}Valid: deal
{
"id": "f:example:deal:1",
"kind": "deal",
"url": "https://openvibe.deals/d/example",
"title": "A deal on a keyboard",
"text": "20% off",
"at": "2026-11-12T09:00:00.000Z",
"site": "deals",
"site_name": "Deals",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {
"likes": 3
}
}Valid: episode
{
"id": "f:example:episode:1",
"kind": "episode",
"url": "https://openvibe.live/rundown/e/3005",
"title": "Rundown: an episode",
"text": "A logline",
"at": "2026-11-12T09:00:00.000Z",
"site": "live",
"site_name": "Live",
"actor": null,
"subject": null,
"media": null,
"counts": {}
}Valid: event
{
"id": "f:example:event:1",
"kind": "event",
"url": "https://openvibe.today/e/tev_example",
"title": "Coming up: a stream about feeds",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "today",
"site_name": "Today",
"actor": null,
"subject": null,
"media": null,
"counts": {},
"starts_at": "2026-11-13T18:00:00.000Z"
}Valid: follow
{
"id": "f:example:follow:1",
"kind": "follow",
"url": "https://openvibe.live/@alex",
"title": "Alex followed Maticus",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "network",
"site_name": "Network",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {}
}Valid: game
{
"id": "f:example:game:1",
"kind": "game",
"url": "https://openvibe.games/world/example",
"title": "Unlocked a level in Scraplandia",
"text": "Level 15",
"at": "2026-11-12T09:00:00.000Z",
"site": "games",
"site_name": "Games",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {}
}Valid: live
{
"id": "f:example:live:1",
"kind": "live",
"url": "https://openvibe.live/@alex",
"title": "Alex went live: building a feed",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "live",
"site_name": "Live",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {
"watchers": 12
},
"live_now": true,
"label": "Live"
}Valid: member
{
"id": "f:example:member:1",
"kind": "member",
"url": "https://openvibe.network/stats",
"title": "5 people joined today",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "network",
"site_name": "Network",
"actor": null,
"subject": null,
"media": null,
"counts": {},
"label": "System"
}Valid: meme
{
"id": "f:example:meme:1",
"kind": "meme",
"url": "https://openvibe.meme/m/example",
"title": "A meme with receipts",
"text": "Weaker rulers show mercy.",
"at": "2026-11-12T09:00:00.000Z",
"site": "meme",
"site_name": "Meme",
"actor": null,
"subject": null,
"media": null,
"counts": {},
"label": "System"
}Valid: news
{
"id": "f:example:news:1",
"kind": "news",
"url": "https://openvibe.news/s/example",
"title": "News: the feed ships",
"text": "Sourced, not spun.",
"at": "2026-11-12T09:00:00.000Z",
"site": "news",
"site_name": "News",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {}
}Valid: paste
{
"id": "f:example:paste:1",
"kind": "paste",
"url": "https://openvibe.community/p/example",
"title": "A paste",
"text": "print(\"hello\")",
"at": "2026-11-12T09:00:00.000Z",
"site": "community",
"site_name": "Community",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {
"views": 10,
"likes": 1
}
}Valid: pic
{
"id": "f:example:pic:1",
"kind": "pic",
"url": "https://openvibe.pics/p/pic_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"title": "A picture",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "pics",
"site_name": "Pics",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": {
"thumb": "https://openvibe.media/t/example.jpg",
"ratio": 1.5
},
"counts": {}
}Valid: poll
{
"id": "f:example:poll:1",
"kind": "poll",
"url": "https://openvibe.community/forum/status/poll",
"title": "Which feed kind do you open first?",
"text": "A poll",
"at": "2026-11-12T09:00:00.000Z",
"site": "community",
"site_name": "Community",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {
"people": 41
}
}Valid: post
{
"id": "f:example:post:1",
"kind": "post",
"url": "https://openvibe.community/forum/status/hello",
"title": "Hello, network",
"text": "A first post.",
"at": "2026-11-12T09:00:00.000Z",
"site": "community",
"site_name": "Community",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {
"likes": 4,
"comments": 2
}
}Valid: quest
{
"id": "f:example:quest:1",
"kind": "quest",
"url": "https://openvibe.quest/",
"title": "Finished a quest: first broadcast",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "quest",
"site_name": "Quest",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {}
}Valid: report
{
"id": "f:example:report:1",
"kind": "report",
"url": "https://openvibe.report/network",
"title": "Peak today: 1,204 people online at 9 PM",
"text": "Online peak across the network",
"at": "2026-11-12T09:00:00.000Z",
"site": "report",
"site_name": "Report",
"actor": null,
"subject": null,
"media": null,
"counts": {
"people": 1204
},
"trend": {
"direction": "up",
"pct": 12
},
"label": "System"
}Valid: rundown
{
"id": "f:example:rundown:1",
"kind": "rundown",
"url": "https://openvibe.live/rundown/e/3005",
"title": "Rundown: the case for a feed",
"text": "What the episode was about.",
"at": "2026-11-12T09:00:00.000Z",
"site": "live",
"site_name": "Live",
"actor": null,
"subject": null,
"media": {
"thumb": "https://openvibe.media/t/episode.jpg",
"ratio": 1.777
},
"counts": {},
"label": "System"
}Valid: site
{
"id": "f:example:site:1",
"kind": "site",
"url": "https://openvibe.network/",
"title": "A new site joined the network",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "network",
"site_name": "Network",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {}
}Valid: space
{
"id": "f:example:space:1",
"kind": "space",
"url": "https://openvibe.space/s/example",
"title": "A new space",
"text": "A site for a project.",
"at": "2026-11-12T09:00:00.000Z",
"site": "space",
"site_name": "Space",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {}
}Valid: stream
{
"id": "f:example:stream:1",
"kind": "stream",
"url": "https://openvibe.live/@alex",
"title": "312 people watched Alex tonight",
"text": "A watched-together stream.",
"at": "2026-11-12T09:00:00.000Z",
"site": "live",
"site_name": "Live",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {
"watchers": 312,
"views": 410
},
"live_now": false
}Valid: update
{
"id": "f:example:update:1",
"kind": "update",
"url": "https://openvibe.blog/p/feed",
"title": "New post on the OpenVibe blog",
"text": "The feed is one.",
"at": "2026-11-12T09:00:00.000Z",
"site": "blog",
"site_name": "Blog",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {}
}Valid: video
{
"id": "f:example:video:1",
"kind": "video",
"url": "https://openvibe.video/watch?v=example",
"title": "Recording: an evening",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "live",
"site_name": "Live",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": {
"thumb": "https://openvibe.media/t/vod.jpg",
"ratio": 1.777,
"length": 3600
},
"counts": {}
}Valid: wiki
{
"id": "f:example:wiki:1",
"kind": "wiki",
"url": "https://openvibe.wiki/space/page",
"title": "Wiki: OpenVibe / feed",
"text": "How the one feed works.",
"at": "2026-11-12T09:00:00.000Z",
"site": "wiki",
"site_name": "Wiki",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {}
}Valid: work
{
"id": "f:example:work:1",
"kind": "work",
"url": "https://openvibe.work/jobs",
"title": "14 new jobs this hour",
"text": "Remote: GIS engineer, and more",
"at": "2026-11-12T09:00:00.000Z",
"site": "work",
"site_name": "Work",
"actor": null,
"subject": null,
"media": null,
"counts": {
"people": 14
}
}Rejected: bad-subject
{
"id": "f:example:post:1",
"kind": "post",
"url": "https://openvibe.example/reference",
"title": "An example post",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "example",
"site_name": "Example",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "alex",
"media": null,
"counts": {}
}Rejected: group-too-small
{
"id": "f:example:video:1",
"kind": "video",
"url": "https://openvibe.example/reference",
"title": "An example video",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "example",
"site_name": "Example",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {},
"group": {
"count": 1,
"items": []
}
}Rejected: missing-id
{
"kind": "post",
"url": "https://openvibe.example/reference",
"title": "An example post",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "example",
"site_name": "Example",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {}
}Rejected: negative-count
{
"id": "f:example:post:1",
"kind": "post",
"url": "https://openvibe.example/reference",
"title": "An example post",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "example",
"site_name": "Example",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {
"likes": -1
}
}Rejected: not-a-url
{
"id": "f:example:post:1",
"kind": "post",
"url": "openvibe.community/no-scheme",
"title": "Hello, network",
"text": "A first post.",
"at": "2026-11-12T09:00:00.000Z",
"site": "community",
"site_name": "Community",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {
"likes": 4,
"comments": 2
}
}Rejected: private-visibility
{
"id": "f:example:post:1",
"kind": "post",
"url": "https://openvibe.example/reference",
"title": "An example post",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "example",
"site_name": "Example",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {},
"visibility": "private"
}Rejected: trend-without-direction
{
"id": "f:example:report:1",
"kind": "report",
"url": "https://openvibe.example/reference",
"title": "An example report",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "example",
"site_name": "Example",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {},
"trend": {
"pct": 4
}
}Rejected: unknown-kind
{
"id": "f:example:post:1",
"kind": "not-a-kind",
"url": "https://openvibe.example/reference",
"title": "An example post",
"text": "",
"at": "2026-11-12T09:00:00.000Z",
"site": "example",
"site_name": "Example",
"actor": {
"name": "Alex",
"avatar": null
},
"subject": "usr_01JQ8Z6K6M0V8Y2Q3R4S5T6V7W",
"media": null,
"counts": {}
}Validate
const contracts = require('openvibe-contracts');
contracts.validate('feed.item@1', value); // { valid, errors: [{ path, message }] }