JobMaster API
The JobMaster.Api package exposes your JobMaster clusters as a RESTful HTTP service — query jobs, inspect workers, manage recurring schedules, and stream logs, all from a single authenticated endpoint.
The API server only needs access to the master database. It does not need to run workers or agents, and it does not need to share a process with the consumer/producer servers. You can deploy it as a dedicated monitoring and operations server that connects directly to the master DB over the network — completely independent from where jobs are actually processed.
Prerequisites
- Master DB access: at least one cluster master connection must be reachable from the API server.
- Runtime initialized:
StartJobMasterRuntimeAsync()must be called so the API can resolve cluster components. Without workers this is lightweight — it opens DB connections but starts no background processing.
Installation
dotnet add package JobMaster
dotnet add package JobMaster.Api
Cluster Connection
How you register clusters depends on whether the API runs in its own dedicated process or alongside your worker application.
Option 1 — Dedicated API server (recommended)
The API runs as a separate process with no job processing. Only the master database connection is needed — no workers, no agent connections.
Use AddJobMasterClusterForApi for this scenario. It exposes only the connection-related settings, making the intent explicit.
// API-only process — master DB connection is the only requirement
builder.Services.AddJobMasterClusterForApi("payroll-cluster", config =>
{
config.UsePostgresForMaster("Host=db.internal;Database=jobmaster_payroll;Username=app;Password=...;");
});
builder.Services.UseJobMasterApi(o =>
{
o.BasePath = "/jm-api";
o.RequireAuthentication = true;
o.EnableSwagger = true;
o.IncludeClusterIdsInOpenApi();
o.UseApiKeyAuth().AddApiKey("dashboard", "secure-key-here");
});
var app = builder.Build();
await app.Services.StartJobMasterRuntimeAsync();
app.MapJobMasterApi();
app.Run();
Multiple clusters can be registered independently. The API will serve endpoints for each of them under their own cluster ID path segment (e.g. /jm-api/payroll-cluster/jobs).
builder.Services.AddJobMasterClusterForApi("payroll-cluster", config =>
{
config.UsePostgresForMaster(payrollConnectionString);
});
builder.Services.AddJobMasterClusterForApi("notifications-cluster", config =>
{
config.UsePostgresForMaster(notificationsConnectionString);
});
Only the cluster ID and connection string are required. You do not need to repeat any of the cluster settings (e.g. TransientThreshold, DefaultJobTimeout) that are already configured on your worker nodes. On startup, JobMaster only applies settings that are explicitly configured locally — anything not set falls back to whatever is already persisted in the database. Your worker configuration is preserved.
Option 2 — Co-located with the worker application
The API runs inside the same process as the worker. The cluster is already registered via AddJobMasterCluster, so no extra setup is needed — just add UseJobMasterApi and MapJobMasterApi to the existing application.
// Worker + API in the same process — cluster already registered
builder.Services.AddJobMasterCluster("payroll-cluster", config =>
{
config.UsePostgresForMaster(masterConnectionString);
config.AddAgentConnectionConfig("agent-1").UsePostgresForAgent(agentConnectionString);
config.AddWorker();
});
builder.Services.UseJobMasterApi(o =>
{
o.BasePath = "/jm-api";
o.RequireAuthentication = true;
o.UseApiKeyAuth().AddApiKey("dashboard", "secure-key-here");
});
var app = builder.Build();
await app.Services.StartJobMasterRuntimeAsync();
app.MapJobMasterApi();
app.Run();
See Providers for the full list of supported database providers.
Base Configuration
The API is configured during the service registration phase. You must call app.MapJobMasterApi() after building the app to map the internal routes.
builder.Services.UseJobMasterApi(o =>
{
o.BasePath = "/jm-api"; // Base route for all JobMaster endpoints
o.RequireAuthentication = true; // Global toggle for security
o.EnableSwagger = true; // Enables isolated Swagger UI
o.EnableLogging = true; // Logs API requests to the Cluster Logger
});
var app = builder.Build();
// This maps the endpoints (e.g., /jm-api/my-cluster/jobs)
app.MapJobMasterApi();
Authentication
Set RequireAuthentication = true in the base configuration to enforce authentication globally, then configure one of the providers below.
builder.Services.UseJobMasterApi(o =>
{
o.RequireAuthentication = true;
o.UseApiKeyAuth()... // or UseUserPwdAuth() / UseJwtBearerAuth()
});
API Keys
Ideal for server-to-server communication or simple monitoring tools.
builder.Services.UseJobMasterApi(o =>
{
o.UseApiKeyAuth()
.ApiKeyHeader("x-api-key") // Custom header name (Default is x-api-key)
.AddApiKey("Grafana-Monitor", "secure-key-123")
.AddApiKey("Admin-Tool", "another-secure-key");
});
User & Password
Provides credential-based access. Passwords are encrypted using PBKDF2 (SHA256) with 100,000 iterations.
builder.Services.UseJobMasterApi(o =>
{
o.UseUserPwdAuth()
.UserNameHeaderName("X-User-Name") // Default: X-User-Name
.PwdHeaderName("X-Password") // Default: X-Password
.AddUserPwd("hugo", "p@ssword_master");
});
JWT Bearer
Integrate with your existing Identity Provider (IdentityServer, Auth0, etc.) or use the built-in provider. The default provider automatically detects the key type to use the appropriate signing algorithm:
- Symmetric Keys: Uses HmacSha256Signature.
- Asymmetric Keys (RSA): Uses RsaSha256.
builder.Services.UseJobMasterApi(o =>
{
o.UseJwtBearerAuth()
.Scheme("Bearer") // Default: Bearer
.AuthorizationHeaderName("Authorization")
.RegisterDefaultJwtBearerAuthProvider(new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "https://your-auth-server.com",
IssuerSigningKey = mySecurityKey
// ... other standard parameters
});
});
The default provider also exposes a GenerateToken method via IJobMasterJwtBearerAuthProvider. This is perfect for issuing internal tokens without requiring an external Identity Server — the interface is automatically registered in your DI container.
Advanced Customization
Completely replace how JobMaster identifies and authorizes requests across all mechanisms.
Global Identity & Authorization Overrides
- Custom Identity
o.UseCustomizeJobMasterIdentityProvider<MyExternalIdProvider>();
- Custom Authorization
o.UseCustomizeJobMasterAuthorizationProvider<MyExternalAuthorizationProvider>();
Specific Auth Type Overrides
Keep the API infrastructure but change the credential retrieval logic for a specific type:
- RegisterApiKeyAuthProvider: Fetch identity details from a database based on the API key.
- RegisterUserPwdAuthProvider: Validate credentials against your own User store.
- RegisterJwtBearerAuthProvider: Replace the default JWT validation logic entirely.
Isolated Swagger UI
JobMaster API comes with a dedicated Swagger interface. It is kept separate from your main application's Swagger to prevent clutter and ensure clean security definitions.
- URL:
{BasePath}/swagger(e.g.,http://localhost:5000/jm-api/swagger) - Features: Supports testing API Key, Basic Auth, and JWT Bearer directly from the UI.
- Isolation: Your host application's endpoints will not appear here, and JobMaster endpoints will not appear in your host's primary Swagger.
Cluster Discovery via OpenAPI
Call IncludeClusterIdsInOpenApi() to embed all registered cluster IDs into the OpenAPI document's info extensions. This allows other tools — such as the JobMaster Dashboard — to discover which clusters are available without requiring separate configuration.
builder.Services.UseJobMasterApi(o =>
{
o.EnableSwagger = true;
o.IncludeClusterIdsInOpenApi(); // Adds x-jobmaster-clusters to the OpenAPI info
});
The cluster IDs are published under the x-jobmaster-clusters extension key in the OpenAPI info object:
{
"info": {
"x-jobmaster-clusters": ["payroll-cluster", "notifications-cluster"]
}
}