Agent skill
Cuuro Webhooks Skill
Receive and verify Cuuro webhooks, which are delivered by Svix. Use when setting up a Cuuro webhook endpoint, getting the whsec_ webhook secret, debugging svix-id / svix-timestamp / svix-signature verification, or handling due-diligence events like startup.created, analysis.created and analysis.completed.
Install this skill
npx skills add hookdeck/webhook-skills --skill cuuro-webhooks
Cuuro is an AI due-diligence platform for investors. An API integration can send a signed JSON webhook when a target company or an analysis is created, and when an analysis completes. Deliveries are sent through Svix, so they carry svix-id, svix-timestamp and svix-signature headers.
When to Use This Skill
- How do I receive Cuuro webhooks?
- How do I verify a Cuuro webhook signature (
svix-signature)? - Where do I find my Cuuro webhook secret (
whsec_...)? - How do I handle
startup.created,analysis.createdoranalysis.completedevents? - Where is the event type in a Cuuro webhook? (Top-level
event, besidedata.) - Why is my Cuuro webhook verification failing?
Verification (core)
| Header | Value |
|---|---|
svix-id | Identifies the message; retries reuse it |
svix-timestamp | Unix seconds |
svix-signature | One or more space-separated v1,<base64> entries |
Signed content is {svix-id}.{svix-timestamp}.{raw body}, HMAC-SHA256, base64 output. The key is the base64-decoded part of the whsec_... secret after the prefix. Compare against every v1, entry; ignore other versions. Cuuro names no headers itself: they are Svix's defaults. If svix-id is absent, read webhook-id, webhook-timestamp and webhook-signature instead, as Hookdeck does. The examples use the svix library (npm and PyPI):
import { Webhook } from 'svix';
// rawBody: exact request body (Buffer or string), NOT re-serialised JSON
function verifyCuuro(rawBody, headers, secret = process.env.CUURO_WEBHOOK_SECRET) {
const p = headers['svix-id'] ? 'svix' : 'webhook'; // fall back to the Standard Webhooks names
const [id, timestamp, signature] = ['id', 'timestamp', 'signature'].map((k) => headers[`${p}-${k}`]);
new Webhook(secret).verify(rawBody, {
'svix-id': id, 'svix-timestamp': timestamp, 'svix-signature': signature,
'webhook-id': id, 'webhook-timestamp': timestamp, 'webhook-signature': signature,
}); // throws on a bad signature or stale timestamp → respond 400
}
The library always enforces a fixed 5-minute timestamp window. Hookdeck retries and replays resend the original svix-timestamp, so the library rejects them once they are more than 5 minutes old. If you receive through Hookdeck and need late retries to pass, use the manual verifier in references/verification.md, which can drop or widen the window. In Express, read the body with express.raw({ type: '*/*' }) so any content type verifies, and parse JSON only after the check.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Type and Payload
The body is { data, event }: event is the event type and data.id is the company or analysis id. Only the id is sent, so fetch the full resource from the Cuuro API.
{ "data": { "id": "0191e29a-1644-7972-9466-18ada4a3636e" }, "event": "analysis.completed" }
Common Event Types
| Event | Fired when |
|---|---|
startup.created | A new target company is created; data.id is the company id |
analysis.created | A new analysis is created; data.id is the analysis id |
analysis.completed | An analysis completes; data.id is the analysis id |
These are the 3 documented types; the list may not be exhaustive, so ignore types you don't recognise. See references/overview.md.
Delivery Semantics
Cuuro's docs state no acknowledgement, timeout or retry policy. Return a 2xx quickly, process asynchronously, and deduplicate on svix-id (stable across retries). Return 400 for missing headers or a bad signature, and make handlers idempotent.
Environment Variables
CUURO_WEBHOOK_SECRET=whsec_xxxxx # full Svix signing secret for the webhook; handlers fail closed (500) without it
Local Development
# Express / Next.js (use 8000 for FastAPI). No install or account required.
npx hookdeck-cli listen 3000 cuuro --path /webhooks/cuuro
Set the printed URL as the webhook URL under Settings > Developers > your API integration > Webhooks.
Reference Materials
- references/overview.md - Event catalog and payload fields
- references/setup.md - Set the webhook URL, find the secret, test
- references/verification.md - Library and manual verification, gotchas, debugging