StockroomAPIOpen the app
Reference

Webhooks

Subscriptions to a URL, the delivery log, a test event and redelivery. The signing secret is in the creation response and nowhere else.

#

List webhooks

GETapi.stockroom.app/v2/webhooks
Responses
200
Every webhook on the store (at most 10).
a page of Webhook in data, with next_cursor and has_more
Request
curl "https://api.stockroom.app/v2/webhooks" \
  -H "Authorization: Bearer $STOCKROOM_KEY"
Response 200
{
  "data": [
    {
      "id": 3,
      "url": "https://erp.example.com/hooks/stockroom",
      "description": "ERP sync",
      "events": [
        "purchase_order.received",
        "receipt.voided"
      ],
      "status": "active",
      "api_version": 2,
      "consecutive_failures": 0,
      "last_delivered_at": "2026-09-11T08:12:44Z",
      "last_failed_at": null,
      "disabled_at": null,
      "disabled_reason": null,
      "created_at": "2026-09-01T10:00:00Z",
      "updated_at": "2026-09-11T08:12:44Z"
    }
  ]
}
#

Create a webhook

POSTapi.stockroom.app/v2/webhooks

url must be https and resolve to a public address. events is a list from the catalogue or ["*"] for everything. The signing secret is returned once, in this response only. Deliveries carry X-Stockroom-Signature: sha256=HMAC(secret, timestamp + "." + body).

BodyJSON
urlstring (uri)required
eventsarray of WebhookEventrequired
descriptionstring or null
Responses
201
The webhook, with its secret.
Request
curl -X POST "https://api.stockroom.app/v2/webhooks" \
  -H "Authorization: Bearer $STOCKROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://erp.example.com/hooks/stockroom",
  "events": [
    "*"
  ],
  "description": "ERP sync"
}'
Body
{
  "url": "https://erp.example.com/hooks/stockroom",
  "events": [
    "*"
  ],
  "description": "ERP sync"
}
Response 201
{
  "data": {
    "secret": "whsec_9f86d081884c7d659a2feaa0c55ad015",
    "id": 1,
    "url": "https://erp.example.com/hooks/stockroom",
    "description": "Freight",
    "events": [
      "*"
    ],
    "status": "active",
    "api_version": 2,
    "consecutive_failures": 0,
    "last_delivered_at": "2026-09-11T08:12:44Z",
    "last_failed_at": null,
    "disabled_at": null,
    "disabled_reason": null,
    "created_at": "2026-09-01T10:00:00Z",
    "updated_at": "2026-09-11T08:12:44Z"
  }
}
#

Get a webhook

GETapi.stockroom.app/v2/webhooks/{id}
Path
idintegerpathrequired
Responses
200
The webhook.
Webhook in data
Request
curl "https://api.stockroom.app/v2/webhooks/3" \
  -H "Authorization: Bearer $STOCKROOM_KEY"
Response 200
{
  "data": {
    "id": 3,
    "url": "https://erp.example.com/hooks/stockroom",
    "description": "ERP sync",
    "events": [
      "purchase_order.received",
      "receipt.voided"
    ],
    "status": "active",
    "api_version": 2,
    "consecutive_failures": 0,
    "last_delivered_at": "2026-09-11T08:12:44Z",
    "last_failed_at": null,
    "disabled_at": null,
    "disabled_reason": null,
    "created_at": "2026-09-01T10:00:00Z",
    "updated_at": "2026-09-11T08:12:44Z"
  }
}
#

Update a webhook

PATCHapi.stockroom.app/v2/webhooks/{id}

Partial. Setting status to active re-enables a paused or auto-disabled webhook and resets its failure count.

Path
idintegerpathrequired
BodyJSON
urlstring (uri)
eventsarray of WebhookEvent
descriptionstring or null
statusstring
activepaused
Responses
200
The webhook.
Webhook in data
Request
curl -X PATCH "https://api.stockroom.app/v2/webhooks/3" \
  -H "Authorization: Bearer $STOCKROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://erp.example.com/hooks/stockroom",
  "events": [
    "*"
  ],
  "description": "Freight",
  "status": "active"
}'
Body
{
  "url": "https://erp.example.com/hooks/stockroom",
  "events": [
    "*"
  ],
  "description": "Freight",
  "status": "active"
}
Response 200
{
  "data": {
    "id": 3,
    "url": "https://erp.example.com/hooks/stockroom",
    "description": "ERP sync",
    "events": [
      "purchase_order.received",
      "receipt.voided"
    ],
    "status": "active",
    "api_version": 2,
    "consecutive_failures": 0,
    "last_delivered_at": "2026-09-11T08:12:44Z",
    "last_failed_at": null,
    "disabled_at": null,
    "disabled_reason": null,
    "created_at": "2026-09-01T10:00:00Z",
    "updated_at": "2026-09-11T08:12:44Z"
  }
}
#

Delete a webhook

DELETEapi.stockroom.app/v2/webhooks/{id}
Path
idintegerpathrequired
Responses
200
Deleted.
200 answer, in data
idintegerrequired
deletedbooleanrequired
Request
curl -X DELETE "https://api.stockroom.app/v2/webhooks/3" \
  -H "Authorization: Bearer $STOCKROOM_KEY"
Response 200
{
  "data": {
    "id": 1,
    "deleted": true
  }
}
#

A webhook's delivery log

GETapi.stockroom.app/v2/webhooks/{id}/deliveries

One row per attempt, newest first. Filter by status and event_id. Kept 30 days.

Path
idintegerpathrequired
Query
limitintegerquery
cursorstringquery
next_cursor from the previous page. A cursor that cannot be read is 400 invalid_cursor.
statusstringquery
succeededfailed
event_idstringquery
Responses
200
A page of deliveries.
a page of WebhookDelivery in data, with next_cursor and has_more
Request
curl "https://api.stockroom.app/v2/webhooks/3/deliveries" \
  -H "Authorization: Bearer $STOCKROOM_KEY"
Response 200
{
  "data": [
    {
      "id": 1,
      "webhook_id": 3,
      "event_id": "evt_01J9Z8K3M4N5P6Q7R8S9T0V1W2",
      "event": "purchase_order.received",
      "attempt": 1,
      "status": "succeeded",
      "status_code": 200,
      "duration_ms": 412,
      "error": null,
      "next_attempt_at": null,
      "created_at": "2026-09-01T10:00:00Z"
    }
  ],
  "next_cursor": "eyJpZCI6MTA0Mn0",
  "has_more": true
}
#

Retry one delivery's event now

POSTapi.stockroom.app/v2/webhooks/{id}/deliveries/{delivery_id}/redeliver
Path
idintegerpathrequired
delivery_idintegerpathrequired
Request
curl -X POST "https://api.stockroom.app/v2/webhooks/3/deliveries/9120/redeliver" \
  -H "Authorization: Bearer $STOCKROOM_KEY"
Response 202
{
  "data": {
    "queued": 12,
    "event_ids": [
      "evt_01J9Z8K3M4N5P6Q7R8S9T0V1W2"
    ]
  }
}
#

Resend past events

POSTapi.stockroom.app/v2/webhooks/{id}/redeliver

By event_id (one event) or since (every event this webhook subscribes to from that moment, newest 500). Each is a fresh attempt 1.

Path
idintegerpathrequired
BodyJSON
event_idstring
Required without since.
sincestring (date-time)
Required without event_id.
Request
curl -X POST "https://api.stockroom.app/v2/webhooks/3/redeliver" \
  -H "Authorization: Bearer $STOCKROOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "event_id": "evt_01J9Z8K3M4N5P6Q7R8S9T0V1W2",
  "since": "2026-09-11T02:00:00Z"
}'
Body
{
  "event_id": "evt_01J9Z8K3M4N5P6Q7R8S9T0V1W2",
  "since": "2026-09-11T02:00:00Z"
}
Response 202
{
  "data": {
    "queued": 12,
    "event_ids": [
      "evt_01J9Z8K3M4N5P6Q7R8S9T0V1W2"
    ]
  }
}
#

Send a test event

POSTapi.stockroom.app/v2/webhooks/{id}/test

One webhook.test delivery to this endpoint, whatever it subscribes to.

Path
idintegerpathrequired
Responses
202
Queued.
202 answer, in data
event_idstringrequired
eventstringrequired
queuedbooleanrequired
Request
curl -X POST "https://api.stockroom.app/v2/webhooks/3/test" \
  -H "Authorization: Bearer $STOCKROOM_KEY"
Response 202
{
  "data": {
    "event_id": "evt_01J9Z8K3M4N5P6Q7R8S9T0V1W2",
    "event": "webhook.test",
    "queued": true
  }
}