# SaveCustomReport - Advanced Documentation ## Model Shape CustomReportInfo (BaBackOffice.Model): ``` ID: long Name: string (required) Description: string Timestamp: DateTime (UTC only - setter forces DateTimeKind.Utc) Type: string - "Search" or "Custom" (CustomReportInfo.CustomReportType), immutable after creation AdvancedSearch: AdvancedSearchInfoBasic (required when Type == "Search") File: FileInfo (required when Type == "Custom" - the uploaded Telerik .trdp; File.StorageName must be set) RelatedSearches: AdvancedSearchInfoBasic[] ScheduledNotifications: ScheduleInfo[] FirmID: long UpdatedBy: IDNameEmail (server-populated) Subject: string Message: string CompiledFile: FileInfo (server-populated only - never set this yourself) Roles: Role[] (required - the roles allowed to run the report, stored in CustomReportRoles; an empty list means no restriction, anyone with the link can run it; null is rejected with "Roles is required to save custom report[ID].") ``` FileInfo (Radolo.Common.Model): ``` ID, FirmID: long Name, StorageName: string IsURLOnly: bool ExternalId, OwnerEntity: string OwnerRecordID: long DownloadUrl, ViewUrl: string (computed, read-only) UploadedDate: DateTime UploadedBy: string Tags: string[] Comments: CommentInfo[] ObjectSize: long ``` ## Traps - Report.Type cannot be changed after creation. Decide Search vs Custom up front. - For Type == "Custom", Report.File must be set with a real StorageName or the call throws "A report definition is required for Custom reports." Upload the .trdp through the normal file-upload flow first, then reference it via File. - Never set CompiledFile yourself. The server downloads your uploaded File, compiles it through Telerik, and writes the compiled output back into CompiledFile on save. Anything you send in CompiledFile is ignored/overwritten. - A .trdp is itself a zip container (Telerik's report-package format) - its entries are Images/*, definition.xml, and [Content_Types].xml. Don't try to read it as plain XML. - If your Custom report's Telerik report is built against a SecureApi WebServiceDataSource (calling back into this same API from inside the report), the WebServiceDataSource's Authentication must be a two-step auth whose LoginUrl ends with /Services/AuthenticationApi.svc/GetAccessToken, and its auth body must contain "ApiKey" and "SharedSecret" values (parsed by regex, not JSON, since the body may also carry Telerik parameter expressions). - The ApiKey/SharedSecret baked into the report are validated against the database at execution time (must belong to the report's firm) - but the actual bearer token used to call back into the API is generated for whichever person is currently running the report, not any identity baked into the file. A CurrentPersonID/FirmID >= 1 must resolve at execution time or the report fails with "A CurrentPersonID parameter is required to run a report." / "A FirmID parameter is required to run a report." - Convention: firms keep a single shared API key named "Reporting" for this purpose rather than minting one per report. - Every firm gets a set of starter Custom/Search reports cloned from a template company when the firm is provisioned - see GetCustomReports for examples of a working WebServiceDataSource setup. ## Script Example ```javascript // Create a Custom report backed by an already-uploaded Telerik .trdp var report = await CallEndpoint("SecureApi", "SaveCustomReport", { Report: { ID: 0, Name: "Open Claims by Adjuster", Description: "Custom Telerik report calling back into SecureApi via a WebServiceDataSource.", Type: "Custom", File: { StorageName: uploadedFile.StorageName, Name: "OpenClaimsByAdjuster.trdp" }, FirmID: currentFirmID, Roles: [] } }); // report.CompiledFile is populated by the server - do not send your own CompiledFile. ``` ## Cross-References | Endpoint | Relationship | |---|---| | GetCustomReports | Lists existing reports for the current company, including template-cloned sample reports | | FileService.GetDownloadUrl | Download an existing report's .trdp to inspect its WebServiceDataSource setup | | AuthenticationApi.GetAccessToken | The endpoint a report's WebServiceDataSource authenticates against at execution time |