# GET /partner/webhooks/{webhook_id}

Read one notification subscription of your brand, with its address, its event types and its delivery health. Scope webhooks:read.

Source: https://docs.sealtrust.io/en/reference/get-partner-webhooks-id/

---

You read a single notification subscription, designated by its number. By the
end of this page, you will know how to retrieve its destination address, the
list of events it is subscribed to, whether it is on or off, and whether our
deliveries to it succeed.

Full address:

```http
GET https://api.sealtrust.io/v1/partner/webhooks/{webhook_id}
```

The same endpoint also answers without the `/v1` prefix, at
`https://api.sealtrust.io/partner/webhooks/{webhook_id}`. Both addresses call
the same code. Use the `/v1` form for a new integration.

## Authorization

An API key in the `Authorization` header, in `Bearer` form, carrying the
`webhooks:read` scope. A key that does not carry this scope receives a 403
whose message names the missing scope.

A subscription is visible only to a key of the brand it belongs to. A
subscription number that belongs to another brand answers 404, just like a
number that does not exist. The response never says whether the subscription
exists elsewhere.

This read stays open whatever your plan. Only creating and changing a
subscription require your plan to include notifications.

## Rate limit

We measure the rate over a fixed 60 second window. We apply the value set on
your account if we set one, otherwise the one from your plan. With neither,
the fallback value is 120 calls per window.

Two counters overlap, with the same limit: one per key, one for the sum of all
your brand's keys. Creating extra keys therefore does not raise the total rate
allowed.

We authenticate your key first. We then check the limit, before checking the
`webhooks:read` scope and before any read of the subscription.

A refusal returns 429 with `Retry-After`, expressed in seconds remaining in
the current window. This value is never below 1. Set your retries from
`Retry-After`.

### Rate limit headers

The response carries a set of `X-RateLimit-*` headers describing the more
constraining of the two counters, your key's or your brand's.

| Header | Content |
| --- | --- |
| `X-RateLimit-Limit` | the limit applied over the window |
| `X-RateLimit-Remaining` | what you have left in the current window |
| `X-RateLimit-Reset` | the timestamp of the end of the window, in seconds |
| `X-RateLimit-Scope` | `key` or `brand`, the counter used as the reference |

> [!ATTENTION] Three of these headers reach you twice
> A second counter, which does not depend on your key, adds
> `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` in
> turn, with its own values. The response therefore carries each of these
> three names twice. Only `X-RateLimit-Scope` appears once, and it accompanies
> the set described in the table above. Most HTTP libraries merge the
> repetitions into a single comma separated string, of the form `119, 599`. So
> do not convert these headers into a number. We are working on this point. In
> the meantime, set your pace from `Retry-After`.

We check your key before running its counter. A 401 response therefore carries
only the set from the second counter, and no `X-RateLimit-Scope`.

> [!INFO] This read consumes no quota
> This call eats into neither your key's daily quota nor your plan's monthly
> product quota. Only the rate limit applies.

## Path and query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `webhook_id` | `integer` | yes | The subscription number, as creation and the list return it in the `id` field. |

This endpoint has no query parameter.

### Headers

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `Authorization` | `string` | yes | `Bearer` followed by your API key. |

## Request body

None. This request has no body.

## Example request

Reading subscription number 7.

:::onglets
```bash title="curl"
curl -i https://api.sealtrust.io/v1/partner/webhooks/7 \
  -H "Authorization: Bearer st_test_0000000000000000000000000000000000000000000000"
```
```typescript
import { SealTrustClient } from "@sealtrust-io/sdk";

const sealtrust = new SealTrustClient({
  apiKey: "st_test_0000000000000000000000000000000000000000000000",
});

const abonnement = await sealtrust.webhooks.get(7);

console.log(abonnement.url, abonnement.events, abonnement.health);
```
```python
import requests

response = requests.get(
    "https://api.sealtrust.io/v1/partner/webhooks/7",
    headers={
        "Authorization": "Bearer st_test_0000000000000000000000000000000000000000000000",
    },
    timeout=30,
)

print(response.status_code)
print(response.json())
```
:::

## Example response

HTTP code `200`.

```json
{
  "id": 7,
  "brand_id": 12,
  "url": "https://exemple-sas.test/sealtrust/evenements",
  "events": [
    "product.minted",
    "batch.completed"
  ],
  "is_active": true,
  "health": "healthy",
  "created_at": "2026-08-14T09:12:44.318000Z",
  "updated_at": "2026-08-20T07:03:11.902000Z"
}
```

The response has eight fields and nothing else.

| Field | Type | Description |
| --- | --- | --- |
| `id` | `integer` | The subscription number. |
| `brand_id` | `integer` | Your brand number. |
| `url` | `string` | The address that receives the notifications. It always starts with `https://`. |
| `events` | `string[]` | The event types this subscription is subscribed to. An empty list means no event will be delivered. |
| `is_active` | `boolean` | `true` when the subscription is on. |
| `health` | `string` | `healthy` or `degraded`. See below. |
| `created_at` | `string` | Date and time of creation, in universal time, formatted as ISO 8601. |
| `updated_at` | `string` | Date and time of the last change, in universal time, formatted as ISO 8601. |

`health` reads `healthy` as long as our deliveries to this address succeed. It
moves to `degraded` when the last retry attempt fails in turn, and it returns
to `healthy` as soon as a delivery succeeds. We write only these two values.

> [!ATTENTION] The signing secret is never returned
> This response does not contain the secret you set when creating the
> subscription. No endpoint reads it back. Keep it on your side at the moment
> you create the subscription.

> [!INFO] The list does not name this field the same way
> `GET /v1/partner/webhooks` returns the same subscriptions, and there names
> the list of event types `event_types`. This endpoint names it `events`. Plan
> for both names if your code reads both responses.

## Errors

The body of an error response carries a `detail` field.

| Code | Condition | What to do |
| --- | --- | --- |
| 401 | The `Authorization` header is missing. `detail` reads `Missing Authorization header`. The response also carries `WWW-Authenticate: Bearer`. | Add the header. |
| 401 | The `Authorization` header does not start with `Bearer ` followed by a space. `detail` reads `Invalid Authorization header format (expected 'Bearer <token>')`. The response also carries `WWW-Authenticate: Bearer`. | Fix the form of the header. |
| 401 | What follows `Bearer ` is empty, or shorter than 40 characters. `detail` reads `Invalid API key format`. This response does not carry `WWW-Authenticate`. | Send the full secret, as the console displayed it to you at creation. |
| 401 | The key sent is unknown. `detail` reads `Invalid API key`. | Check that you are sending the full secret, with no space and no line break. |
| 403 | The key is no longer in the active state. `detail` reads `API key is revoked` or `API key is expired`, depending on its state. | Create a new key in the console. |
| 403 | The key has passed its expiry date. `detail` reads `API key has expired`. We then switch its state to `expired`. | Create a new key. The old one will never become valid again. |
| 403 | The key does not carry the `webhooks:read` scope. `detail` reads `Missing required scope: webhooks:read`. | Create a key carrying this scope. The scopes of an existing key cannot be changed. |
| 404 | No subscription carries this number in your brand. `detail` reads `Webhook subscription not found`. | Check the number with `GET /v1/partner/webhooks`. A subscription of another brand gives the same response, and so does a number larger than any number assigned. |
| 422 | The number sent in the path is not an integer. The response details the rejected field. | Send the `id` field as the list returns it, with no quotation marks and no decimal point. |
| 429 | The rate limit of the key is reached. `detail` names the limit and the window. The `Retry-After` header and the `X-RateLimit-*` family accompany the response, with `X-RateLimit-Scope: key`. | Wait the number of seconds given by `Retry-After`, then retry. |
| 429 | The rate limit of the brand is reached, across all keys. `X-RateLimit-Scope` reads `brand`, and `X-RateLimit-Remaining` reads `0`. | Wait the number of seconds given by `Retry-After`. Creating an extra key does not raise this limit. |
| 500 | A failure on our side. The body carries a short sentence, with no technical detail. Do not compare it character by character. The response carries an `X-Request-Id` header. | Retry. If the failure persists, send us the value of `X-Request-Id`. |
| 503 | The rate limiting service is momentarily unavailable. We refuse the call. `detail` reads `Rate limiting temporarily unavailable, please retry shortly`. | Retry in a few moments. No data was read or changed. |

## See also

- [`GET /partner/webhooks`](/en/reference/get-partner-webhooks/),
  list your notification subscriptions, page by page.
- [`PUT /partner/webhooks/{webhook_id}`](/en/reference/put-partner-webhooks-id/),
  change the address, the events or the secret of a subscription.
- [`DELETE /partner/webhooks/{webhook_id}`](/en/reference/delete-partner-webhooks-id/),
  delete a subscription and the signing secret attached to it.
- [Receiving events by webhook](/en/webhooks/),
  create a subscription, verify a signature, catch up on lost events.
