List a report's audit trail
const url = 'https://secure.ethicsportal.eu/organizations/example/whistleblower/reports/example/audit-log.json?limit=50';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://secure.ethicsportal.eu/organizations/example/whistleblower/reports/example/audit-log.json?limit=50' \ --header 'Authorization: Bearer <token>'One report’s audit trail. Omits anything a reporter did and entries about messages, notes and retaliation, consistent with the data boundary. Entries carry no change details, IP addresses or email addresses. Oldest first. A personal token needs access to the report. Entries appear once they are a minute old, so a collector resuming with created_after cannot skip one that committed late.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Obfuscated organisation ID returned by identity.
Obfuscated report ID; not the reporter access code.
Query Parameters
Section titled “Query Parameters”Follow the Link response header unchanged. Signed cursor expires after one hour.
Exclusive lower bound; resume a log collector from the last entry it stored.
Header Parameters
Section titled “Header Parameters”The ETag of a response you already hold. If nothing changed, the response is 304 Not Modified with no body. It still counts toward rate limits and, where reads are audited, the audit trail.
Responses
Section titled “Responses”Audit trail entries for the report.
object
object
Stable action name, such as whistleblower.report.viewed or invitation.created.
object
object
Pass as the cursor parameter for the next page; null on the last page. The Link header carries the same next page.
Example
{ "data": [ { "id": "hIjKlm", "action": "whistleblower.report.viewed", "actor": { "kind": "member", "name": "Anna Novak", "membership_id": "nOpQrs" }, "subject": { "type": "report", "id": "aBcDef" }, "created_at": "2026-11-09T10:00:00Z" } ], "next_cursor": null}Headers
Section titled “Headers”Weak validator of this response body. Send it back as If-None-Match.
Not modified since the ETag sent in If-None-Match. No body.
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" }}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" }}