Webhooks
Endpoints HILLS Music calls when something happens: releases, store delivery, DDEX, statements, payouts, requests and Live support. Signed; retried.
Endpoints HILLS Music calls when something happens: releases, store delivery, DDEX, statements, payouts, requests and Live support. Signed; retried.
Base URL https://api.hillsmusic.com/v1. Every request sends an API key: Authorization: Bearer hm_live_….
- List webhook event types
- List webhook endpoints
- Create a webhook endpoint
- Get a webhook endpoint
- Update a webhook endpoint
- Delete a webhook endpoint
- Roll a webhook secret
- Send a test event
- List an endpoint’s deliveries
- Retry a delivery
List webhook event types
GET /v1/webhooks/events · Scope: webhooks:read or webhooks:write
Every event type an endpoint can subscribe to, with what it means. Payloads are documented under webhooks in this reference.
Example request
curl "https://api.hillsmusic.com/v1/webhooks/events" \
-H "Authorization: Bearer $HILLS_API_KEY"
Response 200
{
"data": [
{
"type": "release.approved",
"label": "Release approved",
"description": "A release was approved and is queued for its stores."
}
]
}
Errors
| Status | When |
|---|---|
| 401 | unauthenticated, invalid_key, key_revoked, key_expired or test_keys_retired. |
| 403 | insufficient_scope: the key lacks the scope; scope names it. account_inactive or api_not_in_plan. |
| 429 | rate_limited: wait Retry-After seconds. |
List webhook endpoints
GET /v1/webhooks · Scope: webhooks:read or webhooks:write
Your webhook endpoints (at most 20), oldest first. The secret is never listed.
Example request
curl "https://api.hillsmusic.com/v1/webhooks" \
-H "Authorization: Bearer $HILLS_API_KEY"
Response 200
{
"data": [
{
"id": 7,
"url": "https://example.com/hills/webhooks",
"description": "Order system",
"events": [
"release.approved",
"delivery.store_status_changed"
],
"enabled": true,
"secret_prefix": "whsec_3f9a1c",
"created_at": "2026-10-13T09:00:00Z",
"updated_at": "2026-10-13T09:00:00Z",
"last_success_at": null,
"last_failure_at": null,
"failure_count": 0,
"disabled_at": null,
"disabled_reason": null
}
]
}
Errors
| Status | When |
|---|---|
| 401 | unauthenticated, invalid_key, key_revoked, key_expired or test_keys_retired. |
| 403 | insufficient_scope: the key lacks the scope; scope names it. account_inactive or api_not_in_plan. |
| 429 | rate_limited: wait Retry-After seconds. |
Create a webhook endpoint
POST /v1/webhooks · Scope: webhooks:write
Registers an https URL for the events you pick. The answer carries the signing secret, once. The URL must be public: private, local and metadata addresses are refused.
Request body
JSON.
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | Where to POST events: a public https:// URL on port 443. |
description |
string | No | A note for you, e.g. the system that receives them. |
events |
array | Yes | The event types to send (see Webhooks in this reference). |
enabled |
boolean | No | Default true. |
Example request
curl -X POST "https://api.hillsmusic.com/v1/webhooks" \
-H "Authorization: Bearer $HILLS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/hills/webhooks","description":"Order system","events":["release.approved","delivery.store_status_changed"]}'
Response 201
{
"endpoint": {
"id": 7,
"url": "https://example.com/hills/webhooks",
"description": "Order system",
"events": [
"release.approved",
"delivery.store_status_changed"
],
"enabled": true,
"secret_prefix": "whsec_3f9a1c",
"created_at": "2026-10-13T09:00:00Z",
"updated_at": "2026-10-13T09:00:00Z",
"last_success_at": null,
"last_failure_at": null,
"failure_count": 0,
"disabled_at": null,
"disabled_reason": null
},
"secret": "whsec_3f9a1c0d5e7b2a4c6e8f0a1b3c5d7e9f1a2b3c4d5e6f7a8b"
}
Errors
| Status | When |
|---|---|
| 400 | invalid: a parameter is wrong; field names it. invalid_url: Not a public https URL on port 443. |
| 401 | unauthenticated, invalid_key, key_revoked, key_expired or test_keys_retired. |
| 403 | insufficient_scope: the key lacks the scope; scope names it. account_inactive or api_not_in_plan. |
| 409 | too_many: Already 20 endpoints. |
| 429 | rate_limited: wait Retry-After seconds. |
Get a webhook endpoint
GET /v1/webhooks/{id} · Scope: webhooks:read or webhooks:write
One endpoint with its health: last success and failure, failed attempts in a row, and why it is off.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | integer | Yes | The endpoint id. |
Example request
curl "https://api.hillsmusic.com/v1/webhooks/7" \
-H "Authorization: Bearer $HILLS_API_KEY"
Response 200
{
"data": {
"id": 7,
"url": "https://example.com/hills/webhooks",
"description": "Order system",
"events": [
"release.approved",
"delivery.store_status_changed"
],
"enabled": true,
"secret_prefix": "whsec_3f9a1c",
"created_at": "2026-10-13T09:00:00Z",
"updated_at": "2026-10-13T09:00:00Z",
"last_success_at": null,
"last_failure_at": null,
"failure_count": 0,
"disabled_at": null,
"disabled_reason": null
}
}
Errors
| Status | When |
|---|---|
| 401 | unauthenticated, invalid_key, key_revoked, key_expired or test_keys_retired. |
| 403 | insufficient_scope: the key lacks the scope; scope names it. account_inactive or api_not_in_plan. |
| 404 | not_found: no such thing, or not yours. |
| 429 | rate_limited: wait Retry-After seconds. |
Update a webhook endpoint
PATCH /v1/webhooks/{id} · Scope: webhooks:write
Changes its URL, description, events, or switches it off and on (enabled). Switching it on again clears its failure count; deliveries given up while it was off can be sent again with retry.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | integer | Yes | The endpoint id. |
Request body
JSON.
| Field | Type | Required | Description |
|---|---|---|---|
url |
string | No | Where to POST events: a public https:// URL on port 443. |
description |
string | No | A note for you, e.g. the system that receives them. |
events |
array | No | The event types to send (see Webhooks in this reference). |
enabled |
boolean | No | false switches it off; true switches it back on. |
Example request
curl -X PATCH "https://api.hillsmusic.com/v1/webhooks/7" \
-H "Authorization: Bearer $HILLS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled":true}'
Response 200
{
"data": {
"id": 7,
"url": "https://example.com/hills/webhooks",
"description": "Order system",
"events": [
"release.approved",
"delivery.store_status_changed"
],
"enabled": true,
"secret_prefix": "whsec_3f9a1c",
"created_at": "2026-10-13T09:00:00Z",
"updated_at": "2026-10-13T09:00:00Z",
"last_success_at": null,
"last_failure_at": null,
"failure_count": 0,
"disabled_at": null,
"disabled_reason": null
}
}
Errors
| Status | When |
|---|---|
| 400 | invalid: a parameter is wrong; field names it. invalid_url: Not a public https URL on port 443. |
| 401 | unauthenticated, invalid_key, key_revoked, key_expired or test_keys_retired. |
| 403 | insufficient_scope: the key lacks the scope; scope names it. account_inactive or api_not_in_plan. |
| 404 | not_found: no such thing, or not yours. |
| 429 | rate_limited: wait Retry-After seconds. |
Delete a webhook endpoint
DELETE /v1/webhooks/{id} · Scope: webhooks:write
Deletes the endpoint and its delivery log. Nothing more is sent to it.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | integer | Yes | The endpoint id. |
Example request
curl -X DELETE "https://api.hillsmusic.com/v1/webhooks/7" \
-H "Authorization: Bearer $HILLS_API_KEY"
Response 200
{
"ok": true
}
Errors
| Status | When |
|---|---|
| 401 | unauthenticated, invalid_key, key_revoked, key_expired or test_keys_retired. |
| 403 | insufficient_scope: the key lacks the scope; scope names it. account_inactive or api_not_in_plan. |
| 404 | not_found: no such thing, or not yours. |
| 429 | rate_limited: wait Retry-After seconds. |
Roll a webhook secret
POST /v1/webhooks/{id}/roll-secret · Scope: webhooks:write
Makes a new signing secret; the old one stops at once. The new secret is in this answer only.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | integer | Yes | The endpoint id. |
Example request
curl -X POST "https://api.hillsmusic.com/v1/webhooks/7/roll-secret" \
-H "Authorization: Bearer $HILLS_API_KEY"
Response 200
{
"endpoint": {
"id": 7,
"url": "https://example.com/hills/webhooks",
"description": "Order system",
"events": [
"release.approved",
"delivery.store_status_changed"
],
"enabled": true,
"secret_prefix": "whsec_3f9a1c",
"created_at": "2026-10-13T09:00:00Z",
"updated_at": "2026-10-13T09:00:00Z",
"last_success_at": null,
"last_failure_at": null,
"failure_count": 0,
"disabled_at": null,
"disabled_reason": null
},
"secret": "whsec_3f9a1c0d5e7b2a4c6e8f0a1b3c5d7e9f1a2b3c4d5e6f7a8b"
}
Errors
| Status | When |
|---|---|
| 401 | unauthenticated, invalid_key, key_revoked, key_expired or test_keys_retired. |
| 403 | insufficient_scope: the key lacks the scope; scope names it. account_inactive or api_not_in_plan. |
| 404 | not_found: no such thing, or not yours. |
| 429 | rate_limited: wait Retry-After seconds. |
Send a test event
POST /v1/webhooks/{id}/test · Scope: webhooks:write
Queues a webhook.test event to this endpoint, whatever events it has, signed like any other. Its outcome shows in the deliveries.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | integer | Yes | The endpoint id. |
Example request
curl -X POST "https://api.hillsmusic.com/v1/webhooks/7/test" \
-H "Authorization: Bearer $HILLS_API_KEY"
Response 202
{
"delivery": {
"id": 1042,
"endpoint_id": 7,
"event": "webhook.test",
"event_id": "5d0c2b7e-8a43-4c4e-9f1e-2f0b9a6c1d33",
"attempt": 0,
"status": "pending",
"response_status": null,
"response_ms": null,
"next_attempt_at": "2026-10-13T09:05:00Z",
"error": null,
"created_at": "2026-10-13T09:05:00Z",
"delivered_at": null,
"payload": {
"id": "5d0c2b7e-8a43-4c4e-9f1e-2f0b9a6c1d33",
"type": "release.approved",
"api_version": "v1",
"occurred_at": "2026-10-13T09:05:00Z",
"account_id": 12,
"data": {
"release": {
"id": 812,
"title": "Midnight Run"
}
}
}
}
}
Errors
| Status | When |
|---|---|
| 401 | unauthenticated, invalid_key, key_revoked, key_expired or test_keys_retired. |
| 403 | insufficient_scope: the key lacks the scope; scope names it. account_inactive or api_not_in_plan. |
| 404 | not_found: no such thing, or not yours. |
| 409 | endpoint_disabled: The endpoint is off. |
| 429 | rate_limited: wait Retry-After seconds. |
List an endpoint’s deliveries
GET /v1/webhooks/{id}/deliveries · Scope: webhooks:read or webhooks:write
What was sent to the endpoint in the last 30 days, newest first: the payload, attempts, the last HTTP status and time, and the error.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | integer | Yes | The endpoint id. |
status |
query | pending | succeeded | failed |
No | Only these. |
event |
query | string | No | Only this event type. |
limit |
query | number | No | 1–100, default 50. |
before |
query | number | No | Deliveries older than this delivery id (the next page). |
Example request
curl "https://api.hillsmusic.com/v1/webhooks/7/deliveries?status=failed" \
-H "Authorization: Bearer $HILLS_API_KEY"
Response 200
{
"data": [
{
"id": 1042,
"endpoint_id": 7,
"event": "release.approved",
"event_id": "5d0c2b7e-8a43-4c4e-9f1e-2f0b9a6c1d33",
"attempt": 1,
"status": "succeeded",
"response_status": 200,
"response_ms": 184,
"next_attempt_at": "2026-10-13T09:05:00Z",
"error": null,
"created_at": "2026-10-13T09:05:00Z",
"delivered_at": "2026-10-13T09:05:01Z",
"payload": {
"id": "5d0c2b7e-8a43-4c4e-9f1e-2f0b9a6c1d33",
"type": "release.approved",
"api_version": "v1",
"occurred_at": "2026-10-13T09:05:00Z",
"account_id": 12,
"data": {
"release": {
"id": 812,
"title": "Midnight Run"
}
}
}
}
],
"has_more": false
}
Errors
| Status | When |
|---|---|
| 400 | invalid: a parameter is wrong; field names it. |
| 401 | unauthenticated, invalid_key, key_revoked, key_expired or test_keys_retired. |
| 403 | insufficient_scope: the key lacks the scope; scope names it. account_inactive or api_not_in_plan. |
| 404 | not_found: no such thing, or not yours. |
| 429 | rate_limited: wait Retry-After seconds. |
Retry a delivery
POST /v1/webhooks/{id}/deliveries/{delivery_id}/retry · Scope: webhooks:write
Sends the same event (same id and payload) to the endpoint again now.
Parameters
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id |
path | integer | Yes | The endpoint id. |
delivery_id |
path | integer | Yes | The delivery id. |
Example request
curl -X POST "https://api.hillsmusic.com/v1/webhooks/7/deliveries/1042/retry" \
-H "Authorization: Bearer $HILLS_API_KEY"
Response 202
{
"delivery": {
"id": 1042,
"endpoint_id": 7,
"event": "release.approved",
"event_id": "5d0c2b7e-8a43-4c4e-9f1e-2f0b9a6c1d33",
"attempt": 1,
"status": "pending",
"response_status": 200,
"response_ms": 184,
"next_attempt_at": "2026-10-13T09:05:00Z",
"error": null,
"created_at": "2026-10-13T09:05:00Z",
"delivered_at": "2026-10-13T09:05:01Z",
"payload": {
"id": "5d0c2b7e-8a43-4c4e-9f1e-2f0b9a6c1d33",
"type": "release.approved",
"api_version": "v1",
"occurred_at": "2026-10-13T09:05:00Z",
"account_id": 12,
"data": {
"release": {
"id": 812,
"title": "Midnight Run"
}
}
}
}
}
Errors
| Status | When |
|---|---|
| 401 | unauthenticated, invalid_key, key_revoked, key_expired or test_keys_retired. |
| 403 | insufficient_scope: the key lacks the scope; scope names it. account_inactive or api_not_in_plan. |
| 404 | not_found: no such thing, or not yours. |
| 409 | endpoint_disabled: The endpoint is off. |
| 429 | rate_limited: wait Retry-After seconds. |