Create and Manage Report Packages
Create and manage PBRS report packages through the REST API, including package schedules, report members, output settings, and delivery destinations.
A report package brings multiple reports into one scheduled delivery workflow. Use packages to deliver related reports together, combine compatible outputs into a PDF or Excel file, or generate reports from a data driver.
For a complete PowerShell example that creates a package containing two Power BI reports, exports them as PDFs to a folder, and monitors execution, see Create a Report Package and Execute It.
The PBRS REST API supports package definitions containing Power BI, Power BI paginated, SQL Server Reporting Services, and Power BI Report Server reports.
Before you begin
You need:
- A running PBRS API service.
- A valid access token and permission to modify schedules.
- A destination folder in PBRS.
- Configured reporting accounts with access to the reports.
- The source details, filters, parameters, and rendering settings required by each report.
- Valid email recipients or an accessible disk destination.
Send these headers with JSON requests:
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
Accept: application/jsonThe examples use illustrative IDs, report URLs, recipients, and paths. Replace them with values from your environment.
Package endpoints
| Action | Request | Successful response |
|---|---|---|
| Create a package schedule | POST /api/PackageSchedule/Create | Object containing uniqueid |
| Retrieve a package schedule | GET /api/PackageSchedule/Get?Id=201 | Package definition |
| Search package schedules | GET /api/PackageSchedule/GetAll with a JSON body | Array of package definitions |
| Update a package schedule | POST /api/PackageSchedule/Update | Submitted package model |
| Delete a package schedule | DELETE /api/PackageSchedule/Delete?Id=201 | JSON boolean |
| Clone a package | GET /api/Package/Clone?Id=201 | New package ID as a JSON integer |
| List reports in a package | GET /api/Package/GetReportsInPackage?Id=201 | Array of report summaries |
| Retrieve through the Package controller | GET /api/Package/Get?Id=201 | Package definition |
| Create through the Package controller | POST /api/Package/Create | Object containing uniqueid |
This guide uses PackageSchedule/Create and PackageSchedule/Update for authoring.
Package/Create is a separate operation with different validation behavior. Do not treat the two create endpoints as interchangeable fallbacks.
Understand the package definition
A package combines its own schedule and destinations with source-specific report collections.
| Field | Purpose |
|---|---|
ScheduleName | Package schedule name |
FolderPath | Folder path within PBRS |
uniqueid | Existing package ID; required for updates |
Description | Package description |
Keywords | Searchable package keywords |
Schedule | Recurrence and enabled state |
EmailDestinations | Complete email destination collection |
DiskDestinations | Complete disk destination collection |
PowerBIReports | Power BI report definitions |
PowerBiPaginatedReports | Power BI paginated report definitions |
SsrsReports | SSRS report definitions |
PBIRSReports | Power BI Report Server report definitions |
DataDriven | Whether the package uses a data driver |
DataDriver | JSON or SQL data-driver configuration |
MergePDFFiles | PDF merging setting |
MergedPDFFileName | Merged PDF filename |
MergeExcelFiles | Excel merging setting |
MergedExcelFileName | Merged Excel filename |
GroupByEmail | Email grouping setting for data-driven packages |
Use ScheduleName for the package name.
Always include both destination arrays. Use [] for an unused destination type. For a standard package without a data driver, provide at least one email or disk destination.
Include each report in the collection matching its source. Use empty arrays for unused report collections.
Create a package
The following example creates a package containing two Power BI reports and one email destination. Both reports use PDF output, and the package requests a merged PDF.
The recurrence starts disabled so that you can inspect the saved configuration before enabling scheduled delivery.
POST /api/PackageSchedule/Create{
"ScheduleName": "Daily sales package",
"FolderPath": "/API Reports",
"Description": "Sales overview and detail reports",
"Keywords": "api,sales",
"Schedule": {
"Frequency": "Daily",
"StartDate": "2026-10-01",
"ExecutionTime": "09:00",
"HasEndDate": false,
"Repeat": false,
"Enabled": false,
"DailyRepeatInterval": 1
},
"EmailDestinations": [
{
"DestinationName": "Operations email",
"DestinationType": "Email",
"Enabled": true,
"OutputFormat": "Acrobat Format (*.pdf)",
"To": [
"[email protected]"
],
"Cc": [],
"Bcc": [],
"Subject": "Daily sales package",
"Body": "Attached are the daily sales reports.",
"BodyFormat": "TEXT"
}
],
"DiskDestinations": [],
"DataDriven": false,
"GroupByEmail": false,
"MergePDFFiles": true,
"MergedPDFFileName": "Daily sales package.pdf",
"MergeExcelFiles": false,
"PowerBIReports": [
{
"ReportName": "Sales overview",
"PowerBiAccountId": "7f320302-7ccb-4aba-a052-56197424ab36",
"ObjectType": "REPORT",
"ReportUrl": "https://app.powerbi.com/reportEmbed?reportId=64e25062-4786-43be-bfea-56ec8b9aaeca",
"PowerBiReportName": "Sales overview",
"WorkspaceName": "Sales",
"OutputFormat": "Acrobat Format (*.pdf)",
"OrderNumber": 0,
"RenderingSettings": {
"MinLoadingTime": 10,
"MaxLoadingTime": 60,
"PageWidth": 1300,
"PageHeight": 800,
"PagesToRender": "1",
"RenderingMethod": 1
},
"BasicFilters": [],
"AdvancedFilters": []
},
{
"ReportName": "Sales detail",
"PowerBiAccountId": "7f320302-7ccb-4aba-a052-56197424ab36",
"ObjectType": "REPORT",
"ReportUrl": "https://app.powerbi.com/reportEmbed?reportId=6bbcb4d2-e2a1-445a-8d73-9f90533455e6",
"PowerBiReportName": "Sales detail",
"WorkspaceName": "Sales",
"OutputFormat": "Acrobat Format (*.pdf)",
"OrderNumber": 1,
"RenderingSettings": {
"MinLoadingTime": 10,
"MaxLoadingTime": 60,
"PageWidth": 1300,
"PageHeight": 800,
"PagesToRender": "1",
"RenderingMethod": 1
},
"BasicFilters": [],
"AdvancedFilters": []
}
],
"PowerBiPaginatedReports": [],
"SsrsReports": [],
"PBIRSReports": []
}Before submitting:
- Replace the account IDs and report URLs with discovered values.
- Set
FolderPathto the intended PBRS folder. - Choose the required start date and execution time.
- Review the recipients.
- Adjust the rendering settings for each report.
ExecutionTime is local PBRS schedule time, not a UTC timestamp. In this example, "PagesToRender": "1" selects only the first page of each report. Use the page selection required for your reports.
A successful request returns HTTP 200:
{
"uniqueid": 201
}Save this package ID.
Retrieve the package and its reports
Read the saved package:
GET /api/PackageSchedule/Get?Id=201Inspect the recurrence, report collections, destinations, and merge settings.
To retrieve report summaries separately:
GET /api/Package/GetReportsInPackage?Id=201The summary response is useful for inspecting package membership. It is not a complete package update request.
Keep identifiers separate
| Identifier | Meaning |
|---|---|
Package-level uniqueid | The package schedule |
Report-level uniqueid | An individual report member |
DestinationId | An individual destination |
ExecutionId | One execution of the package |
Use the package-level uniqueid when retrieving, updating, cloning, deleting, or executing the package.
Include different report sources
A package can contain reports from more than one source. Each report must use the correct collection and field structure.
| Collection | Source details and settings |
|---|---|
PowerBIReports | Power BI account, report URL, workspace, filters, bookmarks, rendering settings, and applicable Excel export settings |
PowerBiPaginatedReports | Power BI account, report URL, workspace, paginated parameters, and native rendering selection |
SsrsReports | Reporting account, virtual directory, report path, data-source credentials, and SSRS parameters |
PBIRSReports | Reporting account, virtual directory, report path, rendering settings, and applicable parameters |
Report member schemas are source-specific. For example, the packaged Power BI and paginated report schemas expose OutputFormat and CustomName; do not add those properties to other member types unless their request schema supports them.
For Power BI reports:
- Set
ApplyBookmarktotrueonly when supplying a nonemptyBookmarkJson. - Preserve the report’s required filters.
- Supply
PowerBIDataOnlyExportSettingswhen exporting visual data usingMS Excel - Data Only (*.xlsx).
Each report’s configured account must have access to its source. Including a report in a package does not grant access to that report.
Set report order and merge output
Use each report member’s OrderNumber to express its intended order. Assign distinct values, such as 0, 1, and 2, and verify the resulting order in the generated output.
Merge PDF output
Include these fields in the complete package definition:
{
"MergePDFFiles": true,
"MergedPDFFileName": "Daily sales package.pdf"
}Configure the participating reports to generate compatible PDF output.
Merge Excel output
Include these fields in the complete package definition:
{
"MergeExcelFiles": true,
"MergedExcelFileName": "Daily sales package.xlsx"
}Configure the participating reports to generate supported Excel output and provide any required export settings.
Merge settings combine compatible generated files. They do not convert arbitrary output types into PDF or Excel.
Package threading and text-merge settings are not writable through these package authoring operations. Do not copy MultiThreaded, MergeTextFiles, or MergedTextFileName from a read response into a create or update request. ThreadCount is also not a supported package authoring input.
Configure delivery destinations
Package destinations are included in the package definition.
For email destinations, provide recipient arrays and the required message and output settings. For disk destinations, provide an OutputPath accessible from the executing PBRS machine.
For example, this object can be included in DiskDestinations:
{
"DestinationName": "Sales archive",
"DestinationType": "Disk",
"Enabled": true,
"OutputFormat": "Acrobat Format (*.pdf)",
"OutputPath": "C:\\PBRSExports\\Sales"
}The execution account must have permission to write to the destination path.
Manage package destinations through the complete package update operation. The email destination endpoints under SingleSchedule are not package destination endpoints.
Configure a data-driven package
Set DataDriven to true and provide a non-null DataDriver.
Choose one data source:
| Source | DataDriverType | Required source configuration |
|---|---|---|
| JSON rows | 1 | Nonempty ValuesJson containing serialized row JSON |
| SQL query | 0 | Nonempty Query and DSN, plus required connection credentials |
Do not supply both sources in the same request. When both are present, SQL processing occurs after JSON validation; they are not a fallback pair.
The following examples are fields to incorporate into a complete package definition, not standalone package requests.
For a complete PowerShell example, follow Create a Data-Driven Report Package. The recipe uses JSON rows to filter two Power BI reports by agent name, verifies the saved package configuration, sends all generated reports to one fixed email recipient, and monitors the execution result.
JSON data driver
{
"DataDriven": true,
"DataDriver": {
"DataDriverType": 1,
"KeyField": "recipient_id",
"ValuesJson": "[{\"recipient_id\":\"1\",\"recipient_email\":\"[email protected]\"}]"
}
}ValuesJson is a string containing a JSON array of row objects. It is not a directly nested array.
Build the rows as structured data, serialize them into ValuesJson, and then serialize the complete package request. This avoids incorrect escaping.
A JSON-only package driver does not require a DSN.
SQL data driver
{
"DataDriven": true,
"DataDriver": {
"DataDriverType": 0,
"KeyField": "recipient_id",
"DSN": "PBRSReporting",
"UserId": "pbrs_reader",
"Password": "REPLACE_WITH_DATABASE_PASSWORD",
"Query": "SELECT recipient_id, recipient_email FROM dbo.ReportRecipients",
"Timeout": 30
}
}Have an administrator configure the ODBC DSN so that it is available to the PBRS service. Replace the example connection details and query with your configuration.
Package data drivers use Query. Do not substitute the single-schedule field name SQLQuery.
Creating or updating a SQL-driven package can contact the database to validate the driver. Allow for connection and query failures during the configuration request.
Choose the key field
KeyField names the data-driver field that uniquely identifies each record. In the examples above, that field is recipient_id.
Include the named field in every JSON row or in the SQL query result. Its values should be nonblank and unique across the records supplied to the driver.
The key identifies a driver record. It does not automatically select a report filter, bind an email recipient, or enable grouping. If several driver records belong to the same recipient, choose a field that uniquely identifies each record rather than reusing a recipient identifier that repeats.
Use driver values in report filters
A report filter can insert a value from the current data-driver record using this syntax:
<[r]fieldname>
Replace fieldname with the driver field containing the required filter value.
For example, prepare these driver rows:
[
{
"recipient_id": "101",
"agent_name": "Alex"
},
{
"recipient_id": "102",
"agent_name": "Morgan"
}
]The corresponding package-level driver configuration is:
{
"DataDriven": true,
"DataDriver": {
"DataDriverType": 1,
"KeyField": "recipient_id",
"ValuesJson": "[{\"recipient_id\":\"101\",\"agent_name\":\"Alex\"},{\"recipient_id\":\"102\",\"agent_name\":\"Morgan\"}]"
}
}For a Power BI report member, configure its agent-name filter with the driver insert in BasicValues:
{
"BasicFilters": [
{
"TargetField": "Sales.AgentName",
"FieldDataType": "String",
"FilterLevel": 0,
"Operator": "In",
"BasicValues": [
"<[r]agent_name>"
],
"IgnoreBlankDataDrivenInserts": false
}
]
}This filter object is a fragment of an individual report member in PowerBIReports, not a top-level package filter or a complete request. Include it in each report member that needs this filter, preserving all other required filters.
Replace Sales.AgentName with the actual target field in that report's model. The report field and the data-driver field serve different purposes:
| Setting | Purpose |
|---|---|
TargetField: "Sales.AgentName" | Selects the report field to filter. |
<[r]agent_name> | Supplies the current driver record's agent_name value. |
KeyField: "recipient_id" | Identifies the driver record. |
For the first record, the filter uses Alex. For the second, it uses Morgan. Verify that each generated report contains the intended agent's data and excludes unrelated agents.
This example uses report filter values. It does not establish that the same insert syntax is supported in every subject, filename, path, parameter, or recipient field.
For filter operators and targeting options, see Configure Filters, Parameters, Bookmarks, and Rendering.
Row values and recipient grouping
Providing a column named recipient_email does not automatically bind it to the email destination. Configure the intended data-driven inserts in the destination and report settings.
Match the driver’s column names to the configured inserts. Validate blank values, nulls, duplicate keys, and repeated recipient addresses before enabling recurring delivery.
GroupByEmail is the package field for requesting email-based grouping. Check the saved configuration and a controlled execution before relying on grouping in production. Confirm which files each recipient receives, particularly when several rows use the same address.
Grouping is not an access-control rule. Report filters, parameters, and recipient mappings must independently select the correct data and recipient.
Validate the driver and generated packages
Keep recurring delivery disabled and use controlled test destinations while checking the driver.
| Test case | What to verify |
|---|---|
| Two records with different filter values | Each record generates reports containing only the intended data. |
| Missing filter field | Identify the missing field before production use; inspect the execution result and any output in a controlled test. |
| Blank or null filter value | Verify the actual filter and output behavior. Do not assume a blank value safely excludes all data. |
| Missing or blank key | Ensure every record supplies a usable value for the field named by KeyField. |
| Duplicate key values | Detect and correct duplicate record identifiers before enabling recurring delivery. |
| Repeated email address with distinct record keys | Verify the intended GroupByEmail setting, the number of deliveries, and the files included in each delivery. |
| Different recipient mappings | Confirm each recipient receives only the reports and data intended for them. |
Do not assume invalid records will always be rejected, skipped, or grouped in a particular way. Check the execution result and generated output for the configured package.
Do not use IgnoreBlankDataDrivenInserts as a substitute for validating the driver. Keep filter validation separate from recipient and destination validation.
Update a package
Submit the complete intended package definition:
POST /api/PackageSchedule/UpdateInclude the existing package’s uniqueid.
An update is not a partial patch. Do not send only the changed package name, one report, or one destination.
Preserve the complete configuration
Before updating:
- Retrieve the current package.
- Combine the response with your authoritative configuration.
- Include the existing package-level
uniqueid. - Preserve the recurrence and intended enabled state.
- Include all report members that should remain.
- Preserve existing report member identifiers where supplied.
- Include both complete destination arrays.
- Preserve each retained destination’s
DestinationId. - Preserve filters, parameters, bookmarks, rendering settings, export settings, and the data driver.
- Apply the intended changes and submit the complete request.
Omitting an existing destination from its collection removes it. An empty destination array requests an empty collection; it does not mean “leave unchanged.”
A read response is not a lossless backup of every setting. Do not interpret missing fields as instructions to clear them, and do not submit read-only fields as authoring fields.
Understand the update response
A successful update returns HTTP 200 with the submitted package model.
This response is not an independent read of the saved package. Retrieve the package again to verify the result.
Preserve an existing SQL password
For PackageSchedule/Update, this exact value has special meaning:
{
"Password": "*****"
}When used in the existing package’s SQL DataDriver, it reuses that package’s stored password. An existing data-driver record must be available.
Changing the DSN, user ID, or query can trigger validation using the retained password. To change the password, supply the new value.
This preservation rule applies to package updates. Do not use the marker as a credential when creating a package.
Search package schedules
GetAll searches package keywords. Supply explicit, nonempty filter values.
This endpoint uses a JSON body with a GET request. Use a client that supports that combination.
curl --request GET "${PBRS_BASE_URL}/api/PackageSchedule/GetAll" \
--header "Authorization: Bearer ${PBRS_TOKEN}" \
--header "Content-Type: application/json" \
--data-raw '{
"FilterValues": ["sales"],
"FilterOperator": "LIKE",
"MatchType": "ALL"
}'Set PBRS_BASE_URL to the service origin without a trailing /api.
Supported operators are LIKE, NOT LIKE, =, and <>. Use ALL or ANY to combine comparisons when supplying multiple filter values.
Do not omit the body to request every package. Missing or empty filters trigger a generated search value. The default LIKE behavior normally finds nothing, while negative operators can behave differently.
Clone a package
GET /api/Package/Clone?Id=201A successful response contains the new package ID as a JSON integer:
202Although this endpoint uses GET, it creates persisted data. Do not automatically retry an ambiguous response because another request can create another copy.
Retrieve the new package and review its name, recurrence, enabled state, report members, destinations, and data-driver configuration before using it.
Execute and monitor a package
Use the package’s ID with ScheduleType set to package:
POST /api/Schedule/ExecuteScheduleAsync{
"ScheduleType": "package",
"uniqueid": 201,
"RunBy": "API"
}The response supplies an ExecutionId. Use it to retrieve execution status:
GET /api/Schedule/GetExecutionStatus?ExecutionId=RETURNED_EXECUTION_IDPoll at a bounded interval and inspect ResultJson when the execution finishes.
Completed does not necessarily mean success. Check the execution result and verify the actual files and delivery.
For a controlled first execution, confirm:
- Every intended report was generated.
- Filters and parameters selected the correct data.
- Report ordering and merged output are correct.
- Each recipient received only the intended files.
- Disk output reached the correct location.
- Data-driven grouping behaved as intended.
Enable recurring delivery only after verifying the configuration and output.
Delete a package
Confirm the package ID, then send:
DELETE /api/PackageSchedule/Delete?Id=201A successful deletion returns HTTP 200 with a JSON boolean:
trueDeletion does not recall previously delivered files. Do not use deletion as a substitute for cancelling an execution already in progress.
Handle errors and retries
Package operations can return HTTP 500 for validation, permission, database, or other processing failures. Inspect the available error details rather than assuming every failure is transient.
After a timeout or lost connection during creation, cloning, updating, or execution submission, check the package or execution state before retrying.
Use meaningful keywords and retain returned IDs to help reconcile uncertain outcomes. These values do not provide an idempotency guarantee.
| Problem | What to check |
|---|---|
| Package creation fails | Required fields, folder path, reporting accounts, report definitions, recurrence, and both destination arrays |
| SQL driver validation fails | DSN availability, credentials, query, and database access |
| JSON driver validation fails | ValuesJson contains valid serialized row JSON |
| Search unexpectedly returns nothing | Explicit keyword filters and the selected comparison operator |
| A destination disappears after updating | The complete destination lists and retained DestinationId values |
| Update response looks correct but delivery differs | Retrieve the saved package and inspect a controlled execution |
| Report output is incomplete | Page selection, filters, parameters, rendering settings, and source permissions |
| Execution is completed but no files arrived | ResultJson, execution errors, and destination delivery |
Updated about 21 hours ago

