Skip to main content
POST
Ingest an event with an ingest key, routed by the project's mapping rules, with the key's default dataset as the fallback

Authorizations

Authorization
string
header
required

An ik_live_… ingest key. Stored hashed; the plaintext is returned once at creation.

Headers

Idempotency-Key
string

Optional. If a submission with this key already exists for the tenant, the request short-circuits with 200 before the monthly quota is charged.

X-Hookie-Signature
string

Required only when the ingest key has require_signature set; ignored otherwise. Format: 't=,v1=' where v1 is HMAC-SHA-256 over '.' keyed with the key's signing secret.

Example:

"t=1700000000,v1=3ba8c0e9..."

Path Parameters

ingest_key
string
required

The plaintext ingest key. It is looked up by its full SHA-256 hash (#177), among keys that are neither revoked nor expired, and compared constant-time; key_prefix is only a display label.

Body

Content-Type selects the parser: 'application/x-www-form-urlencoded' and 'multipart/form-data' are flattened into a shallow object (repeated fields become arrays; file parts become {filename, type, size} and their bytes are discarded). EVERY other body is parsed as JSON first, whatever its Content-Type (missing or wrong ones included, which is deliberate so webhook providers that send JSON with an unhelpful Content-Type still work). A body that is not JSON is kept as text when its Content-Type is textual: text/* (plain, csv, xml, ...), application/xml or any +xml type, NDJSON / JSON Lines (application/x-ndjson, application/ndjson, application/jsonl, application/x-jsonlines), application/csv and YAML. It is stored as one event, {"body": "<the text, verbatim>", "content_type": "<media type, lowercased, parameters stripped>"}, decoded as UTF-8; NDJSON is not split into several events. A body that does parse as JSON is stored as JSON whatever its Content-Type, exactly as before, so a provider that posts JSON as text/plain still gets an object. A non-JSON body that declares JSON (or no Content-Type) is 400, and one of any other type (application/octet-stream, image/*, ...) is 415. An empty body is accepted and becomes {}. Over 1,000,000 bytes yields 413.

Any JSON value. On this route the payload is fed to the project's mapping rules, so its shape is whatever the rules' 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.

submission_id
string
required
idempotent
enum<boolean>
required
Available options:
true
deduplicated_by
enum<string>

Which dedup source matched.

Available options:
idempotency-key,
standard-webhooks,
svix,
github,
shopify,
gitlab,
twilio,
linear,
twitch,
atlassian,
stripe,
slack