---
title: "Set the project's webhook endpoint"
method: PUT
path: "/v1/webhook"
tags: ["Webhooks"]
---

# Set the project's webhook endpoint

`PUT /v1/webhook`

## Request

- Base URL: `https://api.tessio.eu`
- URL: `https://api.tessio.eu/v1/webhook`
- Auth: HTTP bearer

Registers (or replaces) the endpoint + the events it should receive (omit `events` to subscribe to all). The first call for a project returns a signing **secret** (shown once); a later call that changes the URL or the event list returns `secret: null` and leaves the existing secret in place, so your signature check keeps working across an edit. Replace it deliberately with `POST /v1/webhook/secret`. Each delivery is POSTed with an `X-Tessio-Event` header and an `X-Tessio-Signature: t=<unix>,v1=<hex hmac-sha256(secret, "{t}.{body}")>` header so you can verify it. Deliveries retry with backoff; `GET /v1/age-checks/{id}` stays the reliable fallback.

## Request body

- SetWebhookRequest
  - `url` string, uri, required: Absolute https URL to receive events. Any other scheme is refused: http with insecure_url, the rest with invalid_url.
  - `events` WebhookEventType[]: Event types to receive. Omit (or empty) to subscribe to all.

## Response `200`

Endpoint set (secret returned once, only when created)

- WebhookCreated
  - `url` string, uri
  - `events` WebhookEventType[]
  - `secret` string, nullable: Signing secret (whsec_…), shown once, store it to verify X-Tessio-Signature. Present when the call created the endpoint; null when it changed one that already had a secret, because editing the URL or the event list leaves the secret in place so your signature check keeps working. Ask for a replacement with `POST /v1/webhook/secret`.

## Other responses

- `400`: invalid_url (not an absolute http(s) URL) or insecure_url. The endpoint must be https: a delivery is signed, but the signature proves integrity and not confidentiality, so the check outcome would otherwise travel in clear. The hosted service accepts https only.
- `401`: Missing / invalid / revoked key
- `403`: insufficient_scope. The key is valid but may only read stats (`stats:read`), so it cannot call this route. Use a project's key with scope `full`.

---

[API](https://skmtc.dev/tessio/apis/tessio-cloud-api.md) · [All operations](https://skmtc.dev/tessio/apis/tessio-cloud-api/llms.txt) · [OpenAPI document](https://skmtc.dev/tessio/apis/tessio-cloud-api/revisions/536b3f4769fb?raw)
