---
title: Get webhook
description: Read a webhook by ID or name, with its status and delivery summary.
doc_version: 0.1.0-preview
last_updated: 2026-09-14
---

# Get webhook

`GET /webhooks/{webhook}`

Read a webhook by ID or name, with its status and delivery summary.

**Permission:** Cluster key, or a scoped key with read on a namespace or collection that the webhook covers.

[API key types and authentication](/engine/reference/authentication.md).

The answer never holds the secret. signing.secret_fingerprint identifies it.

A key gets 404 for a webhook that covers nothing in its scope, and 403 for one that it can see but cannot manage.

## Example request

```http
GET /webhooks/acme-status HTTP/1.1
Host: engine.example.com
Authorization: Bearer <GRAPHON_ENGINE_API_KEY>
```

## Path parameters

### `webhook`

**string · required**

Webhook ID with the whk_ prefix, or the webhook’s name.

## Responses

### 200 · Success

The webhook with its status and delivery summary.

Content type: `application/json`.

#### Response headers

##### `x-engine-request-id`

**string · required**

The Engine request ID. Retain it when investigating a failed or unexpected request.

##### `x-request-id`

**string · required**

The X-Request-Id you sent, echoed when it is a valid ID. Otherwise, the Engine request ID.

```json
{
  "id": "whk_01K6Q6N7D8E9F0G1H2J3K4M5N6",
  "name": "acme-status",
  "scope": {
    "kind": "namespace",
    "namespaces": [
      "acme"
    ]
  },
  "url": "https://hooks.acme.example/graphon-events",
  "types": [
    "job.updated",
    "file.status_changed"
  ],
  "enabled": true,
  "disabled_reason": null,
  "signing": {
    "secret_fingerprint": "sha256:9b2d44f0",
    "previous_secret_fingerprint": null,
    "previous_secret_expires_at": null
  },
  "delivery": {
    "pending_deliveries": 0,
    "last_succeeded_at": "2026-10-06T10:05:13Z",
    "last_failed_at": null
  },
  "created_at": "2026-10-06T10:00:00Z",
  "updated_at": "2026-10-06T10:00:00Z"
}
```

#### Response fields

##### `created_at`

**string (date-time) · required**

Time the resource was created, as an ISO 8601 UTC timestamp.

##### `delivery`

**object · required**

Delivery summary: pending events and the times of the last success and failure.

###### `delivery.last_failed_at`

**string (date-time) · required · nullable**

Time of the last attempt that did not succeed. Null before the first one.

###### `delivery.last_succeeded_at`

**string (date-time) · required · nullable**

Time of the last 2xx answer. Null before the first one.

###### `delivery.pending_deliveries`

**integer · required**

Events that are queued or waiting for a retry.

##### `disabled_reason`

**string · required · nullable**

Why the webhook is off: requested (a key set enabled false) or failing (Engine turned it off). Null while it is on.

- `requested`: A key set enabled to false.
- `failing`: Engine turned the webhook off: the receiver answered 410, or sent no 2xx for 72 hours.

##### `enabled`

**boolean · required**

Whether Engine queues and sends events.

##### `id`

**string · required**

Immutable webhook ID with the whk_ prefix.

##### `name`

**string · required**

Webhook name. It never changes. Each POST sends it in X-Graphon-Events-Webhook.

##### `scope`

**object · required**

The webhook’s scope. A namespace or collection key sees only its own namespace and collections in it.

###### cluster

###### `scope.kind`

**"cluster" · required**

Every event in the cluster. Only a cluster key can create or see this webhook.

###### namespace

###### `scope.kind`

**"namespace" · required**

Every event of the listed namespaces, including collections created later and namespace jobs.

###### `scope.namespaces`

**array<string> · required**

Nonempty list of namespace names or IDs. More than one needs a cluster key. A scoped key sees only its own namespace here.

###### collection

###### `scope.collections`

**array<string> · required**

Nonempty list of collection names or IDs in the namespace. The webhook gets their events.

###### `scope.kind`

**"collection" · required**

The events of the named collections in one namespace.

###### `scope.namespace`

**string · required · nullable**

Namespace name or ID that holds the collections. Null in a response when the namespace is being deleted.

##### `secret`

**string · optional · nullable**

The signing secret: whsec_ and the base64 of 32 random bytes. Only the answers to Create and Rotate hold it. Store it before you continue.

##### `signing`

**object · required**

Fingerprints that identify the signing secrets, and the end of a rotation overlap.

###### `signing.previous_secret_expires_at`

**string (date-time) · required · nullable**

When the previous secret stops signing. Null outside a rotation overlap.

###### `signing.previous_secret_fingerprint`

**string · required · nullable**

Fingerprint of the previous secret during a rotation overlap. Otherwise null.

###### `signing.secret_fingerprint`

**string · required**

sha256: and 8 hex characters that identify the current secret without showing it.

##### `types`

**array<string> · required · nullable**

Event types that the webhook gets. Null means every type, also types added later.

##### `updated_at`

**string (date-time) · required**

Time the resource last changed, as an ISO 8601 UTC timestamp.

##### `url`

**string · required**

Receiver URL that Engine POSTs to.

## Errors

[Error format and retry guidance](/engine/reference/errors.md).

### 400 · `invalid_request`

A field, path, parameter, or conditional request rule is invalid.

Correct the request. Unknown fields are rejected.

```json
{
  "error": {
    "code": "invalid_request",
    "message": "A field, path, parameter, or conditional request rule is invalid.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 401 · `unauthenticated`

The API key is missing, invalid, or revoked.

Send an active key in Authorization: Bearer or X-API-Key.

```json
{
  "error": {
    "code": "unauthenticated",
    "message": "The API key is missing, invalid, or revoked.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 403 · `forbidden`

The key can see the resource but lacks permission for this operation.

Use a key with the required action or scope kind.

```json
{
  "error": {
    "code": "forbidden",
    "message": "The key can see the resource but lacks permission for this operation.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 404 · `not_found`

The resource is absent, deleted, or outside this key’s scope.

Check the identifier and the key’s namespace and collection scope.

```json
{
  "error": {
    "code": "not_found",
    "message": "The resource is absent, deleted, or outside this key’s scope.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

### 503 · `temporarily_unavailable`

A dependency, cluster readiness condition, or capacity limit prevents this operation.

Inspect retryable and details. Retry only when permitted, preserving the idempotency key for the same write.

```json
{
  "error": {
    "code": "temporarily_unavailable",
    "message": "A dependency, cluster readiness condition, or capacity limit prevents this operation.",
    "request_id": "req_example",
    "retryable": true,
    "details": {}
  }
}
```

### 500 · `internal_error`

An unexpected server failure prevented this request from completing.

Retain request_id for investigation. Follow retryable before retrying a write.

```json
{
  "error": {
    "code": "internal_error",
    "message": "An unexpected server failure prevented this request from completing.",
    "request_id": "req_example",
    "retryable": false,
    "details": {}
  }
}
```

## Other request languages

### cURL

```bash
curl -sS "$GRAPHON_ENGINE_URL/webhooks/acme-status" \
  -H "Authorization: Bearer $GRAPHON_ENGINE_API_KEY"
```

### Python

```python
import json
import os
import urllib.request

request = urllib.request.Request(
    os.environ["GRAPHON_ENGINE_URL"] + "/webhooks/acme-status",
    headers={
        "Authorization": "Bearer " + os.environ["GRAPHON_ENGINE_API_KEY"],
    },
    method="GET",
)
with urllib.request.urlopen(request) as response:
    print(response.read().decode())
```

### JavaScript

```javascript
const response = await fetch(process.env.GRAPHON_ENGINE_URL + "/webhooks/acme-status", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.GRAPHON_ENGINE_API_KEY}`,
  },
});
console.log(response.status, await response.text());
```

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
