BASE URL https://api.hookstride.comCopy
BEFORE YOU BUILD What is Hookstride, and when should you use it? One transport and retry layer for application events—not a product tied to payments, framework, or industry.
WHAT Webhook delivery infrastructure Hookstride accepts custom events such as sensor.temperature.high, message.created or task.updated, stores them, and delivers signed HTTP POST requests to your registered destinations. Eligible failures are retried and attempts are recorded.
WHY Receivers are not always available When a downstream service times out or returns a transient error, you should not need to build your own queue, retry scheduler, and delivery dashboard.
Your backend Creates the event → Hookstride Persists, signs and retries → Receiver Verifies and processes
Your responsibility: decide when events happen, create or choose receiving endpoints, verify delivery signatures, and process events idempotently. Hookstride's responsibility: reliably attempt delivery and show you the outcome. Read the plain-English explanation →
APPLICATION EVENTS Use Hookstride for events across your systems Your systems decide what constitutes an event and how to process it.
IOT & MONITORING sensor.temperature.highDeliver a device-gateway alert to your monitoring backend; MQTT-to-HTTP bridging remains outside Hookstride.
TEAM COLLABORATION task.updatedNotify a task automation or audit service after a committed change; your app still manages live collaboration.
NOTIFICATIONS notification.requestedSignal an email, SMS, or push worker; Hookstride is not the message sender.
AI & AUTOMATION document.processedTrigger downstream processing when a service finishes a job.
LOGISTICS vehicle.arrivedUpdate a tracking service when a vehicle reaches a checkpoint.
BOOKINGS booking.confirmedNotify scheduling or operations after a reservation changes state.
WHEN TO USE IT Delivery when an event matters Hookstride handles transport, not your application's business logic. Your receiver remains responsible for verifying signatures and processing duplicate deliveries safely.
YOUR BACKEND Defines the event Commit the business action, then submit an event to Hookstride.
HOOKSTRIDE Delivers and retries Store the event, sign HTTP requests, monitor attempts and retry eligible failures.
GET STARTED Integrate in three steps The core flow for sending your first signed webhook. You own the receiver and its business logic.
01 ◎
Create an account We create your project automatically.
Create your workspace → 02 ↗
Add a destination Create or choose a POST receiver (not your homepage), then enter its public URL.
Add your URL → 03 ⌘
Generate a key & send Copy your API key, then send your first event with one request.
Get your key → DEVELOPER TOOLS CLI & interactive console Inspect projects and deliveries from your terminal.
Install Copy uv tool install hookstride-cliCLI: run a command Copy printf 'Control token: '
read -rs HOOKSTRIDE_CONTROL_TOKEN
printf '\n'
export HOOKSTRIDE_CONTROL_TOKEN
hookstride --env production projects list
hookstride --env production projects use PROJECT_UUID
hookstride --env production events listInteractive console Copy hookstride --env production console
auth login
show projects
use PROJECT_UUID
show events
exitUse a Control API token from your dashboard, not a webhook API key. The CLI defaults to development; --env production selects production. The console prompt shows [production] or [production:project-id]. Install from PyPI after the first CLI package release.
SENDING Send an event from your backend Your server calls Hookstride after the business action succeeds. Keep credentials out of the browser and mobile app.
Before you send Create a destination under Destinations . Save the returned endpoint UUID and signing secret. Then create an API key under API keys .
Set HOOKSTRIDE_API_KEY and HOOKSTRIDE_ENDPOINT_ID in your server's environment. The endpoint UUID identifies which destination receives this event; it is different from your API key.
cURL: create a receiver destination Copy curl -X POST 'https://api.hookstride.com/api/v1/endpoints/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Task notifications","url":"https://your-app.example.com/webhooks/hookstride"}'cURL: send a booking-confirmed event Copy curl -X POST 'https://api.hookstride.com/api/v1/events/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: booking-BK-1042-confirmed' \
-d '{
"endpoint": "DESTINATION_UUID",
"event_type": "booking.confirmed",
"payload": {
"booking_id": "BK-1042",
"property_id": "PROP-21",
"status": "confirmed"
}
}'cURL: task collaboration event Copy curl -X POST 'https://api.hookstride.com/api/v1/events/' -H "Authorization: Bearer $HOOKSTRIDE_API_KEY" -H 'Content-Type: application/json' -H 'Idempotency-Key: task-T-104-updated' -d '{"endpoint":"DESTINATION_UUID","event_type":"task.updated","payload":{"task_id":"T-104","status":"done"}}'cURL: IoT gateway alert Copy curl -X POST 'https://api.hookstride.com/api/v1/events/' -H "Authorization: Bearer $HOOKSTRIDE_API_KEY" -H 'Content-Type: application/json' -H 'Idempotency-Key: alert-SENSOR-7-20261103' -d '{"endpoint":"DESTINATION_UUID","event_type":"sensor.temperature.high","payload":{"device_id":"SENSOR-7","temperature_c":42.8}}'Python / Django backend Copy import os
import requests
def notify_booking_confirmed(booking):
response = requests.post(
"https://api.hookstride.com/api/v1/events/",
headers={
"Authorization": "Bearer " + os.environ["HOOKSTRIDE_API_KEY"],
"Idempotency-Key": f"booking-{booking.pk}-confirmed",
},
json={
"endpoint": os.environ["HOOKSTRIDE_ENDPOINT_ID"],
"event_type": "booking.confirmed",
"payload": {
"booking_id": str(booking.pk),
"property_id": str(booking.property_id),
"status": "confirmed",
},
},
timeout=10,
)
response.raise_for_status()
return response.json()JavaScript / Node.js backend Copy const response = await fetch("https://api.hookstride.com/api/v1/events/", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.HOOKSTRIDE_API_KEY,
"Content-Type": "application/json",
"Idempotency-Key": "booking-BK-1042-confirmed",
},
body: JSON.stringify({
endpoint: process.env.HOOKSTRIDE_ENDPOINT_ID,
event_type: "booking.confirmed",
payload: { booking_id: "BK-1042", property_id: "PROP-21", status: "confirmed" },
}),
});
if (!response.ok) throw new Error("Hookstride rejected the event");
console.log(await response.json());Call these examples only after the business state is committed. For critical events, use a durable outbox in your own database so a temporary network failure while calling Hookstride can be retried safely.
RECEIVING Build the destination that gets the webhook Hookstride sends signed HTTP POST requests to a URL you control. The destination is a receiving route—not your homepage or Hookstride's API.
Example: an online store receiver If your website is shop.example, you might implement https://api.shop.example/webhooks/hookstride/ in your backend. This is an illustrative URL: Hookstride cannot create the route in your application.
When registering a destination, save its one-time signing secret on the receiving server as HOOKSTRIDE_SIGNING_SECRET. This secret is not your project API key.
1. Django model: durable, duplicate-safe receipt Copy # models.py — run makemigrations and migrate after adding
from django.db import models
class HookstrideReceipt(models.Model):
event_id = models.UUIDField(primary_key=True)
event_type = models.CharField(max_length=160)
envelope = models.JSONField()
processed = models.BooleanField(default=False)
received_at = models.DateTimeField(auto_now_add=True)2. Django view: verify before accepting Copy # views.py
import hashlib
import hmac
import json
import os
import time
import uuid
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from .models import HookstrideReceipt
@csrf_exempt # External webhooks use HMAC, not browser CSRF tokens.
@require_POST
def receive_hookstride(request):
raw = request.body
timestamp = request.headers.get("X-Webhook-Timestamp", "")
signature = request.headers.get("X-Webhook-Signature", "")
event_id = request.headers.get("X-Webhook-Id", "")
try:
event_uuid = uuid.UUID(event_id)
seconds = int(timestamp)
if abs(time.time() - seconds) > 300:
return HttpResponse(status=401)
except (ValueError, TypeError):
return HttpResponse(status=401)
secret = os.environ["HOOKSTRIDE_SIGNING_SECRET"].encode()
digest = hmac.new(
secret, timestamp.encode() + b"." + raw, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, "v1=" + digest):
return HttpResponse(status=401)
try:
envelope = json.loads(raw)
if envelope["id"] != str(event_uuid):
return HttpResponse(status=400)
HookstrideReceipt.objects.get_or_create(
event_id=event_uuid,
defaults={
"event_type": envelope["type"],
"envelope": envelope,
},
)
except (ValueError, KeyError, TypeError):
return HttpResponse(status=400)
# A separate worker should process unprocessed receipts idempotently.
return JsonResponse({"received": True})3. Django URL Copy # urls.py
from django.urls import path
from .views import receive_hookstride
urlpatterns = [
path("api/v1/webhooks/hookstride/", receive_hookstride),
]This example durably accepts a verified event; it does not send tickets or emails by itself. Add a worker to process pending receipts and mark them processed after your business operation succeeds. Make that operation idempotent, recover unprocessed receipts, and return HTTP 5xx when you cannot safely persist the event.
FastAPI / Python receiver example This verifies a delivery but still needs a durable, unique-ID receipt store and background processor before production use.
Copy import hashlib
import hmac
import json
import os
import time
import uuid
from fastapi import FastAPI, HTTPException, Request
app = FastAPI()
@app.post("/webhooks/hookstride")
async def hookstride_receiver(request: Request):
body = await request.body()
timestamp = request.headers.get("x-webhook-timestamp", "")
signature = request.headers.get("x-webhook-signature", "")
event_id = request.headers.get("x-webhook-id", "")
try:
uuid.UUID(event_id)
if abs(time.time() - int(timestamp)) > 300:
raise ValueError("Expired signature")
except ValueError:
raise HTTPException(status_code=401, detail="Invalid timestamp or ID")
digest = hmac.new(
os.environ["HOOKSTRIDE_SIGNING_SECRET"].encode(),
timestamp.encode() + b"." + body,
hashlib.sha256,
).hexdigest()
if not hmac.compare_digest(signature, "v1=" + digest):
raise HTTPException(status_code=401, detail="Invalid signature")
try:
envelope = json.loads(body)
if not isinstance(envelope, dict) or envelope.get("id") != event_id:
raise ValueError("Webhook ID does not match signed payload")
except (ValueError, UnicodeError, TypeError):
raise HTTPException(status_code=400, detail="Invalid event body")
# TODO: durably store envelope["id"] as UNIQUE before returning 2xx,
# and process pending receipts idempotently with your worker.
return {"received": True}Express / Node.js receiver example Verify the raw body and signature before processing. Add a durable receipt store for production.
Copy import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
const app = express();
app.post("/webhooks/hookstride",
express.raw({ type: "application/json" }), async (req, res) => {
const timestamp = req.get("X-Webhook-Timestamp") || "";
const signature = req.get("X-Webhook-Signature") || "";
const eventId = req.get("X-Webhook-Id") || "";
const seconds = Number(timestamp);
if (!eventId || !Number.isInteger(seconds) ||
Math.abs(Date.now() / 1000 - seconds) > 300) {
return res.sendStatus(401);
}
const expected = "v1=" + createHmac(
"sha256", process.env.HOOKSTRIDE_SIGNING_SECRET
).update(timestamp + ".").update(req.body).digest("hex");
const actual = Buffer.from(signature);
const correct = Buffer.from(expected);
if (actual.length !== correct.length ||
!timingSafeEqual(actual, correct)) return res.sendStatus(401);
let envelope;
try {
envelope = JSON.parse(req.body.toString("utf8"));
} catch {
return res.sendStatus(400);
}
if (!envelope || typeof envelope !== "object" ||
Array.isArray(envelope) || envelope.id !== eventId) {
return res.sendStatus(400);
}
// TODO: durably store envelope.id with a unique constraint
// before returning 2xx; process with an idempotent worker.
return res.status(200).json({ received: true });
}
);Laravel / PHP receiver example Validate the timestamp, raw body and HMAC using the destination signing secret.
Copy // Laravel route handler (inside your controller)
$raw = $request->getContent();
$timestamp = $request->header('X-Webhook-Timestamp', '');
$provided = $request->header('X-Webhook-Signature', '');
$eventId = $request->header('X-Webhook-Id', '');
if (!ctype_digit($timestamp) ||
abs(time() - (int) $timestamp) > 300 ||
empty($eventId)) {
abort(401);
}
$expected = 'v1=' . hash_hmac(
'sha256',
$timestamp . '.' . $raw,
config('services.hookstride.signing_secret')
);
if (!hash_equals($expected, $provided)) abort(401);
$envelope = json_decode($raw, true);
if (!is_array($envelope) || !isset($envelope['id']) ||
!is_string($envelope['id']) || $envelope['id'] !== $eventId) {
abort(400);
}
// TODO: durably store $envelope['id'] as UNIQUE before returning 2xx,
// and process with a queued, idempotent job.
return response()->json(['received' => true]);CREDENTIALS & PROVIDERS Which keys go where? Do not mix API authentication keys, provider webhook verification secrets, and Hookstride's own keys. Here is where each value belongs.
Value Use and storage location Hookstride project API key whrk_... Generate in Dashboard → API keys . Store in the sending backend as HOOKSTRIDE_API_KEY, used for POST /api/v1/events/. Hookstride destination signing secret Shown once when you create a destination . Store in the receiving backend as HOOKSTRIDE_SIGNING_SECRET. It verifies requests signed by Hookstride. Stripe publishable pk_... and secret API key sk_... Publishable key: your checkout frontend . Secret API key: your backend's secret store . Neither is required for outgoing Hookstride events. Stripe webhook signing secret whsec_... Configure on the optional Stripe incoming source once its generated gateway URL is registered with Stripe; otherwise keep this secret in your own Stripe receiver. Paystack public and secret keys Public key in the checkout frontend; secret key in the backend. Paystack verifies webhooks with its full-access API secret key , so a Hookstride incoming connection requires security review. PayPal client ID, client secret and webhook ID Use the backend for confidential credentials. PayPal is not a native Hookstride incoming source yet. Didit API key and signing secret Keep these in your backend and verified Didit receiver. Didit is not a native Hookstride incoming source yet.
Using Hookstride to send your own events (available now) Example: ABC Store at shop.example accepts payments using Stripe. The store keeps its Stripe API key on its own backend, verifies the payment, then submits order.paid to Hookstride using only its Hookstride project API key. Hookstride delivers the event to the fulfillment endpoint, which verifies the destination signing secret.
No Stripe, PayPal, Didit or Paystack key is entered into Hookstride for this outgoing workflow. See sending events and receiving deliveries .
Optional: forwarding external Stripe or Paystack webhooks When the inbound gateway is enabled, open Dashboard → Connections and choose a provider and destination. For Stripe, register the generated inactive source URL with Stripe, obtain its whsec_... secret, save it in Hookstride and activate the source only after a route is configured.
STRIPE Use the whsec_... secret Do not paste the Stripe sk_... API key into the signing-secret field. Configure the verified webhook destination and an active route before enabling the source.
PAYSTACK Understand the access required Paystack signs webhooks with the full API secret key. Use the incoming connection only after security review, or verify Paystack events in your own backend and send outgoing Hookstride events.
What about PayPal or Didit? Hookstride has native incoming verifiers for Stripe, Paystack, GitHub and custom HMAC only. Do not select Custom to bypass another provider's signature verification. Receive and verify those webhooks in your own backend first.
AUTHENTICATION One API key for your app Generate a project key in your dashboard and pass it with each request.
Authorization header Authorization: Bearer $API_KEY
The dashboard handles account setup, projects, and key generation. Your backend only needs this key to send events.
For advanced account management, /control/v1/ uses a separate short-lived control token. Owners and administrators must complete MFA before receiving privileged credentials.
INBOUND GATEWAY One URL. Multiple destinations. Receive signed events from external services and route them to the right application.
01 Connect your sender Create a source in the dashboard and copy its gateway URL into the sender's webhook settings.
Connect a source → 02 Choose destinations Add any number of destinations. Route by event type, JSON field or a reference registered by your backend.
Manage destinations → 03 Let delivery run Each matching destination receives a signed event with retries, history and replay.
Explore inbound endpoints → Gateway URL format https://api.hookstride.com/ingest/v1/SOURCE_UUID/
Custom HMAC, GitHub, Stripe and Paystack verifiers are available. Other senders require a signed adapter.
Trusted reference routing Sharing one sender account across several applications? Register each operation's reference from its owning backend before the sender creates it. When a signed webhook arrives, the gateway routes it only to the destination bound to that reference—not to every application.
Use a project API key or a limited registration key for the trusted registration request. An untrusted application ID in a webhook payload cannot select the destination.
Register an application reference → RELIABILITY Delivery & security Every event is persisted before asynchronous delivery, with a signed request and audit trail.
AUTHENTICITY Verify every payload Outbound requests include X-Webhook-Id, X-Webhook-Event, X-Webhook-Timestamp and X-Webhook-Signature.
v1 = HMAC_SHA256(secret, timestamp + "." + raw_body)
Use the exact raw request body and compare signatures in constant time. Check timestamp tolerance and deduplicate by webhook ID.
RECOVERY Automatic backoff Network failures and transient HTTP responses are retried. The default retry schedule follows increasing delays:
30s 2m 10m 1h 6h Failed
Events are delivered at least once. Your receiver should be idempotent.
API REFERENCE · v1 The API reference Everything you need to integrate, manage, and troubleshoot.
⌕ / 42 endpoints
GET /api/v1/project/ Project ⌄ 200 response Project Get the project associated with your project API key.
Project key
No JSON request body required.
{
"id": "PROJECT_UUID",
"name": "Production",
"slug": "production",
"monthly_event_quota": 1000
}curl -X GET 'https://api.hookstride.com/api/v1/project/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/project/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/api/v1/project/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()GET /api/v1/usage/ Usage ⌄ 200 response Usage Return this month's event quota and delivery counters.
Project key
No JSON request body required.
{
"period_start": "2026-10-01",
"events_ingested": 125,
"delivery_attempts": 142,
"events_delivered": 120,
"events_failed": 3,
"event_quota": 1000,
"events_remaining": 875
}curl -X GET 'https://api.hookstride.com/api/v1/usage/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/usage/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/api/v1/usage/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()GET /api/v1/endpoints/ List endpoints ⌄ 200 response List endpoints List paginated delivery destinations for the current project.
Project key
No JSON request body required.
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": "ENDPOINT_UUID",
"name": "Production receiver",
"url": "https://example.com/webhooks",
"is_active": true
}
]
}curl -X GET 'https://api.hookstride.com/api/v1/endpoints/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/endpoints/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/api/v1/endpoints/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()POST /api/v1/endpoints/ Create endpoint ⌄ 201 response Create endpoint Register a public HTTPS webhook destination. The signing secret appears only in this response.
Project key
{
"name": "Production receiver",
"url": "https://example.com/webhooks"
}{
"id": "ENDPOINT_UUID",
"name": "Production receiver",
"url": "https://example.com/webhooks",
"is_active": true,
"signing_secret": "SECRET_SAVE_ONCE"
}curl -X POST 'https://api.hookstride.com/api/v1/endpoints/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Production receiver","url":"https://example.com/webhooks"}'const response = await fetch("https://api.hookstride.com/api/v1/endpoints/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY","Content-Type":"application/json"},
body: JSON.stringify({"name":"Production receiver","url":"https://example.com/webhooks"}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/api/v1/endpoints/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
json=json.loads("{\"name\":\"Production receiver\",\"url\":\"https://example.com/webhooks\"}"),
timeout=20,
)
response.raise_for_status()GET /api/v1/endpoints/{id}/ Endpoint details ⌄ 200 response Endpoint details Inspect one destination belonging to the authenticated project.
Project key
No JSON request body required.
{
"id": "ENDPOINT_UUID",
"name": "Production receiver",
"url": "https://example.com/webhooks",
"is_active": true
}curl -X GET 'https://api.hookstride.com/api/v1/endpoints/{id}/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/endpoints/{id}/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/api/v1/endpoints/{id}/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()PATCH /api/v1/endpoints/{id}/ Update endpoint ⌄ 200 response Update endpoint Change a destination's name, URL, or active status.
Project key
{
"name": "Updated receiver",
"is_active": true
}{
"id": "ENDPOINT_UUID",
"name": "Updated receiver",
"url": "https://example.com/webhooks",
"is_active": true
}curl -X PATCH 'https://api.hookstride.com/api/v1/endpoints/{id}/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Updated receiver","is_active":true}'const response = await fetch("https://api.hookstride.com/api/v1/endpoints/{id}/", {
method: "PATCH",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY","Content-Type":"application/json"},
body: JSON.stringify({"name":"Updated receiver","is_active":true}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"PATCH", "https://api.hookstride.com/api/v1/endpoints/{id}/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
json=json.loads("{\"name\":\"Updated receiver\",\"is_active\":true}"),
timeout=20,
)
response.raise_for_status()POST /api/v1/endpoints/{id}/rotate-secret/ Rotate signing secret ⌄ 200 response Rotate signing secret Issue a new endpoint signing secret. Rotation takes effect immediately.
Project key
No JSON request body required.
{
"endpoint": "ENDPOINT_UUID",
"signing_secret": "NEW_SECRET_SAVE_ONCE"
}curl -X POST 'https://api.hookstride.com/api/v1/endpoints/{id}/rotate-secret/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/endpoints/{id}/rotate-secret/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/api/v1/endpoints/{id}/rotate-secret/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()GET /api/v1/events/ List events ⌄ 200 response List events List paginated events. Filter by status, event_type, or endpoint UUID.
Project key
No JSON request body required.
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"id": "EVENT_UUID",
"event_type": "payment.completed",
"status": "delivered",
"attempt_count": 1
}
]
}curl -X GET 'https://api.hookstride.com/api/v1/events/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/events/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/api/v1/events/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()POST /api/v1/events/ Send an event ⌄ 201 response Send an event Persist and enqueue a signed webhook. Send an optional Idempotency-Key header to prevent duplicates.
Project key
{
"endpoint": "ENDPOINT_UUID",
"event_type": "payment.completed",
"payload": {
"payment_id": "pay_123",
"amount": 250,
"currency": "GHS"
}
}{
"id": "EVENT_UUID",
"endpoint": "ENDPOINT_UUID",
"event_type": "payment.completed",
"status": "pending",
"attempt_count": 0,
"attempts": []
}curl -X POST 'https://api.hookstride.com/api/v1/events/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"endpoint":"ENDPOINT_UUID","event_type":"payment.completed","payload":{"payment_id":"pay_123","amount":250,"currency":"GHS"}}'const response = await fetch("https://api.hookstride.com/api/v1/events/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY","Content-Type":"application/json"},
body: JSON.stringify({"endpoint":"ENDPOINT_UUID","event_type":"payment.completed","payload":{"payment_id":"pay_123","amount":250,"currency":"GHS"}}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/api/v1/events/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
json=json.loads("{\"endpoint\":\"ENDPOINT_UUID\",\"event_type\":\"payment.completed\",\"payload\":{\"payment_id\":\"pay_123\",\"amount\":250,\"currency\":\"GHS\"}}"),
timeout=20,
)
response.raise_for_status()GET /api/v1/events/{id}/ Event details ⌄ 200 response Event details Retrieve an event with delivery attempts, errors, and timing information.
Project key
No JSON request body required.
{
"id": "EVENT_UUID",
"event_type": "payment.completed",
"status": "delivered",
"attempt_count": 1,
"attempts": [
{
"attempt_number": 1,
"status_code": 200,
"duration_ms": 110
}
]
}curl -X GET 'https://api.hookstride.com/api/v1/events/{id}/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/events/{id}/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/api/v1/events/{id}/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()POST /api/v1/events/{id}/replay/ Replay event ⌄ 201 response Replay event Requeue a delivered or permanently failed event as a new event linked to its original.
Project key
No JSON request body required.
{
"id": "NEW_EVENT_UUID",
"replayed_from": "EVENT_UUID",
"status": "pending",
"attempts": []
}curl -X POST 'https://api.hookstride.com/api/v1/events/{id}/replay/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/events/{id}/replay/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/api/v1/events/{id}/replay/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()GET /api/v1/stats/ Delivery statistics ⌄ 200 response Delivery statistics Retrieve totals, successful deliveries, failures, pending events, and success rate.
Project key
No JSON request body required.
{
"project": "PROJECT_UUID",
"events": {
"total": 100,
"delivered": 92,
"failed": 3,
"pending": 5,
"success_rate": 92
}
}curl -X GET 'https://api.hookstride.com/api/v1/stats/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/stats/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/api/v1/stats/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()GET /api/v1/sources/ List connected sources ⌄ 200 response List connected sources Retrieve your project's external and custom webhook sources.
Project key
No JSON request body required.
{
"results": [
{
"id": "SOURCE_UUID",
"name": "Shared provider",
"provider": "custom",
"routing": "rules"
}
]
}curl -X GET 'https://api.hookstride.com/api/v1/sources/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/sources/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/api/v1/sources/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()POST /api/v1/sources/ Connect a webhook source ⌄ 201 response Connect a webhook source Register a signed sender. Copy the returned ingress URL into its webhook settings.
Project key
{
"name": "Shared sender",
"provider": "custom",
"routing": "rules"
}{
"id": "SOURCE_UUID",
"name": "Shared sender",
"ingest_url": "https://example.com/ingest/v1/SOURCE_UUID/",
"signing_secret": "ONE_TIME_SECRET"
}curl -X POST 'https://api.hookstride.com/api/v1/sources/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Shared sender","provider":"custom","routing":"rules"}'const response = await fetch("https://api.hookstride.com/api/v1/sources/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY","Content-Type":"application/json"},
body: JSON.stringify({"name":"Shared sender","provider":"custom","routing":"rules"}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/api/v1/sources/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
json=json.loads("{\"name\":\"Shared sender\",\"provider\":\"custom\",\"routing\":\"rules\"}"),
timeout=20,
)
response.raise_for_status()PATCH /api/v1/sources/{id}/ Update source ⌄ 200 response Update source Pause or rename an existing incoming source.
Project key
{
"is_active": false
}{
"id": "SOURCE_UUID",
"name": "Shared sender",
"is_active": false
}curl -X PATCH 'https://api.hookstride.com/api/v1/sources/{id}/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"is_active":false}'const response = await fetch("https://api.hookstride.com/api/v1/sources/{id}/", {
method: "PATCH",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY","Content-Type":"application/json"},
body: JSON.stringify({"is_active":false}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"PATCH", "https://api.hookstride.com/api/v1/sources/{id}/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
json=json.loads("{\"is_active\":false}"),
timeout=20,
)
response.raise_for_status()POST /api/v1/sources/{id}/rotate-secret/ Rotate source secret ⌄ 200 response Rotate source secret Replace the stored source signing secret; sender updates must be coordinated.
Project key
{}{
"id": "SOURCE_UUID",
"rotated": true,
"signing_secret": "ONE_TIME_SECRET_FOR_CUSTOM"
}curl -X POST 'https://api.hookstride.com/api/v1/sources/{id}/rotate-secret/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'const response = await fetch("https://api.hookstride.com/api/v1/sources/{id}/rotate-secret/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY","Content-Type":"application/json"},
body: JSON.stringify({}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/api/v1/sources/{id}/rotate-secret/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
json=json.loads("{}"),
timeout=20,
)
response.raise_for_status()POST /api/v1/routes/ Create event routing rule ⌄ 201 response Create event routing rule Forward signed events to a project-owned destination matching an event type and optional field.
Project key
{
"source": "SOURCE_UUID",
"endpoint": "ENDPOINT_UUID",
"event_pattern": "order.*"
}{
"id": "RULE_UUID",
"source": "SOURCE_UUID",
"endpoint": "ENDPOINT_UUID"
}curl -X POST 'https://api.hookstride.com/api/v1/routes/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"source":"SOURCE_UUID","endpoint":"ENDPOINT_UUID","event_pattern":"order.*"}'const response = await fetch("https://api.hookstride.com/api/v1/routes/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY","Content-Type":"application/json"},
body: JSON.stringify({"source":"SOURCE_UUID","endpoint":"ENDPOINT_UUID","event_pattern":"order.*"}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/api/v1/routes/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
json=json.loads("{\"source\":\"SOURCE_UUID\",\"endpoint\":\"ENDPOINT_UUID\",\"event_pattern\":\"order.*\"}"),
timeout=20,
)
response.raise_for_status()GET /api/v1/routes/ List event routing rules ⌄ 200 response List event routing rules Review subscriptions controlling inbound fan-out.
Project key
No JSON request body required.
{
"results": []
}curl -X GET 'https://api.hookstride.com/api/v1/routes/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/routes/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/api/v1/routes/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()POST /api/v1/reference-bindings/ Register trusted reference ⌄ 201 response Register trusted reference Bind an external reference to one destination before the external event is created.
Project key
{
"source": "SOURCE_UUID",
"endpoint": "ENDPOINT_UUID",
"reference": "ORDER-123"
}{
"id": "BINDING_UUID",
"reference": "ORDER-123"
}curl -X POST 'https://api.hookstride.com/api/v1/reference-bindings/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"source":"SOURCE_UUID","endpoint":"ENDPOINT_UUID","reference":"ORDER-123"}'const response = await fetch("https://api.hookstride.com/api/v1/reference-bindings/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY","Content-Type":"application/json"},
body: JSON.stringify({"source":"SOURCE_UUID","endpoint":"ENDPOINT_UUID","reference":"ORDER-123"}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/api/v1/reference-bindings/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
json=json.loads("{\"source\":\"SOURCE_UUID\",\"endpoint\":\"ENDPOINT_UUID\",\"reference\":\"ORDER-123\"}"),
timeout=20,
)
response.raise_for_status()GET /api/v1/reference-bindings/ List trusted references ⌄ 200 response List trusted references View pre-registered event ownership bindings.
Project key
No JSON request body required.
{
"results": []
}curl -X GET 'https://api.hookstride.com/api/v1/reference-bindings/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/reference-bindings/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/api/v1/reference-bindings/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()GET /api/v1/inbound-events/ Incoming event history ⌄ 200 response Incoming event history Inspect accepted upstream events without exposing their payload by default.
Project key
No JSON request body required.
{
"results": [
{
"id": "MESSAGE_UUID",
"event_type": "order.created",
"route_count": 1
}
]
}curl -X GET 'https://api.hookstride.com/api/v1/inbound-events/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/inbound-events/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/api/v1/inbound-events/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()POST /api/v1/inbound-events/{id}/redispatch/ Redispatch an incoming event ⌄ 200 response Redispatch an incoming event Send a stored upstream event to newly matching destinations without duplicating prior deliveries.
Project key
No JSON request body required.
{
"id": "MESSAGE_UUID",
"routed": 1,
"duplicate": false
}curl -X POST 'https://api.hookstride.com/api/v1/inbound-events/{id}/redispatch/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/inbound-events/{id}/redispatch/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/api/v1/inbound-events/{id}/redispatch/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()GET /api/v1/ingress-stats/ Incoming event statistics ⌄ 200 response Incoming event statistics Monitor received, unmatched and delivered events in your project.
Project key
No JSON request body required.
{
"sources": 1,
"received": 120,
"unrouted": 2,
"outbound_deliveries": 118
}curl -X GET 'https://api.hookstride.com/api/v1/ingress-stats/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/ingress-stats/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/api/v1/ingress-stats/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()POST /api/v1/reference-registration-keys/ Create a limited registration key ⌄ 201 response Create a limited registration key Issue a one-time key for exactly one external source and one destination. Store it only on that app's backend.
Project key
{
"source": "SOURCE_UUID",
"endpoint": "DESTINATION_UUID",
"name": "Voting app"
}{
"id": "KEY_UUID",
"masked_key": "whrr_...abcd",
"token": "ONE_TIME_RESTRICTED_KEY",
"expires_at": "2027-01-28T00:00:00Z"
}curl -X POST 'https://api.hookstride.com/api/v1/reference-registration-keys/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"source":"SOURCE_UUID","endpoint":"DESTINATION_UUID","name":"Voting app"}'const response = await fetch("https://api.hookstride.com/api/v1/reference-registration-keys/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY","Content-Type":"application/json"},
body: JSON.stringify({"source":"SOURCE_UUID","endpoint":"DESTINATION_UUID","name":"Voting app"}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/api/v1/reference-registration-keys/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
json=json.loads("{\"source\":\"SOURCE_UUID\",\"endpoint\":\"DESTINATION_UUID\",\"name\":\"Voting app\"}"),
timeout=20,
)
response.raise_for_status()GET /api/v1/reference-registration-keys/ List limited registration keys ⌄ 200 response List limited registration keys View masked, expiring credentials for each app without exposing token values.
Project key
No JSON request body required.
{
"results": [
{
"id": "KEY_UUID",
"name": "Voting app",
"masked_key": "whrr_...abcd"
}
]
}curl -X GET 'https://api.hookstride.com/api/v1/reference-registration-keys/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/reference-registration-keys/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/api/v1/reference-registration-keys/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()POST /api/v1/reference-registration-keys/{id}/revoke/ Revoke a limited registration key ⌄ 200 response Revoke a limited registration key Immediately disable one app's registration access without affecting other destinations.
Project key
No JSON request body required.
{
"id": "KEY_UUID",
"is_active": false
}curl -X POST 'https://api.hookstride.com/api/v1/reference-registration-keys/{id}/revoke/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/reference-registration-keys/{id}/revoke/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/api/v1/reference-registration-keys/{id}/revoke/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()POST /ingest/v1/register-reference/ Register an application reference ⌄ 201 response Register an application reference Use the app's limited token to claim its own order, transaction or subscription reference before the provider emits an event. No source or destination ID needed.
Registration key
{
"reference": "ORDER-APP-123"
}{
"id": "BINDING_UUID",
"reference": "ORDER-APP-123"
}curl -X POST 'https://api.hookstride.com/ingest/v1/register-reference/' \
-H "Authorization: Bearer $HOOKSTRIDE_CONTROL_TOKEN" \
-H "Content-Type: application/json" \
-d '{"reference":"ORDER-APP-123"}'const response = await fetch("https://api.hookstride.com/ingest/v1/register-reference/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_CONTROL_TOKEN","Content-Type":"application/json"},
body: JSON.stringify({"reference":"ORDER-APP-123"}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/ingest/v1/register-reference/",
headers={"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
json=json.loads("{\"reference\":\"ORDER-APP-123\"}"),
timeout=20,
)
response.raise_for_status()GET /api/v1/destinations/{id}/delivery/ Inspect destination delivery format ⌄ 200 response Inspect destination delivery format See how a destination receives JSON and which authentication mode is configured. Secrets are never returned.
Project key
No JSON request body required.
{
"endpoint": "DESTINATION_UUID",
"payload_format": "envelope",
"field_map": {},
"auth_mode": "signed",
"credential_configured": false
}curl -X GET 'https://api.hookstride.com/api/v1/destinations/{id}/delivery/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY"const response = await fetch("https://api.hookstride.com/api/v1/destinations/{id}/delivery/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/api/v1/destinations/{id}/delivery/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
timeout=20,
)
response.raise_for_status()PUT /api/v1/destinations/{id}/delivery/ Configure destination delivery ⌄ 200 response Configure destination delivery Choose standard envelope, original JSON or bounded field mapping; optionally add an encrypted Bearer token or X-Api-Key. Every delivery still carries the destination's HMAC signature.
Project key
{
"payload_format": "mapped",
"field_map": {
"transaction.reference": "payload.data.reference",
"transaction.event": "event.type",
"app": {
"const": "my-application"
}
},
"auth_mode": "bearer",
"credential": "DESTINATION_TOKEN"
}{
"endpoint": "DESTINATION_UUID",
"payload_format": "mapped",
"field_map": {
"transaction.reference": "payload.data.reference",
"transaction.event": "event.type",
"app": {
"const": "my-application"
}
},
"auth_mode": "bearer",
"credential_configured": true
}curl -X PUT 'https://api.hookstride.com/api/v1/destinations/{id}/delivery/' \
-H "Authorization: Bearer $HOOKSTRIDE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"payload_format":"mapped","field_map":{"transaction.reference":"payload.data.reference","transaction.event":"event.type","app":{"const":"my-application"}},"auth_mode":"bearer","credential":"DESTINATION_TOKEN"}'const response = await fetch("https://api.hookstride.com/api/v1/destinations/{id}/delivery/", {
method: "PUT",
headers: {"Authorization":"Bearer YOUR_PROJECT_API_KEY","Content-Type":"application/json"},
body: JSON.stringify({"payload_format":"mapped","field_map":{"transaction.reference":"payload.data.reference","transaction.event":"event.type","app":{"const":"my-application"}},"auth_mode":"bearer","credential":"DESTINATION_TOKEN"}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"PUT", "https://api.hookstride.com/api/v1/destinations/{id}/delivery/",
headers={"Authorization":"Bearer YOUR_PROJECT_API_KEY"},
json=json.loads("{\"payload_format\":\"mapped\",\"field_map\":{\"transaction.reference\":\"payload.data.reference\",\"transaction.event\":\"event.type\",\"app\":{\"const\":\"my-application\"}},\"auth_mode\":\"bearer\",\"credential\":\"DESTINATION_TOKEN\"}"),
timeout=20,
)
response.raise_for_status()POST /control/v1/auth/email/start/ Request an email sign-in code ⌄ 200 response Request an email sign-in code Send a single-use, 10-minute code. Optionally set a workspace name when registering; passwords are not supported.
Public
{
"email": "dev@example.com",
"workspace_name": "Acme"
}{
"challenge": "EMAIL_CHALLENGE_UUID",
"expires_in": 600
}curl -X POST 'https://api.hookstride.com/control/v1/auth/email/start/' \
-H "Content-Type: application/json" \
-d '{"email":"dev@example.com","workspace_name":"Acme"}'const response = await fetch("https://api.hookstride.com/control/v1/auth/email/start/", {
method: "POST",
headers: {},
body: JSON.stringify({"email":"dev@example.com","workspace_name":"Acme"}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/control/v1/auth/email/start/",
headers={},
json=json.loads("{\"email\":\"dev@example.com\",\"workspace_name\":\"Acme\"}"),
timeout=20,
)
response.raise_for_status()POST /control/v1/auth/email/verify/ Verify a one-time email code ⌄ 202 response Verify a one-time email code Complete email verification. Privileged members must additionally complete MFA; the Next.js flow receives a 202 challenge, never a privileged token from email verification alone.
Public
{
"challenge": "EMAIL_CHALLENGE_UUID",
"code": "ABCD2345"
}{
"requires_mfa": true,
"enrollment_required": true,
"challenge": "MFA_CHALLENGE",
"expires_in": 600
}curl -X POST 'https://api.hookstride.com/control/v1/auth/email/verify/' \
-H "Content-Type: application/json" \
-d '{"challenge":"EMAIL_CHALLENGE_UUID","code":"ABCD2345"}'const response = await fetch("https://api.hookstride.com/control/v1/auth/email/verify/", {
method: "POST",
headers: {},
body: JSON.stringify({"challenge":"EMAIL_CHALLENGE_UUID","code":"ABCD2345"}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/control/v1/auth/email/verify/",
headers={},
json=json.loads("{\"challenge\":\"EMAIL_CHALLENGE_UUID\",\"code\":\"ABCD2345\"}"),
timeout=20,
)
response.raise_for_status()POST /control/v1/auth/google/verify/ Verify a Google sign-in identity token ⌄ 202 response Verify a Google sign-in identity token Verify a Google OIDC ID token using Google's JWKS and the configured client audience and one-time nonce. Privileged users receive an MFA challenge instead of a bearer token.
Public
{
"id_token": "GOOGLE_OIDC_ID_TOKEN",
"nonce": "OAUTH_ONE_TIME_NONCE"
}{
"requires_mfa": true,
"enrollment_required": false,
"challenge": "MFA_CHALLENGE",
"expires_in": 600
}curl -X POST 'https://api.hookstride.com/control/v1/auth/google/verify/' \
-H "Content-Type: application/json" \
-d '{"id_token":"GOOGLE_OIDC_ID_TOKEN","nonce":"OAUTH_ONE_TIME_NONCE"}'const response = await fetch("https://api.hookstride.com/control/v1/auth/google/verify/", {
method: "POST",
headers: {},
body: JSON.stringify({"id_token":"GOOGLE_OIDC_ID_TOKEN","nonce":"OAUTH_ONE_TIME_NONCE"}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/control/v1/auth/google/verify/",
headers={},
json=json.loads("{\"id_token\":\"GOOGLE_OIDC_ID_TOKEN\",\"nonce\":\"OAUTH_ONE_TIME_NONCE\"}"),
timeout=20,
)
response.raise_for_status()POST /control/v1/auth/logout/ Sign out ⌄ 204 response Sign out Revoke the current control token. Returns no response body.
Control token
No JSON request body required.
204 No Contentcurl -X POST 'https://api.hookstride.com/control/v1/auth/logout/' \
-H "Authorization: Bearer $HOOKSTRIDE_CONTROL_TOKEN"const response = await fetch("https://api.hookstride.com/control/v1/auth/logout/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/control/v1/auth/logout/",
headers={"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
timeout=20,
)
response.raise_for_status()GET /control/v1/me/ Your profile ⌄ 200 response Your profile Retrieve the authenticated user and their account memberships.
Control token
No JSON request body required.
{
"user": {
"id": 1,
"email": "dev@example.com"
},
"memberships": [
{
"account_name": "Acme",
"role": "owner"
}
]
}curl -X GET 'https://api.hookstride.com/control/v1/me/' \
-H "Authorization: Bearer $HOOKSTRIDE_CONTROL_TOKEN"const response = await fetch("https://api.hookstride.com/control/v1/me/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/control/v1/me/",
headers={"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
timeout=20,
)
response.raise_for_status()GET /control/v1/projects/ List projects ⌄ 200 response List projects List projects accessible to your account.
Control token
No JSON request body required.
[
{
"id": "PROJECT_UUID",
"name": "Production",
"role": "owner",
"monthly_event_quota": 1000
}
]curl -X GET 'https://api.hookstride.com/control/v1/projects/' \
-H "Authorization: Bearer $HOOKSTRIDE_CONTROL_TOKEN"const response = await fetch("https://api.hookstride.com/control/v1/projects/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/control/v1/projects/",
headers={"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
timeout=20,
)
response.raise_for_status()POST /control/v1/projects/ Create project ⌄ 201 response Create project Create an additional project within an account where you have admin access.
Control token
{
"account_id": "ACCOUNT_UUID",
"name": "Staging",
"slug": "staging"
}{
"id": "PROJECT_UUID",
"name": "Staging",
"slug": "staging",
"role": "owner"
}curl -X POST 'https://api.hookstride.com/control/v1/projects/' \
-H "Authorization: Bearer $HOOKSTRIDE_CONTROL_TOKEN" \
-H "Content-Type: application/json" \
-d '{"account_id":"ACCOUNT_UUID","name":"Staging","slug":"staging"}'const response = await fetch("https://api.hookstride.com/control/v1/projects/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_CONTROL_TOKEN","Content-Type":"application/json"},
body: JSON.stringify({"account_id":"ACCOUNT_UUID","name":"Staging","slug":"staging"}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/control/v1/projects/",
headers={"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
json=json.loads("{\"account_id\":\"ACCOUNT_UUID\",\"name\":\"Staging\",\"slug\":\"staging\"}"),
timeout=20,
)
response.raise_for_status()GET /control/v1/projects/{project_id}/ Project details ⌄ 200 response Project details Read the project configuration and subscription summary.
Control token
No JSON request body required.
{
"id": "PROJECT_UUID",
"name": "Production",
"monthly_event_quota": 1000,
"subscription": {
"plan_code": "free",
"status": "active"
}
}curl -X GET 'https://api.hookstride.com/control/v1/projects/{project_id}/' \
-H "Authorization: Bearer $HOOKSTRIDE_CONTROL_TOKEN"const response = await fetch("https://api.hookstride.com/control/v1/projects/{project_id}/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/control/v1/projects/{project_id}/",
headers={"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
timeout=20,
)
response.raise_for_status()GET /control/v1/projects/{project_id}/usage/ Project usage history ⌄ 200 response Project usage history Get current usage, up to 12 months of history, and subscription details.
Control token
No JSON request body required.
{
"current": {
"events_ingested": 125,
"event_quota": 1000
},
"history": [],
"subscription": {
"plan_code": "free",
"status": "active"
}
}curl -X GET 'https://api.hookstride.com/control/v1/projects/{project_id}/usage/' \
-H "Authorization: Bearer $HOOKSTRIDE_CONTROL_TOKEN"const response = await fetch("https://api.hookstride.com/control/v1/projects/{project_id}/usage/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/control/v1/projects/{project_id}/usage/",
headers={"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
timeout=20,
)
response.raise_for_status()GET /control/v1/projects/{project_id}/subscription/ Subscription details ⌄ 200 response Subscription details Read the project's current subscription status.
Control token
No JSON request body required.
{
"provider": "direct",
"plan_code": "free",
"status": "active"
}curl -X GET 'https://api.hookstride.com/control/v1/projects/{project_id}/subscription/' \
-H "Authorization: Bearer $HOOKSTRIDE_CONTROL_TOKEN"const response = await fetch("https://api.hookstride.com/control/v1/projects/{project_id}/subscription/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/control/v1/projects/{project_id}/subscription/",
headers={"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
timeout=20,
)
response.raise_for_status()GET /control/v1/projects/{project_id}/api-keys/ List API keys ⌄ 200 response List API keys List masked keys for a project. The raw secret is never returned again.
Control token
No JSON request body required.
[
{
"id": "KEY_UUID",
"name": "Production server",
"masked_key": "whrk_••••",
"is_active": true
}
]curl -X GET 'https://api.hookstride.com/control/v1/projects/{project_id}/api-keys/' \
-H "Authorization: Bearer $HOOKSTRIDE_CONTROL_TOKEN"const response = await fetch("https://api.hookstride.com/control/v1/projects/{project_id}/api-keys/", {
method: "GET",
headers: {"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"GET", "https://api.hookstride.com/control/v1/projects/{project_id}/api-keys/",
headers={"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
timeout=20,
)
response.raise_for_status()POST /control/v1/projects/{project_id}/api-keys/ Issue API key ⌄ 201 response Issue API key Create a project-scoped key. Store the raw key securely: it is shown only at issuance.
Control token
{
"name": "Production server",
"expires_days": 90
}{
"api_key": {
"id": "KEY_UUID",
"name": "Production server"
},
"key": "whrk_••••"
}curl -X POST 'https://api.hookstride.com/control/v1/projects/{project_id}/api-keys/' \
-H "Authorization: Bearer $HOOKSTRIDE_CONTROL_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Production server","expires_days":90}'const response = await fetch("https://api.hookstride.com/control/v1/projects/{project_id}/api-keys/", {
method: "POST",
headers: {"Authorization":"Bearer YOUR_CONTROL_TOKEN","Content-Type":"application/json"},
body: JSON.stringify({"name":"Production server","expires_days":90}),
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"POST", "https://api.hookstride.com/control/v1/projects/{project_id}/api-keys/",
headers={"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
json=json.loads("{\"name\":\"Production server\",\"expires_days\":90}"),
timeout=20,
)
response.raise_for_status()DELETE /control/v1/projects/{project_id}/api-keys/{key_id}/ Revoke API key ⌄ 204 response Revoke API key Immediately revoke a key. Revoked credentials cannot access the data API.
Control token
No JSON request body required.
204 No Contentcurl -X DELETE 'https://api.hookstride.com/control/v1/projects/{project_id}/api-keys/{key_id}/' \
-H "Authorization: Bearer $HOOKSTRIDE_CONTROL_TOKEN"const response = await fetch("https://api.hookstride.com/control/v1/projects/{project_id}/api-keys/{key_id}/", {
method: "DELETE",
headers: {"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
});
if (!response.ok) throw new Error("Request failed: "+response.status);import json
import requests
response = requests.request(
"DELETE", "https://api.hookstride.com/control/v1/projects/{project_id}/api-keys/{key_id}/",
headers={"Authorization":"Bearer YOUR_CONTROL_TOKEN"},
timeout=20,
)
response.raise_for_status()No matching endpoints. Try a different method, path or keyword.
ERRORS Errors & limits Predictable HTTP responses help integrations recover safely.
Status Meaning Action 400 Invalid request or parameters Correct the body, filter or URL. 401 Invalid or expired credentials Check your bearer token or API key. 403 Insufficient permissions Use an appropriately authorized role. 404 Unknown or inaccessible resource Check the ID and project scope. 409 Idempotency conflict Use new idempotency data or replay a terminal event. 429 Quota or rate limit exceeded Wait for reset or review plan usage.
Examples are illustrative and never contain real API keys.Read the signing and retry guide →