API Reference

Complete reference for the HostWebhook REST API — ingress, webhook management, payload filters, and event operations.

Base URL

https://api.hostwebhook.com
ℹManagement webhooks require an authenticated session. Log in at hostwebhook.com or use hostwh login — the dashboard and CLI handle authentication automatically.

Every node lives in a workspace

⚠Creating a node or a trigger (any POST to a node collection such as /api/webhooks, /api/http-actions or /api/triggers) requires workspaceId in the body. Without it the API answers 400 with "code": "workspace_required". The API no longer picks your Default workspace for you: send the id of the workspace the node belongs to. You can list yours with GET /api/workspaces.

Ingress (webhook receiver)

ℹThis is the only public webhook. No authentication required — the ingress token in the URL identifies the webhook.
POST/api/in/:tokenpublic

Accept an incoming webhook. Responds immediately with 202 Accepted. The payload is stored and queued for delivery.

Path / query params

tokenstringrequiredIngress token from the webhook
HTTP 202 Accepted
{ "eventId": "64f1a2b3c4d5e6f7a8b9c0d1" }

Webhooks

List webhooks

GET/webhooks

List all webhooks for the authenticated user/organization.

[{ "id": "...", "name": "Stripe prod", "targetUrl": "https://...", "isActive": true, ... }]

Create webhook

POST/webhooks

Create a new webhook. Returns the webhook with its ingress URL and signing secret.

Request body

namestringrequiredFriendly label
targetUrlstringYour server's webhook handler URL (optional — leave empty for notification-only webhooks)
descriptionstringOptional description
isActivebooleanWhether it accepts deliveries (default: false — a new webhook starts paused; send true to create it live, or activate it later with PATCH /webhooks/:id or /toggle-active)
maxRetriesnumber0–20 (default: 3)
retryDelaySecondsnumberBase delay for exponential backoff (default: 60, min: 10)
rateLimitPerMinutenumberMax deliveries per minute — 0 = unlimited (default: 0)
incomingSignatureTypestringVerify inbound signatures: none | stripe | github | custom (default: none)
incomingSignatureSecretstringSecret used to verify inbound signatures (required if type ≠ none)
payloadFiltersPayloadFilter[]Rules to discard events before delivery
payloadTransformstringJSON template with {{path}} interpolation — reshapes the payload before it is delivered to targetUrl
degradedAfternumberConsecutive failures before health → degraded (default: 2)
downAfternumberConsecutive failures before health → down (default: 4)
{
  "id": "...",
  "name": "Stripe prod",
  "targetUrl": "https://yourapp.com/webhooks",
  "ingressToken": "tok_...",
  "signingSecret": "whsec_...",
  "isActive": false,
  "maxRetries": 3,
  "retryDelaySeconds": 60,
  "rateLimitPerMinute": 0,
  "incomingSignatureType": "stripe",
  "payloadFilters": [],
  "healthStatus": "healthy",
  "createdAt": "2024-05-01T12:00:00.000Z"
}

Get webhook

GET/webhooks/:id

Get a single webhook by ID.

Path / query params

idstringrequiredWebhook ID

Update webhook

PUT/webhooks/:id

Update any webhook field. All fields are optional — only provided fields are updated.

Path / query params

idstringrequiredWebhook ID

Request body

namestring
targetUrlstring
descriptionstring
maxRetriesnumber
retryDelaySecondsnumber
rateLimitPerMinutenumber
isActiveboolean
incomingSignatureTypestring
incomingSignatureSecretstring
payloadFiltersPayloadFilter[]Replaces the full filter list
payloadTransformstring | nullSet to null to remove the transform

Delete webhook

DELETE/webhooks/:id

Permanently delete a webhook and all its events.

Path / query params

idstringrequiredWebhook ID
204 No Content

Toggle active

PATCH/webhooks/:id/toggle-active

Toggle the webhook's isActive status (pause/activate).

Path / query params

idstringrequiredWebhook ID
{ "isActive": false }

Rotate ingress token

PATCH/webhooks/:id/rotate-token

Generate a new ingress token. The old ingress URL stops working immediately.

Path / query params

idstringrequiredWebhook ID
{ "ingressToken": "tok_newtoken..." }

Rotate signing secret

PATCH/webhooks/:id/rotate-signing-secret

Generate a new signing secret. The old secret stops working immediately.

Path / query params

idstringrequiredWebhook ID
{ "signingSecret": "whsec_newsecret..." }

Payload Filters

Payload filters discard events before delivery. Only events matching all rules are delivered — non-matching events are marked filtered.

PayloadFilter object

FieldTypeDescription
fieldstringDot-notation path (e.g. data.object.status)
operatorstringeq | neq | contains | gt | lt | exists | not_exists | in | not_in
valuestring?Comparison value — not required for exists / not_exists

Example

{
  "payloadFilters": [
    { "field": "type", "operator": "eq", "value": "invoice.paid" },
    { "field": "data.object.amount_paid", "operator": "gt", "value": "1000" }
  ]
}

Events

List events

GET/events

List events with optional filters. Returns paginated results.

Path / query params

webhookIdstringFilter by webhook
statusstringpending | delivered | retrying | failed | filtered
eventTypestringMatch the top-level type or event field in the payload
dateFromstringISO 8601 date (inclusive)
dateTostringISO 8601 date (inclusive)
pagenumberPage number (default: 1)
limitnumberResults per page (default: 15, max: 100)
{
  "data": [{ "id": "...", "status": "delivered", "createdAt": "...", ... }],
  "total": 142,
  "page": 1,
  "limit": 15
}

Get distinct event types

GET/events/types

Get all distinct event types (top-level type field) seen on a webhook. Used to populate filter dropdowns.

Path / query params

webhookIdstringFilter to one webhook
["payment_intent.succeeded", "payment_intent.created", "customer.subscription.updated"]

Get event

GET/events/:id

Get a single event with its full payload.

Path / query params

idstringrequiredEvent ID

Delivery attempts

GET/events/:eventId/deliveries

List all delivery attempts for an event, including response body, latency, and target info.

Path / query params

eventIdstringrequired
[{
  "id": "...",
  "targetUrl": "https://yourapp.com/webhooks",
  "attempt": 1,
  "statusCode": 200,
  "responseBody": "ok",
  "latencyMs": 143,
  "success": true,
  "attemptedAt": "2024-05-01T12:00:00.000Z"
}]

Replay event

POST/events/:eventId/replay

Manually replay an event. Creates a new delivery attempt with the original payload.

Path / query params

eventIdstringrequired
{ "ok": true }

Replay all failed

POST/events/replay-all

Replay all failed events for a webhook.

Path / query params

webhookIdstringrequiredQuery param
{ "queued": 12 }

Cancel event

POST/events/:eventId/cancel

Cancel a pending or retrying event. Cannot be undone, but can be replayed.

Path / query params

eventIdstringrequired
204 No Content

Delivery stats

GET/events/stats

Aggregate delivery stats for the current user.

Path / query params

webhookIdstringFilter to one webhook
{ "total": 1200, "delivered": 1180, "failed": 20, "successRate": 98.3 }

Latency stats

GET/events/latency

Latency percentiles (ms) for successful deliveries.

Path / query params

webhookIdstringFilter to one webhook
{ "avg": 210, "p95": 580, "p99": 1240 }

Routers

GET/routers

List all routers.

POST/routers

Create a router with conditional routing rules.

Request body

namestringrequiredRouter name
routesRoute[]Array of {field, operator, value, outputWebhookId}
PATCH/routers/:id

Update a router.

Path / query params

idstringrequired
DELETE/routers/:id

Delete a router.

Path / query params

idstringrequired
204 No Content

Filter Nodes

GET/filter-nodes

List all filter nodes.

POST/filter-nodes

Create a filter node.

Request body

namestringrequiredFilter name
filterModestring'and' | 'or' (default: 'and')
filtersFilter[]Array of {field, operator, value}
PATCH/filter-nodes/:id

Update a filter node.

Path / query params

idstringrequired
DELETE/filter-nodes/:id

Delete a filter node.

Path / query params

idstringrequired
204 No Content

Transform Nodes

GET/transform-nodes

List all transform nodes.

POST/transform-nodes

Create a transform node.

Request body

namestringrequiredTransform name
transformModestring'handlebars' | 'jq' | 'javascript'
templatestringTransform template string
PATCH/transform-nodes/:id

Update a transform node.

Path / query params

idstringrequired
DELETE/transform-nodes/:id

Delete a transform node.

Path / query params

idstringrequired
204 No Content

Schema Validators

GET/schema-validators

List all schema validators.

POST/schema-validators

Create a schema validator.

Request body

namestringrequiredValidator name
strictModebooleanReject payloads with extra fields (default: false)
fieldsField[]Array of {path, type, required, constraints}
PATCH/schema-validators/:id

Update a schema validator.

Path / query params

idstringrequired
DELETE/schema-validators/:id

Delete a schema validator.

Path / query params

idstringrequired
204 No Content

Delay Nodes

GET/delay-nodes

List all delay nodes.

POST/delay-nodes

Create a delay node.

Request body

namestringrequiredDelay name
delaySecondsnumberrequiredSeconds to delay before forwarding
PATCH/delay-nodes/:id

Update a delay node.

Path / query params

idstringrequired
DELETE/delay-nodes/:id

Delete a delay node.

Path / query params

idstringrequired
204 No Content

Merge Nodes

GET/merge-nodes

List all merge nodes.

POST/merge-nodes

Create a merge node.

Request body

namestringrequiredMerge name
modestring'wait_all' | 'first' (default: 'wait_all')
expectedBranchCountnumberNumber of branches to wait for
PATCH/merge-nodes/:id

Update a merge node.

Path / query params

idstringrequired
DELETE/merge-nodes/:id

Delete a merge node.

Path / query params

idstringrequired
204 No Content

Approval Nodes

GET/approval-nodes

List all approval nodes.

POST/approval-nodes

Create an approval node.

Request body

namestringrequiredApproval gate name
timeoutMinutesnumberAuto-action after timeout (default: 60)
autoActionOnTimeoutstring'approve' | 'reject' (default: 'reject')
notifyEmailsstring[]Emails to notify for approval
POST/approval-nodes/:id/approve

Approve a pending event at this gate.

Path / query params

idstringrequired
POST/approval-nodes/:id/reject

Reject a pending event at this gate.

Path / query params

idstringrequired
PATCH/approval-nodes/:id

Update an approval node.

Path / query params

idstringrequired
DELETE/approval-nodes/:id

Delete an approval node.

Path / query params

idstringrequired
204 No Content

Email Actions

GET/email-actions

List all email actions.

POST/email-actions

Create an email action.

Request body

namestringrequiredEmail action name
tostringrequiredRecipient email
subjectstringEmail subject (supports templates)
bodystringEmail body (supports templates)
triggerOnstring'success' | 'failure' | 'always'
PATCH/email-actions/:id

Update an email action.

Path / query params

idstringrequired
DELETE/email-actions/:id

Delete an email action.

Path / query params

idstringrequired
204 No Content

Gmail

GET/gmail-actions

List all Gmail nodes.

POST/gmail-actions

Create a Gmail node.

Request body

namestringrequiredGmail node name
credentialIdstringConnected Google account
operationstringOne of the 17 operations (default: 'send')
operationConfigobjectParameters for the chosen operation (supports templates)
aiEnabledbooleanExpose the 16-tool toolkit to an AI Node
triggerOnstring'success' | 'always'
PATCH/gmail-actions/:id

Update a Gmail node.

Path / query params

idstringrequiredGmail node ID
DELETE/gmail-actions/:id

Delete a Gmail node.

Path / query params

idstringrequiredGmail node ID
GET/gmail-actions/labels/:credentialId

List the account's Gmail labels.

Path / query params

credentialIdstringrequiredConnected Google account

HTTP Actions

GET/http-actions

List all HTTP actions.

POST/http-actions

Create an HTTP action.

Request body

namestringrequiredHTTP action name
urlstringrequiredTarget URL
methodstringGET | POST | PUT | PATCH | DELETE (default: POST)
headersobjectCustom headers
bodystringRequest body template
triggerOnstring'success' | 'failure' | 'always'
PATCH/http-actions/:id

Update an HTTP action.

Path / query params

idstringrequired
DELETE/http-actions/:id

Delete an HTTP action.

Path / query params

idstringrequired
204 No Content

MongoDB Actions

GET/mongo-actions

List all MongoDB actions.

POST/mongo-actions

Create a MongoDB action.

Request body

namestringrequiredAction name
connectionUristringrequiredMongoDB connection string (encrypted)
databasestringrequiredDatabase name
collectionstringrequiredCollection name
operationstring'insertOne' | 'updateOne' | 'replaceOne'
documentstringDocument template
PATCH/mongo-actions/:id

Update a MongoDB action.

Path / query params

idstringrequired
DELETE/mongo-actions/:id

Delete a MongoDB action.

Path / query params

idstringrequired
204 No Content

Notification Actions

GET/notification-actions

List all notification actions (Slack/Discord).

POST/notification-actions

Create a notification action.

Request body

namestringrequiredAction name
platformstringrequired'slack' | 'discord'
webhookUrlstringrequiredPlatform webhook URL
templatePresetstring'default' | 'detailed' | 'minimal' | 'custom'
triggerOnstring'success' | 'failure' | 'always'
PATCH/notification-actions/:id

Update a notification action.

Path / query params

idstringrequired
DELETE/notification-actions/:id

Delete a notification action.

Path / query params

idstringrequired
204 No Content

Google Sheets Actions

GET/sheets-actions

List all Google Sheets actions.

POST/sheets-actions

Create a Sheets action.

Request body

namestringrequiredAction name
spreadsheetIdstringrequiredGoogle Sheets spreadsheet ID
sheetNamestringSheet tab name (default: Sheet1)
operationstring'append_row' | 'update_cell'
columnsstring[]Template array mapping payload fields to columns
PATCH/sheets-actions/:id

Update a Sheets action.

Path / query params

idstringrequired
DELETE/sheets-actions/:id

Delete a Sheets action.

Path / query params

idstringrequired
204 No Content

Scheduled Workflows

GET/scheduled-workflows

List all scheduled webhooks.

POST/scheduled-workflows

Create a scheduled webhook (cron-based HTTP calls).

Request body

namestringrequiredName
cronExpressionstringrequiredCron expression (e.g. '0 */5 * * *')
urlstringTarget URL (or connect to a webhook)
methodstringHTTP method (default: POST)
headersobjectCustom headers
bodystringRequest body
isActivebooleanEnable/disable (default: true)
PATCH/scheduled-workflows/:id

Update a scheduled webhook.

Path / query params

idstringrequired
DELETE/scheduled-workflows/:id

Delete a scheduled webhook.

Path / query params

idstringrequired
204 No Content

Vector Stores

Everything the dashboard does under Settings → Vector Stores and in the Vector Store node, over REST. The plain-language guide is Vector Stores Quick Start; this section is the exact contract.

Catalog

GET/vector-stores/providers/embeddings

Embedding providers and their models: id, dimensions, max input tokens, price per million tokens, and whether the model is available on the managed backend.

GET/vector-stores/providers/backends

Storage backends: hostwebhook (managed), mongo_atlas, mongo_external, pgvector — with the config fields each one needs.

POST/vector-stores/test-backend

Test a bring-your-own backend before creating the store. Reports server version, vector support, and whether the index or table exists; for Mongo it returns the index JSON to create, for Postgres the SQL.

Request body

backendobjectrequired{ provider, config } — same shape as on create
dimensionsnumberVector size of the model you intend to use (needed to validate the index)
POST/vector-stores/setup-backend

pgvector only: idempotently create the extension, table and index the test reported missing, then re-test.

Request body

backendobjectrequired{ provider: 'pgvector', config }
dimensionsnumberrequiredVector size for the column

Stores

GET/vector-stores

List the organization's stores with their stats.

POST/vector-stores

Create a store. Provider, model and backend are immutable afterwards — every chunk is vectorised under them.

Request body

namestringrequiredDisplay name; also the basis of the AI-tool name (query_<name>)
descriptionstringShown in lists and used as the AI-tool description
embeddingobjectrequired{ provider: 'openai' | 'cohere', model, credentialId }
backendobjectrequired{ provider: 'mongo_atlas' | 'mongo_external' | 'pgvector', config? } — omit config for the managed backend
chunkingobject{ strategy: 'recursive' | 'fixed', chunkSize (50–8000 chars), chunkOverlap (0–4000, < chunkSize) }. Default recursive / 500 / 50
requireCleanScanbooleanWait for a clean malware verdict before storing an uploaded file (default true)
dedupOnIngestbooleanSkip chunks whose content hash already exists (default true)
workspaceIdstringWorkspace the store belongs to
GET/vector-stores/:id

One store, with its embedding config, backend, chunking and stats.

Path / query params

idstringrequired
PATCH/vector-stores/:id

Update what is mutable: name, description, credentialId (same provider only), chunking (applies to future ingests), isActive (pause / resume), requireCleanScan, dedupOnIngest.

Path / query params

idstringrequired
DELETE/vector-stores/:id

Drop the store and every chunk in it. Nodes and AI tools that reference it keep the id and fail until re-pointed — the dashboard shows them as disconnected.

Path / query params

idstringrequired
204 No Content
GET/vector-stores/:id/stats

Current chunk count, stored bytes, tokens embedded, and last usage.

Path / query params

idstringrequired
GET/vector-stores/:id/access-log

Paginated audit log: create, update, insert_chunks, query, delete, delete_chunks — with timestamp, user and source IP.

Path / query params

idstringrequired
limitnumberDefault 100
offsetnumber

Content

POST/vector-stores/:id/ingest

Chunk, embed and store text and/or an uploaded file. Content type (JSON, CSV, Markdown, code, prose) is detected and split structure-aware; a chunk identical to one already stored is skipped when dedup is on.

Path / query params

idstringrequired

Request body

textstringText to ingest. Either text or fileId is required
fileIdstringId of a file uploaded to the organization's file store (PDF, CSV, JSON, text/*)
sourceDocIdstringLogical document id — what delete-by-document and upsert key on
sourceDocNamestringHuman label shown next to every match
sourceDocTypestringOptional type hint
metadataobjectFlat object of scalar values stored on every chunk; queryable via filter
chunkingOverrideobject{ strategy, chunkSize, chunkOverlap } for this ingest only
{
  "chunksInserted": 42,
  "skipped": 3,
  "bytes": 21014,
  "sourceCount": 2,
  "upsertedOver": 18,
  "warnings": ["json: value at chapters[4] exceeded the per-chunk limit and was split as text"],
  "skippedFiles": [{ "name": "logo.png", "reason": "binary" }]
}

upsertedOver, warnings and skippedFiles are present only when they apply. When nothing extractable arrived the call succeeds with chunksInserted: 0 and a skippedReason such as empty text and no file. From the Vector Store node the same object is the node's output, wrapped as { "_meta": { "iterable": false, "count": 1, "success": true, "mode": "insert" }, ... }.

POST/vector-stores/:id/query

Semantic search: the query is embedded with the store's model (billed to the store's key) and compared against every chunk. Returns the top-K matches with scores.

Path / query params

idstringrequired

Request body

querystringrequiredThe question
topKnumberMax matches, 1–50 (default 5)
minScorenumberCosine similarity floor, 0–1; matches below it are dropped
filterobjectFlat object of scalar values matched against chunk metadata (see below)
{
  "matches": [
    {
      "chunkId": "665f...",
      "content": "Refunds are issued to the original payment method within 14 days…",
      "score": 0.871,
      "sourceDocId": "https://example.com/policies/returns",
      "sourceDocName": "Returns policy",
      "metadata": { "tenant": "acme", "lang": "en" }
    }
  ],
  "tookMs": 212
}

From the Vector Store node the output is the same object marked iterable over matches: "_meta": { "iterable": true, "iterateField": "matches", "count": 3, "success": true, "mode": "query" }.

POST/vector-stores/:id/delete-chunks

Delete chunks by id, by document, or by metadata filter. At least one selector is required; an empty selector deletes nothing and returns deletedCount: 0 with a skippedReason.

Path / query params

idstringrequired

Request body

chunkIdsstring[]Specific chunks
sourceDocIdstringEvery chunk of that document
metadataobjectEvery chunk whose metadata matches this flat object
{ "deletedCount": 18 }
POST/vector-stores/:id/flush

Delete every chunk and keep the store, its config and its credential.

Path / query params

idstringrequired
{ "deletedCount": 1204 }
GET/vector-stores/:id/chunks

Paginated browse of chunks, without embeddings — what the Content tab shows.

Path / query params

idstringrequired
limitnumberDefault 50
offsetnumber
sourceDocIdstringOnly this document
sourceDocNamestringOnly documents with this label
searchstringSubstring of the chunk text
⚠Filters are data, never query structure. A filter or metadata selector must be a flat object of scalar values. Objects, arrays and keys starting with $ are rejected with 400, and the operation aborts rather than running with a wider filter. The reason is what a template does with a public payload:
textWhy a filter must be scalars
template   { "customerId": "{{payload.customerId}}" }
payload    { "customerId": { "$ne": "__none__" } }
filter     { "customerId": { "$ne": "__none__" } }   ← would match everyone
On the managed backend only the tenancy field is declared in the vector index, so a filter by a custom metadata key fails instead of returning nothing; on pgvector any key filters with no setup, and on your own Atlas cluster you declare the paths in the index definition.

Canvas Snapshots

GET/canvas-snapshots

List all canvas snapshots.

POST/canvas-snapshots

Save a canvas snapshot (nodes + edges layout).

Request body

namestringrequiredSnapshot name
nodesobject[]requiredReactFlow nodes array
edgesobject[]requiredReactFlow edges array
GET/canvas-snapshots/:id

Get a single snapshot.

Path / query params

idstringrequired
DELETE/canvas-snapshots/:id

Delete a snapshot.

Path / query params

idstringrequired
204 No Content

Time Travel

POST/events/:id/time-travel

Dry-run replay an event through a historical canvas snapshot.

Request body

snapshotIdstringrequiredCanvas snapshot ID to simulate against
{
  "steps": [{ "node": "schema-validator", "name": "...", "result": "pass" }, ...],
  "finalStatus": "delivered",
  "matchedNodes": 4
}

Contract Testing

GET/events/:webhookId/contract-health

Validate recent events against connected schema validators.

Path / query params

webhookIdstringrequiredWebhook ID
limitnumberNumber of recent events to check (default: 100)
{
  "score": 94,
  "totalChecked": 100,
  "passed": 94,
  "failed": 6,
  "validators": [{ "id": "...", "name": "...", "passRate": 94 }]
}

Errors

All error responses follow a consistent shape:

{
  "statusCode": 400,
  "message": "targetUrl must be a URL address",
  "error": "Bad Request"
}
StatusMeaning
400Bad Request — invalid or missing parameters
401Unauthorized — missing or expired session
403Forbidden — resource belongs to another user, or plan limit exceeded
404Not Found — resource doesn't exist
429Too Many Requests — rate limit exceeded
500Internal Server Error — something went wrong on our end
503Service Unavailable — something the request needed failed or didn't answer in time. It may carry a code: upstream_unavailable or upstream_timeout for what used to be a 502 or 504 (see below), and the voice and tunnel pages list their own

An error the API used to raise as 502 or 504 now comes back as 503, with the same message and a code — upstream_unavailable for a 502, upstream_timeout for a 504 — unless it already carried one of its own. Cloudflare, in front of the API, replaces a 502 with its own HTML page, so the message telling you what to fix never reached you.