---
title: "GET /v1/webhooks"
method: GET
path: "/v1/webhooks"
tags: ["Webhooks"]
---

# GET /v1/webhooks

`GET /v1/webhooks`

## Request

- Base URL: `https://api.printhiv3d.com`
- URL: `https://api.printhiv3d.com/v1/webhooks`
- Auth: API key in header `x-api-key`

Subscribe an HTTPS receiver to library events with REST or MCP. API keys need
webhooks:write to create, update, rotate, delete, or test an endpoint. Reads need
read access. Organization comes from the authenticated principal.

## Configuration

REST paths are relative to /v1:

- GET /webhooks lists endpoint metadata without secrets.
- GET /webhooks/{id} reads one endpoint.
- GET /webhooks/{id}/attempts reads the latest 100 delivery attempts and their events.
- POST /webhooks accepts name, url, event_types, and enabled.
- PATCH /webhooks/{id} updates those fields; rotate_secret:true replaces the secret.
- DELETE /webhooks/{id} removes the endpoint.
- POST /webhooks/{id}/test records a targeted synthetic event and returns immediately.

MCP exposes webhooks_list, webhooks_create, webhooks_update, webhooks_delete,
and webhooks_test. Mutations require confirm:true. Each organization may have
20 endpoints. Creation and rotation return signing_secret exactly once. Save it
in the receiver's secret store. Subsequent reads never return it. The API and
worker need the same WEBHOOK_ENCRYPTION_KEY, a base64-encoded 32-byte key.
Secrets use AES-256-GCM encryption at rest. Keep that key stable across restarts.
Rotating an endpoint invalidates its previous secret for subsequent attempts.

## Event bodies

Every POST has this JSON envelope. Receivers must tolerate additional data fields:

```json
{
  "id": "00000000-0000-4000-8000-000000000001",
  "type": "model.created",
  "occurred_at": "2026-10-15T12:00:00.000Z",
  "org_id": "00000000-0000-4000-8000-000000000002",
  "data": {
    "model_id": "00000000-0000-4000-8000-000000000003",
    "name": "Example model"
  }
}
```

model.created records a newly created Model, including Models created in the web
app and through Harvest. Duplicate saves of the same Model do not create a new event.

model_file.uploaded uses the same envelope with type model_file.uploaded and:

```json
{
  "model_file_id": "00000000-0000-4000-8000-000000000004",
  "model_id": "00000000-0000-4000-8000-000000000003",
  "filename": "example.3mf",
  "size": 12345,
  "download_path": "/v1/files/00000000-0000-4000-8000-000000000004/download-url"
}
```

webhook.test uses the same envelope with type webhook.test and data {"test":true}.
It is a synthetic event targeted only at the endpoint selected for a test.
Tests are limited to one per endpoint every 60 seconds (HTTP 429).

The file event means .3mf bytes have been registered, not that processing or
slicing has completed. Use the authenticated download reference with your own
organization-scoped credential. No credential or long-lived signed URL travels
in an event. Delivery order is not guaranteed. A file event can arrive before
its Model event. Receivers must deduplicate using id before starting work.

## Delivery and signature

The API transaction writes only an outbox event. A BullMQ worker fans out
committed events and performs HTTPS delivery. Redis downtime does not discard
committed events. Responses never wait for a receiver. Only public HTTPS
destinations with DNS hostnames are allowed; IP literal URLs are refused.
Hostnames must resolve through the worker's configured DNS servers. Hosts-file
and NSS overrides are not consulted. The worker validates every returned A and
AAAA address before connecting; any private or special-use answer rejects the
whole destination. Connections may try multiple validated addresses, including
IPv6 when IPv4 is unavailable, within one 15-second DNS and HTTPS deadline.
The worker pins that validated answer set and does not follow redirects.
A connection that fails or stalls before TLS completes may fall back to another
validated address within that deadline. After TLS completes, a failed send or
HTTP response ends this attempt; the durable delivery retry handles failures.

Headers:

- Content-Type: application/json
- X-Hive-Event-Id: the envelope id, stable across retries and endpoints
- X-Hive-Signature: t=<unix-seconds>,v1=<hex-digest>

The digest is HMAC-SHA256(secret, timestamp + "." + rawRequestBody).
Verify the original bytes before JSON parsing. Check timestamp freshness and use
a constant-time comparison. Each attempt gets a fresh timestamp and signature.
This TypeScript/Node example verifies the signature:

```typescript
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyHiveWebhook(
  rawBody: Buffer, signature: string, secret: string,
  nowSeconds = Math.floor(Date.now() / 1000)
): boolean {
  const match = /^t=(\d+),v1=([a-f0-9]{64})$/.exec(signature);
  if (!match) return false;
  const timestamp = Number(match[1]);
  if (!Number.isSafeInteger(timestamp) || Math.abs(nowSeconds - timestamp) > 300) {
    return false;
  }
  const expected = createHmac("sha256", secret)
    .update(timestamp + ".").update(rawBody).digest();
  return timingSafeEqual(expected, Buffer.from(match[2], "hex"));
}
```

Return a 2xx response after durably accepting the event. Delivery is at least
once, so a worker crash after your response can cause a duplicate. Non-2xx and
transport failures retry with exponential backoff for at most 24 hours before
dead-letter. Delivery attempts persist endpoint, event id, attempt number,
status code, latency, a redacted error, and next retry. At most five deliveries
per endpoint run concurrently across workers. Ten consecutive failed delivery
attempts disable an endpoint and create an organization notification. Disabled
endpoints pause queued deliveries; re-enabling resumes them within the 24-hour
window. Local encryption configuration failures do not penalize the endpoint.
Dispatched events and their terminal delivery history are retained for 30 days.
Correct the URL
and explicitly re-enable the endpoint after investigating.

## Response `200`

Default Response

- object[]
  - `id` string
  - `organization_id` string
  - `name` string
  - `url` string
  - `event_types` string[]
  - `enabled` boolean
  - `consecutive_failures` integer
  - `created_at` string
  - `updated_at` string

## Other responses

- `default`: Unexpected error response

## Changes

- **2026-10-27** `46fbe0518d41`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/printhiv3d/apis/hive-api-documentation/changes/v1/webhooks/get.md)

---

[API](https://skmtc.dev/printhiv3d/apis/hive-api-documentation.md) - [All operations](https://skmtc.dev/printhiv3d/apis/hive-api-documentation/llms.txt) - [OpenAPI document](https://skmtc.dev/printhiv3d/apis/hive-api-documentation/revisions/906fa9f15f12?raw)
