---
title: "composer.prefs@1 · OpenVibe.Services"
canonical: "https://openvibe.services/docs/contracts/composer.prefs"
updated: null
---

# composer.prefs@1 · OpenVibe.Services

HTML: [composer.prefs@1 · OpenVibe.Services](https://openvibe.services/docs/contracts/composer.prefs)

# ComposerPrefs `composer.prefs@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: first-party
- Status: active
- Compatibility: backward
- Schema: [`https://openvibe.network/contracts/composer/prefs.v1.json`](/docs/contracts/composer.prefs.json)

composer.prefs@1: a person's composer settings, the same on every OpenVibe site because Network stores them (GET/PUT /api/v1/me/composer). \`rev\` is the stored revision: send the one you read back with your change, and a tab that read an older copy gets 409 with the current document instead of overwriting the other tab. \`send\_key\` says whether Enter or Ctrl/Cmd+Enter sends. \`tools\` is the one order of the composer's tools and which are hidden; a tool id the client does not know is kept, so a site can add one without another site dropping the person's order. \`snippets\` are the person's saved inserts, triggered by typing a slash word.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `rev` | integer | yes | Revision of the stored document. A missing document is rev 0 with the defaults; every successful PUT answers rev + 1. | - `minimum 0` |
| `send_key` | enum | yes | enter: Enter sends and Shift+Enter breaks the line. mod-enter: Ctrl/Cmd+Enter sends and Enter breaks the line (the default on a phone keyboard is enter). | - `one of "enter", "mod-enter"` |
| `tools` | array of object | yes | Every tool in the order the person wants them, each marked hidden or not. | - `maxItems 100`<br>- `items: no other fields` |
| `tools[].id` | string | yes |  | - `pattern ^[a-z0-9][a-z0-9._-]*$`<br>- `minLength 1`<br>- `maxLength 40` |
| `tools[].hidden` | boolean | yes |  |  |
| `density` | enum | yes | How much room the composer takes: comfy is the default on a computer, compact on a phone. | - `one of "comfy", "compact"` |
| `voice` | boolean | yes | Whether the composer offers dictation (where the device has it). |  |
| `snippets` | array of object | yes | Saved inserts. Typing the trigger (a slash word such as /sig) in the composer offers the snippet. | - `maxItems 50`<br>- `items: no other fields` |
| `snippets[].trigger` | string | yes | The slash word that offers the snippet, e.g. /sig. Lower-case letters, digits, \_ and - only. | - `pattern ^/[a-z0-9][a-z0-9_-]{0,31}$` |
| `snippets[].label` | string | yes | What the menu shows. | - `minLength 1`<br>- `maxLength 80` |
| `snippets[].text` | string | yes | What is inserted. | - `minLength 1`<br>- `maxLength 4000` |

## Examples

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

 Valid: default

~~~~
{
  "rev": 0,
  "send_key": "mod-enter",
  "tools": [
    {
      "id": "attach",
      "hidden": false
    },
    {
      "id": "camera",
      "hidden": false
    },
    {
      "id": "voice",
      "hidden": false
    },
    {
      "id": "gif",
      "hidden": true
    }
  ],
  "density": "comfy",
  "voice": true,
  "snippets": [
    {
      "trigger": "/sig",
      "label": "My signature",
      "text": "— Sam"
    }
  ]
}
~~~~

Valid: snippet-max

~~~~
{
  "rev": 7,
  "send_key": "enter",
  "tools": [],
  "density": "compact",
  "voice": false,
  "snippets": [
    {
      "trigger": "/long",
      "label": "Long text",
      "text": "x"
    }
  ]
}
~~~~

Rejected: bad-trigger

~~~~
{
  "rev": 0,
  "send_key": "enter",
  "tools": [],
  "density": "comfy",
  "voice": false,
  "snippets": [
    {
      "trigger": "sig",
      "label": "Signature",
      "text": "— Sam"
    }
  ]
}
~~~~

Rejected: too-many-snippets

~~~~
{
  "rev": 0,
  "send_key": "enter",
  "tools": [],
  "density": "comfy",
  "voice": false,
  "snippets": [
    {
      "trigger": "/s1",
      "label": "s1",
      "text": "1"
    },
    {
      "trigger": "/s2",
      "label": "s2",
      "text": "2"
    },
    {
      "trigger": "/s3",
      "label": "s3",
      "text": "3"
    },
    {
      "trigger": "/s4",
      "label": "s4",
      "text": "4"
    },
    {
      "trigger": "/s5",
      "label": "s5",
      "text": "5"
    },
    {
      "trigger": "/s6",
      "label": "s6",
      "text": "6"
    },
    {
      "trigger": "/s7",
      "label": "s7",
      "text": "7"
    },
    {
      "trigger": "/s8",
      "label": "s8",
      "text": "8"
    },
    {
      "trigger": "/s9",
      "label": "s9",
      "text": "9"
    },
    {
      "trigger": "/s10",
      "label": "s10",
      "text": "10"
    },
    {
      "trigger": "/s11",
      "label": "s11",
      "text": "11"
    },
    {
      "trigger": "/s12",
      "label": "s12",
      "text": "12"
    },
    {
      "trigger": "/s13",
      "label": "s13",
      "text": "13"
    },
    {
      "trigger": "/s14",
      "label": "s14",
      "text": "14"
    },
    {
      "trigger": "/s15",
      "label": "s15",
      "text": "15"
    },
    {
      "trigger": "/s16",
      "label": "s16",
      "text": "16"
    },
    {
      "trigger": "/s17",
      "label": "s17",
      "text": "17"
    },
    {
      "trigger": "/s18",
      "label": "s18",
      "text": "18"
    },
    {
      "trigger": "/s19",
      "label": "s19",
      "text": "19"
    },
    {
      "trigger": "/s20",
      "label": "s20",
      "text": "20"
    },
    {
      "trigger": "/s21",
      "label": "s21",
      "text": "21"
    },
    {
      "trigger": "/s22",
      "label": "s22",
      "text": "22"
    },
    {
      "trigger": "/s23",
      "label": "s23",
      "text": "23"
    },
    {
      "trigger": "/s24",
      "label": "s24",
      "text": "24"
    },
    {
      "trigger": "/s25",
      "label": "s25",
      "text": "25"
    },
    {
      "trigger": "/s26",
      "label": "s26",
      "text": "26"
    },
    {
      "trigger": "/s27",
      "label": "s27",
      "text": "27"
    },
    {
      "trigger": "/s28",
      "label": "s28",
      "text": "28"
    },
    {
      "trigger": "/s29",
      "label": "s29",
      "text": "29"
    },
    {
      "trigger": "/s30",
      "label": "s30",
      "text": "30"
    },
    {
      "trigger": "/s31",
      "label": "s31",
      "text": "31"
    },
    {
      "trigger": "/s32",
      "label": "s32",
      "text": "32"
    },
    {
      "trigger": "/s33",
      "label": "s33",
      "text": "33"
    },
    {
      "trigger": "/s34",
      "label": "s34",
      "text": "34"
    },
    {
      "trigger": "/s35",
      "label": "s35",
      "text": "35"
    },
    {
      "trigger": "/s36",
      "label": "s36",
      "text": "36"
    },
    {
      "trigger": "/s37",
      "label": "s37",
      "text": "37"
    },
    {
      "trigger": "/s38",
      "label": "s38",
      "text": "38"
    },
    {
      "trigger": "/s39",
      "label": "s39",
      "text": "39"
    },
    {
      "trigger": "/s40",
      "label": "s40",
      "text": "40"
    },
    {
      "trigger": "/s41",
      "label": "s41",
      "text": "41"
    },
    {
      "trigger": "/s42",
      "label": "s42",
      "text": "42"
    },
    {
      "trigger": "/s43",
      "label": "s43",
      "text": "43"
    },
    {
      "trigger": "/s44",
      "label": "s44",
      "text": "44"
    },
    {
      "trigger": "/s45",
      "label": "s45",
      "text": "45"
    },
    {
      "trigger": "/s46",
      "label": "s46",
      "text": "46"
    },
    {
      "trigger": "/s47",
      "label": "s47",
      "text": "47"
    },
    {
      "trigger": "/s48",
      "label": "s48",
      "text": "48"
    },
    {
      "trigger": "/s49",
      "label": "s49",
      "text": "49"
    },
    {
      "trigger": "/s50",
      "label": "s50",
      "text": "50"
    },
    {
      "trigger": "/s51",
      "label": "s51",
      "text": "51"
    }
  ]
}
~~~~

## Validate

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