encore-webhook — Claude Code skill: inbound webhooks via api.raw
A skill that walks your agent through inbound webhook endpoints on api.raw(), with signature checks for Stripe, GitHub, Slack and more.
npx skills add encoredev/skills --skill encore-webhook -a claude-code
npx skills add encoredev/skills --skill webhook -a claude-codeWhat encore-webhook does
Reach for this skill when a third-party service POSTs events to a URL you own. The agent builds the endpoint with api.raw() from encore.dev/api, which hands you the Node.js-style request and response objects. That matters because signature verification needs the unparsed body, and the body is what proves the request really came from the provider.
The skill lays out the steps and a provider table. Stripe signs with the Stripe-Signature header and verifies through stripe.webhooks.constructEvent(rawBody, sig, secret). GitHub uses HMAC-SHA256 over X-Hub-Signature-256. Slack, Shopify and Twilio each have their own scheme. Signing secrets go through secret(...) from encore.dev/config rather than sitting in the source.
One rule stands out: answer 2xx fast. Senders retry on a non-2xx or a slow reply, so the skill pushes heavy work onto Pub/Sub after verification instead of doing it inside the handler. For typed JSON endpoints in your own service this is the wrong tool — use api(...) and the encore-api skill there.
What it helps with
- accept a Stripe webhook and verify its signature
- read the raw POST body before any parsing
- reject forged events from unknown senders
- acknowledge with 2xx and avoid duplicate deliveries
- hand the verified event off to Pub/Sub
Example prompts for the agent
- add a /webhooks/stripe endpoint with api.raw and verify Stripe-Signature
- I need a GitHub webhook that checks X-Hub-Signature-256 against the raw body
- receive Slack webhooks and publish each event to a topic
How to install encore-webhook
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill encore-webhook -a claude-codenpx skills add encoredev/skills --skill webhook -a claude-code2Manually: put the files in place
~/.claude/skills/webhook/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/webhook/
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill webhook -a codex2Manually: put the files in place
.agents/skills/webhook/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add encoredev/skills --skill webhook -a cursor2Manually: put the files in place
.cursor/skills/webhook/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install encoredev/skills webhook2Manually: put the files in place
.github/skills/webhook/SKILL.md1Skills CLI (Vercel)
npx skills add encoredev/skills --skill webhook -a gemini-cli2Platform CLI
gemini skills install https://github.com/encoredev/skills --path encore/webhookManually: put the files in place
.devin/skills/webhook/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add encoredev/skills --skill webhook -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about encore-webhook
How do I install encore-webhook in Claude?
Run npx skills add encoredev/skills --skill encore-webhook -a claude-code. The skill file lands at ~/.claude/skills/webhook/SKILL.md.
What does encore-webhook do?
It guides the agent through writing an api.raw() endpoint for inbound webhooks: reading the raw body, verifying the provider signature, and returning 2xx quickly.
How is it different from encore-api?
encore-api covers typed JSON endpoints inside your own service. api.raw is for webhooks and other low-level HTTP integrations.
Source text
SKILL.md · Apache-2.0
---
name: encore-webhook
description: Receive inbound webhooks from external services (Stripe, GitHub, Slack, Twilio, etc.) using `api.raw(...)` from `encore.dev/api`. The right skill any time the user names a third-party provider that POSTs events to a URL you own.
when_to_use: >-
User mentions a webhook, a /webhooks/* path, raw HTTP, `api.raw()`, accepting external callbacks, verifying webhook signatures (Stripe-Signature, X-Hub-Signature-256), reading the raw request body, parsing form-encoded payloads, or any time the user names a third-party provider that posts events — Stripe, GitHub, GitLab, Bitbucket, Shopify, Twilio, SendGrid, Mailgun, Auth0, Clerk, Slack, Discord, PayPal, Square. Use `encore-api` instead for typed JSON endpoints in your own service. Trigger phrases: "Stripe webhook", "GitHub webhook", "/webhooks/stripe", "raw HTTP endpoint", "api.raw", "verify the signature", "inbound webhook", "external callback".
---
# Encore Webhook Endpoints
## Instructions
Use `api.raw(...)` to receive inbound webhooks from third-party services. Raw endpoints give you direct access to the Node.js-style request and response objects, which you need for signature verification (the verification typically requires the unparsed raw body).
### 1. Import
```typescript
import { api } from "encore.dev/api";
```
### 2. Define the endpoint with `api.raw`
```typescript
export const stripeWebhook = api.raw(
{ expose: true, path: "/webhooks/stripe", method: "POST" },
async (req, res) => {
const sig = req.headers["stripe-signature"];
// Read raw body
const chunks: Buffer[] = [];
for await (const chunk of req) chunks.push(chunk);
const rawBody = Buffer.concat(chunks).toString("utf8");
// Verify signature, parse event...
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ received: true }));
}
);
```
### 3. Verify the signature
Most providers sign webhooks. Read the secret with `secret(...)` from `encore.dev/config` (see the `encore-secret` skill) and verify before trusting the payload:
```typescript
import { secret } from "encore.dev/config";
const stripeWebhookSecret = secret("StripeWebhookSecret");
// inside handler:
import Stripe from "stripe";
const stripe = new Stripe(stripeApiKey());
const event = stripe.webhooks.constructEvent(rawBody, sig, stripeWebhookSecret());
```
For GitHub, verify the HMAC-SHA256 in the `X-Hub-Signature-256` header against the raw body using your webhook secret.
## Common providers
| Provider | Signature header | Verification |
|---|---|---|
| Stripe | `Stripe-Signature` | `stripe.webhooks.constructEvent(rawBody, sig, secret)` |
| GitHub | `X-Hub-Signature-256` | HMAC-SHA256 over the raw body |
| Slack | `X-Slack-Signature` | HMAC-SHA256 over `v0:{timestamp}:{rawBody}` |
| Shopify | `X-Shopify-Hmac-Sha256` | HMAC-SHA256 (base64) over the raw body |
| Twilio | `X-Twilio-Signature` | HMAC-SHA1 over URL + sorted form fields |
## Always respond quickly
Webhook senders retry on non-2xx or slow responses. Acknowledge with a 2xx as soon as the payload is verified, then enqueue the actual work via Pub/Sub (see `encore-pubsub`) instead of doing it in the request handler.
```typescript
import { Topic } from "encore.dev/pubsub";
interface StripeEvent { id: string; type: string; data: unknown; }
const stripeEvents = new Topic<StripeEvent>("stripe-events", {
deliveryGuarantee: "at-least-once",
});
// inside the raw handler, after verification:
await stripeEvents.publish({ id: event.id, type: event.type, data: event.data });
res.writeHead(200); res.end();
```
## Guidelines
- Use `api.raw` *only* for webhooks and other low-level HTTP integrations.
- Always verify the provider's signature before trusting the payload.
- Always respond 2xx fast — push slow work onto Pub/Sub.
- Store the signing secret with `secret(...)`; never inline it.
- For typed JSON endpoints in your own service, use plain `api(...)` from the `encore-api` skill.
© encoredev · Apache-2.0 · encoredev/skills