---
title: "search.fabric-result@1 · OpenVibe.Services"
canonical: "https://openvibe.services/docs/contracts/search.fabric-result"
updated: null
---

# search.fabric-result@1 · OpenVibe.Services

HTML: [search.fabric-result@1 · OpenVibe.Services](https://openvibe.services/docs/contracts/search.fabric-result)

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

search.fabric-result@1: the answer of search.fabric.query. results come grouped by scope order (the room's own first when the caller asked for it); every web result keeps its provider, URL and date; route.explain says, step by step, what was asked, what answered, from cache or not, and what it cost. The query is never echoed: a page renders results without writing the query into its HTML (the ?q= rule).

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `results` | array of object | yes |  | - `maxItems 100`<br>- `items: no other fields` |
| `results[].scope` | enum | yes |  | - `one of "network", "mine", "web", "news", "images", "video", "products", "people", "places", "code"` |
| `results[].kind` | enum | yes | document: in Search's index (owner, type, id set); page: a web page (provider set). | - `one of "document", "page"` |
| `results[].title` | string | yes |  | - `maxLength 300` |
| `results[].url` | string | yes |  | - `format uri`<br>- `maxLength 2048` |
| `results[].snippet` | string \| null |  | Plain text; never HTML. | - `maxLength 600` |
| `results[].site` | string \| null |  |  | - `maxLength 253` |
| `results[].image` | string \| null |  |  | - `maxLength 2048` |
| `results[].published_at` | string \| null |  |  | - `maxLength 40` |
| `results[].retrieved_at` | string \| null |  | When a web page was read (by the provider or the open index). | - `maxLength 40` |
| `results[].provider` | string \| null |  | page: who found it (open\_index, deepseek, wikipedia, brave, …). | - `maxLength 40` |
| `results[].owner` | string \| null |  |  | - `maxLength 32` |
| `results[].type` | string \| null |  |  | - `maxLength 32` |
| `results[].id` | string \| null |  |  | - `maxLength 200` |
| `results[].score` | number \| null |  | Rank within its scope (higher first); not comparable across scopes. |  |
| `results[].ranks` | object |  | 1-based position in the keyword and vector top-100 lists; null when absent from that list. | - `no other fields` |
| `results[].ranks.keyword` | integer \| null | yes |  | - `minimum 1` |
| `results[].ranks.vector` | integer \| null | yes |  | - `minimum 1` |
| `groups` | object | yes | How many results each scope gave on this page. |  |
| `answer` | object \| null |  |  |  |
| `citations` | array of object |  |  | - `maxItems 20`<br>- `items: no other fields` |
| `citations[].n` | integer | yes |  | - `minimum 1` |
| `citations[].url` | string | yes |  | - `maxLength 2048` |
| `citations[].title` | string | yes |  | - `maxLength 300` |
| `citations[].provider` | string \| null |  |  | - `maxLength 40` |
| `citations[].retrieved_at` | string \| null |  |  | - `maxLength 40` |
| `route` | object | yes |  | - `no other fields` |
| `route.explain` | array of object | yes |  | - `maxItems 20`<br>- `items: no other fields` |
| `route.explain[].scope` | enum | yes |  | - `one of "network", "mine", "web", "news", "images", "video", "products", "people", "places", "code"` |
| `route.explain[].step` | string | yes | index, open\_index, cache, deepseek, wikipedia, … | - `maxLength 40` |
| `route.explain[].ok` | boolean | yes |  |  |
| `route.explain[].count` | integer |  |  | - `minimum 0` |
| `route.explain[].ms` | integer |  |  | - `minimum 0` |
| `route.explain[].cached` | boolean |  |  |  |
| `route.explain[].cost_usd` | number |  |  | - `minimum 0` |
| `route.explain[].skipped` | string \| null |  | Why the step did not run (not allowed for this caller, quick depth, an earlier step answered, provider standing down). | - `maxLength 160` |
| `route.explain[].ranking` | enum |  |  | - `one of "hybrid", "keyword"` |
| `route.explain[].reason` | enum |  |  | - `one of "no_vectors", "ai_unavailable", "short_query"` |
| `route.cost_usd` | number | yes | What this query cost OpenVibe (paid provider calls; cached answers cost 0). | - `minimum 0` |
| `next_cursor` | string \| null | yes |  | - `maxLength 512` |

## Examples

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

 Valid: mixed

~~~~
{
  "results": [
    {
      "scope": "network",
      "kind": "document",
      "title": "surf_utopia in 1:02.4",
      "url": "https://openvibe.live/clips/482",
      "snippet": "A clean line through every stage.",
      "site": "openvibe.live",
      "image": null,
      "published_at": "2026-10-27T21:04:00Z",
      "retrieved_at": null,
      "provider": null,
      "owner": "media-hub",
      "type": "clip",
      "id": "482",
      "score": 0.82
    },
    {
      "scope": "web",
      "kind": "page",
      "title": "Surf (Counter-Strike) - Wikipedia",
      "url": "https://en.wikipedia.org/wiki/Surf_(video_game_mode)",
      "snippet": "Surfing is a game mode in Counter-Strike…",
      "site": "en.wikipedia.org",
      "image": null,
      "published_at": null,
      "retrieved_at": "2026-10-28T11:20:00Z",
      "provider": "wikipedia",
      "owner": null,
      "type": null,
      "id": null,
      "score": 0.61
    }
  ],
  "groups": {
    "network": 1,
    "web": 1
  },
  "answer": null,
  "citations": [
    {
      "n": 1,
      "url": "https://en.wikipedia.org/wiki/Surf_(video_game_mode)",
      "title": "Surf (Counter-Strike) - Wikipedia",
      "provider": "wikipedia",
      "retrieved_at": "2026-10-28T11:20:00Z"
    }
  ],
  "route": {
    "explain": [
      {
        "scope": "network",
        "step": "index",
        "ok": true,
        "count": 1,
        "ms": 18,
        "cached": false,
        "cost_usd": 0
      },
      {
        "scope": "web",
        "step": "open_index",
        "ok": true,
        "count": 0,
        "ms": 9,
        "cost_usd": 0
      },
      {
        "scope": "web",
        "step": "deepseek",
        "ok": true,
        "count": 1,
        "ms": 1420,
        "cached": true,
        "cost_usd": 0
      },
      {
        "scope": "web",
        "step": "brave",
        "ok": false,
        "skipped": "an earlier step answered"
      }
    ],
    "cost_usd": 0
  },
  "next_cursor": null
}
~~~~

Valid: with-answer

~~~~
{
  "results": [
    {
      "scope": "web",
      "kind": "page",
      "title": "A",
      "url": "https://example.org/a",
      "provider": "deepseek"
    }
  ],
  "groups": {
    "web": 1
  },
  "answer": {
    "text": "Surf maps are ramps you slide along [1].",
    "citations": [
      1
    ],
    "label": "Written by AI from these sources"
  },
  "citations": [
    {
      "n": 1,
      "url": "https://example.org/a",
      "title": "A",
      "provider": "deepseek",
      "retrieved_at": "2026-10-28T11:20:00Z"
    }
  ],
  "route": {
    "explain": [
      {
        "scope": "web",
        "step": "deepseek",
        "ok": true,
        "count": 1,
        "cost_usd": 0.00075
      }
    ],
    "cost_usd": 0.00075
  },
  "next_cursor": null
}
~~~~

Rejected: answer-without-citations

~~~~
{
  "results": [],
  "groups": {},
  "answer": {
    "text": "Trust me.",
    "citations": [],
    "label": "Written by AI from these sources"
  },
  "route": {
    "explain": [],
    "cost_usd": 0
  },
  "next_cursor": null
}
~~~~

Rejected: html-in-kind

~~~~
{
  "results": [
    {
      "scope": "web",
      "kind": "html",
      "title": "x",
      "url": "https://x.org/"
    }
  ],
  "groups": {},
  "route": {
    "explain": [],
    "cost_usd": 0
  },
  "next_cursor": null
}
~~~~

## Validate

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