Cloud Public APIs
API: Webhooks
Configure scraper run webhooks and verify signatures safely.
Webhook Configuration
POST /scrapers/:slug/runs accepts optional webhook settings:
webhook.urlwebhook.secretwebhook.events(run.succeeded,run.failed)
OpenSteer signs outgoing webhook payloads using x-opensteer-signature.
Signature Header Format
- Header name:
x-opensteer-signature - Format:
t=<unix_seconds>,v1=<hex_hmac> - Signed payload:
<timestamp>.<raw_request_body>
Queue Run Example
curl -X POST "https://api.opensteer.com/scrapers/my-scraper/runs" \
-H "x-api-key: osk_xxx" \
-H "Idempotency-Key: req_12345" \
-H "content-type: application/json" \
-d '{
"input": {},
"webhook": {
"url": "https://example.com/webhooks/scraper",
"secret": "whsec_xxx",
"events": ["run.succeeded", "run.failed"]
}
}'
Signature Verification Example (Node.js)
import { createHmac, timingSafeEqual } from "node:crypto"
export function verifyWebhookSignature({
body,
header,
secret,
}: {
body: string
header: string
secret: string
}): boolean {
const parts = Object.fromEntries(
header.split(",").map((part) => {
const [k, ...rest] = part.split("=")
return [k.trim(), rest.join("=").trim()]
})
)
const timestamp = parts.t
const signature = parts.v1
if (!timestamp || !signature) return false
const payload = `${timestamp}.${body}`
const expected = createHmac("sha256", secret).update(payload).digest("hex")
const actualBytes = Buffer.from(signature, "utf8")
const expectedBytes = Buffer.from(expected, "utf8")
if (actualBytes.length !== expectedBytes.length) return false
return timingSafeEqual(actualBytes, expectedBytes)
}
Covered Features
api:POST /scrapers/:slug/runs
