API
Webhooks operations
Signed event delivery to your servers.
Signed event delivery to your servers. Paths are relative to https://reuseforge.com/api/v1.
| Operation | Method | Path |
|---|---|---|
| List webhooks | GET | /webhooks |
| Create a webhook | POST | /webhooks |
| Get a webhook | GET | /webhooks/{webhook_id} |
| Update a webhook | PATCH | /webhooks/{webhook_id} |
| Delete a webhook | DELETE | /webhooks/{webhook_id} |
| Rotate the signing secret | POST | /webhooks/{webhook_id}/rotate-secret |
| Send a test ping | POST | /webhooks/{webhook_id}/test |
| List deliveries | GET | /webhooks/{webhook_id}/deliveries |
| Redeliver an event | POST | /webhooks/{webhook_id}/deliveries/{delivery_id}/redeliver |
List webhooks
GET /webhooks Enabled on reuseforge.com
Webhook endpoints of the authenticated account (and of organizations you administer when account is given).
| Property | Value |
|---|---|
| Operation ID | list_webhooks |
| Authentication | Bearer token holding webhooks:manage |
| Rate tier | read: 60/min per IP anonymous, 600/min per token with a token |
| Python SDK | client.list_webhooks(...) |
| JavaScript SDK | client.listWebhooks(...) |
| CLI | rf webhooks list |
| MCP tool | not exposed as a tool |
| Deployments | reuseforge.com: enabled. Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
cursor | query | string | no | Opaque cursor from the previous page's next_cursor. |
limit | query | integer | no | Page size. Default 30. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | A page of webhooks. Secrets are never listed. | application/json: WebhookPage |
| 401 | Missing, malformed, expired or revoked credentials. | Problem. Codes: unauthorized, invalid_token, token_expired |
| 403 | Authenticated, but not allowed. Most often a missing scope (insufficient_scope, with required_scopes), an exceeded spend limit, or a role on the owning account that is too low. Also version_quarantined (the security scan or a moderator blocked the version you asked for) and review_removed (a moderator removed your review). | Problem. Codes: insufficient_scope, forbidden, account_suspended, email_unverified, spend_limit_exceeded, purchase_required, quota_exceeded, version_quarantined, review_removed |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl 'https://reuseforge.com/api/v1/webhooks' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN"Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
for entry in client.list_webhooks(): # iterating fetches further pages on demand
print(entry["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
for await (const entry of client.listWebhooks()) { // iterating fetches further pages on demand
console.log(entry.id);
}CLI
rf webhooks list
# rf webhooks list [options]
# all options: rf webhooks list --helpResponse 200 OK:
{
"data": [
{
"id": "whk_ordersandpub",
"object": "webhook",
"url": "https://hooks.acme.example/reuseforge",
"description": "Production order and publish events",
"events": [
"item.published",
"item.version.published",
"order.created",
"order.funded",
"order.released"
],
"account": "acme",
"status": "enabled",
"disabled_reason": null,
"api_version": "1",
"consecutive_failures": 0,
"last_delivery_at": "2026-11-03T10:08:31Z",
"last_success_at": "2026-11-03T10:08:31Z",
"created_at": "2026-10-01T12:00:00Z",
"updated_at": "2026-10-01T12:00:00Z"
}
],
"next_cursor": null,
"has_more": false
}Create a webhook
POST /webhooks Enabled on reuseforge.com
Registers an HTTPS endpoint. The signing secret (secret, whsec_...) is returned once; store it. The URL must be HTTPS on a public host (no IP literals, no private or link-local ranges, no credentials in the URL, port 443 or 8443 only; plain http://localhost is accepted only by self-hosted nodes). Redirects are not followed. At most 20 endpoints per account and one endpoint per URL (409 already_exists). The platform sends a ping event right after creation.
Receiving an event is reading the resource in it, so the credential also needs the read scope of every subscribed type: orders:read for order.* and payout.sent, items:read for item.*, review.created and verification.completed, projects:read for project.*. * needs all three. Without them: 403 insufficient_scope.
| Property | Value |
|---|---|
| Operation ID | create_webhook |
| Authentication | Bearer token holding webhooks:manage |
| Rate tier | write: not allowed anonymous, 120/min per token with a token |
| Idempotency | Idempotency-Key header, optional. The SDKs and the CLI send one for you. |
| Python SDK | client.create_webhook(...) |
| JavaScript SDK | client.createWebhook(...) |
| CLI | rf webhooks create |
| MCP tool | not exposed as a tool |
| Deployments | reuseforge.com: enabled. Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
Idempotency-Key | header | string | no | Client-chosen unique key (1 to 255 printable ASCII characters, a UUID is ideal). A repeat of the same request with the same key within 24 hours returns the first response with Idempotent-Replayed: true. The same key with a different body returns 422 idempotency_key_reuse; a repeat while the first call is still running returns 409 idempotency_in_progress. |
Request body
application/json, required, schema WebhookCreate
| Field | Type | Required | Description |
|---|---|---|---|
url | string (uri) | yes | |
description | string | no | |
events | array of EventType or string: * | yes | |
account | string | no | Organization handle you administer. Defaults to you. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 201 Created | The webhook with its one-time secret. | application/json: WebhookWithSecret |
| 400 | The request is malformed (bad JSON, unknown or mistyped parameter, invalid cursor). | Problem. Codes: invalid_request, invalid_json, invalid_parameter, invalid_cursor, idempotency_key_required |
| 401 | Missing, malformed, expired or revoked credentials. | Problem. Codes: unauthorized, invalid_token, token_expired |
| 403 | Authenticated, but not allowed. Most often a missing scope (insufficient_scope, with required_scopes), an exceeded spend limit, or a role on the owning account that is too low. Also version_quarantined (the security scan or a moderator blocked the version you asked for) and review_removed (a moderator removed your review). | Problem. Codes: insufficient_scope, forbidden, account_suspended, email_unverified, spend_limit_exceeded, purchase_required, quota_exceeded, version_quarantined, review_removed |
| 409 | The request conflicts with current state (duplicate resource, wrong lifecycle state, idempotency conflict). | Problem. Codes: conflict, already_exists, invalid_state, idempotency_in_progress |
| 422 | The body is well-formed JSON but invalid (validation_failed with per-field errors), or a business rule rejects it (payment_invalid, webhook_url_forbidden, idempotency_key_reuse). | Problem. Codes: validation_failed, payment_invalid, rail_unsupported, idempotency_key_reuse, webhook_url_forbidden, invalid_signature, integrity_mismatch |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
| 503 | Temporarily unable to serve (maintenance or a dependency is down). Honour Retry-After. | Problem. Codes: unavailable |
Example
curl
curl -X POST 'https://reuseforge.com/api/v1/webhooks' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.acme.example/reuseforge",
"description": "Production order and publish events",
"events": [
"item.published",
"item.version.published",
"order.created",
"order.funded",
"order.released"
],
"account": "acme"
}'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.create_webhook(
url="https://hooks.acme.example/reuseforge",
description="Production order and publish events",
events=[
"item.published",
"item.version.published",
"order.created",
"order.funded",
"order.released",
],
account="acme",
)
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.createWebhook({
url: "https://hooks.acme.example/reuseforge",
description: "Production order and publish events",
events: [
"item.published",
"item.version.published",
"order.created",
"order.funded",
"order.released",
],
account: "acme",
});
console.log(result.id);CLI
rf webhooks create --url https://hooks.acme.example/reuseforge --description 'Production order and publish events' --events item.published,item.version.published,order.created,order.funded,order.released --account acme
# rf webhooks create [options]
# all options: rf webhooks create --helpResponse 201 Created:
{
"id": "whk_ordersandpub",
"object": "webhook",
"url": "https://hooks.acme.example/reuseforge",
"description": "Production order and publish events",
"events": [
"item.published",
"item.version.published",
"order.created",
"order.funded",
"order.released"
],
"account": "acme",
"status": "enabled",
"disabled_reason": null,
"api_version": "1",
"consecutive_failures": 0,
"last_delivery_at": null,
"last_success_at": null,
"created_at": "2026-10-01T12:00:00Z",
"updated_at": "2026-10-01T12:00:00Z",
"secret": "whsec_kq7x3n2m5v4b6c8d9f2g3h4j5k6m7n8p"
}Get a webhook
GET /webhooks/{webhook_id} Enabled on reuseforge.com
One endpoint with its delivery health.
| Property | Value |
|---|---|
| Operation ID | get_webhook |
| Authentication | Bearer token holding webhooks:manage |
| Rate tier | read: 60/min per IP anonymous, 600/min per token with a token |
| Python SDK | client.get_webhook(...) |
| JavaScript SDK | client.getWebhook(...) |
| CLI | rf webhooks get |
| MCP tool | not exposed as a tool |
| Deployments | reuseforge.com: enabled. Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
webhook_id | path | string | yes |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The webhook. | application/json: Webhook |
| 401 | Missing, malformed, expired or revoked credentials. | Problem. Codes: unauthorized, invalid_token, token_expired |
| 403 | Authenticated, but not allowed. Most often a missing scope (insufficient_scope, with required_scopes), an exceeded spend limit, or a role on the owning account that is too low. Also version_quarantined (the security scan or a moderator blocked the version you asked for) and review_removed (a moderator removed your review). | Problem. Codes: insufficient_scope, forbidden, account_suspended, email_unverified, spend_limit_exceeded, purchase_required, quota_exceeded, version_quarantined, review_removed |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl 'https://reuseforge.com/api/v1/webhooks/whk_p8d3f6j1s5a2' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN"Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.get_webhook("whk_p8d3f6j1s5a2")
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.getWebhook("whk_p8d3f6j1s5a2");
console.log(result.id);CLI
rf webhooks get whk_p8d3f6j1s5a2
# rf webhooks get <webhook_id>
# all options: rf webhooks get --helpResponse 200 OK:
{
"id": "whk_ordersandpub",
"object": "webhook",
"url": "https://hooks.acme.example/reuseforge",
"description": "Production order and publish events",
"events": [
"item.published",
"item.version.published",
"order.created",
"order.funded",
"order.released"
],
"account": "acme",
"status": "enabled",
"disabled_reason": null,
"api_version": "1",
"consecutive_failures": 0,
"last_delivery_at": "2026-11-03T10:08:31Z",
"last_success_at": "2026-11-03T10:08:31Z",
"created_at": "2026-10-01T12:00:00Z",
"updated_at": "2026-10-01T12:00:00Z"
}Update a webhook
PATCH /webhooks/{webhook_id} Enabled on reuseforge.com
Change the URL, events or description, or set status to enabled to re-enable an endpoint that was automatically disabled (the failure counter resets). Adding event types, and pointing an endpoint at another URL, need the read scope of the types it receives (see create_webhook); renaming, narrowing and disabling need webhooks:manage only. A URL another endpoint of the account already uses is 409 already_exists.
| Property | Value |
|---|---|
| Operation ID | update_webhook |
| Authentication | Bearer token holding webhooks:manage |
| Rate tier | write: not allowed anonymous, 120/min per token with a token |
| Python SDK | client.update_webhook(...) |
| JavaScript SDK | client.updateWebhook(...) |
| CLI | rf webhooks update |
| MCP tool | not exposed as a tool |
| Deployments | reuseforge.com: enabled. Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
webhook_id | path | string | yes |
Request body
application/json, required, schema WebhookUpdate
| Field | Type | Required | Description |
|---|---|---|---|
url | string (uri) | no | |
description | string | no | |
events | array of EventType or string: * | no | |
status | string: enabled, disabled | no |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The updated webhook. | application/json: Webhook |
| 400 | The request is malformed (bad JSON, unknown or mistyped parameter, invalid cursor). | Problem. Codes: invalid_request, invalid_json, invalid_parameter, invalid_cursor, idempotency_key_required |
| 401 | Missing, malformed, expired or revoked credentials. | Problem. Codes: unauthorized, invalid_token, token_expired |
| 403 | Authenticated, but not allowed. Most often a missing scope (insufficient_scope, with required_scopes), an exceeded spend limit, or a role on the owning account that is too low. Also version_quarantined (the security scan or a moderator blocked the version you asked for) and review_removed (a moderator removed your review). | Problem. Codes: insufficient_scope, forbidden, account_suspended, email_unverified, spend_limit_exceeded, purchase_required, quota_exceeded, version_quarantined, review_removed |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 409 | The request conflicts with current state (duplicate resource, wrong lifecycle state, idempotency conflict). | Problem. Codes: conflict, already_exists, invalid_state, idempotency_in_progress |
| 422 | The body is well-formed JSON but invalid (validation_failed with per-field errors), or a business rule rejects it (payment_invalid, webhook_url_forbidden, idempotency_key_reuse). | Problem. Codes: validation_failed, payment_invalid, rail_unsupported, idempotency_key_reuse, webhook_url_forbidden, invalid_signature, integrity_mismatch |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl -X PATCH 'https://reuseforge.com/api/v1/webhooks/whk_p8d3f6j1s5a2' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "enabled"
}'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.update_webhook("whk_p8d3f6j1s5a2", status="enabled")
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.updateWebhook("whk_p8d3f6j1s5a2", { status: "enabled" });
console.log(result.id);CLI
rf webhooks update whk_p8d3f6j1s5a2 --status enabled
# rf webhooks update <webhook_id> [options]
# all options: rf webhooks update --helpResponse 200 OK:
{
"id": "whk_ordersandpub",
"object": "webhook",
"url": "https://hooks.acme.example/reuseforge",
"description": "Production order and publish events",
"events": [
"item.published",
"item.version.published",
"order.created",
"order.funded",
"order.released"
],
"account": "acme",
"status": "enabled",
"disabled_reason": null,
"api_version": "1",
"consecutive_failures": 0,
"last_delivery_at": "2026-11-03T10:08:31Z",
"last_success_at": "2026-11-03T10:08:31Z",
"created_at": "2026-10-01T12:00:00Z",
"updated_at": "2026-10-01T12:00:00Z"
}Delete a webhook
DELETE /webhooks/{webhook_id} Enabled on reuseforge.com
Stops all future deliveries and drops pending retries. Idempotent.
| Property | Value |
|---|---|
| Operation ID | delete_webhook |
| Authentication | Bearer token holding webhooks:manage |
| Rate tier | write: not allowed anonymous, 120/min per token with a token |
| Python SDK | client.delete_webhook(...) |
| JavaScript SDK | client.deleteWebhook(...) |
| CLI | rf webhooks delete |
| MCP tool | not exposed as a tool |
| Deployments | reuseforge.com: enabled. Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
webhook_id | path | string | yes |
Responses
| Status | Meaning | Body |
|---|---|---|
| 204 No Content | Deleted. | no body |
| 401 | Missing, malformed, expired or revoked credentials. | Problem. Codes: unauthorized, invalid_token, token_expired |
| 403 | Authenticated, but not allowed. Most often a missing scope (insufficient_scope, with required_scopes), an exceeded spend limit, or a role on the owning account that is too low. Also version_quarantined (the security scan or a moderator blocked the version you asked for) and review_removed (a moderator removed your review). | Problem. Codes: insufficient_scope, forbidden, account_suspended, email_unverified, spend_limit_exceeded, purchase_required, quota_exceeded, version_quarantined, review_removed |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl -X DELETE 'https://reuseforge.com/api/v1/webhooks/whk_p8d3f6j1s5a2' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN"Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
client.delete_webhook("whk_p8d3f6j1s5a2")JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
await client.deleteWebhook("whk_p8d3f6j1s5a2");CLI
rf webhooks delete whk_p8d3f6j1s5a2
# rf webhooks delete <webhook_id>
# all options: rf webhooks delete --helpResponse 204 No Content with an empty body.
Rotate the signing secret
POST /webhooks/{webhook_id}/rotate-secret Enabled on reuseforge.com
Issues a new secret. For 24 hours every delivery carries two v1 signatures (new first, old second) so receivers can switch without downtime; after that only the new secret signs.
| Property | Value |
|---|---|
| Operation ID | rotate_webhook_secret |
| Authentication | Bearer token holding webhooks:manage |
| Rate tier | write: not allowed anonymous, 120/min per token with a token |
| Idempotency | Idempotency-Key header, optional. The SDKs and the CLI send one for you. |
| Python SDK | client.rotate_webhook_secret(...) |
| JavaScript SDK | client.rotateWebhookSecret(...) |
| CLI | rf webhooks rotate-secret |
| MCP tool | not exposed as a tool |
| Deployments | reuseforge.com: enabled. Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
webhook_id | path | string | yes | |
Idempotency-Key | header | string | no | Client-chosen unique key (1 to 255 printable ASCII characters, a UUID is ideal). A repeat of the same request with the same key within 24 hours returns the first response with Idempotent-Replayed: true. The same key with a different body returns 422 idempotency_key_reuse; a repeat while the first call is still running returns 409 idempotency_in_progress. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The webhook with the new one-time secret. | application/json: WebhookWithSecret |
| 401 | Missing, malformed, expired or revoked credentials. | Problem. Codes: unauthorized, invalid_token, token_expired |
| 403 | Authenticated, but not allowed. Most often a missing scope (insufficient_scope, with required_scopes), an exceeded spend limit, or a role on the owning account that is too low. Also version_quarantined (the security scan or a moderator blocked the version you asked for) and review_removed (a moderator removed your review). | Problem. Codes: insufficient_scope, forbidden, account_suspended, email_unverified, spend_limit_exceeded, purchase_required, quota_exceeded, version_quarantined, review_removed |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
| 503 | Temporarily unable to serve (maintenance or a dependency is down). Honour Retry-After. | Problem. Codes: unavailable |
Example
curl
curl -X POST 'https://reuseforge.com/api/v1/webhooks/whk_p8d3f6j1s5a2/rotate-secret' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.rotate_webhook_secret("whk_p8d3f6j1s5a2")
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.rotateWebhookSecret("whk_p8d3f6j1s5a2");
console.log(result.id);CLI
rf webhooks rotate-secret whk_p8d3f6j1s5a2
# rf webhooks rotate-secret <webhook_id>
# all options: rf webhooks rotate-secret --helpResponse 200 OK:
{
"id": "whk_ordersandpub",
"object": "webhook",
"url": "https://hooks.acme.example/reuseforge",
"description": "Production order and publish events",
"events": [
"item.published",
"item.version.published",
"order.created",
"order.funded",
"order.released"
],
"account": "acme",
"status": "enabled",
"disabled_reason": null,
"api_version": "1",
"consecutive_failures": 0,
"last_delivery_at": "2026-11-03T10:08:31Z",
"last_success_at": "2026-11-03T10:08:31Z",
"created_at": "2026-10-01T12:00:00Z",
"updated_at": "2026-11-03T10:30:00Z",
"secret": "whsec_w4x9c2v7b1n6m3q8r5t0y2u4i6o8p1as",
"previous_secret_expires_at": "2026-11-04T10:30:00Z"
}Send a test ping
POST /webhooks/{webhook_id}/test Enabled on reuseforge.com
Synchronously delivers a signed ping event and returns the delivery result (success or the failure you would see in production). Does not count toward auto-disable.
| Property | Value |
|---|---|
| Operation ID | test_webhook |
| Authentication | Bearer token holding webhooks:manage |
| Rate tier | write: not allowed anonymous, 120/min per token with a token |
| Python SDK | client.test_webhook(...) |
| JavaScript SDK | client.testWebhook(...) |
| CLI | rf webhooks test |
| MCP tool | not exposed as a tool |
| Deployments | reuseforge.com: enabled. Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
webhook_id | path | string | yes |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The delivery attempt. | application/json: WebhookDelivery |
| 401 | Missing, malformed, expired or revoked credentials. | Problem. Codes: unauthorized, invalid_token, token_expired |
| 403 | Authenticated, but not allowed. Most often a missing scope (insufficient_scope, with required_scopes), an exceeded spend limit, or a role on the owning account that is too low. Also version_quarantined (the security scan or a moderator blocked the version you asked for) and review_removed (a moderator removed your review). | Problem. Codes: insufficient_scope, forbidden, account_suspended, email_unverified, spend_limit_exceeded, purchase_required, quota_exceeded, version_quarantined, review_removed |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
| 503 | Temporarily unable to serve (maintenance or a dependency is down). Honour Retry-After. | Problem. Codes: unavailable |
Example
curl
curl -X POST 'https://reuseforge.com/api/v1/webhooks/whk_p8d3f6j1s5a2/test' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN"Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.test_webhook("whk_p8d3f6j1s5a2")
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.testWebhook("whk_p8d3f6j1s5a2");
console.log(result.id);CLI
rf webhooks test whk_p8d3f6j1s5a2
# rf webhooks test <webhook_id>
# all options: rf webhooks test --helpResponse 200 OK:
{
"id": "whd_pingdelivery2",
"object": "webhook_delivery",
"webhook_id": "whk_ordersandpub",
"event_id": "evt_pingevent222",
"event_type": "ping",
"attempt": 1,
"state": "succeeded",
"scheduled_at": "2026-11-03T10:31:00Z",
"delivered_at": "2026-11-03T10:31:00Z",
"response_status": 200,
"response_ms": 96,
"response_excerpt": "pong",
"error": null,
"next_attempt_at": null
}List deliveries
GET /webhooks/{webhook_id}/deliveries Enabled on reuseforge.com
Delivery attempts for the last 30 days, newest first, with the response status and an excerpt of the receiver's reply.
| Property | Value |
|---|---|
| Operation ID | list_webhook_deliveries |
| Authentication | Bearer token holding webhooks:manage |
| Rate tier | read: 60/min per IP anonymous, 600/min per token with a token |
| Python SDK | client.list_webhook_deliveries(...) |
| JavaScript SDK | client.listWebhookDeliveries(...) |
| CLI | rf webhooks deliveries |
| MCP tool | not exposed as a tool |
| Deployments | reuseforge.com: enabled. Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
webhook_id | path | string | yes | |
state | query | string: pending, retrying, succeeded, failed, exhausted | no | |
event_type | query | EventType | no | |
cursor | query | string | no | Opaque cursor from the previous page's next_cursor. |
limit | query | integer | no | Page size. Default 30. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | A page of deliveries. | application/json: WebhookDeliveryPage |
| 401 | Missing, malformed, expired or revoked credentials. | Problem. Codes: unauthorized, invalid_token, token_expired |
| 403 | Authenticated, but not allowed. Most often a missing scope (insufficient_scope, with required_scopes), an exceeded spend limit, or a role on the owning account that is too low. Also version_quarantined (the security scan or a moderator blocked the version you asked for) and review_removed (a moderator removed your review). | Problem. Codes: insufficient_scope, forbidden, account_suspended, email_unverified, spend_limit_exceeded, purchase_required, quota_exceeded, version_quarantined, review_removed |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl 'https://reuseforge.com/api/v1/webhooks/whk_p8d3f6j1s5a2/deliveries' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN"Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
for entry in client.list_webhook_deliveries("whk_p8d3f6j1s5a2"): # iterating fetches further pages on demand
print(entry["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
for await (const entry of client.listWebhookDeliveries("whk_p8d3f6j1s5a2")) { // iterating fetches further pages on demand
console.log(entry.id);
}CLI
rf webhooks deliveries whk_p8d3f6j1s5a2
# rf webhooks deliveries <webhook_id> [options]
# all options: rf webhooks deliveries --helpResponse 200 OK:
{
"data": [
{
"id": "whd_deliveryok22",
"object": "webhook_delivery",
"webhook_id": "whk_ordersandpub",
"event_id": "evt_orderfunded2",
"event_type": "order.funded",
"attempt": 1,
"state": "succeeded",
"scheduled_at": "2026-11-03T10:07:12Z",
"delivered_at": "2026-11-03T10:07:13Z",
"response_status": 200,
"response_ms": 184,
"response_excerpt": "ok",
"error": null,
"next_attempt_at": null
}
],
"next_cursor": null,
"has_more": false
}Redeliver an event
POST /webhooks/{webhook_id}/deliveries/{delivery_id}/redeliver Enabled on reuseforge.com
Re-sends the same event (same evt_ id, fresh signature timestamp) as a new delivery attempt. Receivers must treat the event id as the idempotency key. The credential needs the read scope of the event's type (see create_webhook); pings need none and count toward the per-minute ping limit of test_webhook. 409 invalid_state while the delivery is still being attempted or the endpoint is disabled, 410 gone once the event is older than 30 days.
| Property | Value |
|---|---|
| Operation ID | redeliver_webhook |
| Authentication | Bearer token holding webhooks:manage |
| Rate tier | write: not allowed anonymous, 120/min per token with a token |
| Idempotency | Idempotency-Key header, optional. The SDKs and the CLI send one for you. |
| Python SDK | client.redeliver_webhook(...) |
| JavaScript SDK | client.redeliverWebhook(...) |
| CLI | rf webhooks redeliver |
| MCP tool | not exposed as a tool |
| Deployments | reuseforge.com: enabled. Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
webhook_id | path | string | yes | |
delivery_id | path | string | yes | |
Idempotency-Key | header | string | no | Client-chosen unique key (1 to 255 printable ASCII characters, a UUID is ideal). A repeat of the same request with the same key within 24 hours returns the first response with Idempotent-Replayed: true. The same key with a different body returns 422 idempotency_key_reuse; a repeat while the first call is still running returns 409 idempotency_in_progress. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 202 Accepted | Scheduled. | application/json: WebhookDelivery |
| 401 | Missing, malformed, expired or revoked credentials. | Problem. Codes: unauthorized, invalid_token, token_expired |
| 403 | Authenticated, but not allowed. Most often a missing scope (insufficient_scope, with required_scopes), an exceeded spend limit, or a role on the owning account that is too low. Also version_quarantined (the security scan or a moderator blocked the version you asked for) and review_removed (a moderator removed your review). | Problem. Codes: insufficient_scope, forbidden, account_suspended, email_unverified, spend_limit_exceeded, purchase_required, quota_exceeded, version_quarantined, review_removed |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 409 | The request conflicts with current state (duplicate resource, wrong lifecycle state, idempotency conflict). | Problem. Codes: conflict, already_exists, invalid_state, idempotency_in_progress |
| 410 | The resource existed and is permanently unavailable (retracted item, expired plan). | Problem. Codes: gone, plan_expired |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
| 503 | Temporarily unable to serve (maintenance or a dependency is down). Honour Retry-After. | Problem. Codes: unavailable |
Example
curl
curl -X POST 'https://reuseforge.com/api/v1/webhooks/whk_p8d3f6j1s5a2/deliveries/whd_r2v7c4x9n6e1/redeliver' \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" \
-H "Idempotency-Key: $(uuidgen)"Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.redeliver_webhook("whk_p8d3f6j1s5a2", "whd_r2v7c4x9n6e1")
print(result["id"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.redeliverWebhook("whk_p8d3f6j1s5a2", "whd_r2v7c4x9n6e1");
console.log(result.id);CLI
rf webhooks redeliver whk_p8d3f6j1s5a2 whd_r2v7c4x9n6e1
# rf webhooks redeliver <webhook_id> <delivery_id>
# all options: rf webhooks redeliver --helpResponse 202 Accepted:
{
"id": "whd_redelivery222",
"object": "webhook_delivery",
"webhook_id": "whk_ordersandpub",
"event_id": "evt_orderfunded2",
"event_type": "order.funded",
"attempt": 2,
"state": "pending",
"scheduled_at": "2026-11-03T10:40:00Z",
"delivered_at": null,
"response_status": null,
"response_ms": null,
"response_excerpt": null,
"error": null,
"next_attempt_at": "2026-11-03T10:40:00Z"
}