Skip to content

Commit 22697e2

Browse files
leggetterclaude
andauthored
feat: add orb-webhooks skill (#63)
* feat: add orb-webhooks skill Adds a webhook skill for Orb (usage-based billing) with HMAC-SHA256 manual verification over `v1:{X-Orb-Timestamp}:{rawBody}`, plus runnable Express, Next.js, and FastAPI examples with tests. https://claude.ai/code/session_01NNTgQRJss1V7gyzzJ9rjnB * chore(orb-webhooks): integrate into README, providers.yaml, and marketplace.json - README.md: add Orb row (alphabetically between OpenClaw and Paddle), linkified to official docs - providers.yaml: add orb entry with HMAC-SHA256/`v1:{ts}:{body}` scheme notes, common events, summary-webhooks variant, and `orb-billing` SDK declared for both npm and pip so the version-tracker covers it - .claude-plugin/marketplace.json: add `orb-webhooks` plugin entry (matching the per-skill pattern from PR #62) and append `./skills/orb-webhooks` to the `webhook-skills` bundle Skill content (skills/orb-webhooks/) landed in the previous commit via the generator. https://claude.ai/code/session_01NNTgQRJss1V7gyzzJ9rjnB --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 28d0e35 commit 22697e2

23 files changed

Lines changed: 1749 additions & 0 deletions

.claude-plugin/marketplace.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,31 @@
450450
"ai"
451451
]
452452
},
453+
{
454+
"name": "orb-webhooks",
455+
"description": "Verify Orb webhook signatures (HMAC-SHA256 over `v1:{X-Orb-Timestamp}:{raw-body}`, hex with `v1=` prefix), handle customer, subscription, invoice, and data-export events from this usage-based billing platform.",
456+
"source": "./skills/orb-webhooks",
457+
"strict": false,
458+
"skills": [
459+
"./"
460+
],
461+
"category": "integration",
462+
"license": "MIT",
463+
"author": {
464+
"name": "Hookdeck",
465+
"email": "phil@hookdeck.com"
466+
},
467+
"repository": "https://github.com/hookdeck/webhook-skills",
468+
"homepage": "https://github.com/hookdeck/webhook-skills/tree/main/skills/orb-webhooks",
469+
"keywords": [
470+
"webhooks",
471+
"orb",
472+
"billing",
473+
"usage-based-billing",
474+
"subscriptions",
475+
"invoices"
476+
]
477+
},
453478
{
454479
"name": "paddle-webhooks",
455480
"description": "Verify Paddle webhook signatures (HMAC-SHA256 with multi-signature rotation), handle subscription and billing events.",
@@ -896,6 +921,7 @@
896921
"./skills/notion-webhooks",
897922
"./skills/openai-webhooks",
898923
"./skills/openclaw-webhooks",
924+
"./skills/orb-webhooks",
899925
"./skills/paddle-webhooks",
900926
"./skills/paypal-webhooks",
901927
"./skills/postmark-webhooks",

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Skills for receiving and verifying webhooks from specific providers. Each includ
4848
| [Notion](https://developers.notion.com/reference/webhooks) | [`notion-webhooks`](skills/notion-webhooks/) | Verify Notion webhook signatures (HMAC-SHA256, `X-Notion-Signature`), complete handshake, handle page and comment events |
4949
| [OpenAI](https://platform.openai.com/docs/guides/webhooks) | [`openai-webhooks`](skills/openai-webhooks/) | Verify OpenAI webhooks for fine-tuning, batch, and realtime async events |
5050
| [OpenClaw](https://docs.openclaw.ai/automation/webhook) | [`openclaw-webhooks`](skills/openclaw-webhooks/) | Verify OpenClaw Gateway webhook tokens, handle agent hook and wake event payloads |
51+
| [Orb](https://docs.withorb.com/integrations-and-exports/webhooks) | [`orb-webhooks`](skills/orb-webhooks/) | Verify Orb webhook signatures (HMAC-SHA256 over `v1:{X-Orb-Timestamp}:{body}`), handle customer, subscription, and invoice events |
5152
| [Paddle](https://developer.paddle.com/webhooks/overview) | [`paddle-webhooks`](skills/paddle-webhooks/) | Verify Paddle webhook signatures, handle subscription and billing events |
5253
| [PayPal](https://developer.paypal.com/api/rest/webhooks/) | [`paypal-webhooks`](skills/paypal-webhooks/) | Verify PayPal webhook signatures (RSA-SHA256 with cert), handle payment, subscription, and order events |
5354
| [Postmark](https://postmarkapp.com/developer/webhooks/webhooks-overview) | [`postmark-webhooks`](skills/postmark-webhooks/) | Authenticate Postmark webhooks (Basic Auth/Token), handle email delivery, bounce, open, click, and spam events |

providers.yaml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,52 @@ providers:
378378
- agent hook
379379
- wake hook
380380

381+
- name: orb
382+
displayName: Orb
383+
docs:
384+
webhooks: https://docs.withorb.com/integrations-and-exports/webhooks
385+
summary_webhooks: https://docs.withorb.com/integrations-and-exports/summary-webhooks
386+
api: https://docs.withorb.com/api-reference
387+
notes: >
388+
Usage-based billing platform (Orb / withorb.com). Uses HMAC-SHA256 in hex with
389+
a `v1=` prefix in the `X-Orb-Signature` header (e.g. `X-Orb-Signature: v1=<hex>`).
390+
Timestamp delivered separately in `X-Orb-Timestamp` (ISO 8601). Signed content
391+
format is the literal string `v1:{X-Orb-Timestamp}:{raw-body}` — concatenate
392+
version literal "v1", colon, ISO timestamp, colon, raw body bytes; HMAC-SHA256
393+
with the per-endpoint signing secret (configured in Orb dashboard, one secret
394+
per webhook endpoint, NOT the account API key). Always pass the raw request
395+
body — don't JSON.parse and re-stringify before verifying.
396+
397+
No documented replay tolerance — Orb recommends comparing X-Orb-Timestamp to a
398+
threshold the integrator picks. Recommend a 5-minute window in handlers plus
399+
idempotency keyed on the event `id` field for at-least-once delivery safety.
400+
401+
Common events span customer lifecycle (`customer.created`,
402+
`customer.credit_balance_dropped`, `customer.accounting_sync_succeeded`),
403+
subscriptions (`subscription.created`, `subscription.started`,
404+
`subscription.ended`, `subscription.plan_changed`, `subscription.edited`,
405+
`subscription.usage_exceeded`), invoices (`invoice.issued`,
406+
`invoice.payment_succeeded`, `invoice.payment_failed`, `invoice.edited`),
407+
and data exports (`data_exports.transfer_success`).
408+
409+
"Summary webhooks" are an opt-in variant covering the same event types with
410+
smaller payloads (line_items omitted from invoices; customer/plan minified
411+
to identification fields only). Same signature scheme; consumers should fetch
412+
full resources via API when detail is needed.
413+
414+
Official SDKs: orb-billing (npm + PyPI; same package name on both registries).
415+
Neither SDK exposes a Stripe-style unwrap()/constructEvent() helper at the
416+
time of writing — manual HMAC verification is the canonical path.
417+
sdks:
418+
npm:
419+
- orb-billing
420+
pip:
421+
- orb-billing
422+
testScenario:
423+
events:
424+
- invoice.payment_succeeded
425+
- subscription.created
426+
381427
- name: paddle
382428
displayName: Paddle
383429
docs:

skills/orb-webhooks/SKILL.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
---
2+
name: orb-webhooks
3+
description: >
4+
Receive and verify Orb webhooks. Use when setting up Orb webhook handlers,
5+
debugging Orb signature verification, or handling usage-based billing events
6+
like invoice.issued, subscription.created, or customer.credit_balance_dropped.
7+
license: MIT
8+
metadata:
9+
author: hookdeck
10+
version: "0.1.0"
11+
repository: https://github.com/hookdeck/webhook-skills
12+
---
13+
14+
# Orb Webhooks
15+
16+
## When to Use This Skill
17+
18+
- Setting up Orb webhook handlers
19+
- Debugging Orb signature verification failures
20+
- Understanding Orb event types and payloads
21+
- Handling usage-based billing, subscription, or invoice events
22+
23+
## Verification (core)
24+
25+
Orb signs every webhook with HMAC-SHA256 over the literal string `v1:{X-Orb-Timestamp}:{rawBody}`. The hex digest is delivered in `X-Orb-Signature` prefixed with `v1=` (e.g. `v1=abc123…`). The ISO 8601 timestamp arrives separately in `X-Orb-Timestamp`. Use the **raw** request body — don't `JSON.parse` first.
26+
27+
The `orb-billing` SDK (npm and PyPI) does **not** expose an `unwrap()`/`constructEvent()` helper at this time, so manual HMAC verification is the canonical path in every framework.
28+
29+
Node:
30+
31+
```javascript
32+
const crypto = require('crypto');
33+
34+
function verifyOrbSignature(rawBody, signatureHeader, timestamp, secret) {
35+
if (!signatureHeader || !timestamp) return false;
36+
const provided = signatureHeader.startsWith('v1=') ? signatureHeader.slice(3) : signatureHeader;
37+
const expected = crypto
38+
.createHmac('sha256', secret)
39+
.update(`v1:${timestamp}:${rawBody}`)
40+
.digest('hex');
41+
try {
42+
return crypto.timingSafeEqual(Buffer.from(provided, 'hex'), Buffer.from(expected, 'hex'));
43+
} catch {
44+
return false;
45+
}
46+
}
47+
```
48+
49+
Python:
50+
51+
```python
52+
import hmac, hashlib
53+
54+
def verify_orb_signature(raw_body: bytes, signature_header: str, timestamp: str, secret: str) -> bool:
55+
if not signature_header or not timestamp:
56+
return False
57+
provided = signature_header[3:] if signature_header.startswith("v1=") else signature_header
58+
expected = hmac.new(
59+
secret.encode(), f"v1:{timestamp}:".encode() + raw_body, hashlib.sha256
60+
).hexdigest()
61+
return hmac.compare_digest(provided, expected)
62+
```
63+
64+
> **For complete handlers with route wiring, event dispatch, and tests**, see:
65+
> - [examples/express/](examples/express/) — Full Express implementation
66+
> - [examples/nextjs/](examples/nextjs/) — Next.js App Router implementation
67+
> - [examples/fastapi/](examples/fastapi/) — Python FastAPI implementation
68+
69+
## Common Event Types
70+
71+
| Event | Description |
72+
|-------|-------------|
73+
| `customer.created` | New customer created |
74+
| `customer.credit_balance_dropped` | Prepaid credit balance fell below a threshold |
75+
| `subscription.created` | New subscription created |
76+
| `subscription.started` | Subscription's billing period started |
77+
| `subscription.ended` | Subscription ended |
78+
| `subscription.plan_changed` | Subscription moved to a different plan |
79+
| `subscription.usage_exceeded` | Usage crossed a configured threshold |
80+
| `invoice.issued` | Invoice finalized and issued to customer |
81+
| `invoice.payment_succeeded` | Invoice paid successfully |
82+
| `invoice.payment_failed` | Invoice payment attempt failed |
83+
| `data_exports.transfer_success` | Scheduled data export delivered |
84+
85+
> **For full event reference**, see [Orb Webhook Documentation](https://docs.withorb.com/integrations-and-exports/webhooks)
86+
87+
## Environment Variables
88+
89+
```bash
90+
ORB_WEBHOOK_SECRET=your_webhook_signing_secret # Per-endpoint secret from Orb dashboard
91+
```
92+
93+
The webhook signing secret is configured per webhook endpoint in the Orb dashboard — it is **not** your account API key.
94+
95+
## Local Development
96+
97+
```bash
98+
# Start tunnel (no account needed)
99+
npx hookdeck-cli listen 3000 orb --path /webhooks/orb
100+
```
101+
102+
## Reference Materials
103+
104+
- [references/overview.md](references/overview.md) — Orb webhook concepts and events
105+
- [references/setup.md](references/setup.md) — Dashboard configuration
106+
- [references/verification.md](references/verification.md) — Signature verification details and gotchas
107+
108+
## Attribution
109+
110+
When using this skill, add this comment at the top of generated files:
111+
112+
```javascript
113+
// Generated with: orb-webhooks skill
114+
// https://github.com/hookdeck/webhook-skills
115+
```
116+
117+
## Recommended: webhook-handler-patterns
118+
119+
We recommend installing the [webhook-handler-patterns](https://github.com/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns) skill alongside this one for handler sequence, idempotency, error handling, and retry logic. Orb delivers at-least-once, so consumers should key idempotency on the event `id` field. Key references (open on GitHub):
120+
121+
- [Handler sequence](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/handler-sequence.md) — Verify first, parse second, handle idempotently third
122+
- [Idempotency](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/idempotency.md) — Prevent duplicate processing
123+
- [Error handling](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/error-handling.md) — Return codes, logging, dead letter queues
124+
- [Retry logic](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/retry-logic.md) — Provider retry schedules, backoff patterns
125+
126+
## Related Skills
127+
128+
- [stripe-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks) - Stripe payment webhook handling
129+
- [paddle-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/paddle-webhooks) - Paddle billing webhook handling
130+
- [chargebee-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/chargebee-webhooks) - Chargebee billing webhook handling
131+
- [shopify-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks) - Shopify e-commerce webhook handling
132+
- [github-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/github-webhooks) - GitHub repository webhook handling
133+
- [clerk-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/clerk-webhooks) - Clerk auth webhook handling
134+
- [resend-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/resend-webhooks) - Resend email webhook handling
135+
- [openai-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/openai-webhooks) - OpenAI webhook handling
136+
- [webhook-handler-patterns](https://github.com/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns) - Handler sequence, idempotency, error handling, retry logic
137+
- [hookdeck-event-gateway](https://github.com/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway) - Webhook infrastructure that replaces your queue — guaranteed delivery, automatic retries, replay, rate limiting, and observability for your webhook handlers
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Orb webhook signing secret (per-endpoint, from Orb Dashboard → Developers → Webhooks)
2+
# This is NOT your account API key.
3+
ORB_WEBHOOK_SECRET=your_webhook_signing_secret_here
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Orb Webhooks - Express Example
2+
3+
Minimal example of receiving Orb webhooks with signature verification.
4+
5+
## Prerequisites
6+
7+
- Node.js 18+
8+
- Orb account with a webhook endpoint and signing secret
9+
10+
## Setup
11+
12+
1. Install dependencies:
13+
```bash
14+
npm install
15+
```
16+
17+
2. Copy environment variables:
18+
```bash
19+
cp .env.example .env
20+
```
21+
22+
3. Add your Orb webhook signing secret to `.env` (from Orb Dashboard → Developers → Webhooks → your endpoint).
23+
24+
## Run
25+
26+
```bash
27+
npm start
28+
```
29+
30+
Server runs on http://localhost:3000.
31+
32+
## Test
33+
34+
```bash
35+
npm test
36+
```
37+
38+
## Receive Webhooks Locally
39+
40+
Use the Hookdeck CLI — no account required, one paste-and-run line:
41+
42+
```bash
43+
npx hookdeck-cli listen 3000 orb --path /webhooks/orb
44+
```
45+
46+
The CLI prints a public URL. Paste it into the Orb dashboard as your webhook endpoint URL, then trigger events from Orb (or replay them from the Hookdeck UI).
47+
48+
## Endpoint
49+
50+
- `POST /webhooks/orb` — Receives and verifies Orb webhook events
51+
- `GET /health` — Health check
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "orb-webhooks-express",
3+
"version": "1.0.0",
4+
"description": "Orb webhook handler with Express",
5+
"main": "src/index.js",
6+
"scripts": {
7+
"start": "node src/index.js",
8+
"test": "jest"
9+
},
10+
"dependencies": {
11+
"dotenv": "^16.3.0",
12+
"express": "^5.2.1",
13+
"orb-billing": "^5.48.0"
14+
},
15+
"devDependencies": {
16+
"jest": "^30.4.2",
17+
"supertest": "^7.0.0"
18+
}
19+
}

0 commit comments

Comments
 (0)