---
title: "frame.menu-cards@1 · OpenVibe.Services"
canonical: "https://openvibe.services/docs/contracts/frame.menu-cards"
updated: null
---

# frame.menu-cards@1 · OpenVibe.Services

HTML: [frame.menu-cards@1 · OpenVibe.Services](https://openvibe.services/docs/contracts/frame.menu-cards)

# FrameMenuCards `frame.menu-cards@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: network
- Visibility: public
- Status: active
- Compatibility: backward
- Schema: [`https://openvibe.network/contracts/frame/menu-cards.v1.json`](/docs/contracts/frame.menu-cards.json)

frame.menu-cards@1: the public file a site serves at /.well-known/openvibe-menu.json — an array of at most three frame.menu-card@1 cards from one site. Network fetches it every five minutes with a five-second timeout and a 64 KB cap, drops unknown fields and markup, and keeps the site's last good card for 30 minutes if the file fails. An empty array is valid and means the site publishes no card right now.

## Fields

This schema has no named fields.

## Examples

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

 Valid: empty

~~~~
[]
~~~~

Valid: three

~~~~
[
  {
    "id": "live-now",
    "site": "live",
    "title": "Live now",
    "kind": "rail",
    "items": [
      {
        "title": "Nova",
        "meta": "12 watching",
        "url": "https://openvibe.live/@nova"
      }
    ],
    "more": {
      "label": "See all",
      "url": "https://openvibe.live/live"
    },
    "updated_at": "2026-11-07T08:00:00Z",
    "ttl_s": 30
  },
  {
    "id": "new-worlds",
    "site": "games",
    "title": "New worlds",
    "kind": "rows",
    "items": [
      {
        "title": "Harbour Town",
        "meta": "Built by 4 people",
        "url": "https://openvibe.games/w/harbour-town"
      }
    ],
    "updated_at": "2026-11-07T07:58:00Z",
    "ttl_s": 300
  },
  {
    "id": "trending",
    "site": "media-hub",
    "title": "Trending",
    "kind": "rows",
    "items": [
      {
        "title": "Sunday market",
        "meta": "2,100 views this week",
        "url": "https://openvibe.video/watch/abc"
      }
    ],
    "updated_at": "2026-11-07T07:55:00Z",
    "ttl_s": 300
  }
]
~~~~

Rejected: bad-card

~~~~
[
  {
    "id": "live-now",
    "site": "live",
    "title": "Live now",
    "kind": "rail",
    "items": [],
    "updated_at": "yesterday",
    "ttl_s": 30
  }
]
~~~~

Rejected: four-cards

~~~~
[
  {
    "id": "one",
    "site": "live",
    "title": "One",
    "kind": "rail",
    "items": [],
    "updated_at": "2026-11-07T08:00:00Z",
    "ttl_s": 30
  },
  {
    "id": "two",
    "site": "live",
    "title": "Two",
    "kind": "rows",
    "items": [],
    "updated_at": "2026-11-07T08:00:00Z",
    "ttl_s": 30
  },
  {
    "id": "three",
    "site": "live",
    "title": "Three",
    "kind": "chips",
    "items": [],
    "updated_at": "2026-11-07T08:00:00Z",
    "ttl_s": 30
  },
  {
    "id": "four",
    "site": "live",
    "title": "Four",
    "kind": "rows",
    "items": [],
    "updated_at": "2026-11-07T08:00:00Z",
    "ttl_s": 30
  }
]
~~~~

## Validate

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