Five-minute webhook quickstart

Receive a signed webhook in five minutes

You need an Oway bearer token, a company API key (oway_sk_... or oway_ck_...), curl, jq,
and a public HTTPS receiver. Signed-in portal users are scoped through their membership, but
external API clients send both credentials. Keep the raw request body; serializing parsed JSON
again will change the signature input.

1. Start a receiver

Create a route that accepts POST, reads the body as bytes, and captures these headers:

webhook-id
webhook-timestamp
webhook-signature

Use one of the executable verification examples in
Verify webhook signatures. Verify first, deduplicate the parsed event_id
second, enqueue your work third, and return 204 immediately.

2. Inspect your available events

export OWAY_API_TOKEN='replace-with-your-token'
export OWAY_COMPANY_API_KEY='replace-with-your-company-key'
curl --fail-with-body \
  --header "Authorization: Bearer $OWAY_API_TOKEN" \
  --header "x-oway-api-key: $OWAY_COMPANY_API_KEY" \
  https://api.oway.io/v1/webhooks/catalog | jq

The catalog is audience-aware. Do not hardcode event names that are absent from this response.

3. Create an endpoint

export RECEIVER_URL='https://webhooks.example.com/oway'
ENDPOINT_RESPONSE="$(curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer $OWAY_API_TOKEN" \
  --header "x-oway-api-key: $OWAY_COMPANY_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: endpoint-$(date +%s)" \
  --data "$(jq -n \
    --arg url "$RECEIVER_URL" \
    '{url:$url,description:"Oway production",event_types:["shipment.delivered"]}')" \
  https://api.oway.io/v1/webhooks/endpoints)"

printf '%s\n' "$ENDPOINT_RESPONSE" | jq '.endpoint | {id}'
export OWAY_WEBHOOK_SECRET="$(jq -r .secret <<<"$ENDPOINT_RESPONSE")"
export OWAY_ENDPOINT_ID="$(jq -r .endpoint.id <<<"$ENDPOINT_RESPONSE")"
unset ENDPOINT_RESPONSE

Move OWAY_WEBHOOK_SECRET to your secret manager now. It appears only in this response. Do not
write the response or your shell environment to a shared file.

4. Send a real signed test

curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer $OWAY_API_TOKEN" \
  --header "x-oway-api-key: $OWAY_COMPANY_API_KEY" \
  --header "Idempotency-Key: test-$(date +%s)" \
  "https://api.oway.io/v1/webhooks/endpoints/$OWAY_ENDPOINT_ID/test" | jq

The response is 202 Accepted because the test goes through the normal asynchronous delivery
path. Your receiver should verify it and return 2xx.

5. Confirm the attempt

curl --fail-with-body \
  --header "Authorization: Bearer $OWAY_API_TOKEN" \
  --header "x-oway-api-key: $OWAY_COMPANY_API_KEY" \
  --get \
  --data-urlencode "endpoint_id=$OWAY_ENDPOINT_ID" \
  --data-urlencode "limit=10" \
  https://api.oway.io/v1/webhooks/deliveries | jq

A successful test reaches succeeded with a succeeded attempt. If it does not, use the delivery
ID with GET /v1/webhooks/deliveries/{delivery_id} and follow
Troubleshooting.


Did this page help you?