Appearance
Report Scheduling
GrydReports scheduling is optional and powered by GrydReports.Scheduling.GrydJobs. If your application does not use recurring reports, you can use GrydReports without GrydJobs.
Prerequisites for Scheduling
Install and register the optional scheduling integration:
bash
dotnet add package GrydReports.Scheduling.GrydJobs
dotnet add package GrydJobscsharp
using GrydJobs;
using GrydReports.Scheduling.GrydJobs;
builder.Services.AddGrydJobs(options =>
{
options.ConnectionString = builder.Configuration.GetConnectionString("Jobs")!;
});
builder.Services.AddGrydReportsScheduling();
if (app.Environment.IsDevelopment())
{
await app.ApplyGrydReportsMigrationsAsync();
await app.ApplyGrydJobsMigrationsAsync();
}WARNING
If AddGrydReportsScheduling() is not registered, scheduling commands/endpoints are not executable.
Overview
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ ReportSchedule │──────>│ ScheduledReportJob│──────>│ IReportGenerator│
│ (Entity) │ cron │ (GrydJobs) │ │ (Orchestrator) │
│ │ │ │ │ │
│ CronExpression │ │ On Tick: │ │ Generate + │
│ TemplateId │ │ Fetch schedule │ │ Store + │
│ Format │ │ Call generator │ │ Deliver │
│ DeliveryOptions │ │ Update status │ │ │
└──────────────────┘ └──────────────────┘ └──────────────────┘Create a Schedule
Via API
bash
POST /api/v1/reports/schedules
Content-Type: application/json
{
"templateId": "daily-sales",
"name": "Daily Sales Summary",
"cronExpression": "0 8 * * *",
"format": 1,
"parameters": {
"region": "south"
},
"delivery": {
"method": 2,
"recipients": ["sales-team@company.com"],
"emailSubject": "Daily Sales Summary"
},
"description": "Generates daily sales report at 8 AM"
}Response 201 Created:
json
{
"id": "c1d2e3f4-...",
"templateId": "daily-sales",
"name": "Daily Sales Summary",
"cronExpression": "0 8 * * *",
"format": 1,
"isActive": true,
"nextExecutionAt": "2026-02-20T08:00:00Z",
"executionCount": 0,
"consecutiveFailures": 0,
"createdAt": "2026-02-19T14:00:00Z"
}Via MediatR
csharp
var result = await mediator.Send(new CreateReportScheduleCommand(
TemplateId: "daily-sales",
Name: "Daily Sales Summary",
CronExpression: "0 8 * * *",
Format: ReportFormat.Pdf,
Parameters: new Dictionary<string, object> { ["region"] = "south" },
Delivery: new DeliveryOptions
{
Method = DeliveryMethod.Email,
Recipients = ["sales-team@company.com"]
},
Description: "Daily sales report at 8 AM"
));Cron Expressions
Standard 5-field cron expressions are supported (powered by Cronos):
| Expression | Description |
|---|---|
0 8 * * * | Every day at 8:00 AM |
0 9 * * MON | Every Monday at 9:00 AM |
0 0 1 * * | First day of every month at midnight |
0 18 * * FRI | Every Friday at 6:00 PM |
*/30 * * * * | Every 30 minutes |
0 8 1,15 * * | 1st and 15th of each month at 8:00 AM |
0 0 * * MON-FRI | Weekdays at midnight |
Manage Schedules
List Schedules
bash
GET /api/v1/reports/schedules?templateId=daily-sales&isActive=true&pageNumber=1&pageSize=20Get Schedule Details
bash
GET /api/v1/reports/schedules/{id}Update Schedule
bash
PUT /api/v1/reports/schedules/{id}
Content-Type: application/json
{
"name": "Updated Name",
"cronExpression": "0 9 * * *",
"format": 2,
"description": "Changed to 9 AM, Excel format"
}Delete Schedule
bash
DELETE /api/v1/reports/schedules/{id}Schedule Controls
Pause
Stops automatic execution without deleting the schedule:
bash
POST /api/v1/reports/schedules/{id}/pauseResume
Reactivates a paused schedule:
bash
POST /api/v1/reports/schedules/{id}/resumeTrigger Now
Immediately executes the schedule out-of-cycle:
bash
POST /api/v1/reports/schedules/{id}/triggerReturns 202 Accepted — the execution runs in background.
IReportScheduler
The scheduling interface:
csharp
public interface IReportScheduler
{
Task<ReportSchedule> CreateScheduleAsync(
string templateId,
string cronExpression,
ReportFormat format,
Dictionary<string, object>? parameters = null,
DeliveryOptions? delivery = null,
CancellationToken ct = default);
Task UpdateScheduleAsync(Guid scheduleId, UpdateReportScheduleDto update, CancellationToken ct = default);
Task DeleteScheduleAsync(Guid scheduleId, CancellationToken ct = default);
Task PauseScheduleAsync(Guid scheduleId, CancellationToken ct = default);
Task ResumeScheduleAsync(Guid scheduleId, CancellationToken ct = default);
Task TriggerNowAsync(Guid scheduleId, CancellationToken ct = default);
}Schedule Configuration
csharp
builder.Services.ConfigureGrydReportsScheduling(opts =>
{
// 0 = unlimited
opts.MaxSchedulesPerTenant = 0;
// Auto-pause after N consecutive failures
opts.MaxConsecutiveFailures = 3;
// Minimum allowed cron interval
opts.MinimumInterval = TimeSpan.FromHours(1);
// Notify when schedule is auto-paused
opts.NotifyOnAutoPause = true;
});Schedule Lifecycle
Created (Active) ──> Executing ──> Complete ──> Next Tick...
│ │
│ Failed (retry)
│ │
├── Paused ────────┘ (consecutive failures)
│
└── DeletedThe ReportScheduleDto exposes tracking fields:
| Field | Description |
|---|---|
isActive | Whether the schedule is currently active |
lastExecutedAt | Timestamp of last execution |
nextExecutionAt | Calculated next execution time |
executionCount | Total number of successful executions |
consecutiveFailures | Number of failures since last success |
Delivery with Schedules
Combine scheduling with delivery to create fully automated reporting workflows. The schedule's DeliveryOptions are applied after each successful generation.