Getting started
Authentication
The management API uses Bearer tokens. Webhooks HookFlow forwards to you are signed so you can prove they came from us.
Bearer tokens
Send a token in the Authorization header of every request to /api/v1/*. Tokens are opaque strings; don't parse them.
GET /api/v1/endpoints HTTP/1.1
Host: api.hookflow.dev
Authorization: Bearer hf_at_Q2xhdWRlIHdhcyBoZXJlThere are two kinds of token:
- Access tokens (
hf_at_…) come fromPOST /api/v1/auth/token, last one hour and represent a user. Use them from scripts and the CLI. - API keys (
hf_live_…) are created under Settings → API keys or withPOST /api/v1/keys. They don't expire and represent the workspace. Use them from servers and CI.
Getting and refreshing a token
curl -X POST https://api.hookflow.dev/api/v1/auth/token \
-H "Content-Type: application/json" \
-d '{"email": "you@company.com", "password": "••••••••"}'
Before the access token expires, exchange the refresh token for a new pair. Refresh tokens are single-use.
curl -X POST https://api.hookflow.dev/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token": "hf_rt_dGhpcyBpcyBhIGRlbW8"}'API keys
The full key is shown once, when it is created. Store it in your secret manager; HookFlow keeps only a hash and the first characters so you can recognise it in the dashboard.
curl -X POST https://api.hookflow.dev/api/v1/keys \
-H "Authorization: Bearer $HOOKFLOW_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "CI pipeline"}'
Revoke a key with DELETE /api/v1/keys/{id}. Requests using it fail with 401 within a few seconds.
Verifying forwarded webhooks
Every request HookFlow sends to your endpoint includes X-HookFlow-Signature and X-HookFlow-Timestamp. The signature is an HMAC-SHA256 of timestamp + "." + raw_body, keyed with the endpoint's signing secret (whsec_…).
import hashlib, hmac, time
def verify(raw_body: bytes, timestamp: str, signature: str, secret: str) -> bool:
if abs(time.time() - int(timestamp)) > 300: # reject replays older than 5 min
return False
expected = hmac.new(secret.encode(), f"{timestamp}.".encode() + raw_body,
hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)Errors
Authentication failures return a JSON error body:
{ "error": { "status": 401, "message": "Incorrect email or password" } }A request with a missing, malformed or expired token gets 401. Access tokens are valid for one hour; use the refresh token to obtain a new pair before then.