Job Definitions
Job Definitions are resources that wrap components to make them schedulable. They define what code should run when a job is executed.
What are Job Definitions?
A Job Definition is a simple resource that associates a unique ID with a component. It acts as a template for creating jobs:
Component (code) → Job Definition (wrapper) → Job (scheduled instance)Why Job Definitions?
Job Definitions provide a layer of indirection that enables:
- Reusability: One component can have multiple job definitions
- Organization: Group related scheduled tasks
- Permissions: Control who can schedule which components
- Statistics: Track performance by job definition
Structure
A Job Definition consists of just two fields:
jobDefinitionId- Unique identifier for the definitioncomponentId- Which component to execute
uri: /resources/job-definitions/nightly-order-processing
spec:
componentId: process-orders.hreactorComplete Example
Here's how to create a scheduled background task:
Create a component with the logic to execute:
filtrera//process-orders.hreactor import 'resources' param processingDate: text let orders = query orders(orderNumber) filter $'createdAt >= {processingDate}' orderBy 'createdAt asc' from orders select order => sendEmail { to = order.customer.email subject = 'Processing Update' body = { html = '<p>Your order is being processed</p>' } dynamic = { orderId = order.id } }Create a job definition that wraps the component:
yamluri: /resources/components/process-orders.hreactor spec: codeFile: process-orders.hreactor --- uri: /resources/job-definitions/nightly-order-processing spec: componentId: process-orders.hreactorDeploy the manifest:
bashh_ manage apply h_manifest.yamlSchedule a job using the definition:
httpPOST /resources/jobs { "jobDefinitionId": "nightly-order-processing", "parameters": { "processingDate": "2025-11-15T00:00:00Z" }, "runAt": "2025-11-16T02:00:00Z" }
Now the component will run at 2 AM with the specified parameters.
One Component, Multiple Definitions
A single component can be wrapped by multiple job definitions for different purposes:
# Same component for different use cases
uri: /resources/job-definitions/hourly-sync
spec:
componentId: data-sync.hreactor
---
uri: /resources/job-definitions/daily-full-sync
spec:
componentId: data-sync.hreactor
---
uri: /resources/job-definitions/manual-sync
spec:
componentId: data-sync.hreactorThis allows:
- Different scheduling patterns (hourly vs daily)
- Different monitoring/statistics tracking
- Different permission scopes
- Same underlying logic
Managing Job Definitions
Job definitions are managed via the /resources/job-definitions API:
- Create/Update:
PUT /resources/job-definitions/{definitionId} - Get:
GET /resources/job-definitions/{definitionId} - List:
GET /resources/job-definitions - Delete:
DELETE /resources/job-definitions/{definitionId}
See the API Reference for complete endpoint documentation.
WARNING
Deletion Warning: If you delete a job definition while scheduled jobs for it still exist, those jobs will fail when they attempt to run. Consider waiting for pending jobs to complete before deleting definitions.
Job Definition vs Job
Job Definition:
- Template/configuration
- Points to a component
- Permanent resource
- One per scheduled task type
Job:
- Scheduled instance
- References a job definition
- Temporary (deleted after retention period)
- Many instances per definition
Example:
- Job Definition:
nightly-order-processing(permanent) - Jobs: Individual executions (Nov 15 2AM, Nov 16 2AM, Nov 17 2AM, etc.)
Access Control
Managing job definitions requires permissions:
job-definitions:read # View job definitions
job-definitions:write # Create, update, delete job definitionsStatistics & Monitoring
Job statistics are tracked by jobDefinitionId, making it easy to monitor performance of specific scheduled tasks:
GET /resources/jobs/statistics?jobDefinitionId=nightly-order-processingThis allows you to track:
- Success/failure rates for this specific task
- Execution time trends
- Queue depth for this definition
See Jobs documentation for complete statistics information.
Recurring Jobs Pattern
Hantera doesn't have built-in cron scheduling. Instead, components can schedule themselves to create recurring jobs using relative time scheduling.
How It Works
A component schedules the next run at the end of its execution:
//nightly-processor.hreactor
// Do the work
let orders = query orders(orderNumber)
filter 'status = pending'
let processResult = orders select order => processOrder(order)
// Schedule next run (24 hours from now)
from {
effect = 'scheduleJob'
definition = 'nightly-order-processing'
at = now addDays 1
parameters = {}
}This creates a self-perpetuating chain: Job completes → Schedules next run → Next job executes → Schedules another run → ...
INFO
Timing: The at time is relative to when the job completes, not when it starts. If a job takes 10 minutes to run and schedules itself with now addHours 1, the next run is 1 hour and 10 minutes from the previous start.
Common Patterns
Hourly Processing
// At end of component
from {
effect = 'scheduleJob'
definition = 'hourly-sync'
at = now addHours 1
parameters = {}
}Daily Processing
// At end of component
from {
effect = 'scheduleJob'
definition = 'daily-report'
at = now addDays 1
parameters = {}
}Weekly Processing
// At end of component
from {
effect = 'scheduleJob'
definition = 'weekly-cleanup'
at = now addDays 7
parameters = {}
}Custom Intervals
// Every 30 minutes
from {
effect = 'scheduleJob'
definition = 'frequent-sync'
at = now addMinutes 30
parameters = {}
}Starting the Recurring Chain
Create the first job manually via API or rule to start the chain:
POST /resources/jobs
{
"jobDefinitionId": "nightly-order-processing",
"parameters": {},
"runAt": "2025-11-16T02:00:00Z"
}After this first execution, the job schedules itself automatically.
Stopping Recurring Jobs
To stop a recurring job chain, cancel the next pending job:
DELETE /resources/jobs/{jobId}When a job is cancelled, it never executes, so it never schedules the next job. The chain breaks automatically.
Alternative methods:
- Update the component to remove self-scheduling logic (permanent change)
- Delete the job definition (will fail any pending jobs)
Best Practices
Use Descriptive IDs
Choose clear job definition IDs that describe purpose and frequency:
- ✅
hourly-inventory-sync,daily-report-generation,weekly-cleanup - ❌
job1,sync,task
Use Separate Definitions for Different Purposes
Create separate job definitions when you need distinct monitoring or different use cases:
# Same component, different purposes/monitoring
uri: /resources/job-definitions/sync-customers
spec:
componentId: sync.hreactor
---
uri: /resources/job-definitions/sync-products
spec:
componentId: sync.hreactorThis allows independent statistics tracking and clearer monitoring of each use case.
Check for Pending Jobs Before Deletion
Before deleting a job definition, verify no pending jobs exist:
GET /resources/jobs?jobDefinitionId=my-definition&status=pendingSee Also
- Components - Learn about creating components
- Jobs - Scheduling and monitoring job executions
- API Reference - Complete API documentation