Skip to content

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_Q2xhdWRlIHdhcyBoZXJl

There are two kinds of token:

  • Access tokens (hf_at_…) come from POST /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 with POST /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.