curl --request POST \
--url https://app.hookie.ai/{workspace}/{project}/{webhook}/{dataset} \
--header 'Content-Type: application/json' \
--data '"<unknown>"'const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify('<unknown>')
};
fetch('https://app.hookie.ai/{workspace}/{project}/{webhook}/{dataset}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://app.hookie.ai/{workspace}/{project}/{webhook}/{dataset}"
payload = "<unknown>"
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"submission_id": "<string>",
"idempotent": true
}{
"submission_id": "<string>",
"routed": [
"<string>"
],
"records": 123
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>"
}Ingest an event through the public webhook URL, overriding the dataset
Four-segment form of the public webhook URL. POST only (index.ts:190); other methods fall through to the SPA. The fourth segment overrides the webhook’s configured dataset for this request only.
curl --request POST \
--url https://app.hookie.ai/{workspace}/{project}/{webhook}/{dataset} \
--header 'Content-Type: application/json' \
--data '"<unknown>"'const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify('<unknown>')
};
fetch('https://app.hookie.ai/{workspace}/{project}/{webhook}/{dataset}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://app.hookie.ai/{workspace}/{project}/{webhook}/{dataset}"
payload = "<unknown>"
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"submission_id": "<string>",
"idempotent": true
}{
"submission_id": "<string>",
"routed": [
"<string>"
],
"records": 123
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>"
}{
"error": "<string>",
"retry_after": 123,
"upgrade_url": "<string>"
}Headers
Optional. A repeat key short-circuits with 200 before quota is charged.
Path Parameters
Workspace (tenant) slug; must not be a reserved root (api, admin, stripe, v1, assets, settings, favicon.ico, robots.txt, sitemap.xml, index.html, app.js, styles.css, export.js, portal, brand, trial-info, .well-known, mcp).
Project slug within the workspace.
Webhook slug — the credential. Only an enabled webhook matches.
Dataset to store the record in, overriding the webhook's configured dataset for this request. No validation is performed; the name is used as given.
Body
Same parsing as the 3-segment form: urlencoded and multipart are flattened (repeats become arrays, file parts become {filename, type, size} with the bytes discarded); every other Content-Type is parsed as JSON; an empty body becomes {}; a non-object payload is wrapped as {"value": }. Over 1,000,000 bytes yields 413.
Any JSON value.
Response
Idempotent replay — a submission with this Idempotency-Key already exists for the tenant. No quota is charged when the duplicate is detected up front; a request that loses the unique-index race has already been counted against the monthly quota.