Receiving events
The payload is POSTed to your URL as the entire request body — there is no wrapper around it. These headers come with it:
| Header | Meaning |
|---|---|
Content-Type |
Always application/json. |
x-outpost-topic |
The topic, e.g. levno.collection.finished. |
x-outpost-event-id |
Levno’s id for this event. Identifies the payload across its retries. |
x-outpost-timestamp |
When this delivery attempt was made (RFC 3339). Differs between retries of the same payload. |
x-outpost-signature |
HMAC signature of the request body — see verifying webhooks. |
Endpoint shape
Section titled “Endpoint shape”Accept POST requests over HTTPS and parse the body as JSON. Keep the handler small: verify the signature, record the delivery, queue internal work, and respond.
type LevnoEventEnvelope = { schema_version: '1.0' type: 'levno.farm.milk.volume.updated' | 'levno.milking.finished' | 'levno.collection.finished' date_time: string id: string data: Record<string, any> subject: { levno_farm_id: number supply_number: string | null partner_defined_id: string | null partner_defined_name: string | null } delivery: { id: string }}The type above models the confirmed payload. See the event model for the fields specific to each event type.
Processing order
Section titled “Processing order”- Read the raw request body and keep it as received — you need those exact bytes for signature verification.
- Verify the signature. Reject the request if it fails.
- Parse and validate the JSON shape and
schema_version. - Deduplicate using the payload’s
id. - Store or queue the event for your own processing.
- Return an HTTP status below 400 within 5 seconds.
Return success as soon as you have stored the payload, and do the real work asynchronously — see delivery and retries for what happens when your endpoint is slow or unavailable.