Agent skill
Spike API Webhooks Skill
Receive and verify Spike API webhooks. Use when setting up Spike API (unified health and wearables data API) webhook handlers, debugging X-Body-Signature verification, or handling record_change, provider_integration_created and provider_integration_deleted events.
Install this skill
npx skills add hookdeck/webhook-skills --skill spike-api-webhooks
When to Use This Skill
- How do I receive Spike API webhooks?
- How do I verify Spike API webhook signatures (
X-Body-Signature)? - How do I handle
record_change,provider_orintegration_ created provider_events?integration_ deleted - Why is my Spike API webhook signature verification failing?
- Why does Spike API keep retrying my webhook?
Verification (core)
Spike API signs the raw request body with HMAC-SHA256, keyed with the shared secret from the admin console (used as-is, as UTF-8 bytes), and sends the bare lowercase hex digest (no prefix, no timestamp) as the whole X-Body-Signature header value. Always verify against the raw body, never re-serialized JSON. There is no Spike API webhook SDK, so verify manually:
const crypto = require('crypto');
function verifySpikeSignature(rawBody, signature, secret) {
// Check shape first: Buffer.from(x, 'hex') silently drops invalid characters
if (typeof signature !== 'string' || !/^[0-9a-f]{64}$/i.test(signature)) return false;
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest();
const received = Buffer.from(signature.toLowerCase(), 'hex');
return received.length === expected.length && crypto.timingSafeEqual(expected, received);
}
import hashlib, hmac, re
def verify(raw_body: bytes, signature: str, secret: str) -> bool:
if not re.fullmatch(r"[0-9a-fA-F]{64}", signature):
return False
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
# Compare bytes, never str
return hmac.compare_digest(expected.encode(), signature.lower().encode())
A missing, empty or malformed header, or a mismatch, is a 401. Read the body with express.raw({ type: '*/*' }) (Express), await request.arrayBuffer() (Next.js) or await request.body() (FastAPI), and parse JSON only after the check.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Common Event Types
The body is a JSON array; one delivery can batch several events, so loop over every element. Each element carries its own event_type.
| Event | Triggered When | Common Use Cases |
|---|---|---|
record_change | New or updated data arrives from a provider | Fetch the changed records (metrics, activity_types, earliest_record_start_at..latest_record_end_at) |
provider_ | A user connects a provider | Mark the integration active |
provider_ | A user's integration is deleted | Stop syncing, clean up |
Treat every parsed field as untrusted: check that the body is an array and that each element and nested field has the expected type before reading it. See references/overview.md.
Delivery Rules
Respond with exactly HTTP 200 within 30 seconds. Any other status, a network error or a timeout is retried up to 10 times (5 s, 2 min, 30 min, 2 h, then every 12 h); after that the event is discarded. Return 200 quickly and process asynchronously, and make handling idempotent.
Environment Variables
# Shared HMAC key from the Spike API admin console
SPIKE_API_WEBHOOK_SECRET=your_spike_api_webhook_secret
Local Development
For local webhook testing, run the Hookdeck CLI via npx — no install required:
npx hookdeck-cli listen 3000 spike-api --path /webhooks/spike-api
No account required — the CLI creates a guest account on first run and provides a local tunnel + web UI for inspecting requests. Express and Next.js examples use port 3000; the FastAPI example uses 8000.
Resources
- Overview - What Spike API webhooks are, event types and payloads
- Setup - Configure the endpoint and get the shared key
- Verification - Signature details and gotchas
- Express Example - Express.js implementation
- Next.js Example - Next.js App Router implementation
- FastAPI Example - Python FastAPI implementation