curl --request POST \
--url https://app.hookie.ai/v1/webhooks/{ingest_key}/{slug} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '"<unknown>"'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify('<unknown>')
};
fetch('https://app.hookie.ai/v1/webhooks/{ingest_key}/{slug}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://app.hookie.ai/v1/webhooks/{ingest_key}/{slug}"
payload = "<unknown>"
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"submission_id": "<string>",
"idempotent": true,
"deduplicated_by": "idempotency-key"
}{
"submission_id": "<string>",
"routed": [
"<string>"
],
"records": 123
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "quota_exceeded",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"limit": 123,
"reset_at": "2023-11-07T05:31:56Z"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "endpoint_disabled",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}Ingest an event into a configured endpoint using an ingest key
curl --request POST \
--url https://app.hookie.ai/v1/webhooks/{ingest_key}/{slug} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '"<unknown>"'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify('<unknown>')
};
fetch('https://app.hookie.ai/v1/webhooks/{ingest_key}/{slug}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://app.hookie.ai/v1/webhooks/{ingest_key}/{slug}"
payload = "<unknown>"
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"submission_id": "<string>",
"idempotent": true,
"deduplicated_by": "idempotency-key"
}{
"submission_id": "<string>",
"routed": [
"<string>"
],
"records": 123
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "quota_exceeded",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>",
"limit": 123,
"reset_at": "2023-11-07T05:31:56Z"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "endpoint_disabled",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>",
"reason": "<string>",
"code": "agent_scope_insufficient",
"required_scope": "hookie:read",
"granted_scopes": [
"<string>"
],
"manage_url": "<string>",
"scheme": "<string>"
}Authorizations
An ik_live_… ingest key. Stored hashed; the plaintext is returned once at creation.
Headers
Optional. A repeat key short-circuits with 200 — after the webhook slug is resolved, but before the monthly quota is charged.
Required only when the ingest key has require_signature set (this route uses the KEY's signature setting and signing secret, not the webhook's). Format 't=,v1=', v1 = HMAC-SHA-256 over '.'.
Path Parameters
The plaintext ingest key (looked up by its full SHA-256 hash, #177; revoked and expired keys never match). It authenticates the request; the tenant it belongs to scopes the webhook lookup.
Webhook slug within the key's project; a slug of another project, even in the same workspace, is 404. A bare slug selects the most recently created enabled webhook with that base slug; a versioned slug of the form '-v' (version = 1+ digits, optionally .digits, e.g. 'orders-v2' or 'orders-v2.1') pins that exact enabled version. When the slug names only a disabled webhook the answer is 503.
Body
Form encodings are flattened (repeats become arrays, file parts become {filename, type, size} with the bytes discarded); every other body is parsed as JSON first, and textual non-JSON bodies are stored as {body, content_type} (see ingestEvent), which the rule's conditions and mappings address as body and content_type; an empty body becomes {}. The parsed payload is evaluated against the webhook's rule conditions and reshaped by its mappings (empty conditions match everything, empty mappings are identity). Over 1,000,000 bytes yields 413.
Any JSON value; its shape is whatever the webhook's rule conditions and mappings expect.
Response
Duplicate - this endpoint (or, on /v1/ingest, this ingest key) already stored a submission with the same dedup key, so the retry is answered with the first submission's id and nothing is stored or counted (ING-9, #193). The dedup key is, in order: an Idempotency-Key header; else a provider delivery id kept the same across that provider's retries - webhook-id (Standard Webhooks), svix-id, X-GitHub-Delivery, X-Shopify-Webhook-Id, X-Gitlab-Event-UUID, I-Twilio-Idempotency-Token, Linear-Delivery, Twitch-Eventsub-Message-Id, X-Atlassian-Webhook-Identifier; else Stripe's event id (a body with object 'event' and an evt_ id) or Slack's event_id (type 'event_callback'). The key space is the ENDPOINT's, not the workspace's: two endpoints may receive the same key. deduplicated_by names which source matched. A request that loses the unique-index race has already been counted against the monthly quota.