CronSynth: Shared Time
Externalized Cron for Autonomous Agents
The Serverless Problem
Autonomous agents increasingly run in serverless environments. Vercel functions. Cloudflare Workers. AWS Lambda. These platforms are perfect for event-driven workloads—they scale instantly, cost nothing when idle, and require zero infrastructure management.
But they have one fatal flaw: no background processes.
You can't run setInterval(). You can't spin up a cron daemon. You can't maintain a persistent timer. When the function returns, it's gone.
This breaks a fundamental agent capability: time-awareness.
Agents need to:
- Check for updates every 5 minutes
- Run daily reports at 9 AM
- Execute trades at market open
- Retry failed operations after delays
- Coordinate scheduled operations across swarms
Without background processes, none of this works natively.
The Traditional Workarounds
Platform Cron Jobs
Vercel has cron. Cloudflare has Cron Triggers. But they're:
- Platform-locked: Can't move your schedules
- Limited resolution: Often 1-minute minimum, sometimes worse
- Opaque: No on-chain proof of execution
- Non-composable: Can't integrate with agent reputation systems
External Schedulers
Services like EasyCron or cron-job.org exist, but they're:
- Human-focused: Dashboards, not APIs
- Trust-based: No verification of execution
- Payment friction: Credit cards, not x402
Self-Hosted Infrastructure
You could run your own scheduler, but then you're:
- Back to managing servers: Defeating serverless benefits
- Single point of failure: Your cron server goes down, everything stops
- Duplicating effort: Every agent operator runs the same infrastructure
What CronSynth Does
CronSynth externalizes scheduling so serverless agents get time-awareness without infrastructure.
Your Agent CronSynth Time
Core Capabilities
Cron Scheduling
- Standard 5-field cron expressions
- 1-minute minimum resolution
- UTC timezone (no ambiguity)
- Automatic next-run calculation
Reliable Delivery
- HMAC-signed webhooks
- 5-second timeout (acknowledge fast, process async)
- Automatic retry with exponential backoff
- Delivery receipts for confirmation
Verification
- On-chain attestation (optional)
- Run history with timestamps
- Auditable execution records
Agent-Native Payment
- x402 micropayments
- Free tier for basic usage
- Pay-per-trigger billing
- No accounts, no API keys
Schedule Registration
API Endpoint
POST /api/schedule
Request
json
{
"webhook_url": "https://my-agent.com/cron/daily-report",
"cron": "0 9 * * *",
"timezone": "UTC",
"metadata": {
"name": "Daily Portfolio Report",
"agent_id": "agent_001"
}
}
Response
json
{
"schedule_id": "sch_a1b2c3d4e5f6",
"cron": "0 9 * * *",
"next_run": "2025-01-28T09:00:00Z",
"webhook_secret": "whsec_...",
"status": "active"
}
Code Example
typescript
import { CronSynthClient } from '@echorift/cronsynth';
const client = new CronSynthClient({
signer: agentWallet,
});
// Register a schedule
const schedule = await client.createSchedule({
webhookUrl: 'https://my-agent.com/cron/tick',
cron: '*/5 * * * *', // Every 5 minutes
metadata: {
name: 'Health Check',
},
});
console.log('Schedule ID:', schedule.id);
console.log('Next run:', schedule.nextRun);
console.log('Webhook secret:', schedule.webhookSecret);
Webhook Payloads
When a schedule triggers, CronSynth sends a POST request to your webhook URL.
Request Headers
Content-Type: application/json
X-CronSynth-Signature:
X-CronSynth-Timestamp:
X-CronSynth-Schedule-Id:
Request Body
json
{
"schedule_id": "sch_a1b2c3d4e5f6",
"run_number": 48,
"timestamp": 1701433200,
"cron": "*/5 * * * *",
"scheduled_time": "2025-01-28T12:00:00Z",
"actual_time": "2025-01-28T12:00:01Z",
"metadata": {
"name": "Health Check"
}
}
Webhook Verification
Always verify webhooks before processing:
``typescript
import { verifyWebhook } from '@echorift/cronsynth';
app.post('/cron/tick', (req, res) => {
const isValid = verifyWebhook({
payload: JSON.stringify(req.body),
signature: req.headers['x-cronsynth-signature'],
timestamp: req.headers['x-cronsynth-timestamp'],
secret: process.env.CRONSYNTH_WEBHOOK_SECRET,
});
if (!isValid) {
return res.status(401).send('Invalid signature');
}
// Process the trigger
console.log(Run #${req.body.run_number} at ${req.body.timestamp});
// Respond quickly! Process async if needed.
res.status(200).send('OK');
});
Response Requirements
- Status 2xx: Trigger acknowledged
- Status 4xx/5xx: Trigger failed, will retry
- Timeout (>5s): Trigger failed, will retry
Important: Respond within 5 seconds. If your processing takes longer, acknowledge immediately and process asynchronously.
Cron Expression Reference
CronSynth uses standard 5-field cron expressions:
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-6, Sunday=0)
- * * * *
`
Common Patterns
Special Characters
= every minute = at 0 and 30 minutes = minutes 0 through 5 = every 15 minutesSchedule Management
List Schedules
GET /api/schedules
json
{
"schedules": [
{
"schedule_id": "sch_a1b2c3d4e5f6",
"cron": "*/5 * * * *",
"webhook_url": "https://my-agent.com/cron/tick",
"status": "active",
"next_run": "2025-01-28T12:05:00Z",
"run_count": 48,
"last_run": "2025-01-28T12:00:00Z",
"last_status": "success"
}
]
}
Unschedule
POST /api/unschedule
json
{
"schedule_id": "sch_a1b2c3d4e5f6"
}
Code Example
typescript
// List all schedules
const schedules = await client.listSchedules();
// Unschedule
await client.unschedule('sch_a1b2c3d4e5f6');
On-Chain Attestation
CronSynth can attest schedule executions on-chain for:
- Reputation systems: Prove your agent runs reliably
- Audit trails: Verifiable execution history
- Dispute resolution: On-chain proof of trigger delivery
How It Works
- Schedule trigger fires
- Webhook delivered successfully
- Execution record created
- (Optional) Attestation submitted to Base L2
Attestation Data
solidity
struct ScheduleAttestation {
bytes32 scheduleId;
uint256 runNumber;
uint256 timestamp;
bytes32 webhookHash; // Hash of webhook URL
bool success;
}
Querying Attestations
typescript
// Get attestation for a specific run
const attestation = await client.getAttestation({
scheduleId: 'sch_a1b2c3d4e5f6',
runNumber: 48,
});
// Verify on-chain
console.log('TX Hash:', attestation.txHash);
console.log('Block:', attestation.blockNumber);
Pricing
Free Tier
- 1 active schedule
- Unlimited triggers
- No payment required
- Great for testing
Pay-Per-Trigger
Subscription Plans
Payment
All payments via x402 protocol:
- Token: USDC on Base
- Settlement: Instant
- No accounts required
Integration Patterns
Pattern 1: Periodic Health Check
Agent checks its own status every 5 minutes:
typescript
// Registration
await cronsynth.createSchedule({
webhookUrl: 'https://my-agent.com/health',
cron: '*/5 * * * *',
});
// Webhook handler
app.post('/health', async (req, res) => {
// Verify signature...
// Check agent health
const status = await checkAgentHealth();
if (!status.healthy) {
await alertOperator(status);
}
res.status(200).send('OK');
});
Pattern 2: Daily Report Generation
Agent generates reports at 9 AM:
typescript
// Registration
await cronsynth.createSchedule({
webhookUrl: 'https://my-agent.com/daily-report',
cron: '0 9 * * *',
});
// Webhook handler
app.post('/daily-report', async (req, res) => {
// Acknowledge immediately
res.status(200).send('OK');
// Process async
setImmediate(async () => {
const report = await generateDailyReport();
await distributeReport(report);
});
});
Pattern 3: Market Hours Trading
Agent operates during market hours (9:30 AM - 4 PM ET, weekdays):
typescript
// Registration - check every minute during market hours
// Note: Cron is UTC, so 9:30 AM ET = 14:30 UTC (EST) or 13:30 UTC (EDT)
await cronsynth.createSchedule({
webhookUrl: 'https://my-agent.com/market-tick',
cron: '30-59 13-20 * * 1-5', // Approximate market hours
});
// Webhook handler
app.post('/market-tick', async (req, res) => {
// Verify we're actually in market hours (handle DST)
if (!isMarketOpen()) {
return res.status(200).send('Market closed');
}
await executeMarketStrategy();
res.status(200).send('OK');
});
Pattern 4: Swarm Coordination
Leader agent triggers swarm-wide sync:
typescript
// Leader registration
await cronsynth.createSchedule({
webhookUrl: 'https://leader-agent.com/sync-trigger',
cron: '0 * * * *', // Every hour
});
// Leader webhook handler
app.post('/sync-trigger', async (req, res) => {
// Broadcast to swarm via Switchboard
await switchboard.broadcast({
swarmId: 'my-swarm',
message: {
type: 'SYNC',
timestamp: req.body.timestamp,
runNumber: req.body.run_number,
},
});
res.status(200).send('OK');
});
Retry Behavior
When webhook delivery fails (timeout, 4xx, 5xx):
After 5 failed attempts, the trigger is marked as failed and no further retries occur.
Handling Failures
typescript
// Check for failed triggers
const history = await client.getRunHistory({
scheduleId: 'sch_a1b2c3d4e5f6',
status: 'failed',
});
// Retry manually
for (const run of history.runs) {
await processManually(run);
}
Contract Reference
CronSynth Registry (Base Mainnet)
Address: 0x3846Ab73eCb4A1590B56cEB88D9779471B82A314`
Key Functions:
solidity
// Register a schedule (attestation-enabled)
function registerSchedule(
bytes32 scheduleId,
bytes32 webhookHash,
string calldata cron
) external;
// Attest execution
function attestExecution(
bytes32 scheduleId,
uint256 runNumber,
bool success
) external;
// Query attestations
function getAttestation(
bytes32 scheduleId,
uint256 runNumber
) external view returns (ScheduleAttestation memory);
Best Practices
Webhook Design
- Respond fast: Return 200 within 5 seconds
- Process async: Use queues for long operations
- Idempotent handlers: Same run_number may arrive twice
- Verify always: Check HMAC signature
Schedule Management
- Use descriptive metadata: Makes debugging easier
- Monitor run history: Catch failures early
- Right-size frequency: Don't poll more than needed
- Consider time zones: All cron is UTC
Error Handling
- Log everything: Include schedule_id and run_number
- Alert on failures: Don't let schedules silently break
- Have fallbacks: What if the trigger doesn't arrive?
SDK Reference
Installation
bash
npm install @echorift/cronsynth ethers
Client Initialization
typescript
import { CronSynthClient } from '@echorift/cronsynth';
const client = new CronSynthClient({
signer: wallet, // ethers Signer for x402 payments
});
Full API
typescript
// Schedules
await client.createSchedule({ webhookUrl, cron, metadata });
await client.listSchedules();
await client.getSchedule(scheduleId);
await client.unschedule(scheduleId);
// Run history
await client.getRunHistory({ scheduleId, limit, status });
await client.getAttestation({ scheduleId, runNumber });
// Utilities
verifyWebhook({ payload, signature, timestamp, secret });
parseCron(expression); // Returns next N run times
Related Resources
- Documentation: cronsynth.echorift.xyz/docs
- API Guide: cronsynth.echorift.xyz/ai-api-guide.txt
- AI Docs: cronsynth.echorift.xyz/llms.txt
- OpenAPI: cronsynth.echorift.xyz/.well-known/openapi.json
- Contract: basescan.org/address/0x3846Ab73eCb4A1590B56cEB88D9779471B82A314
*CronSynth. Shared time for the machine age.*