- What is TestInvite?
- Build Your First Test
- Run Your First Assessment
- Taking the Assessment
- Viewing the Results
- Question Bank Overview
- Common Question Features
- Scoring
- Question Types
- Question Authoring
- Browsing Questions
- Content Blocks
- Media Library
- Metadata Rules & Schema
- Tests Overview
- My Tests
- Creating a Test
- The Test Editor
- Test Settings
- Sections & Pages
- Adding Questions
- Page Builders
- Test Profile
- Reporting
- Test Papers
- Analytics
- Publishing a Test
- Test Library
- Marketplace
- Tasks Overview
- Creating a Task
- Task Dashboard
- Steps
- Task Settings
- Candidates
- Test Sessions
- Sent Mails
- Proctoring
- Analytics
- Analytics Overview
- Test Analytics
- Question Analytics
- Per-Question Analytics
- Results Explorer
- Segment Analysis
Build Your First Custom Report
Download a completed session's Report Input, create a handler with the test's ID, paste the input as sample data, write the function and template, then attach the handler to the test as a custom report and make it the default.
This walkthrough takes you from a completed session to a custom report in production. You need at least one completed session of the test, and permission to manage the test and the organization's snippets.
1. Download a real Report Input
Open a completed test session from the task and choose Report Input in the session's actions. This downloads test-instance-report-input_<id>.json — the exact object your function will receive. Download two or three: a high scorer, a low scorer, and one with unusual data (long name, unanswered sections).
2. Create the handler
Go to Organization › Snippets › Handlers and create a handler:
- Title and Description — for your team.
- Use Case — Test session report summary.
- Test ID — the ID of the test this report is for. This is required in practice: when you attach a custom report to a test, the Handler list only shows handlers whose Test ID matches that test.
3. Add your sample inputs
Save the handler once, then under Sample data choose New input and paste each downloaded JSON, giving it a title such as “High scorer”. Switching between saved inputs re-runs the preview, so you can check every case while you work.
4. Write the function
Turn on Enable function and start with a function that passes the input straight through:
function anonymous(data) {
return data;
}The Output: Data panel now shows what your template will receive. Replace the body step by step with the values your report needs — see Writing the Function for the rules and a complete example.
5. Write the template
Turn on Enable template, set Template engine to Nunjucks and Type to Plain HTML. A minimal first version that works against the pass-through function above:
<div class="ti-page" style="width:210mm;min-height:297mm;padding:16mm;box-sizing:border-box;font-family:Arial,sans-serif">
<h1>{{ info.testTitle }}</h1>
<p>{{ ticket.info.fullname }} scored {{ result.stats.point }} / {{ result.stats.totalPoint }}</p>
</div>Output: Report shows the rendered page. When it looks right for every sample input, save the handler. For the full syntax see Writing the Template.
6. Attach it to the test
Open the test, go to Reporting › Template and click Create. Enter a title and description, choose your handler under Handler, pick the Access (Private, All organizations, or Selected organizations) and save. The new custom report appears as a row next to the Standard Template.
7. Choose where it is shown
- For the whole test — click the radio button on its row to make it the test's default report.
- For one delivery — in the task, open the step's Admin Report or User Report settings and select the custom report, or keep Standard and turn on Extend with Custom Template to embed it at the top or bottom.
8. Verify with real sessions
Open several completed sessions and check the report. Use Export as PDF and confirm that each .ti-page fills exactly one A4 sheet — especially for the session with the longest texts. See Print & PDF Layout.
Keep a function in the handler even when it only returnsdata. Output: Data then shows exactly what the template receives, and the natural place for the first calculation is already there.