Webhooks
Webhook endpoints
Subscribe a URL to events, such as a client added or a check-in submitted, and manage it.
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.
Attributes
- idstring
Its unique ID.
- namestring
- urlstring
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.
- eventsarray of enumsKnown values
client.createdcheck_in_submission.createdcompleted_workout.createdpackage_purchase.createdquestionnaire_submission.created - statusenum
An
enabledendpoint gets nothing while the workspace is off a plan with webhooks, unpaid or suspended, and nothing from that time once it's back.Known valuesenableddisabled Why the endpoint is
disabled, andnullwhile it'senabled. These can be turned back on:manual, turned off withstatus: disabledor in Settings;gone, after a 410 reply; andfailing, 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; androle_changed, the key's coach was made a Standard member.Known valuesmanualgonefailingapi_key_revokedapi_key_expiredcoach_removedrole_changedShow 3 child attributesHide child attributes
- outcomeenum
deliveredafter a 2xx reply.retrywhen the attempt failed and another is scheduled,failedwhen it failed with no attempt left.goneafter a 410 reply, which turns the endpoint off.blockedwhen the URL led to an address webhooks can't be sent to, such as a private one: it isn't retried.Known valuesdeliveredretryfailedgoneblocked The reply's status.
nullif there was none, such as a timeout.- attemptedAttimestamp
- createdAttimestamp
When it was created.
- updatedAttimestamp
When it last changed.
{
"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"
}The JSON body of every webhook request. See Webhooks for its headers and how to verify it.
Attributes
- idstring
The event's id, as in
webhook-id. - typeenum
The event type. New types can appear.
Known valuesclient.createdcheck_in_submission.createdcompleted_workout.createdpackage_purchase.createdquestionnaire_submission.created - createdAttimestamp
When it was created.
- dataobject
The record the event is about. Fetch it with your key to read it as it is now.
{
"id": "evt_6ad6af72e7830e73e784a170",
"type": "check_in_submission.created",
"createdAt": "2026-10-26T14:32:06.000Z",
"data": {
"id": "6ad6af25540e6a26540ffd23",
"clientId": "6ad6af41d3199442d31b2743"
}
}https://api.hubfit.com/v1/webhook-endpointsEvery 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.
This endpoint takes no parameters.
Returns
The workspace's endpoints.
curl https://api.hubfit.com/v1/webhook-endpoints \
-H "Authorization: Bearer $HUBFIT_API_KEY"const response = await fetch("https://api.hubfit.com/v1/webhook-endpoints", {
headers: {
Authorization: `Bearer ${process.env.HUBFIT_API_KEY}`,
},
});
const { data } = await response.json();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"]{
"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"
}
]
}https://api.hubfit.com/v1/webhook-endpointsSubscribes 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-Keystring
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.
Body parameters
What your team calls the endpoint, e.g.
CRM. Can't be blank.Where to send events, e.g.
https://example.com/hubfit-webhooks.- One of
client.createdcheck_in_submission.createdcompleted_workout.createdpackage_purchase.createdquestionnaire_submission.created
Returns
The new endpoint, and its signing secret.
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"]
}'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();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"]{
"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="
}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
A webhook endpoint
idfromGET /webhook-endpoints.
Returns
The endpoint.
curl https://api.hubfit.com/v1/webhook-endpoints/6ad6affd3db7e9fc3db656fb \
-H "Authorization: Bearer $HUBFIT_API_KEY"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();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"]{
"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"
}
}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
A webhook endpoint
idfromGET /webhook-endpoints.
Body parameters
Returns
The endpoint as it is now.
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"
}'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();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"]{
"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"
}
}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
A webhook endpoint
idfromGET /webhook-endpoints.
Returns
It's deleted.
curl -X DELETE https://api.hubfit.com/v1/webhook-endpoints/6ad6affd3db7e9fc3db656fb \
-H "Authorization: Bearer $HUBFIT_API_KEY"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();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"]{
"data": {
"id": "6ad6af165d04c7155d033418",
"deleted": true
}
}