From the Architect Panel → Tasks, click New. The configuration form asks you to define when the task should run and what it should do when it does.
Configuration
- Name — descriptive internal name.
- Schedule — cron-style expression. See the Cron-style Scheduling article for the syntax.
- Handler — pick a built-in action or specify a PHP function name. Built-in actions cover common patterns (purge records, send digest emails, sync external data). Custom PHP handlers handle anything else.
- Parameters — JSON object passed to the handler. The handler uses these to know what specifically to do (e.g. "which datastore to clean up", "which template to send", "what URL to ping").
- Enabled — toggle to disable temporarily without deleting the task.
- Timeout — maximum runtime in seconds before the task is killed (for tasks that might hang).
- Retry on failure — whether failed runs should retry, and how many times.
Schedule examples
Cron syntax is five fields: minute, hour, day-of-month, month, day-of-week. Common patterns:
* * * * *— every minute.*/5 * * * *— every 5 minutes.0 * * * *— top of every hour.0 2 * * *— 2am every day.0 9 * * 1-5— 9am Monday to Friday.0 0 1 * *— midnight on the 1st of each month.0 0 1 1 *— midnight on January 1st (annual).
One-off vs recurring
Most tasks are recurring — they fire on a schedule indefinitely until you disable or delete them. For one-off tasks ("run this once next Tuesday at 9am"):
- Set the schedule to match the desired time (e.g.
0 9 * * 2for 9am Tuesday). - Add logic to the handler that disables the task after successful execution (or use the platform's one-shot task feature if available).
Idempotency
Tasks should be idempotent — running the same task twice should produce the same end state as running it once. This matters because:
- Tasks can occasionally fire twice (e.g. if the cron entry overlaps a long-running task).
- Retries on failure mean a task that partially completed might be retried.
- Manual re-runs (via the Re-run action in the Task Log) should be safe.
Idempotency is typically achieved by:
- Checking before acting (don't insert a record if it already exists; don't send an email if it's already been sent).
- Using transactions to make changes all-or-nothing.
- Recording task progress so a re-run can pick up where it left off.
Three real-world task configurations
Example 1: Daily report email
Schedule: 0 8 * * 1-5 (8am weekdays). Handler: sendDailyReport. Parameters: {"template": "daily-summary", "recipients": ["team@acme.com"]}. Generates yesterday's metrics and emails the team.
Example 2: Sync customers from CRM
Schedule: */15 * * * * (every 15 minutes). Handler: syncCustomersFromCRM. Parameters: {"limit": 100, "since": "last_run"}. Pulls updates from the CRM since the last successful run; idempotent so duplicates are detected and skipped.
Example 3: One-off historical data backfill
Schedule: 0 22 31 12 * (set to a far-future date). Handler: backfillHistoricalOrders. Parameters: {"start_year": 2020, "end_year": 2024}. Self-disables after successful completion. Used once during a data migration and then disabled forever.
