BETA
Skip to content

Ingest endpoint ​

POST https://app.krafter.dev/in/:slug

The ingest endpoint is the public-facing URL you give to providers (Stripe, GitHub, Shopify, custom). It accepts unauthenticated POSTs — the provider's signature is the only credential.

The :slug is the auto-generated identifier returned by POST /api/v1/webhooks/sources and exposed to you on the source object as url.

What gets stored ​

Every accepted POST creates exactly one event row capturing:

  • The resolved event type — extracted from the body or a header per the source's provider configuration.
  • The raw request body (as bytes) and a lowercased headers map.
  • The signature_valid flag (true / false / null).
  • A status of received initially.

Fan-out happens immediately afterwards — see Replay for the lifecycle.

Limits & responses ​

ConditionHTTP statusBody
Accepted200{"ok":true}
Unknown slug404{"error":"not_found"}
Source's enabled: false410{"error":"source_disabled"}
Body larger than 256 KiB413{"error":"payload_too_large"}
Same slug exceeds 100 requests / second429{"error":"rate_limited"}

The 200 response is returned even when the signature is invalid. The event is stored with signature_valid: false for inspection but not fanned out to destinations. This matches the security model of Stripe and GitHub: never reveal to an attacker whether their forged signature was rejected.

Body parsing ​

Krafter caches the raw bytes of the request body before Plug.Parsers runs (via KrafterWeb.Plugs.CacheRawBody). HMAC verification always uses those exact bytes — re-serialised JSON would not match the provider's signature.

If the body parses as JSON, it is also stored on the event for use by event_type_path extraction. Non-JSON bodies are stored as-is in raw_body; event_type is then resolved from headers if the source's provider supports it.

Why the ingest URL has no auth ​

Inbound webhooks don't have access to your Krafter API key — they arrive from a third party. Authentication is therefore purely signature-based:

  1. The provider signs the body using a shared secret.
  2. Krafter recomputes the HMAC and compares it constant-time.
  3. Mismatched signatures result in signature_valid: false and the event is not forwarded. Matched signatures fan out normally.

Without signing_secret configured on the source, every event is stored with signature_valid: nil and not forwarded. Use this mode for one-shot inspection while wiring up a provider for the first time.

Built by Krafter Studio