For the complete documentation index, see llms.txt. This page is also available as Markdown.

Webhook endpoints

Register HTTPS URLs to receive completion events.

Endpoints

MethodPathScopeCreditsDescription
POST/v1/webhook-endpointswebhooks:write0Create
GET/v1/webhook-endpointswebhooks:read0List
GET/v1/webhook-endpoints/{id}webhooks:read0Retrieve, incl. recent delivery status
DELETE/v1/webhook-endpoints/{id}webhooks:write0Delete

Create

ParameterTypeRequiredDescription
urlstringyesHTTPS only; must respond 2xx within 10 seconds
eventsstring[]yesEvent types to receive, or ["*"]
descriptionstringno
json
{ "object": "webhook_endpoint", "id": "whk_0plq8vn2ta", "url": "https://example.com/hooks/scispace", "events": ["search.completed", "search.failed"], "secret": "whsec_9f2b7a41c0d85e3f", "status": "enabled", "created_at": "2026-08-10T11:55:02Z" }

The secret is returned once

whsec_… appears only in the create response. Store it in your secret manager — you need it for every verification.

Verifying signatures

Each delivery carries:

Text
SciSpace-Signature: t=1754827200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

Compute HMAC-SHA256(secret, "{t}.{raw_request_body}") and compare in constant time. Reject if the timestamp is more than 5 minutes old — that check is what stops replay.

import hashlib, hmac, time def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool: parts = dict(p.split("=", 1) for p in header.split(",")) if abs(time.time() - int(parts["t"])) > tolerance: return False expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, parts["v1"])

Verify against the raw body

Signing covers the exact bytes we sent. If your framework parses JSON before you verify, capture the raw body first or every signature will fail.

Handling deliveries

  1. Verify the signature

  2. Return 2xx immediately — do the work asynchronously

  3. Deduplicate by event.id

  4. Treat the resource's status as truth; ignore arrival order

Retries and disabling

5 attempts at 1 m, 5 m, 30 m, 2 h, 12 h. Recent failures are visible on GET /v1/webhook-endpoints/{id}. An endpoint that fails every delivery for 7 consecutive days is disabled and the org owner is emailed.

Errors

StatuscodeWhen
400url_insecurenon-HTTPS URL
400parameter_invalidunknown event type
409endpoint_existsthat URL is already registered

webhook · jobs · streaming-deep-review

Last updated