Security

What protects your webhooks

WebhookPush turns a public URL into a notification on your phone, and can send your answer back out again. Both of those directions are worth securing properly. Below is what is in the code today — named, not implied.

01

Accounts and sign-in

Everything that happens before you are you — how a password is stored, how a session is proven, and what a failed attempt is allowed to reveal.

Passwords are hashed with scrypt

Each password gets its own random salt and is stored only as a scrypt hash — never in plain text, never reversibly. Verification is a constant-time comparison, so the time a wrong password takes doesn't leak how much of it was right.

Access tokens are short-lived

AUTH-003

A signed access token is valid for one hour. Refresh tokens are opaque and stateful — only their hash is stored, and they rotate on every silent refresh, so a captured refresh token stops working once the real client uses its successor.

Sign-in codes expire in minutes and work once

AUTH-002

The one-time code that hands a browser session to the app is valid for two minutes and is consumed on first use. It is bound to the client with PKCE (S256), so intercepting the code alone is not enough to redeem it.

Identity comes only from a verified token

SEC-004

Every authenticated route derives the caller from a cryptographically verified Bearer token. An earlier header-based fallback that let a caller assert an identity to the public API was removed — asserting is no longer a thing the API accepts.

Signup can't be used to test whether you have an account

SEC-005

Signing up with an address that already exists returns exactly the same response as a fresh signup. Instead of telling the stranger, we email the real owner that someone tried. The HTTP response cannot be used as an account-existence oracle.

Verification and reset codes are stored hashed, and expire

Email-verification and password-reset codes are kept only as hashes with short lifetimes — a day for verification, an hour for a reset. Reading the database does not yield a usable code.

02

Keys and secrets

Long-lived credentials exist for headless callers — CI, scripts, MCP servers. They are the ones worth being strictest about.

API keys are shown once and stored hashed

A new key is displayed exactly once, at creation. Only its hash is persisted, so it cannot be recovered from our side — if it is lost, you revoke it and mint another. Keys are capped per account and each can be revoked independently.

An API key cannot mint another API key

Creating a key requires an interactive session, not a key. That closes the path where one leaked credential quietly becomes permanent access that survives revoking the original.

Production refuses to deploy with a weak signing secret

SEC-001

The token-signing secret is per-environment and validated at deploy time: production will not build with the development placeholder or with anything short. This is enforced by the deployment itself, not by a checklist.

03

Webhook ingestion

Your webhook URL is a public endpoint by nature — anyone who learns it can call it. These are the controls that decide what happens next.

A per-webhook secret, compared in constant time

Callers present a secret alongside the request. It is verified with a constant-time comparison so the check cannot be turned into a guessing oracle, and it is verified before anything is counted, stored or delivered.

Burst and daily limits, then automatic shutoff

A short-window burst guard blunts floods within seconds regardless of plan; a daily quota tied to your live plan bounds sustained volume; and a webhook that keeps hammering well past its quota is disabled automatically rather than being allowed to bleed cost. Edge throttling sits in front of all of it — the application-level limits are the backstop, not the only line.

Rejections are made as cheaply as possible

Unknown or disabled webhooks are rejected on a single lookup, and a bad secret is rejected before any counter is written. An attacker cannot make rejected traffic expensive.

04

Outbound callbacks

When you answer a notification, we POST back to your endpoint. That request has to be provable — and unrepeatable.

Every callback is signed

Callbacks carry an HMAC-SHA256 signature over the timestamp and the raw body, the same way major webhook providers sign their own deliveries. Your receiver can verify the request came from us and not from someone who guessed your URL.

A captured callback cannot be replayed

The timestamp is inside the signed string, not merely alongside it, so a recorded delivery cannot be re-sent later with a fresh timestamp — the signature would no longer match. Receivers are told to reject anything outside a five-minute window.

A slow receiver cannot hang us

REL-002

Outbound calls are bounded by a short timeout, so an endpoint that stops responding degrades that one callback instead of holding a request open.

An answer can only be given once

Replying to a message is a conditional write: the first tap wins and a second is refused. A question that has been answered cannot be quietly answered again.

05

The request itself

Hardening that applies to every call to the management API, regardless of who is making it.

State-changing requests must be JSON

SEC-003

Any request that writes must arrive as application/json. This blocks form-based cross-site request forgery and content-type confusion outright — a hostile page cannot make your browser submit a form that changes your account. (Ingestion is a separate service and intentionally accepts arbitrary webhook bodies.)

Public forms are throttled per sender

SEC-002

The contact and feedback forms send real email, which makes them a target for email-bombing and cost abuse. They are rate-limited per origin, and addresses are validated and length-bounded before anything is sent.

06

Your data

What we keep, for how long, and what leaving looks like.

Deletion removes stored payloads too

PRIV-002

Deleting your account purges the stored request payloads in object storage as well as the database rows. Expiry alone would have left payload objects behind — deletion is explicit about them.

Messages expire on their own

Every message carries a retention window from your plan and expires automatically once it passes. Keeping something longer is a deliberate act — archive it in the app.

Export and deletion are self-service

You can download everything we hold as JSON, and you can delete the account and all of its data yourself, from Settings, without contacting us first.

You choose the region

Each webhook is pinned to a processing region, so traffic is ingested in the zone you picked rather than wherever it happens to land.

Reporting a vulnerability

If you have found something, tell us before you tell anyone else and we will work it with you. Reach us through support. Please include what you did, what you saw, and anything needed to reproduce it.

Codes such as SEC-004 are our internal tracking ids, shown so a claim on this page maps to a specific change rather than to a slogan.