Praxsuite

Endpoint Security

Vincent Depassier · August 30, 2026

An endpoint is a public URL with no API key in front of it. Anyone who learns the address can POST to it. This page is about making sure only the sender you meant can get anything to happen.


What a request goes through

In order, and each step can stop it:

  POST arrives
     │
     ├─ endpoint exists and is Active?      no → 404
     ├─ origin allowed, if rules exist?     no → 403
     ├─ body within the plan's size limit?  no → 413
     ├─ signature valid, if configured?     no → 401
     │
     ▼
  event stored, then the automation runs

Two things worth noticing about that order. The origin check happens before the body is read, so a rejected origin costs nothing. And the event is stored after validation, so your event history is a list of requests that were legitimate — a flood of forged calls does not fill it.


Signature verification

The real authentication. The sender computes an HMAC of the request body using a secret you both hold, puts it in a header, and the gateway recomputes it and compares.

Configure three things:

Setting

Example

Header name

Stripe-Signature, X-Hub-Signature-256

Algorithm

HmacSha256 or HmacSha512

Secret

The shared secret, stored encrypted

The secret is never shown again after you set it, and it can be regenerated from the endpoint's menu — which is also how you rotate one.

Two formats, because the industry has two

Default — signs the raw body. The header carries the hex digest, optionally prefixed with sha256= or sha512=. This is what GitHub and most generic webhooks do.

X-Hub-Signature-256: sha256=7d38cdd689735b008b3c702edd92eea23791c5f6

Timestamp-dot-payload — signs "{timestamp}.{body}", and the header carries both:

Stripe-Signature: t=1614556823,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

This is Stripe's and Slack's shape. Pick the one your sender documents; getting it wrong produces a signature mismatch that looks exactly like a wrong secret.

The comparison itself is constant-time, so a wrong signature reveals nothing about how wrong it was.

`None` means anyone can call it. An endpoint with no signature configured accepts any POST from anyone who knows the URL. That is occasionally what you want — a public contact form whose automation only ever inserts a row — but it should be a decision, not an oversight. If the automation writes anything that matters, or costs anything to run, configure a signature.


The challenge handshake

Some services verify they own a URL before they will send anything to it. Meta's platforms do this: they GET your endpoint with hub.mode, hub.verify_token and hub.challenge, and expect the challenge echoed back as plain text.

Set a verify token on the endpoint and the gateway answers that handshake for you:

GET /{workspaceId}/endpoint/{endpointId}?hub.mode=subscribe
                                        &hub.verify_token=your-token
                                        &hub.challenge=1158201444
→ 200  1158201444

A mismatched token answers 403, and so does a GET to an endpoint with no verify token configured — the handshake is off unless you turn it on.


Origin rules

An origin rule restricts which websites' browsers may call an endpoint. It matters for one specific shape: a Sync endpoint called by JavaScript on your own site.

Pattern

Matches

https://app.example.com

exactly that origin

*.example.com

any subdomain of example.com

http://localhost:3000

local development

An endpoint with no origin rules accepts every origin. This is the one place in the gateway that fails open rather than closed, and the reason is that most endpoints are called by servers, which send no Origin header at all — defaulting to deny would break every webhook the moment the feature existed.

Which leads to the limitation worth stating plainly: origin rules are a browser control, not a security boundary. The Origin header is set by browsers and can be omitted or forged by anything that is not one. They stop your endpoint being called from someone else's web page; they do not stop it being called by curl. For that you need the signature.

Origins are validated when you save them, capped in count by your plan, and de-duplicated.

The other origin list

Credentials have their own, separate origin list:

List

Restricts

Endpoint origins

which sites may POST to that endpoint

Credential origins

which sites may use that API key

They are configured in different places and neither one implies the other. The credential list is what you use to pin a pk_live_ key to your own domain, so a copy of it lifted from your bundle does not work from somebody else's page. Same caveat applies: it constrains browsers, not servers.


Practical guidance

  • Always configure a signature on an endpoint whose automation writes. The URL will end up in a log, a config file, or a support ticket eventually.

  • Rotate by regenerating the secret, then updating the sender. There is a gap between the two where calls fail — do it when a few minutes of retries are acceptable, which for most webhook senders they are.

  • Use origin rules on Sync endpoints your site calls, and do not rely on them for anything else.

  • Deactivate rather than delete an endpoint you are retiring, so the event history survives.

  • Read the event list first when something breaks. It distinguishes "never arrived" from "arrived and failed", and those have completely different causes.


Next