Skip to content

Webhooks

An administrator opens Organisation → Integrations → Webhooks, registers a public HTTPS destination on port 443 and selects events. Each organisation can have up to ten destinations.

Webhooks belong to the organisation, not to the administrator who created them. They receive events for every report in the organisation and keep delivering when staff change. Only administrators can see, create or operate them.

Copy the signing secret when it is displayed after creation. Store it in the receiver’s secret store. Use Send test event to queue a webhook.test event containing only the organisation ID. A queued event is not proof of delivery; inspect delivery history.

Everything on the webhook screen is also available to a token with webhooks:read and webhooks:write: list, create, read, update and delete webhooks, rotate the signing secret, send a test event, list deliveries and retry a failed delivery. A personal token also needs its user to be a current administrator. The API reference lists each operation.

Terminal window
curl --fail-with-body -X POST \
-H "Authorization: Bearer $ETHICSPORTAL_TOKEN" \
-H 'Content-Type: application/json' \
-H "Idempotency-Key: $(uuidgen)" \
--data '{"webhook":{"name":"Case dashboard","url":"https://receiver.example/ethicsportal","events":["report.created","report.overdue"]}}' \
"$ETHICSPORTAL_ORIGIN/organizations/$ETHICSPORTAL_ORGANIZATION_ID/webhooks.json"

The 201 response contains the signing secret once, as does rotating it; reading a webhook never returns it. Store it before doing anything else. If the response is lost, repeat the request with the same Idempotency-Key to receive it again within 24 hours.

Event Meaning
report.created A report was created
report.acknowledged An acknowledgement timestamp was recorded
report.feedback_given A feedback timestamp was recorded
report.closed A report was closed, without the closure outcome
report.reopened A report was reopened
report.assignment_changed Assignment changed between present and absent
report.due_soon An outstanding deadline entered the two-day window
report.overdue An outstanding deadline passed

Replacing one assignee with another does not emit an event because assignment remains present. Closure emits one closure event instead of extra events for automatically filled acknowledgement/feedback timestamps. Messages, notes, files and other excluded content do not generate events.

Every envelope contains id, type, created_at and data. Lifecycle events contain the report metadata snapshot at event creation, without url and web_url. Deadline data contains only report_id, organization_id, deadline_type and deadline_at.

Read the generated webhook schema for exact fields and event variants. Snapshots can differ from current state by delivery time. Subscriptions do not receive events from before their creation.

Deadline events deduplicate by report, event type, deadline kind and due time within the 30-day retention window. An unresolved deadline may produce another reminder after the earlier event has expired.

X-EthicsPortal-Event-Id: <event-id>
X-EthicsPortal-Signature: t=<unix-seconds>,v1=<hex-digest>

Compute HMAC-SHA256 using your secret over the timestamp, a full stop and the exact raw request bytes. Compare the hexadecimal digest in constant time. Reject timestamps more than five minutes from your clock, then parse the JSON and require its id to match the event header.

Use the tested Node.js verification example. Do not parse and re-serialize JSON before signature verification.

Persist a verified event in a durable inbox with a unique event ID before returning any 2xx response. If that event already exists, acknowledge it without repeating its effects. Perform business processing asynchronously from that inbox. If persistence fails, return a non-2xx status so delivery can retry.

Delivery is at least once. There is no ordering guarantee. A lost response can cause a duplicate with the same event ID/body and a new signature timestamp. Reconcile current state through the API after missed or out-of-order events.

Non-2xx responses and connection failures retry after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 24 hours: seven attempts over about 32 hours 36 minutes, plus queue delays. Unsafe destinations fail permanently. Redirects are not followed.

The endpoint screen shows the latest 100 deliveries with the stable event ID, status, attempts, HTTP status or a fixed error code. For a pending retry, it shows the next scheduled attempt time. This is the earliest eligible time; queue delays can postpone it. Paused retries are labelled when integrations are disabled. Match the event ID to your receiver logs; manual retries retain it. Failed deliveries can be retried manually while retained. A manual retry uses the endpoint’s current URL and signing secret, so retry after fixing your receiver. Events and delivery history expire after 30 days. Receiver response bodies are not retained. Deleting a report removes its retained events; deleting an endpoint removes its delivery history.

When a delivery fails permanently or exhausts its retries, the webhook displays a warning. Organisation administrators receive an in-app notification and email. Notifications contain a generic warning and a link to delivery history, without report data, the destination URL or the signing secret. Email checks current access and failure state again before sending.

Further failed deliveries do not create another alert during the same failure episode. The warning clears after a successful delivery or a change to the destination, secret, event selection or enabled state. A manual retry alone does not clear it. A later failure after recovery can create a new alert. Email delivery is asynchronous and may repeat if a job is recovered after an interrupted send.

Failure does not automatically disable the webhook. New events continue to be attempted. Inspect the receiver, correct the configuration if necessary, and send a test event. Reconcile missed state through the API.

Generate a replacement secret in the endpoint screen and update the receiver. There is no overlap period. Changing the URL, secret, event selection or enabled state invalidates pending deliveries from the previous configuration. Send a fresh test event and reconcile through the API after changes.

If API access is suspended, sends pause and event capture stops. An in-flight HTTP request cannot be recalled. When access is restored, retained pending deliveries resume; report changes made during the suspension are not backfilled. The organisation’s agreement is checked again before each delivery. Existing endpoints can still be deleted during a suspension.