Create a webhook
const url = 'https://secure.ethicsportal.eu/organizations/example/webhooks.json';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"webhook":{"name":"Case dashboard","url":"https://receiver.example/ethicsportal","events":["report.created","report.overdue"]}}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://secure.ethicsportal.eu/organizations/example/webhooks.json \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "webhook": { "name": "Case dashboard", "url": "https://receiver.example/ethicsportal", "events": [ "report.created", "report.overdue" ] } }'Register a public HTTPS destination for the selected events. The response contains the signing secret once. A personal token also needs its user to be a current administrator of the organisation. Each organisation can have up to ten webhooks.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Obfuscated organisation ID returned by identity.
Header Parameters
Section titled “Header Parameters”Recommended on every POST. Retrying with the same key and the same request within 24 hours returns the first response (with Idempotent-Replayed: true) instead of acting twice. Only successful responses and validation errors are stored; any other outcome releases the key. Reusing a key with a different request returns 409 idempotency_key_reused; a retry while the first request is still running returns 409 idempotency_key_in_progress. Keys are scoped to the token.
Request Bodyrequired
Section titled “Request Bodyrequired”object
object
Public HTTPS URL on port 443. Redirects are not followed.
Example
{ "webhook": { "name": "Case dashboard", "url": "https://receiver.example/ethicsportal", "events": [ "report.created", "report.overdue" ] }}Responses
Section titled “Responses”Webhook created; store the secret now.
object
The destination EthicsPortal sends events to, as in Stripe’s API. Other *_url fields are links within this API.
Set when a delivery failed permanently or exhausted its retries; cleared by a later success or a configuration change.
Signing secret. Returned only when created or rotated; store it immediately.
Example
{ "id": "pQrStu", "name": "Case dashboard", "url": "https://receiver.example/ethicsportal", "enabled": true, "events": [ "report.created", "report.overdue" ], "failing_since": null, "created_at": "2026-11-09T10:00:00Z", "updated_at": "2026-11-09T10:00:00Z", "secret": "ep_whsec_example", "deliveries_url": "https://secure.ethicsportal.eu/organizations/gHiJkl/webhooks/pQrStu/deliveries.json"}Invalid query, body or cursor
object
object
Where to tell EthicsPortal what went wrong: POST product feedback here with the same token.
Example
{ "error": { "code": "invalid_request", "feedback_url": "https://secure.ethicsportal.eu/me/feedback.json" }}Missing, unknown, expired or revoked token
object
object
Where to tell EthicsPortal what went wrong: POST product feedback here with the same token.
Example
{ "error": { "code": "invalid_token", "feedback_url": "https://secure.ethicsportal.eu/me/feedback.json" }}The token lacks the required scope (WWW-Authenticate names it), the user’s role does not allow the action, or an agreement must be accepted in the web application
object
object
Where to tell EthicsPortal what went wrong: POST product feedback here with the same token.
Example
{ "error": { "code": "insufficient_scope", "feedback_url": "https://secure.ethicsportal.eu/me/feedback.json" }}Organisation or resource missing or inaccessible, or API access suspended
object
object
Where to tell EthicsPortal what went wrong: POST product feedback here with the same token.
Example
{ "error": { "code": "not_found", "feedback_url": "https://secure.ethicsportal.eu/me/feedback.json" }}This Idempotency-Key was already used with a different request, or its first request is still running
object
object
Where to tell EthicsPortal what went wrong: POST product feedback here with the same token.
Example
{ "error": { "code": "idempotency_key_reused", "feedback_url": "https://secure.ethicsportal.eu/me/feedback.json" }}Validation failed; fields are named, values are not echoed
object
object
Where to tell EthicsPortal what went wrong: POST product feedback here with the same token.
Example
{ "error": { "code": "validation_failed", "fields": [ "url" ], "feedback_url": "https://secure.ethicsportal.eu/me/feedback.json" }}Rate limited; retry after the Retry-After interval
object
object
Where to tell EthicsPortal what went wrong: POST product feedback here with the same token.
Example
{ "error": { "code": "rate_limited", "feedback_url": "https://secure.ethicsportal.eu/me/feedback.json" }}