# Webhook endpoints

> Subscribe a URL to events, such as a client added or a check-in submitted, and manage it.

Source: https://hubfit.com/developers/api/webhook-endpoints


A webhook endpoint is a URL HubFit sends events to as they happen: a client added, a check-in or questionnaire submitted, a workout completed, a package purchased. Each event carries ids, not the record: fetch it with your own key.

Only a key that belongs to the workspace's owner or an admin, with `webhooks:write`, manages endpoints. It sees every endpoint in the workspace, including those added in Settings > API, but changes only the ones it made. How deliveries are signed, retried and turned off is in [Webhooks](https://hubfit.com/developers/webhooks).

Endpoints:

- `GET /v1/webhook-endpoints`: [List webhook endpoints](https://hubfit.com/developers/api/webhook-endpoints#list-webhook-endpoints)
- `POST /v1/webhook-endpoints`: [Add a webhook endpoint](https://hubfit.com/developers/api/webhook-endpoints#create-webhook-endpoint)
- `GET /v1/webhook-endpoints/{endpointId}`: [Get a webhook endpoint](https://hubfit.com/developers/api/webhook-endpoints#get-webhook-endpoint)
- `PATCH /v1/webhook-endpoints/{endpointId}`: [Change a webhook endpoint](https://hubfit.com/developers/api/webhook-endpoints#update-webhook-endpoint)
- `DELETE /v1/webhook-endpoints/{endpointId}`: [Delete a webhook endpoint](https://hubfit.com/developers/api/webhook-endpoints#delete-webhook-endpoint)

## Webhook endpoint object

### Attributes

- `id` (string): Its unique ID.
- `name` (string)
- `url` (string): The full URL for an endpoint this key made. For any other, only its origin, since a URL like a Zapier hook's runs whatever it's attached to.
- `events` (array of enums): Known values `client.created`, `check_in_submission.created`, `completed_workout.created`, `package_purchase.created`, `questionnaire_submission.created`.
- `status` (enum): An `enabled` endpoint gets nothing while the workspace is off a plan with webhooks, unpaid or suspended, and nothing from that time once it's back. Known values `enabled`, `disabled`.
- `disabledReason` (enum, nullable): Why the endpoint is `disabled`, and `null` while it's `enabled`. These can be turned back on: `manual`, turned off with `status: disabled` or in Settings; `gone`, after a 410 reply; and `failing`, after 5 days without a success. These stop it for good, with every other endpoint its key made: `api_key_revoked`, its key was revoked; `api_key_expired`, its key expired; `coach_removed`, the key's coach was removed from the workspace or their account was deleted; and `role_changed`, the key's coach was made a Standard member. Known values `manual`, `gone`, `failing`, `api_key_revoked`, `api_key_expired`, `coach_removed`, `role_changed`.
- `lastDelivery` (object, nullable)
  - `outcome` (enum): `delivered` after a 2xx reply. `retry` when the attempt failed and another is scheduled, `failed` when it failed with no attempt left. `gone` after a 410 reply, which turns the endpoint off. `blocked` when the URL led to an address webhooks can't be sent to, such as a private one: it isn't retried. Known values `delivered`, `retry`, `failed`, `gone`, `blocked`.
  - `statusCode` (integer, nullable): The reply's status. `null` if there was none, such as a timeout.
  - `attemptedAt` (timestamp)
- `createdAt` (timestamp): When it was created.
- `updatedAt` (timestamp): When it last changed.

### Example

```json
{
  "id": "6ad6affd3db7e9fc3db656fb",
  "name": "CRM",
  "url": "https://example.com/hubfit-webhooks",
  "events": ["client.created", "check_in_submission.created"],
  "status": "enabled",
  "disabledReason": null,
  "lastDelivery": {
    "outcome": "delivered",
    "statusCode": 200,
    "attemptedAt": "2026-10-26T14:32:05.000Z"
  },
  "createdAt": "2026-03-02T15:04:11.000Z",
  "updatedAt": "2026-10-26T09:12:40.000Z"
}
```

## Event object

The JSON body of every webhook request. See [Webhooks](https://hubfit.com/developers/webhooks) for its headers and how to verify it.

### Attributes

- `id` (string): The event's id, as in `webhook-id`.
- `type` (enum): The event type. New types can appear. Known values `client.created`, `check_in_submission.created`, `completed_workout.created`, `package_purchase.created`, `questionnaire_submission.created`.
- `createdAt` (timestamp): When it was created.
- `data` (object): The record the event is about. Fetch it with your key to read it as it is now.
  - `id` (string): The record's `id`.
  - `clientId` (string): The client it belongs to. For `client.created`, the client itself.

### Example

```json
{
  "id": "evt_6ad6af72e7830e73e784a170",
  "type": "check_in_submission.created",
  "createdAt": "2026-10-26T14:32:06.000Z",
  "data": {
    "id": "6ad6af25540e6a26540ffd23",
    "clientId": "6ad6af41d3199442d31b2743"
  }
}
```

## List webhook endpoints

`GET https://api.hubfit.com/v1/webhook-endpoints`

Every endpoint in the workspace, newest first, whole (a workspace has at most 10), made with any key or in Settings. An endpoint this key didn't make shows only the origin of its URL. Requires `webhooks:write`.

### Returns

The workspace's endpoints.

### Example request

cURL:

```bash
curl https://api.hubfit.com/v1/webhook-endpoints \
  -H "Authorization: Bearer $HUBFIT_API_KEY"
```

Node.js:

```javascript
const response = await fetch("https://api.hubfit.com/v1/webhook-endpoints", {
  headers: {
    Authorization: `Bearer ${process.env.HUBFIT_API_KEY}`,
  },
});

const { data } = await response.json();
```

Python:

```python
import os
import requests

response = requests.get(
    "https://api.hubfit.com/v1/webhook-endpoints",
    headers={"Authorization": f"Bearer {os.environ['HUBFIT_API_KEY']}"},
)

data = response.json()["data"]
```

### Example response (200 OK)

```json
{
  "data": [
    {
      "id": "6ad6affd3db7e9fc3db656fb",
      "name": "CRM",
      "url": "https://example.com/hubfit-webhooks",
      "events": ["client.created", "check_in_submission.created"],
      "status": "enabled",
      "disabledReason": null,
      "lastDelivery": {
        "outcome": "delivered",
        "statusCode": 200,
        "attemptedAt": "2026-10-26T14:32:05.000Z"
      },
      "createdAt": "2026-03-02T15:04:11.000Z",
      "updatedAt": "2026-10-26T09:12:40.000Z"
    }
  ]
}
```

## Add a webhook endpoint

`POST https://api.hubfit.com/v1/webhook-endpoints`

Subscribes a URL to events. The endpoint is `enabled` at once and gets events from then on.

The URL must be HTTPS on port 443, with a public domain name (never an IP address) written in lowercase, and no user name, password or `#` fragment. Every event type needs the scope that reads its records (`clients:read`). The response carries the endpoint's signing secret, the only time the API shows it. A workspace has at most 10 endpoints. One key can't add the same URL twice: that's `409 webhook_endpoint_exists`. Requires `webhooks:write`, and a key belonging to the workspace's owner or an admin: anyone else's gets `403 role_required`. The endpoint gets events about every client, and stops for good (`role_changed`) if the key's coach is made a Standard member.

### Headers

- `Idempotency-Key` (string): Any unique string, such as a UUID, up to 255 characters. Retrying with the same key within 24 hours returns the first response instead of repeating the write. See [Idempotency](https://hubfit.com/developers/idempotency).

### Body parameters

- `name` (string, required): What your team calls the endpoint, e.g. `CRM`. Can't be blank.
- `url` (string, required): Where to send events, e.g. `https://example.com/hubfit-webhooks`.
- `events` (array of enums, required): One of `client.created`, `check_in_submission.created`, `completed_workout.created`, `package_purchase.created`, `questionnaire_submission.created`.

### Returns

The new endpoint, and its signing secret.

### Example request

cURL:

```bash
curl -X POST https://api.hubfit.com/v1/webhook-endpoints \
  -H "Authorization: Bearer $HUBFIT_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CRM",
    "url": "https://example.com/hubfit-webhooks",
    "events": ["client.created", "check_in_submission.created"]
  }'
```

Node.js:

```javascript
const response = await fetch("https://api.hubfit.com/v1/webhook-endpoints", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.HUBFIT_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID(),
  },
  body: JSON.stringify({
    name: "CRM",
    url: "https://example.com/hubfit-webhooks",
    events: ["client.created", "check_in_submission.created"],
  }),
});

const { data } = await response.json();
```

Python:

```python
import os
import uuid
import requests

response = requests.post(
    "https://api.hubfit.com/v1/webhook-endpoints",
    headers={"Authorization": f"Bearer {os.environ['HUBFIT_API_KEY']}", "Idempotency-Key": str(uuid.uuid4())},
    json={
        "name": "CRM",
        "url": "https://example.com/hubfit-webhooks",
        "events": ["client.created", "check_in_submission.created"],
    },
)

data = response.json()["data"]
```

### Example response (201 Created)

```json
{
  "data": {
    "id": "6ad6affd3db7e9fc3db656fb",
    "name": "CRM",
    "url": "https://example.com/hubfit-webhooks",
    "events": ["client.created", "check_in_submission.created"],
    "status": "enabled",
    "disabledReason": null,
    "lastDelivery": {
      "outcome": "delivered",
      "statusCode": 200,
      "attemptedAt": "2026-10-26T14:32:05.000Z"
    },
    "createdAt": "2026-03-02T15:04:11.000Z",
    "updatedAt": "2026-10-26T09:12:40.000Z"
  },
  "secret": "whsec_EXAMPLE+ONLY+notARealHubFitSigningSecret+00="
}
```

## Get a webhook endpoint

`GET https://api.hubfit.com/v1/webhook-endpoints/{endpointId}`

One endpoint in the workspace. If this key didn't make it, only the origin of its URL is shown. Requires `webhooks:write`.

### Path parameters

- `endpointId` (string, required): A webhook endpoint `id` from `GET /webhook-endpoints`.

### Returns

The endpoint.

### Example request

cURL:

```bash
curl https://api.hubfit.com/v1/webhook-endpoints/6ad6affd3db7e9fc3db656fb \
  -H "Authorization: Bearer $HUBFIT_API_KEY"
```

Node.js:

```javascript
const response = await fetch("https://api.hubfit.com/v1/webhook-endpoints/6ad6affd3db7e9fc3db656fb", {
  headers: {
    Authorization: `Bearer ${process.env.HUBFIT_API_KEY}`,
  },
});

const { data } = await response.json();
```

Python:

```python
import os
import requests

response = requests.get(
    "https://api.hubfit.com/v1/webhook-endpoints/6ad6affd3db7e9fc3db656fb",
    headers={"Authorization": f"Bearer {os.environ['HUBFIT_API_KEY']}"},
)

data = response.json()["data"]
```

### Example response (200 OK)

```json
{
  "data": {
    "id": "6ad6affd3db7e9fc3db656fb",
    "name": "CRM",
    "url": "https://example.com/hubfit-webhooks",
    "events": ["client.created", "check_in_submission.created"],
    "status": "enabled",
    "disabledReason": null,
    "lastDelivery": {
      "outcome": "delivered",
      "statusCode": 200,
      "attemptedAt": "2026-10-26T14:32:05.000Z"
    },
    "createdAt": "2026-03-02T15:04:11.000Z",
    "updatedAt": "2026-10-26T09:12:40.000Z"
  }
}
```

## Change a webhook endpoint

`PATCH https://api.hubfit.com/v1/webhook-endpoints/{endpointId}`

Changes only the fields you send. A new `url` takes effect at once. `status: disabled` stops it at once and cancels what's waiting to be sent. `status: enabled` turns a disabled endpoint back on, for events from then on, if its `disabledReason` is `manual`, `gone` or `failing` (otherwise `409 webhook_endpoint_disabled`). Only an endpoint this key made can be changed; any other is `403 webhook_endpoint_read_only`. Requires `webhooks:write`.

### Path parameters

- `endpointId` (string, required): A webhook endpoint `id` from `GET /webhook-endpoints`.

### Body parameters

- `name` (string)
- `url` (string)
- `events` (array of enums): One of `client.created`, `check_in_submission.created`, `completed_workout.created`, `package_purchase.created`, `questionnaire_submission.created`.
- `status` (enum): One of `enabled`, `disabled`.

### Returns

The endpoint as it is now.

### Example request

cURL:

```bash
curl -X PATCH https://api.hubfit.com/v1/webhook-endpoints/6ad6affd3db7e9fc3db656fb \
  -H "Authorization: Bearer $HUBFIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "enabled"
  }'
```

Node.js:

```javascript
const response = await fetch("https://api.hubfit.com/v1/webhook-endpoints/6ad6affd3db7e9fc3db656fb", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.HUBFIT_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    status: "enabled",
  }),
});

const { data } = await response.json();
```

Python:

```python
import os
import requests

response = requests.patch(
    "https://api.hubfit.com/v1/webhook-endpoints/6ad6affd3db7e9fc3db656fb",
    headers={"Authorization": f"Bearer {os.environ['HUBFIT_API_KEY']}"},
    json={
        "status": "enabled",
    },
)

data = response.json()["data"]
```

### Example response (200 OK)

```json
{
  "data": {
    "id": "6ad6affd3db7e9fc3db656fb",
    "name": "CRM",
    "url": "https://example.com/hubfit-webhooks",
    "events": ["client.created", "check_in_submission.created"],
    "status": "enabled",
    "disabledReason": null,
    "lastDelivery": {
      "outcome": "delivered",
      "statusCode": 200,
      "attemptedAt": "2026-10-26T14:32:05.000Z"
    },
    "createdAt": "2026-03-02T15:04:11.000Z",
    "updatedAt": "2026-10-26T09:12:40.000Z"
  }
}
```

## Delete a webhook endpoint

`DELETE https://api.hubfit.com/v1/webhook-endpoints/{endpointId}`

Deletes the endpoint and cancels what's waiting to be sent to it. Only an endpoint this key made can be deleted (`403 webhook_endpoint_read_only`). Requires `webhooks:write`.

### Path parameters

- `endpointId` (string, required): A webhook endpoint `id` from `GET /webhook-endpoints`.

### Returns

It's deleted.

### Example request

cURL:

```bash
curl -X DELETE https://api.hubfit.com/v1/webhook-endpoints/6ad6affd3db7e9fc3db656fb \
  -H "Authorization: Bearer $HUBFIT_API_KEY"
```

Node.js:

```javascript
const response = await fetch("https://api.hubfit.com/v1/webhook-endpoints/6ad6affd3db7e9fc3db656fb", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.HUBFIT_API_KEY}`,
  },
});

const { data } = await response.json();
```

Python:

```python
import os
import requests

response = requests.delete(
    "https://api.hubfit.com/v1/webhook-endpoints/6ad6affd3db7e9fc3db656fb",
    headers={"Authorization": f"Bearer {os.environ['HUBFIT_API_KEY']}"},
)

data = response.json()["data"]
```

### Example response (200 OK)

```json
{
  "data": {
    "id": "6ad6af165d04c7155d033418",
    "deleted": true
  }
}
```
