Developer docs
Build reliable webhook integrations with the contracts RawNak actually serves.
Every limit and route below is based on the current Flask implementation.
Quick start
Create a workspace, copy its generated company endpoint, send one JSON request, then inspect the event and delivery attempt in the dashboard.
- Create an account from Start free trial.
- Create or select a destination in the dashboard.
- Send a POST request to /webhook/<company>.
- Open Deliveries to inspect status, response code, latency, attempts and replay actions.
curl -X POST "https://api.rawnakwebhook.com/webhook/<company>" \
-H "Content-Type: application/json" \
-d '{"event":"order.created","id":"evt_123"}'Authentication and secrets
Dashboard APIs require the Bearer token issued by the authentication flow. Webhook signing uses the workspace signing secret and the X-Rawnak-Signature header.
- Keep the signing secret server-side; never put it in browser code.
- Use Security to rotate the inbound signing secret. The new secret is revealed only by the rotation response.
- The API also exposes a one-time initial-secret reveal flow at POST /api/security/reveal-initial-secret.
- The dashboard token is separate from the webhook signing secret.
Receiving webhooks
The public ingest route is POST /webhook/<company>. The Flask application applies MAX_CONTENT_LENGTH; the default configured value is 1,048,576 bytes.
- The request body is treated as the raw payload for signing and is stored as the event payload.
- The service accepts JSON payloads for normal dashboard workflows; Content-Type: application/json is used by the product examples.
- Successful acceptance returns HTTP 200. Invalid or inactive company lifecycle states are rejected before event insertion; exact error payloads are JSON with success:false, error and message.
- The company identifier is the endpoint segment after /webhook/.
Destinations and forwarding
Each destination has a URL, secret, timeout_seconds, max_attempts, event filters and optional rate/concurrency controls.
- Automatic delivery starts at attempt 1 and supports up to 3 attempts.
- Retry delays in the worker are 180 seconds after attempt 1 and 360 seconds after attempt 2.
- Destination timeouts are bounded by the API between 3 and 15 seconds; outbound requests disable redirects and use the secure HTTP layer.
- Retryable HTTP responses include 408, 425, 429, 500, 502, 503 and 504 while attempts remain.
- Replay is a new delivery run; Retry delivery advances an eligible retrying attempt.
curl -X POST "https://api.rawnakwebhook.com/webhook/<company>" \
-H "Content-Type: application/json" \
-d '{"event":"order.created","id":"evt_123"}'import crypto from "node:crypto";
const signature = crypto.createHmac("sha256", process.env.RAWNAK_SECRET)
.update(rawBody).digest("hex");
// Send: X-Rawnak-Signature: sha256=<signature>import hashlib, hmac
signature = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
headers = {"X-Rawnak-Signature": f"sha256={signature}"}$signature = hash_hmac('sha256', $rawBody, $secret);
$headers = ['X-Rawnak-Signature: sha256=' . $signature];Signature verification
RawNak signs the exact request body with HMAC-SHA256. The outbound header is X-Rawnak-Signature with the sha256= prefix.
- Read the raw bytes before parsing JSON.
- Compute HMAC-SHA256 with the workspace secret.
- Compare the hexadecimal digest using a constant-time comparison.
- Reject requests with a missing or mismatched signature.
import crypto from "node:crypto";
const signature = crypto.createHmac("sha256", process.env.RAWNAK_SECRET)
.update(rawBody).digest("hex");
// Send: X-Rawnak-Signature: sha256=<signature>import hashlib, hmac
signature = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
headers = {"X-Rawnak-Signature": f"sha256={signature}"}$signature = hash_hmac('sha256', $rawBody, $secret);
$headers = ['X-Rawnak-Signature: sha256=' . $signature];Plans and limits
The following values are the current PLAN_LIMITS and feature maps in webhook_server.py.
- Free trial: 1 destination, 1,000 monthly events, 5 events/second, 1 concurrent request; core feature only.
- Starter: 3 destinations, 10,000 monthly events, 5 events/second, 1 concurrent request; core feature only.
- Growth: 10 destinations, 100,000 monthly events, 100 events/second, 10 concurrent requests; Telegram alerts and rate limiting.
- Enterprise: 50 destinations, 1,000,000 monthly events, 1,000 events/second, 20 concurrent requests; payload filtering, bulk replay and team audit.
- Retention is configured per workspace in settings.retention_days and bounded by the backend between 1 and 3,650 days; the default is trial 7 days, Starter 30 days, Growth 90 days, Enterprise 180 days.
Errors reference
API errors use JSON responses. The exact message may depend on lifecycle, validation and plan state.
- 400 — invalid input, unsupported plan, malformed request or invalid signature payload.
- 401 — missing, expired or invalid authentication; also invalid admin password/MFA credentials.
- 402 — billing/lifecycle access is required before using the requested API.
- 403 — authenticated user lacks the required admin or company permission.
- 404 — resource or route not found.
- 409 — conflicting state such as an existing subscription or duplicate action.
- 429 — rate limit; retry after the Retry-After delay.
- 500/503 — temporary server issue; retry later or contact [email protected].
Security best practices
Treat webhook secrets, dashboard tokens and destination credentials as server-side secrets.
- Verify X-Rawnak-Signature against the raw request bytes.
- Use HTTPS for destinations and keep redirects disabled.
- Rotate signing secrets when a credential may have been exposed.
- Use the smallest plan feature set and least privilege needed.
- Do not log authorization headers, cookies or webhook secrets.
FAQ
The free trial lasts 7 days. Plan access and lifecycle rules are enforced by the backend, not by the browser. For implementation-specific behavior, use the endpoint and error references above.
Changelog
Release 4-1 adds this developer documentation surface and technical SEO preparation. Endpoint behavior remains owned by the backend implementation.