Skip to content

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.

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.

  1. Read the raw request body and keep it as received — you need those exact bytes for signature verification.
  2. Verify the signature. Reject the request if it fails.
  3. Parse and validate the JSON shape and schema_version.
  4. Deduplicate using the payload’s id.
  5. Store or queue the event for your own processing.
  6. 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.