User Guide
Welcome to ZReport. This guide will help you master report design, from simple lists to complex analytical dashboards.
Interface Overview
The designer is divided into four main areas:
1. Toolbox (Left)
Your palette of building blocks. Contains Text, Image, Table, Chart, and more.Drag and drop these items onto the canvas to add them to your report.
2. Design Canvas (Center)
This represents your page. It is divided into horizontal Bands. What you see here is a template; the final report will repeat these bands for each record in your data.
3. Data Dictionary (Panel)
Shows your data structure. Drag fields like ProductName directly onto the canvas to create data-bound text boxes instantly.
4. Properties (Right)
Fine-tune the selected element. Change fonts, colors, borders, or edit the Expression to add logic.
Tutorial: Your First Report
Let's build a simple Employee List report.
Step 1: Setup
Click New Report β Blank Report. In the Page Setup dialog, choose A4 and Portrait.
Step 2: Add a Title
Drag a Text element into the Report Header band.
- Double-click and type "Employee Directory".
- In Properties, set Font Size to 24 and Bold.
- Align it to Center.
Step 3: Define Data
Ensure you have a data source connected (e.g., "Employees"). The Detail band represents one row of data.
Step 4: Add Fields
Drag 3 Text elements into the Detail band:
- Type
{Employees.FirstName}in the first box. - Type
{Employees.LastName}in the second. - Type
{Employees.Department}in the third.
Tip: You can also drag fields directly from the Data Dictionary.
Step 5: Add Headers
In the Page Header band (above Detail), add 3 Text elements to label your columns: "First Name", "Last Name", "Department". Make them Bold with a bottom border.
Step 6: Preview
Click the Preview button (Eye icon). You should see a list of all employees formatted as you designed!
Bands Explained
Bands are the containers for your elements. Understanding bands is key to mastering reporting.
| Band Type | Frequency | Example Content |
|---|---|---|
| Report Header | Once (Start) | Report Title, Company Logo, Cover Letter |
| Page Header | Every Page (Top) | Column Headers, Date, "Confidential" label |
| Group Header | Per Group | "Region: North", Group-specific charts |
| Detail | Per Record | Transaction rows, Product details |
| Group Footer | Per Group | Subtotals: {Sum(Sales)}, "Total for North" |
| Report Footer | Once (End) | Grand Totals, Signatures, Terms & Conditions |
| Page Footer | Every Page (Bottom) | Page Numbers ({PageNumber}), Copyright |
| Summary | Once (End, separate page) | Executive summary, final charts |
| No Data | If query is empty | "No records found for this period." |
Grouping Data
Grouping allows you to organize data into logical sections, such as Sales by Region or Employees by Department.
How to Create a Group
- Right-click on the Detail band or use the "Add Group" button.
- Define the Group Expression (e.g.,
$F{Region}). whenever this value changes, a new group starts. - This creates a Group Header (before the details) and a Group Footer (after the details).
Group-Level Aggregations
In the Group Footer, you can calculate totals specifically for that group.
$V{TotalSales_Region}Reset Type: "Group"
Elements Guide
T Text Element
The most versatile element. Supports both static text and expressions.
- Mixed Content:
Total: $F{Amount:currency} - Font Styling: Customize font, size, weight, and color in the Properties panel.
- Alignment: Precise control over horizontal and vertical text alignment.
β¦ Table & Grid
Tables provide structured row/column layout, while Grids offer flexible responsive layouts.
- Dynamic Columns: Tables can automatically generate columns based on data.
- Alternate Row Colors: Built-in support for zebra-striping.
- Grid Layout: Use the Grid element for complex dashboard layouts (CSS Grid-like).
π Chart & Visualization
Visualize your data with Bar, Line, Pie, Area, Scatter, and Donut charts.
- Series Mapping: Map data fields to X and Y axes.
- Styling: Control colors, legends, and axis titles.
- Barcode/QR: Generate standard barcodes (Code128, EAN) or QR codes from field data.
π Advanced Layouts
- Subreports: Embed another report for master-detail views.
- CrossTab: Create pivot tables for complex data intersections (Row/Column summaries).
- Rich Text: Use the Tiptap-powered editor for formatted paragraphs.
Data Sources
The Data Dictionary panel is where you define and manage data sources for a project. These sources are shared across all reports in the same project.
Add a Data Source
- Open the Data Dictionary panel.
- Click the + button.
- Enter a name and choose a type.
- For SQL or REST, provide the query or URL.
Use Fields
- Expand a source to see its fields once a schema is available.
- Drag a field onto the canvas to create a bound Text element.
- Reference fields in expressions with
$F{fieldName}.
SQL (Relational)
Use a SQL query to fetch rows from a configured database connection.
JSON / REST API
Connect to a REST endpoint and provide a URL that returns JSON data.
Parameter
Use report parameters as a lightweight data source for parameter-driven elements.
Data Binding ($F, $P, $V, $R)
ZReport uses a JasperReports-style syntax to reference data. Every data-bound expression must be wrapped in curly braces {...} with a prefix.
$F (Fields)
Raw data from your data source. Use for row-level details.
$F{Name}$P (Parameters)
Global inputs passed at runtime (e.g. Date ranges, User filters).
$P{StartDate}$V (Variables)
Calculated values like sums or counts. Managed by the engine.
$V{TotalSales}$R (Rich Content)
Structured rich content (formatted text, tables, images) from a parameter. See docs.
$R{content}Parameters & Variables Detail
Parameters ($P)
Parameters are external inputs. You define them in the Report Inspector.
- Types: String, Number, Boolean, Date.
- Prompting: If
Is For Promptingis checked, the user will see a dialog form before running the report. - Default Value: Fallback value if no input is provided.
System Variables
ZReport provides built-in variables to handle page numbering and report metadata.
| Variable | Description |
|---|---|
| {PageNumber} | Current page index |
| {TotalPages} | Total page count (valid in Page headers/footers) |
| {RecordNumber} | Current row index (1-based) |
| {PrintDate} | Report generation timestamp |
| {GroupRecordNumber} | Row index within current group |
Formatting & Patterns
Apply display patterns to your data using the colon : syntax within the expression.
Number & Currency
$F{Price:currency}β $1,234.56$F{Quantity:number:0}β 1235 (Rounded)$F{Score:number:2}β 98.50FORMAT(1234.5, 2, ",")β 1,234.50
Dates & Time
Default format is yyyy-MM-dd if no pattern is specified.
$F{CreatedAt:date:yyyy-MM-dd}β 2024-03-15$F{CreatedAt:date:MMM dd, yyyy}β Mar 15, 2024$F{CreatedAt:date:HH:mm}β 14:30
Date Management Detail
Dates are stored as real Date values. If you render a date without a format, ZReport will display it using the short default format yyyy-MM-dd.
Date value.{PrintDate} is the current timestamp.NOW() for date+time and TODAY() for date-only values.Date values are rendered in the browser's local timezone. If you need a specific timezone, pass a preformatted string or include the timezone in the data source.
Text Transformation
$F{Code:uppercase}β ABC-123$F{Status:capitalize}β Active
Functions Reference
ZReport includes a powerful functional library with 50+ functions for calculations and data manipulation.
Logical
- IF(cond, true, false) - Ternary logic
- ISNULL(val) - Checks if value is null
- IFNULL(val, default) - Coalesce null
- COALESCE(a, b, ...) - First non-null value
- ISEMPTY(val) - Null or empty string
- ISBLANK(val) - Null or whitespace
- AND(a, b) / OR(a, b) / NOT(a) - Logical gates
- SWITCH(val, case1, res1, ...) - Multi-case match
- BETWEEN(val, min, max) - Range check
- INLIST(val, a, b, ...) - List membership
Math
- SUM(f) / AVG(f) / MIN(f) / MAX(f) - Aggregations
- ROUND(val, decimals) - Rounding
- FLOOR(val) / CEIL(val) - Round down/up
- ABS(val) - Absolute value
- MOD(n, d) - Modulo remainder
- POW(base, exp) - Power
- SQRT(val) - Square root
String
- CONCAT(a, b, ...) - Join text
- LEFT/RIGHT/MID(str, n) - Substring
- REPLACE(str, find, rep) - Search & replace
- CONTAINS(str, sub) - Search text
- UPPER(str) / LOWER(str) - Case conversion
- PROPER(str) - Capitalize each word
- TRIM(str) - Remove whitespace
- PADLEFT/PADRIGHT(str, len, char) - Padding
- REPEAT(str, n) - Repeat text
- REVERSE(str) - Reverse text
- WORDCOUNT(str) - Count words
- LEN(str) - String length
Date
- NOW() / TODAY() - Current time/date
- YEAR/MONTH/DAY(date) - Extract parts
- DATEADD(date, n, unit) - Add days/months/years
- DATEDIFF(d1, d2) - Diff in days
- FORMATDATE(date, pattern) - Custom format
- AGE(birthdate) - Calculate age in years
Conversion
- TOSTRING(val) - Convert to string
- TONUMBER(val) - Convert to number
- TODATE(val) - Convert to date
- FORMAT(num, decimals, sep) - Number format
- CURRENCY(num, symbol, dec) - Currency format
- PERCENTAGE(num, decimals) - Percentage format
Conditional Logic
You can hide elements or entire bands based on data conditions using the printWhen property.
How it works:
The engine evaluates your expression. If it results in false, 0, or an empty string "", the element is not rendered.
Inline Logic (IF)
Use the IF function for dynamic content within a single text box.
Preview & Exporting
Formats
Click the Preview button to generate the report.
- PDF: High-fidelity print-ready documents (vector based).
- Excel: Data-heavy reports. Preserves numeric types and formulas where possible.
- JSON: Download raw data or report definition.
- HTML: Web-native view.
Saving & Loading
Persistence
When working within a project, ZReport autosaves your changes every 60 seconds. You can manually save at any time using Ctrl+S.
Reports are stored in the database. You can also download the .zreport JSON definition file to version control locally.
Keyboard Shortcuts
General
Tools & Canvas
Limits & Troubleshooting
Expressions not rendering?
Ensure every expression starts with $F, $P, or $V and is wrapped in {...}. Plain braces without a prefix are treated as legacy fields.
TotalPages is always 0?
TotalPages is calculated at the very end of the rendering process. It is only valid in Page Header, Page Footer, or Summary bands.
Current Limitations:
- Charts require at least one category and one value field to render.
- Subreports must exist in the same project to be referenced.
- CrossTab elements are currently limited to one row and one column grouping.
- Rich Text formatting is limited to basic bold/italic/underline and lists in PDF exports.
Expression Engine
The expression parser evaluates JasperReports-style expressions at runtime.
Syntax
| Pattern | Type | Example |
|---|---|---|
| $F{field} | Data field | $F{customerName} |
| $F{source.field} | Qualified field | $F{orders.total} |
| $P{param} | Parameter | $P{reportTitle} |
| $V{var} | Variable | $V{subtotal} |
| $V{SystemVar} | System variable | $V{PageNumber} |
| $R{param} | Rich content | $R{content} |
Format Specifiers
Append :format to apply formatting:
$F{price:currency}β Format as currency$F{date:date:yyyy-MM-dd}β Format as date$F{name:uppercase}β Convert to uppercase$F{code:pad:6:0}β Left-pad with zeros
System Variables
PageNumberβ Current page numberTotalPagesβ Total page count (available after render)PrintDateβ Current date/timeReportNameβ Name of the reportRecordNumberβ Current record indexGroupRecordNumberβ Record index within current group
Rich Content & Rich Text Elements
Rich content is a JSON array of blocks called RichContent (headings, styled paragraphs, lists, tables, images). There are two ways to fill a block at export time β Method 1: a $R{paramName} expression on a Text element, and Method 2: a bindParam on a dedicated Rich Text element. Both read the array from parameters, so the request body is identical. An advanced id-map override is also available when you POST an inline template.
Pick the method by element type. $R{paramName} is evaluated only on a Text element (Method 1). A Rich Text element ignores $R{paramName} and prints it literally β bind that element with bindParam (Method 2) instead. Either way, the element must have canGrow: true (or no canGrow), or the content is clipped to the box.
Method 1 β $R{paramName} on a Text element
The simplest method, and the only one fully supported in the visual designer. Add a Text element, set its content to $R{paramName}, enable Can Grow, then pass the RichContent JSON array as that parameter. Works with a saved report (reportId / reportName) or an inline template.
- Drag a Text element onto the band.
- Set its content to
$R{bodyContent}(any parameter name works). - Enable Can Grow in the element's properties.
- POST the matching parameter as a
RichContentarray:
{
"reportId": "rpt_abc123",
"parameters": {
"bodyContent": [
{
"type": "paragraph",
"align": "center",
"content": [
{ "text": "Normal text " },
{ "text": "bold text", "bold": true },
{ "text": " colored", "color": "#ff0000", "fontSize": 14 }
]
},
{ "type": "heading", "level": 2, "content": [{ "text": "Section Title" }] },
{ "type": "hr" }
]
}
}The parameter name inside $R{β¦} must exactly match the key in parameters, and the value must be a real JSON array β not a JSON-encoded string.
Method 2 β bindParam on a Rich Text element
Use a dedicated richtext element and set its bindParam to a parameter name. At export time the engine resolves parameters[bindParam] to that element's content, so you send the same RichContent array under parameters as in Method 1.
Note: a richtext element does not evaluate $R{paramName} β that is Method 1's Text-element behavior. There is also no field for bindParam in the visual designer yet, so set it directly in the template JSON: either POST an inline template (below) or edit the saved report definition.
{
"template": {
"pages": [
{
"bands": [
{
"type": "detail",
"elements": [
{
"id": "student-comment",
"type": "richtext",
"x": 10,
"y": 10,
"width": 180,
"height": 40,
"bindParam": "commentBody",
"canGrow": true
}
]
}
]
}
]
},
"format": "pdf",
"parameters": {
"commentBody": [
{
"type": "paragraph",
"content": [
{ "text": "Overall performance: ", "bold": true },
{ "text": "Very good" }
]
}
]
}
}Advanced β id-map override by element id
When you POST an inline template (not a saved reportId), you can override any richtext element by its id without setting bindParam. The key in richContent must match the element id, so one template can render different rich text per request. The reportId / reportName Export API routes do not read this richContent map β use Method 1 or Method 2 for saved reports.
{
"template": {
"version": "1.0",
"metadata": {
"id": "student-report",
"name": "Student Report",
"createdAt": "2026-06-01",
"updatedAt": "2026-06-01"
},
"settings": {
"paperSize": { "type": "A4" },
"orientation": "portrait",
"margins": { "top": 20, "right": 20, "bottom": 20, "left": 20 },
"units": "mm"
},
"dataSources": [],
"parameters": [],
"variables": [],
"styles": [],
"pages": [
{
"id": "page-1",
"name": "Page 1",
"bands": [
{
"id": "detail",
"type": "detail",
"height": 80,
"elements": [
{
"id": "student-comment",
"type": "richtext",
"x": 10,
"y": 10,
"width": 180,
"height": 40,
"canGrow": true,
"defaultFont": { "family": "Helvetica", "size": 12 },
"defaultColor": "#000000"
}
]
}
]
}
]
},
"format": "pdf",
"richContent": {
"student-comment": [
{
"type": "heading",
"level": 2,
"content": [{ "text": "Conduct" }]
},
{
"type": "paragraph",
"content": [
{ "text": "Punctuality: ", "bold": true },
{ "text": "Excellent", "color": "#047857" }
]
},
{
"type": "list",
"ordered": false,
"items": [
{ "content": [{ "text": "Participates actively in class" }] },
{ "content": [{ "text": "Completes assignments on time" }] }
]
}
]
}
}Rich Text Resolution Order
When a richtext element is rendered, ZReport uses the first structured content source it finds:
richContent[element.id]in the request body.parameters[element.bindParam], whenbindParamis set.element.richContent, the content saved by the designer.element.content, legacy HTML fallback.
For PDFs, canGrow: true or an omitted canGrow allows rich text to flow across pages when it exceeds the element box. Set canGrow: false to clip content to the element box.
Block Types
| Type | Description | Key Properties |
|---|---|---|
| paragraph | Text paragraph with inline formatting | align, content |
| heading | Heading (h1-h6) | level (1-6), content |
| list | List with configurable style (bullets, numbers, letters, roman, checkboxes) | style, items |
| table | Table with headers and rows | headers, rows |
| image | Inline image (base64 data URI) | src, width, height |
| hr | Horizontal rule / divider | β |
Inline Text Properties
Each block that accepts content takes an array of inline text objects with these properties:
| Property | Type | Description |
|---|---|---|
| text | string (required) | The text content |
| bold | boolean | Bold text |
| italic | boolean | Italic text |
| underline | boolean | Underlined text |
| strikethrough | boolean | Strikethrough text |
| color | string | Text color (hex, e.g. "#ff0000") |
| fontSize | number | Font size in points |
| fontFamily | string | Font family name |
| link | string | URL to make the text a hyperlink |
List Styles
Set the style property on a list block to control the marker type. If omitted, defaults to "bullet" (or "number" when ordered: true for backward compatibility).
| Style | Marker | Example |
|---|---|---|
| bullet | Filled circle | β’ Item |
| number | 1, 2, 3... | 1. Item |
| letter-upper | A, B, C... | A. Item |
| letter-lower | a, b, c... | a. Item |
| roman-upper | I, II, III... | I. Item |
| roman-lower | i, ii, iii... | i. Item |
| check | Checkbox | β Done / β Pending |
| radio | Radio button | β Selected / β Option |
For check and radio lists, each item accepts a checked boolean. It ticks the checkbox (β/β) for check, and fills the radio dot (β/β) for radio \u2014 set checked: true on the option you want pre-selected. radio is cosmetic (it renders the glyph; it does not enforce single-selection), so mark exactly one item if you want true radio semantics. In PDF the checkbox/radio markers are drawn as crisp vector shapes, so they never depend on font glyph coverage; DOCX uses the β/β glyphs. The canvas/HTML preview shows a plain list.
In the designer, the rich-text editor toolbar has a list-style dropdown (Bullet, Numbered, Letters, Roman, Checklist, Radio). For Checklist and Radio, select an item and use the β/β toggle button to mark it as checked / pre-selected.
Add "layout": "horizontal" to flow the items in a wrapping row instead of stacking them β ideal for a rating scale. Works with any style; markers and selection behave the same.
{
"type": "list",
"style": "radio",
"layout": "horizontal",
"items": [
{ "content": [{ "text": "Excellent" }], "checked": false },
{ "content": [{ "text": "Very Good" }], "checked": true },
{ "content": [{ "text": "Good" }], "checked": false },
{ "content": [{ "text": "Fair" }], "checked": false },
{ "content": [{ "text": "Poor" }], "checked": false }
]
}{
"type": "list",
"style": "check",
"items": [
{ "content": [{ "text": "Design mockups" }], "checked": true },
{ "content": [{ "text": "API integration" }], "checked": true },
{ "content": [{ "text": "Write tests" }], "checked": false }
]
}A radio list with one pre-selected option (the item with checked: true renders as β, the rest as β):
{
"type": "list",
"style": "radio",
"items": [
{ "content": [{ "text": "Pass" }], "checked": true },
{ "content": [{ "text": "Pass with corrections" }], "checked": false },
{ "content": [{ "text": "Fail" }], "checked": false }
]
}Full Example
[
{
"type": "heading",
"level": 1,
"content": [{ "text": "Invoice Summary" }]
},
{
"type": "paragraph",
"content": [
{ "text": "Thank you for your order. " },
{ "text": "Order #12345", "bold": true },
{ "text": " has been confirmed." }
]
},
{
"type": "table",
"headers": [
{ "content": [{ "text": "Item" }] },
{ "content": [{ "text": "Qty" }] },
{ "content": [{ "text": "Price" }] }
],
"rows": [
[
{ "content": [{ "text": "Widget A" }] },
{ "content": [{ "text": "2" }] },
{ "content": [{ "text": "$49.99" }] }
]
]
},
{
"type": "list",
"style": "bullet",
"items": [
{ "content": [{ "text": "Free shipping included" }] },
{ "content": [{ "text": "30-day return policy", "italic": true }] }
]
},
{
"type": "list",
"style": "check",
"items": [
{ "content": [{ "text": "Payment received" }], "checked": true },
{ "content": [{ "text": "Order shipped" }], "checked": false }
]
},
{ "type": "hr" },
{
"type": "paragraph",
"align": "center",
"content": [
{ "text": "Visit our website", "link": "https://example.com", "color": "#0563C1" }
]
}
]Supported in PDF and DOCX exports. The element's position and dimensions (x, y, width, height) from the report designer are used to place the rich content block.
Export Services
ZReport supports exporting rendered reports to multiple formats.
High-fidelity vector PDF using @react-pdf/renderer. Supports text, images, tables, charts (rasterized), and barcodes.
Excel
Spreadsheet export using exceljs. Preserves numeric types, applies basic styling, and maps tables to worksheets.
HTML
Web-native HTML output. Useful for embedding reports in portals or sending via email.
DOCX
Word document export using docx. Best for text-heavy reports that need editing downstream.
Export API
Generate reports in various formats programmatically using our REST API.
Base URL
https://api.zreport.cloudAuthentication
All API requests require an API key. Include it in the Authorization header:
Authorization: Bearer YOUR_API_KEY
Generate API keys from your organization settings at Settings β API Keys.
Export Endpoints
/api/export/pdfExport a report as PDF.
POST https://api.zreport.cloud/api/export/pdf
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
{
"reportId": "rpt_abc123",
"data": {
"orders": [
{ "id": 1, "product": "Widget", "amount": 99.99 }
]
},
"parameters": {
"title": "Q4 Sales Report",
"startDate": "2024-10-01"
}
}/api/export/excelExport a report as Excel (.xlsx). Same request body as PDF.
/api/export/htmlExport a report as HTML. Same request body as PDF.
/api/export/docxExport a report as Word document (.docx). Same request body as PDF.
Code Examples
Playground
Test the Export API directly from this page.
Response
On success, the API returns the file as a binary stream with appropriate content-type headers. On error:
{
"success": false,
"error": {
"code": "INVALID_REQUEST",
"message": "Report ID is required"
}
}Rate Limits
| Plan | Exports/day |
|---|---|
| Free | 100 |
| Pro | 5,000 |
| Enterprise | Unlimited |
MCP Server
ZReport exposes a Model Context Protocol server that lets AI agents (Claude Desktop, Claude Code, ChatGPT, custom integrations) read, generate, and design reports in your organization β programmatically, in natural language.
1POST https://api.zreport.cloud/mcp2Authorization: Bearer zr_β¦3Accept: application/json, text/event-stream
Prerequisites
- A ZReport organization with at least one project.
- An API key generated from Settings β API Keys (or via
POST /api/v1/organizations/:orgId/api-keys). Tokens carry thezr_prefix and are shown only once at creation. Legacymr_tokens issued before the rebrand keep working.
Connecting Clients
Most AI clients speak stdio MCP, so we bridge to the HTTP transport via the mcp-remote helper.
Claude Desktop
Edit your Claude Desktop config:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
1{2 "mcpServers": {3 "zreport": {4 "command": "npx",5 "args": [6 "-y",7 "mcp-remote",8 "https://api.zreport.cloud/mcp",9 "--header",10 "Authorization: Bearer zr_abc123..."11 ]12 }13 }14}
Restart Claude Desktop. The 24 ZReport tools appear in the tools panel.
Claude Code
Drop the same block into .mcp.json at your workspace root, or into ~/.claude/mcp.json for a global config.
ChatGPT (Custom GPTs)
- Create a new Custom GPT.
- Open Connectors β Add MCP Server.
- URL:
https://api.zreport.cloud/mcp - Authorization header:
Bearer zr_abc123...
MCP Inspector (Testing)
For one-off debugging, run the official inspector:
1npx @modelcontextprotocol/inspector
Opens a UI at http://localhost:6274. Choose Streamable HTTP, paste the ZReport URL, add the Authorization header, and click Connect.
Tools & Resources
The server exposes 24 tools across three groups, plus rich resource feeds for schemas and existing reports.
Read
| Tool | Purpose |
|---|---|
list_projects | List projects in the organization. |
list_reports | List reports in a project. |
get_report | Fetch the full ReportDefinition JSON. |
list_data_sources | List configured data sources. |
get_data_source_schema | Field names and types for a data source. |
preview_data_source | Up to 50 sample rows. |
Generate
| Tool | Purpose |
|---|---|
generate_report | Render to PDF, Excel, or DOCX. Returns inline base64 plus a single-use 1-hour download URL. |
Design
| Tool | Purpose |
|---|---|
create_report | Create a blank report. Returns page geometry in mm. |
apply_operations β | Batched design β preferred. Apply many ops atomically in one transaction. See below. |
replace_report | Wholesale replace of the entire ReportDefinition. Use for import-template flows; prefer apply_operations for incremental edits. |
set_data_source | Bind a data source to a report. |
add_parameter | Define a report parameter (referenced as $P{name}). |
add_field | Declare an ad-hoc field ($F{name}). |
add_variable | Computed variable, e.g. sum($F{amount}), referenced as $V{name}. |
add_band / update_band | Page Header, Detail, Group, Summary, Footer, etc. Heights in mm. |
add_group | Add a grouping level with header/footer bands. |
add_element / update_element / remove_element | Place and edit text, image, table, chart, barcode, shape, etc. All coordinates are in mm. |
add_page / remove_page / update_page / duplicate_page | Manage multi-page reports. Each page renders as its own section β PDF starts a fresh paper-page sequence, DOCX a new section, Excel a new worksheet. |
Multi-page reports
Reports can carry multiple pages (ReportDefinition.pages: Page[]). Each page has its own bands and renders as a distinct section:
- PDF: each page begins a fresh paper-page sequence;
$V{PageNumber}counts continuously across the whole document. - DOCX: each page becomes its own section β new sections start on a new physical page.
- Excel: each page becomes its own worksheet, named from
Page.name.
The band and element tools target a specific page where applicable:add_band and add_group accept an optional pageId(defaults to the first page); single-instance bands like pageHeader /detail / pageFooter are scoped per page β each page can have its own. update_band, add_element,update_element, and remove_element look their target up by id across every page, so they work regardless of which page it lives on.
Batched design with apply_operations
For anything more than a one-line tweak, batch your mutations into a singleapply_operations call. Each entry has the same shape as the matching single-op tool, minus the top-level reportId, plus an op discriminator (named op rather than type because several schemas already usetype for the value's own type and would otherwise collide):
1{2 "reportId": 42,3 "operations": [4 { "op": "set_data_source", "dataSourceId": "..." },5 { "op": "add_parameter", "name": "asOf", "type": "date" },6 { "op": "add_band", "type": "pageHeader", "height": 30 },7 { "op": "add_element", "bandId": "band-...", "element": { /* full element */ } },8 { "op": "update_band", "bandId": "band-...", "height": 25 }9 ]10}
- Atomic. All ops run in one db transaction. If any fails, nothing is persisted.
- Pre-validation. Element shapes parse before the transaction opens; Zod errors come back with paths like
operations[3].element.font.size. - Error locator. Failures include
data.opIndexanddata.opTypeso the model can target the fix. - Cap: 200 ops per call. Split into turns above that.
Resources
Beyond tools, the server publishes resource URIs that agents can fetch for richer context:
| URI | Contents |
|---|---|
zreport://reports/{reportId} | Full ReportDefinition JSON for an existing report. |
zreport://schemas/element/{type} | JSON Schema for each of the 14 element variants (text, image, table, chart, barcode, β¦). |
zreport://schemas/report-element | Discriminated union over all 14 element types. |
zreport://schemas/expression-functions | Catalog of 70+ expression functions with signatures and examples. |
Expressions
Most element fields and variables accept the same expression syntax used in the designer:
$F{fieldName}β current row's field value$P{paramName}β scalar parameter passed at generation time$V{variableName}β computed variable (often an aggregate)$R{paramName}β rich content (HTML / structured text) sourced from a parameter. Use inside atextelement withisRichText: trueto inject formatted text, tables, or images. Distinct from$P, which yields a plain scalar.- Function calls β
sum($F{amount}),format($F{date}, 'yyyy-MM-dd'),upper($F{name})
Typical Workflow
- Discover β
list_projects,list_data_sources,get_data_source_schema. - Create β
create_report,set_data_source,add_parameter. - Design β
add_band,add_group,add_element. - Populate β
add_field,add_variable,update_elementwith expressions. - Generate β
generate_reportfor a sample preview. - Iterate β refine via
update_elementandupdate_band. - Export β final
generate_reportin PDF / Excel / DOCX.
Limits, Errors & Security
Rate Limits (per API key)
| Endpoint | Default | Notes |
|---|---|---|
General /mcp | 600 / min | All read & design tools. |
generate_report | 200 / min | Resource-intensive renders. |
preview_data_source | 300 / min | Hits live data sources. |
Generated files larger than 25Β MB return a download URL only (no inline base64). Download URLs are single-use and expire after 1 hour.
Error Kinds
MCP errors carry a structured data.kind field for programmatic handling:
| Kind | Meaning |
|---|---|
validation | Input failed Zod schema. Inspect the issues array. |
not_found | Resource missing. Also returned for cross-org access (prevents leakage). |
invariant_failure | Document constraint violation (duplicate IDs, circular refs, β¦). |
data_source_error | Underlying connection or query failed. |
generation_failed | Renderer threw β unsupported combinations, missing fonts, etc. |
quota_exceeded | Per-tool rate limit tripped. Wait and retry. |
unsupported | Feature not yet implemented. |
Troubleshooting
| Symptom | Likely Cause & Fix |
|---|---|
| 401 Unauthorized | Bad / missing Authorization: Bearer zr_β¦ header. Verify or regenerate the token. |
| 406 Not Acceptable | Missing Accept: application/json, text/event-stream. The MCP SDK requires both values. |
| 404 on a download URL | URL expired (>1h) or already claimed. Regenerate the report. |
unsupported for format: 'html' | Server-side HTML export isn't implemented. Use pdf, excel, or docx. |
| Data source returns no rows | Use preview_data_source to diagnose; check the data source configuration and server logs. |
Privacy & Security
- API tokens are bearer credentials. Treat them like passwords β they grant full access to the issuing organization.
- Tokens are SHA-256 hashed at rest. The full value is shown only once at creation; lost tokens must be regenerated.
- Tokens are redacted from access logs (URL-path tokens are rewritten before logging) and from audit arguments (sensitive keys like
password,token,secretare replaced with***). - Cross-org access returns
not_found, neverforbiddenβ this prevents disclosing the existence of resources owned by other organizations.