---
title: "chat.agent-poll@1 · OpenVibe.Services"
canonical: "https://openvibe.services/docs/contracts/chat.agent-poll"
updated: null
---

# chat.agent-poll@1 · OpenVibe.Services

HTML: [chat.agent-poll@1 · OpenVibe.Services](https://openvibe.services/docs/contracts/chat.agent-poll)

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

chat.agent-poll@1 (the Agent Streams plan, S5): a poll the agent asks a Co-pilot room to run, and the result it gets back. The agent writes the question and two to four options ('Which name: parseRows or readTable?') and the poll is open for 30 to 120 seconds. Chat draws it as a poll card with buttons; \`!1\` to \`!4\` vote too. One vote per person, one poll at a time, and at most one poll every five minutes. The owner (or a moderator) can close a poll early or veto its result. A poll can only choose between the options the agent wrote: Chat never adds one. The result carries the counts, nothing else about who voted.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `poll_id` | string | yes | The poll's id (ULID, Crockford base32). | - `pattern ^pol_[0-9A-HJKMNP-TV-Z]{26}$` |
| `room` | string | yes | The room the poll is in: an agent stream's room, \`agent:<ags\_id>\`. | - `pattern ^agent:ags_[0-9A-HJKMNP-TV-Z]{26}$` |
| `question` | string | yes | The question the agent wrote. | - `minLength 1`<br>- `maxLength 200` |
| `options` | array of object | yes | The options the agent wrote, two to four of them. Chat never adds an option of its own. | - `minItems 2`<br>- `maxItems 4`<br>- `items: no other fields` |
| `options[].id` | string | yes | The option's number: \`!1\` to \`!4\` vote for it. | - `pattern ^[1-4]$` |
| `options[].label` | string | yes | The option's text, as the agent wrote it. | - `minLength 1`<br>- `maxLength 80` |
| `options[].votes` | integer |  | How many people chose it. The count is in the result; it says nothing about who. | - `minimum 0` |
| `opened_at` | string | yes | When the poll opened. | - `format date-time` |
| `closes_at` | string | yes | When the poll closes on its own: 30 to 120 seconds after it opened. | - `format date-time` |
| `closed_at` | string \| null |  | When the poll was closed early by the owner or a moderator, or null when it ran its time. | - `format date-time` |
| `state` | enum | yes | Where the poll stands: open, closed (the counts are final), or vetoed (the owner or a moderator refused the result). | - `one of "open", "closed", "vetoed"` |
| `result` | object \| null |  | The result, once the poll is closed: the counts and the winning option. Null while the poll is open. |  |
| `vetoed_by` | string \| null |  | Who vetoed the result (the owner or a moderator), or null. Never a person's id: the handle only. |  |

## Examples

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

 Valid: closed

~~~~
{
  "poll_id": "pol_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "question": "Which name: parseRows or readTable?",
  "options": [
    {
      "id": "1",
      "label": "parseRows",
      "votes": 4
    },
    {
      "id": "2",
      "label": "readTable",
      "votes": 1
    }
  ],
  "opened_at": "2026-11-06T10:00:00.000Z",
  "closes_at": "2026-11-06T10:01:00.000Z",
  "closed_at": "2026-11-06T10:01:00.000Z",
  "state": "closed",
  "result": {
    "counts": [
      4,
      1
    ],
    "total": 5,
    "winner": "1"
  },
  "vetoed_by": null
}
~~~~

Valid: open

~~~~
{
  "poll_id": "pol_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "question": "Which name: parseRows or readTable?",
  "options": [
    {
      "id": "1",
      "label": "parseRows",
      "votes": 0
    },
    {
      "id": "2",
      "label": "readTable",
      "votes": 0
    }
  ],
  "opened_at": "2026-11-06T10:00:00.000Z",
  "closes_at": "2026-11-06T10:01:00.000Z",
  "closed_at": null,
  "state": "open",
  "result": null,
  "vetoed_by": null
}
~~~~

Rejected: five-options

~~~~
{
  "poll_id": "pol_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "question": "Which name?",
  "options": [
    {
      "id": "1",
      "label": "a",
      "votes": 0
    },
    {
      "id": "2",
      "label": "b",
      "votes": 0
    },
    {
      "id": "3",
      "label": "c",
      "votes": 0
    },
    {
      "id": "4",
      "label": "d",
      "votes": 0
    },
    {
      "id": "5",
      "label": "e",
      "votes": 0
    }
  ],
  "opened_at": "2026-11-06T10:00:00.000Z",
  "closes_at": "2026-11-06T10:01:00.000Z",
  "state": "open"
}
~~~~

Rejected: one-option

~~~~
{
  "poll_id": "pol_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "question": "Which name?",
  "options": [
    {
      "id": "1",
      "label": "parseRows",
      "votes": 0
    }
  ],
  "opened_at": "2026-11-06T10:00:00.000Z",
  "closes_at": "2026-11-06T10:01:00.000Z",
  "state": "open"
}
~~~~

Rejected: open-poll-with-a-result

~~~~
{
  "poll_id": "pol_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "room": "agent:ags_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "question": "Which name?",
  "options": [
    {
      "id": "1",
      "label": "a",
      "votes": 0
    },
    {
      "id": "2",
      "label": "b",
      "votes": 0
    }
  ],
  "opened_at": "2026-11-06T10:00:00.000Z",
  "closes_at": "2026-11-06T10:01:00.000Z",
  "state": "open",
  "result": {
    "counts": [
      1,
      0
    ],
    "total": 1,
    "winner": "1"
  }
}
~~~~

## Validate

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