Integration
When the reporter clicks Send report, the report is delivered to
all enabled destinations. Save and download stores the report and
downloads it without sending it. All destinations use the same report
document (schema context-).
Download
- JSON
- The complete report document. The screenshot is embedded as base64 in
attachments.[]. content Base64 - Markdown
- A readable version for tickets and chats, without the screenshot.
JSON and Markdown downloads are recorded in the delivery history.
After sending a report, the reporter can copy a short text summary, the
Markdown version, the link to the report and the JSON document to the
clipboard; administrators can do the same in the report history. The copied
JSON has the same schema, but no content: screenshots are only
included in the downloaded file.
The Markdown version escapes the title, description and other text that users entered, so it cannot add images, links, HTML or structure to a ticket. URLs in user text are shown as code.
Emails are plain text and sent with the TYPO3 mail API and its configured
transport. The subject and the body template contain markers such as
{report.. Unknown markers are left untouched. The screenshot and the
JSON report can be attached. The header X-
contains the report ID; test emails carry X-
instead. See Email status and test email if emails do not arrive.
Markers
| Marker | Value |
|---|---|
{report., {report., {report. | Report ID (for example CR-), title and description |
{report., {report., {report. | Creation time (UTC), entry point and backend link to the report |
{project., {project., {project., {project. | Project settings and backend URL |
{reporter., {reporter., {reporter., {reporter., {reporter. | Reporter details, if shared. {reporter. falls back to the
username, the UID or (not shared) |
{page., {page., {page., {page., {page. | Page of the report |
{record., {record., {record., {record., {record., {record. | Record of the report |
{file., {file., {file., {file., {file. | File of the report; the identifier is the combined identifier,
for example 1:/ |
{folder., {folder., {storage. | Folder of the report or of the reported file, and its storage |
{site., {site. | Site of the page |
{context. | One line, for example `Page Content "Our story" ` |
{context., {context. | Subject and its backend link |
{context. | All technical data as readable text |
{context., {context., {context. | Language, workspace and backend module |
{system., {system., {system. | System information |
{browser., {browser., {browser., {browser. | Browser information, if shared |
Markers without a value are replaced with an empty string. Markers in the subject are reduced to a single line.
Webhook
The webhook sends an HTTP POST request with a JSON body.
POST /your/endpoint HTTP/1.1
Content-Type: application/json; charset=utf-8
User-Agent: TYPO3-Context-Reporter/0.2.0
X-Context-Reporter-Event: report.created
X-Context-Reporter-Report: CR-CR91-A84Z-DA59
X-Context-Reporter-Delivery: 7dd6d20a-ee21-4107-be2e-5767269320d2
X-Context-Reporter-Attempt: 1
X-Context-Reporter-Timestamp: 1789565415
X-Context-Reporter-Signature: t=1789565415,v1=5b0c…
Authorization: Bearer …
The signature header is only sent when webhook.secret is set. The authentication header is only sent when webhook.authHeaderValue is set.
{
"event": "report.created",
"delivery": {
"id": "7dd6d20a-ee21-4107-be2e-5767269320d2",
"attempt": 1,
"sentAt": "2026-09-16T13:30:15+00:00"
},
"report": {
"schema": "context-reporter.report.v1",
"id": "CR-CR91-A84Z-DA59",
"createdAt": "2026-09-16T13:30:15+00:00",
"source": "formEngine",
"title": "Media field shows no files",
"description": "I opened the content element and the image selector stays empty.",
"summary": "Page Content \"Our story\" [tt_content:2] (Text & Media) on page \"About us\" [2] · site main · language English · workspace Live · editing form",
"subject": {
"type": "record",
"table": "tt_content",
"uid": 2,
"label": "Our story",
"typeLabel": "Text & Media",
"backendUrl": "https://www.example.com/typo3/record/edit?edit%5Btt_content%5D%5B2%5D=edit"
},
"project": { "name": "Example", "identifier": "example", "environment": "Production", "backendUrl": "https://www.example.com/typo3/" },
"reporter": { "uid": 3, "username": "editor" },
"context": {
"backend": { "route": { "identifier": "record_edit", "path": "/record/edit" }, "backendLanguage": "en" },
"page": { "uid": 2, "pid": 1, "title": "About us", "slug": "/about-us", "doktype": 1, "doktypeLabel": "Standard", "hidden": false },
"record": {
"table": "tt_content",
"tableTitle": "Page Content",
"uid": 2,
"pid": 2,
"label": "Our story",
"type": { "field": "CType", "value": "textmedia", "label": "Text & Media" },
"languageId": 0,
"colPos": 0,
"hidden": false
},
"formEngine": { "mode": "edit", "records": [{ "table": "tt_content", "uid": 2 }] },
"site": { "identifier": "main", "base": "https://www.example.com/", "rootPageId": 1 },
"language": { "id": 0, "title": "English", "locale": "en-US" },
"workspace": { "id": 0, "title": "Live" },
"visibility": {
"evaluatedAt": "2026-09-16T15:30:15+02:00",
"subject": { "reasons": [] },
"page": { "uid": 2, "title": "About us", "reasons": [] },
"translations": [
{ "languageId": 1, "title": "Deutsch", "page": { "exists": true, "reasons": [] }, "record": { "exists": true, "reasons": ["hidden"], "hidden": true } }
]
},
"fileChecks": {
"references": {
"checked": 2,
"notChecked": 1,
"problems": [
{ "field": "assets", "fieldLabel": "Media elements", "reference": 7, "file": { "uid": 12, "name": "team.jpg" }, "problems": ["hidden"] }
]
}
}
},
"system": { "typo3Version": "14.3.7", "phpVersion": "8.3.33", "applicationContext": "Production" },
"browser": { "summary": "Chrome 148 · macOS · 1440×900", "language": "en-US" },
"attachments": [
{
"type": "screenshot",
"filename": "CR-CR91-A84Z-DA59-screenshot.png",
"mediaType": "image/png",
"size": 102939,
"width": 1440,
"height": 900,
"sha256": "5f03…",
"contentBase64": "iVBORw0KGgo…"
}
],
"links": { "report": "https://www.example.com/typo3/module/system/context-reports/show?report=CR-CR91-A84Z-DA59" },
"generator": { "name": "TYPO3 Context Reporter", "package": "priebera/typo3-context-reporter", "version": "0.2.0" }
}
}
Notes on the report document:
sourceistoolbar,context,Menu form,Engine record,List pageorModule file.List subject.istype backend,page,record,fileorfolder. Files and folders have a combinedsubject., for exampleidentifier 1:/.user_ upload/ logo. png project,reporter,systemandbrowserare omitted when there is nothing to share.contextonly contains the sections that apply, for examplepage,record,file,folder,storage,form,Engine site,language,workspace,backendandrecent.Errors context.(pages and records) lists the stored settings that decide whether TYPO3 shows the object to website visitors, see Visibility settings.visibility subject,page, the entries ofparentand thePages. restricting pageandrecordentries oftranslationshavereasons(hidden,scheduled,expired,access) and the facts behind them:Restricted hidden,starttimeandendtime(ISO 8601),frontend(Groups idandtitle;-1hides the object for logged-in visitors,-2shows it to logged-in visitors only),frontend,Groups Not Listed hiddenandIn Menu workspace(State unchanged,new,changed,deleted; only in a workspace). Translations haveexistsand, for disabled site languages,enabled: false.parentis only present if the reporter may edit Extend to subpages;Pages checkedisUp To Root falsewhen parent pages outside the reporter's access were not read. These are stored settings, not a check of the rendered website. Reports created before version 0.2 have novisibilitysection.context.lists file problems TYPO3 knows about, see File checks. For a reported file,file Checks filehasstorage(Check found,not, orFound notwhen the storage is offline or could not be asked) andChecked problems; for file metadata,filealso has theuidandnameof the file it describes. For a reported page or record,referencescovers the file fields its type shows, for a reported file reference that reference:checkedandnotcount the references (files the reporter may not read are only counted),Checked problemslists at most ten references withfield,field,Label reference(UID of the file reference),file(uidandname; missing when the file no longer exists) and theirproblems;problemsandNot Listed over(references beyond the first 100) follow when needed. Problems areLimit missing(marked as missing in the file index),not(reported file only),In Storage storage,Offline empty,hidden(file reference) andbroken. The section is omitted when nothing could be checked. Reports created before version 0.2 have noReference filesection.Checks - New fields can be added in later versions. Receivers should ignore unknown fields.
- Backend links contain no tokens. Users who are not logged in are asked to log in first.
Test deliveries
Send test webhook in the settings posts a request with the same
headers, signature, authentication and timeout, but with the event test
and without report data. Receivers should accept it (any 2xx response)
and not create a ticket.
{
"event": "test",
"delivery": { "id": "0b8e6f58-7f55-4d86-8a1b-5d8f1e7c2a10", "attempt": 1, "sentAt": "2026-09-17T09:00:00+00:00" },
"test": {
"message": "Test delivery from TYPO3 Context Reporter. It does not contain a report.",
"project": { "name": "Example", "identifier": "example", "environment": "Production" },
"reportSchema": "context-reporter.report.v1"
},
"generator": { "name": "TYPO3 Context Reporter", "package": "priebera/typo3-context-reporter", "version": "0.2.0" }
}
Check X- (report. or test) before
processing a request.
Verifying the signature
The signature header has the form t=<unix timestamp>,v1=<hex digest>.
The digest is HMAC-.
Always verify against the raw body, compare in constant time and reject old
timestamps.
function isValidContextReporterRequest(string $body, string $header, string $secret): bool
{
if (!preg_match('/^t=(\d{1,12}),v1=([a-f0-9]{64})$/D', $header, $matches)) {
return false;
}
if (abs(time() - (int)$matches[1]) > 300) {
return false;
}
$expected = hash_hmac('sha256', $matches[1] . '.' . $body, $secret);
return hash_equals($expected, $matches[2]);
}
$valid = isValidContextReporterRequest(
file_get_contents('php://input'),
$_SERVER['HTTP_X_CONTEXT_REPORTER_SIGNATURE'] ?? '',
getenv('CONTEXT_REPORTER_WEBHOOK_SECRET'),
);
import { createHmac, timingSafeEqual } from 'node:crypto';
function isValidContextReporterRequest(rawBody, header, secret, toleranceSeconds = 300) {
const match = /^t=(\d{1,12}),v1=([a-f0-9]{64})$/.exec(header ?? '');
if (!match || Math.abs(Date.now() / 1000 - Number(match[1])) > toleranceSeconds) {
return false;
}
const expected = createHmac('sha256', secret).update(`${match[1]}.`).update(rawBody).digest();
return timingSafeEqual(expected, Buffer.from(match[2], 'hex'));
}
Response, references and retries
- Any
2xxresponse counts as delivered. Redirects are not followed. - Other responses, connection errors and timeouts count as failed. The beginning of the response body is stored in the delivery history, with URLs and configured secrets removed.
-
If the receiver creates a ticket, it can return its reference:
{"reference": "SUP-42", "url": "https://support.example.com/SUP-42"}Copied!The keys
reference,ticket,Id ticket_,id issue,Key issue_,key key,numberandidare recognized as reference, andurl,html_,url web_,url link,ticketandUrl ticket_as link. They are also read from a nestedurl data,ticket,issueorresultobject. The reference and the link are shown to the reporter and in the report history.Before they are stored, a reference that is a URL or contains a configured secret is dropped. Query parameters that look like credentials (for example
token,access_,token api_,key signatureorpassword) are removed from the link, and a link with user information or a configured secret is dropped. Return a plain ticket number and a link without credentials. - Deliveries are sent while the reporter waits. There is no queue and no
automatic retry. Administrators can retry failed deliveries in the report
history. A retry has a new
delivery.and the nextid attemptnumber, so use the reportidto detect duplicates.
Automation tools such as n8n, Make or Zapier can receive the webhook and create issues in Jira, GitLab, GitHub, Slack or any other system. For n8n, the repository contains a ready-made workflow for GitLab, see n8n: GitLab and Jira issues.