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

# actor.voice@1 · OpenVibe.Services

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

# ActorVoice `actor.voice@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: actor
- Visibility: public
- Status: active
- Compatibility: backward
- Decision: [ADR-044](/docs/adr/ADR-044)
- Schema: [`https://openvibe.network/contracts/actor/voice.v1.json`](/docs/contracts/actor.voice.json)

actor.voice@1: an agent's voice and nothing else — the small document a person hands to somebody else so their agent speaks in the same house voice. It carries the agent's name, a short bio, its instructions (the house voice it speaks in), the rules it keeps, its default approval level, the model it prefers and the connected apps it may use (ids only, never a key), an optional face (an avatar ref), an optional author, and an integrity block. It never carries memory, a connector's token or a file: a voice is a voice, not a person. The INTEGRITY block is a sha256 over the canonical JSON of the document: it shows the file was not edited after it was written, and it says nothing about who wrote it — anyone can write a file that names an author and compute its hash. The AUTHOR is therefore a claim, not a fact: a document may name anyone, and whoever reads it must say so (it says it was made by …; that is not checked). Only the service that owns the agent may vouch for who wrote a voice, and it does so by sharing it itself. The canonical JSON is the document's own fields, in this order and with these names: voice, name, bio, face, instructions, rules, level, model, apps, author; an optional field that is absent or empty is left out, author is { subject } or { subject, name }, and the value is the compact JSON of that object (no spaces). CREATED\_AT is when the document was written; it is not part of the hash. Importing a voice makes a new agent at level ask whatever the file says: a borrowed agent starts cautious, and the person raises it themselves.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `voice` | const | yes | The document marker. This is the agent's voice, not a TTS voice id. | - `= "actor.voice@1"` |
| `name` | string | yes | The agent's name. | - `minLength 1`<br>- `maxLength 60` |
| `bio` | string |  | A short bio: one or two lines on what this agent is for. | - `maxLength 280` |
| `face` | string |  | An avatar ref: an https image URL, a Media id, or a preset face such as preset:researcher. | - `maxLength 500` |
| `instructions` | string | yes | The house voice the agent speaks in: what it does, how it says it, what it never does. | - `minLength 1`<br>- `maxLength 4000` |
| `rules` | array of string |  | The rules it keeps, one line each. | - `maxItems 20`<br>- `items: minLength 1`<br>- `items: maxLength 200` |
| `level` | enum | yes | The approval level it works at (actor.agent@1's levels). The default is ask; an import always starts at ask. | - `one of "ask", "auto-edits", "approve-for-me", "full"` |
| `model` | object |  | The model it prefers, by id. Never a key: the key stays in OpenVibe.AI's vault. | - `no other fields` |
| `model.engine` | enum | yes |  | - `one of "runtime", "own-keys", "open-gpu", "link"` |
| `model.model` | string | yes |  | - `minLength 1`<br>- `maxLength 120` |
| `apps` | array of string |  | The connected app ids it may use (Connect's catalogue slugs). Ids only: a token is never here. | - `maxItems 50`<br>- `unique items`<br>- `items: pattern ^[a-z][a-z0-9._-]{0,63}$` |
| `author` | object |  | Who the document says it is from. A claim, not a fact: a file may name anyone, and the hash says nothing about it. A service that owns the agent may vouch for its author in its own record instead. | - `no other fields` |
| `author.subject` | string | yes | The Network subject the document names. | - `pattern ^usr_[0-9A-HJKMNP-TV-Z]{26}$` |
| `author.name` | string |  |  | - `maxLength 80` |
| `integrity` | object | yes |  | - `no other fields` |
| `integrity.alg` | const | yes | How the document was checked: sha256 over the canonical JSON. | - `= "sha256"` |
| `integrity.hash` | string | yes | The hex sha256 of the canonical JSON. It shows the file was not edited; it proves nothing about who wrote it. | - `pattern ^[0-9a-f]{64}$` |
| `created_at` | string |  | When the document was written. Not part of the hash. | - `format date-time` |

## Examples

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

 Valid: minimal

~~~~
{
  "voice": "actor.voice@1",
  "name": "House voice",
  "instructions": "You are the house voice of this household. Speak plainly, be brief, and never guess.",
  "level": "ask",
  "created_at": "2026-11-07T10:00:00.000Z",
  "integrity": {
    "alg": "sha256",
    "hash": "b2c36f80aa7566925d6a9c6707416a0df9604497fb70c6649852afd7c40ceb5f"
  }
}
~~~~

Valid: researcher

~~~~
{
  "voice": "actor.voice@1",
  "name": "Researcher",
  "bio": "Reads carefully and dates every claim.",
  "face": "preset:researcher",
  "instructions": "Research carefully. Prefer primary sources, date every claim, and say plainly when the evidence does not settle a question.",
  "rules": [
    "Never invent a source.",
    "Say what you could not confirm."
  ],
  "level": "ask",
  "model": {
    "engine": "runtime",
    "model": "smart"
  },
  "apps": [
    "connect.email"
  ],
  "author": {
    "subject": "usr_01M4C49CW34NGX7TR2TNRVFWNY",
    "name": "Ada"
  },
  "created_at": "2026-11-07T09:31:00.000Z",
  "integrity": {
    "alg": "sha256",
    "hash": "c2cf6349a416047e26715f8def1396c42e60698bb9869abd710727c2798fea4b"
  }
}
~~~~

Rejected: bad-level

~~~~
{
  "voice": "actor.voice@1",
  "name": "Researcher",
  "instructions": "Research carefully.",
  "level": "everything",
  "author": {
    "subject": "usr_01M4C49CW34NGX7TR2TNRVFWNY"
  },
  "integrity": {
    "alg": "sha256",
    "hash": "0000000000000000000000000000000000000000000000000000000000000000"
  }
}
~~~~

Rejected: memory

~~~~
{
  "voice": "actor.voice@1",
  "name": "Researcher",
  "instructions": "Research carefully.",
  "level": "ask",
  "author": {
    "subject": "usr_01M4C49CW34NGX7TR2TNRVFWNY"
  },
  "integrity": {
    "alg": "sha256",
    "hash": "0000000000000000000000000000000000000000000000000000000000000000"
  },
  "memory": "mems_01M4C49CW7R8S9T0A1B2C3D4E5"
}
~~~~

Rejected: tokens

~~~~
{
  "voice": "actor.voice@1",
  "name": "Researcher",
  "instructions": "Research carefully.",
  "level": "ask",
  "author": {
    "subject": "usr_01M4C49CW34NGX7TR2TNRVFWNY"
  },
  "integrity": {
    "alg": "sha256",
    "hash": "0000000000000000000000000000000000000000000000000000000000000000"
  },
  "tokens": {
    "connect": {
      "email": "sk-live-1234567890"
    }
  }
}
~~~~

## Validate

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