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

# events.hook@1 · OpenVibe.Services

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

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

events.hook@1: one durable webhook subscription managed through OpenVibe.Website Connect, delivered by OpenVibe.Events. The hook names its share, the public ingress path, the event topics it accepts, filters/transform, the retry window and the signature version. The signing secret is shown once when the hook is created or its secret is rotated; secret\_set says one exists, and no field of this document ever carries the value.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `id` | string | yes |  | - `pattern ^hok_[0-9A-HJKMNP-TV-Z]{26}$` |
| `owner` | [identity.subject-ref](/docs/contracts/identity.subject-ref) | yes |  |  |
| `project_id` | string \| null |  |  | - `pattern ^prj_[0-9A-HJKMNP-TV-Z]{26}$` |
| `share_id` | string | yes | The share whose tunnel receives deliveries; delivery never goes to an arbitrary host. | - `pattern ^shr_[0-9A-HJKMNP-TV-Z]{26}$` |
| `name` | string | yes |  | - `minLength 1`<br>- `maxLength 80` |
| `endpoint` | string | yes | The public ingress, https://hooks.openvibe.website/h/<id>; Connect also records the on-share path https://<share host>/\_\_ov/hook/<id>. | - `pattern ^https://`<br>- `format uri` |
| `topics` | array of string | yes | Event types this hook accepts, e.g. github.push.received or stripe.invoice.paid; Event's topic patterns may be used by the delivery filter. | - `minItems 1`<br>- `maxItems 50`<br>- `unique items`<br>- `items: pattern ^[a-z][a-z0-9_]*(\.[a-z0-9_]+){2,}$` |
| `filters` | array of object |  |  | - `maxItems 20`<br>- `items: no other fields` |
| `filters[].path` | string | yes | A JSONPath into the event payload. | - `minLength 1`<br>- `maxLength 200` |
| `filters[].equals` | one of |  |  |  |
| `transform` | object \| null |  |  |  |
| `retry` | object | yes |  | - `no other fields` |
| `retry.window_seconds` | integer | yes | How long Events keeps retrying while the share is offline; seven days is the hook default. | - `minimum 60`<br>- `maximum 604800` |
| `retry.max_attempts` | integer | yes |  | - `minimum 1`<br>- `maximum 100` |
| `signature` | object | yes |  | - `no other fields` |
| `signature.version` | const | yes |  | - `= "v2"` |
| `signature.tolerance_seconds` | integer | yes | How far a signed timestamp may drift before the Link refuses it; 300 is the default. | - `minimum 1`<br>- `maximum 3600` |
| `max_payload_bytes` | integer |  |  | - `minimum 1`<br>- `maximum 1048576`<br>- `default 1048576` |
| `status` | enum | yes |  | - `one of "active", "paused", "revoked"` |
| `secret_set` | boolean | yes | True once a signing secret exists. The secret itself is returned only in the create/rotate answer and is never stored in this contract, a delivery record or an audit entry. |  |
| `secret_rotated_at` | string \| null |  |  | - `format date-time` |
| `last_delivery_at` | string \| null |  |  | - `format date-time` |
| `dead_at` | string \| null |  | When the hook stopped receiving deliveries after its retry window; null while it is live. | - `format date-time` |
| `created_at` | string | yes |  | - `format date-time` |
| `updated_at` | string | yes |  | - `format date-time` |
| `href` | string |  |  | - `pattern ^https://`<br>- `format uri` |

## Examples

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

 Valid: github

~~~~
{
  "id": "hok_01J8Z3Q4R5S6T7V8W9X0Y1Z2A9",
  "owner": {
    "type": "user",
    "id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
  },
  "project_id": null,
  "share_id": "shr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
  "name": "GitHub pushes",
  "endpoint": "https://hooks.openvibe.website/h/hok_01J8Z3Q4R5S6T7V8W9X0Y1Z2A9",
  "topics": [
    "github.push.received",
    "github.pull_request.received"
  ],
  "filters": [
    {
      "path": "$.repository.full_name",
      "equals": "OpenVibers/OpenVibe.Contracts"
    }
  ],
  "transform": null,
  "retry": {
    "window_seconds": 604800,
    "max_attempts": 30
  },
  "signature": {
    "version": "v2",
    "tolerance_seconds": 300
  },
  "max_payload_bytes": 1048576,
  "status": "active",
  "secret_set": true,
  "secret_rotated_at": null,
  "last_delivery_at": "2026-10-29T09:00:00.000Z",
  "dead_at": null,
  "created_at": "2026-10-29T08:00:00.000Z",
  "updated_at": "2026-10-29T09:00:00.000Z"
}
~~~~

Rejected: http-endpoint

~~~~
{
  "id": "hok_01J8Z3Q4R5S6T7V8W9X0Y1Z2A9",
  "owner": {
    "type": "user",
    "id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
  },
  "share_id": "shr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
  "name": "Insecure hook",
  "endpoint": "http://hooks.openvibe.website/h/hok_01J8Z3Q4R5S6T7V8W9X0Y1Z2A9",
  "topics": [
    "github.push.received"
  ],
  "retry": {
    "window_seconds": 604800,
    "max_attempts": 30
  },
  "signature": {
    "version": "v2",
    "tolerance_seconds": 300
  },
  "status": "active",
  "secret_set": true,
  "created_at": "2026-10-29T08:00:00.000Z",
  "updated_at": "2026-10-29T09:00:00.000Z"
}
~~~~

Rejected: secret-field

~~~~
{
  "id": "hok_01J8Z3Q4R5S6T7V8W9X0Y1Z2A9",
  "owner": {
    "type": "user",
    "id": "usr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A6"
  },
  "share_id": "shr_01J8Z3Q4R5S6T7V8W9X0Y1Z2A3",
  "name": "GitHub pushes",
  "endpoint": "https://hooks.openvibe.website/h/hok_01J8Z3Q4R5S6T7V8W9X0Y1Z2A9",
  "topics": [
    "github.push.received"
  ],
  "retry": {
    "window_seconds": 604800,
    "max_attempts": 30
  },
  "signature": {
    "version": "v2",
    "tolerance_seconds": 300
  },
  "status": "active",
  "secret_set": true,
  "secret": "whsec_this-value-never-belongs-in-a-contract",
  "created_at": "2026-10-29T08:00:00.000Z",
  "updated_at": "2026-10-29T09:00:00.000Z"
}
~~~~

## Validate

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