Coordinating Multiple Endpoints and Jobs
This document shows practical patterns for orchestrating workflows across multiple HTTP jobs and endpoints. Cronicorn is a hosted scheduling service where the AI automatically handles coordination β you don't write orchestration code, parsers, or state machines. Instead, you configure endpoints with descriptions and design response bodies with signals. The AI reads both and coordinates automatically.
Use this guide when you need:
- Multiple HTTP jobs to work together, where one job's response data influences the scheduling of another job
- Endpoints within the same job to coordinate (e.g., health-check triggers recovery)
- Cascading pipelines where each stage depends on the previous stage completing
All configurations use JSON field names that work with the Web UI, MCP Server, and HTTP API. See Core Concepts for the full schema.
Coordinating Interdependent Jobsβ
Cronicorn supports coordination between interdependent HTTP jobs through two mechanisms: sibling visibility (endpoints in the same job) and embedded status (endpoints in different jobs). Both approaches are covered below with complete examples.
How Conflict Resolution Worksβ
Cronicorn prevents conflicting AI decisions through architectural design:
- Independent analysis: The AI analyzes each endpoint in its own session. It can READ sibling state but can only WRITE to the endpoint currently being analyzed.
- One-directional dependencies: Upstream endpoints write signals (e.g.,
ready_for_transform: true). Downstream endpoints read those signals. Dependencies flow one way β no circular references. - Batch ID idempotency: Pipeline stages include batch_id in responses. If a downstream endpoint has already processed a batch, it skips β preventing duplicate work even if the AI triggers it multiple times.
- Atomic claiming: The Scheduler uses database-level atomic operations (SELECT ... FOR UPDATE SKIP LOCKED) to claim endpoints. No two scheduler instances execute the same endpoint simultaneously.
- TTL auto-revert: All AI hints expire. Even if the AI makes a suboptimal decision, the endpoint returns to baseline when the TTL expires.
What this means in practice: If the AI analyzes endpoint A and endpoint B at the same time, they cannot create conflicting schedules because each session only modifies its own endpoint. The AI reads sibling state as a snapshot β it doesn't lock or modify siblings.
Core Concept: Coordination via Descriptions and Response Bodiesβ
Cronicorn doesn't have built-in workflow orchestration or explicit dependencies. Instead, endpoints coordinate through two mechanisms that the AI processes automatically:
1. Descriptions Express Relationshipsβ
Use the description field to tell the AI how endpoints should interact. The AI reads these descriptions and interprets them β you don't write any coordination logic:
{
"name": "health-check",
"description": "Monitors service health. When errors are detected, the trigger-recovery endpoint should run immediately."
}
{
"name": "trigger-recovery",
"description": "Recovery action. Should only run when the health-check endpoint shows errors. Wait at least 5 minutes between attempts."
}
The AI reads both descriptions, calls get_sibling_latest_responses() to check each endpoint's status, and uses propose_next_time() or pause_until() to coordinate β all automatically.
2. Response Bodies Provide Signalsβ
Endpoints coordinate by:
- Your endpoints writing signals to their HTTP response bodies (e.g.,
"ready_for_transform": true) - The AI calling
get_sibling_latest_responses()to read those signals - The AI acting on the signals (pause, run immediately, adjust intervals)
You design the response bodies. The AI parses them automatically. There is no parsing code to write β the AI interprets JSON fields based on the endpoint descriptions.
Pattern 1: Flash Sale Load Managementβ
Scenario: E-commerce site with flash sale. Need to prioritize critical endpoints and pause low-priority work during traffic spikes.
Configurationβ
Create the job and all endpoints via API:
# Create the flash sale management job
curl -X POST https://cronicorn.com/api/jobs \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Flash Sale Management", "description": "Coordinates monitoring during flash sales with priority-based pausing" }'
# Returns: { "id": "job_abc123", ... }
# Add traffic monitor endpoint
curl -X POST https://cronicorn.com/api/jobs/job_abc123/endpoints \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "traffic-monitor",
"url": "https://api.example.com/traffic/stats",
"method": "GET",
"baselineIntervalMs": 60000,
"minIntervalMs": 15000,
"timeoutMs": 10000,
"description": "Monitors traffic load and flash sale status. Returns load_status and recommendations for other endpoints. When load_status is high, siblings with LOW priority should be paused."
}'
# Add order processor endpoint (CRITICAL priority β never paused)
curl -X POST https://cronicorn.com/api/jobs/job_abc123/endpoints \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "order-processor",
"url": "https://api.example.com/orders/process",
"method": "POST",
"baselineIntervalMs": 30000,
"minIntervalMs": 10000,
"timeoutMs": 30000,
"description": "Priority: CRITICAL. Processes customer orders. Must keep running during flash sales. Check traffic-monitor sibling: if load_status is high, tighten to 10 seconds for faster order processing."
}'
# Add inventory sync endpoint (NORMAL priority)
curl -X POST https://cronicorn.com/api/jobs/job_abc123/endpoints \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "inventory-sync",
"url": "https://api.example.com/inventory/sync",
"method": "POST",
"baselineIntervalMs": 300000,
"timeoutMs": 30000,
"description": "Priority: NORMAL. Syncs inventory to warehouse. Check traffic-monitor sibling: if load_status is high and recommendations.pause_low_priority is true, continue running but extend interval to reduce load. Resume normal when load_status returns to normal."
}'
# Add analytics updater endpoint (LOW priority β paused during surges)
curl -X POST https://cronicorn.com/api/jobs/job_abc123/endpoints \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "analytics-updater",
"url": "https://api.example.com/analytics/update",
"method": "POST",
"baselineIntervalMs": 600000,
"timeoutMs": 30000,
"description": "Priority: LOW. Updates dashboards. Check traffic-monitor sibling: if load_status is high and recommendations.pause_low_priority is true, pause until the recommended time. Resume when load_status returns to normal."
}'
# All 4 endpoints are siblings. AI coordinates based on priority levels
# described in each endpoint's description field.
Response Body Structureβ
Traffic Monitor:
{
"visitors_per_min": 5000,
"load_status": "high",
"flash_sale_active": true,
"recommendations": {
"pause_low_priority": true,
"pause_until": "2025-11-02T15:00:00Z"
}
}
Analytics Updater:
{
"last_update_at": "2025-11-02T14:30:00Z",
"records_processed": 1500,
"status": "active"
}
AI Coordination Logicβ
When AI analyzes Analytics Updater:
- Calls
get_sibling_latest_responses() - Finds Traffic Monitor response:
{ "load_status": "high", "recommendations": { "pause_low_priority": true } } - Checks Analytics Updater's priority (low)
- Calls
pause_until("2025-11-02T15:00:00Z", reason="Flash sale traffic spike - pausing low-priority work")
When AI analyzes Order Processor:
- Calls
get_sibling_latest_responses() - Finds Traffic Monitor response:
{ "load_status": "high" } - Checks Order Processor's priority (critical)
- Takes no action (critical endpoints keep running)
When load drops (Traffic Monitor returns "load_status": "normal"):
- AI analyzes Analytics Updater
- Sees normal load status
- Calls
pause_until(null, reason="Load normalized - resuming")
Key Pointsβ
- Traffic Monitor acts as coordinator (writes signals)
- Other endpoints react to signals (via AI reading sibling responses)
- Priority is encoded in endpoint names/descriptions, not the system
- Coordination is eventual (within 5 minutes of AI analysis cycle)
Pattern 2: ETL Pipeline with Dependenciesβ
Scenario: Extract-Transform-Load pipeline where each stage depends on the previous stage completing.
Complete API Setupβ
Create ONE job with THREE endpoints. Endpoints in the same job are siblings β the AI can read their responses via get_sibling_latest_responses():
# Create the pipeline job
curl -X POST https://cronicorn.com/api/jobs \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Customer Data Pipeline",
"description": "ETL pipeline: Extract β Transform β Load with cascading dependencies"
}'
# Returns: { "id": "job_abc123", ... }
# Add Extract endpoint (runs daily at 2 AM via cron)
curl -X POST https://cronicorn.com/api/jobs/job_abc123/endpoints \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "extract-data",
"url": "https://api.example.com/etl/extract",
"method": "POST",
"baselineCron": "0 2 * * *",
"timeoutMs": 120000,
"description": "Extracts customer data from upstream API daily at 2 AM. Response includes ready_for_transform flag and batch_id that downstream siblings use to coordinate."
}'
# Add Transform endpoint (polls every 1 min, waits for extract signal)
curl -X POST https://cronicorn.com/api/jobs/job_abc123/endpoints \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "transform-data",
"url": "https://api.example.com/etl/transform",
"method": "POST",
"baselineIntervalMs": 60000,
"minIntervalMs": 30000,
"timeoutMs": 120000,
"description": "Transforms extracted data. Check extract-data sibling: only process when ready_for_transform is true and batch_id is newer than last processed batch. After success, response includes ready_for_load=true for the load-data sibling. Tighten to 30 seconds when actively processing a batch."
}'
# Add Load endpoint (polls every 1 min, waits for transform signal)
curl -X POST https://cronicorn.com/api/jobs/job_abc123/endpoints \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "load-data",
"url": "https://api.example.com/etl/load",
"method": "POST",
"baselineIntervalMs": 60000,
"minIntervalMs": 30000,
"timeoutMs": 180000,
"description": "Loads transformed data to production database. Check transform-data sibling: only process when ready_for_load is true and batch_id matches. After success, set pipeline_complete=true."
}'
# All three endpoints are now siblings. AI coordinates them automatically
# using get_sibling_latest_responses() β no orchestration code needed.
Error Handling in Pipelinesβ
If any stage fails (HTTP 4xx/5xx or timeout):
- Exponential backoff: The failed endpoint's next run is delayed by
baseline Γ 2^failures(capped at 32Γ). Downstream stages see no newready_for_*signal and stay on their baseline schedule. - AI-driven recovery: The AI Planner detects failure patterns and can override backoff with a tighter interval hint to retry sooner, or pause if failures persist.
- Manual recovery via API:
# Reset backoff on the failed endpoint
curl -X POST https://cronicorn.com/api/endpoints/ENDPOINT_ID/reset-failures \
-H "x-api-key: YOUR_API_KEY"
# Or trigger an immediate one-shot retry
curl -X POST https://cronicorn.com/api/endpoints/ENDPOINT_ID/hints/oneshot \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "nextRunAt": "2026-02-03T02:30:00Z", "ttlMinutes": 30, "reason": "Manual retry" }'
Race Condition Preventionβ
Cronicorn prevents race conditions at multiple levels:
- Atomic job claiming: The Scheduler uses database-level atomic operations (SELECT ... FOR UPDATE SKIP LOCKED) to claim endpoints. No two scheduler instances execute the same endpoint simultaneously.
- Batch ID idempotency: Pipeline stages check batch_id from upstream siblings. If already processed, they skip β preventing duplicate work.
- AI analysis serialization: Each endpoint is analyzed independently. The AI reads sibling state but only modifies the current endpoint.
Endpointsβ
Endpoint 1: Extract Customer Data (cron: 0 2 * * * - 2 AM daily)
- Pulls data from external API
- Returns:
{ "extracted_count": 5000, "batch_id": "2025-11-02", "completed_at": "2025-11-02T02:15:00Z" }
Endpoint 2: Transform Customer Data (1-minute interval, waits for extract)
- Transforms raw data to normalized format
- Returns:
{ "transformed_count": 5000, "batch_id": "2025-11-02", "ready_for_load": true }
Endpoint 3: Load to Database (1-minute interval, waits for transform)
- Loads normalized data to production database
- Returns:
{ "loaded_count": 5000, "batch_id": "2025-11-02", "pipeline_complete": true }
Response Body Structureβ
Extract:
{
"stage": "extract",
"batch_id": "2025-11-02",
"extracted_count": 5000,
"completed_at": "2025-11-02T02:15:00Z",
"ready_for_transform": true,
"status": "success"
}
Transform (before ready):
{
"stage": "transform",
"batch_id": "2025-11-01",
"status": "waiting",
"waiting_for": "extract_2025-11-02"
}
Transform (after ready):
{
"stage": "transform",
"batch_id": "2025-11-02",
"transformed_count": 5000,
"completed_at": "2025-11-02T02:30:00Z",
"ready_for_load": true,
"status": "success"
}
AI Coordination Logicβ
When AI analyzes Transform:
- Calls
get_sibling_latest_responses() - Finds Extract response:
{ "batch_id": "2025-11-02", "ready_for_transform": true } - Checks Transform's last response:
{ "batch_id": "2025-11-01", "status": "waiting" } - Sees new batch available (batch IDs don't match)
- Calls
propose_next_time(now, reason="New batch 2025-11-02 ready for transform")
When AI analyzes Load:
- Calls
get_sibling_latest_responses() - Finds Transform response:
{ "batch_id": "2025-11-02", "ready_for_load": true } - Checks Load's last response:
{ "batch_id": "2025-11-01" } - Sees new batch available
- Calls
propose_next_time(now, reason="Transformed batch 2025-11-02 ready to load")
Key Pointsβ
- Batch IDs enable idempotency (endpoints know what they've processed)
ready_for_*flags signal downstream readiness- Transform and Load run frequently (1-minute interval) but only process when data is ready
- Coordination happens within minutes of upstream completion
Pattern 3: Cooldown-Based Actionsβ
Scenario: Cache warming endpoint that should run immediately when cache hit rate drops, but not more than once per hour.
Endpointβ
Job: "Cache Management"
Endpoint: Cache Monitor (5-minute interval)
- Monitors cache hit rate
- Warms cache if hit rate drops below threshold
- Includes cooldown tracking to prevent duplicate warming
Response Body Structureβ
Normal operation:
{
"cache_hit_rate_pct": 95.5,
"cache_size_mb": 512,
"status": "healthy",
"last_warm_at": "2025-11-02T12:00:00Z"
}
Low hit rate detected:
{
"cache_hit_rate_pct": 65.2,
"cache_size_mb": 512,
"status": "degraded",
"last_warm_at": "2025-11-02T12:00:00Z",
"warm_cache_needed": true,
"cooldown_minutes": 60
}
After warming:
{
"cache_hit_rate_pct": 94.8,
"cache_size_mb": 512,
"status": "healthy",
"last_warm_at": "2025-11-02T13:15:00Z",
"cache_warmed": true
}
AI Coordination Logicβ
When AI analyzes Cache Monitor:
- Calls
get_latest_response() - Sees:
{ "cache_hit_rate_pct": 65.2, "warm_cache_needed": true, "last_warm_at": "2025-11-02T12:00:00Z" } - Calls
get_response_history(limit=5)to check recent warmings - Calculates time since last warm:
now - last_warm_at = 75 minutes - Compares to cooldown:
75 min > 60 min cooldown - Calls
propose_next_time(now, reason="Cache hit rate dropped to 65%, cooldown elapsed")
If cooldown not elapsed:
- AI sees
last_warm_atwas 30 minutes ago - Cooldown is 60 minutes
- Takes no action (reasoning: "Cache warming needed but cooldown active - 30 min elapsed of 60 min required")
Key Pointsβ
- Cooldown prevents duplicate expensive actions
last_warm_attimestamp enables cooldown calculation- AI checks response history to verify pattern
- Endpoint's response body drives action (not external state)
Pattern 4: Tiered Priority Systemβ
Scenario: Multiple monitoring endpoints with different priorities. Under load, pause lower tiers.
Endpoint Naming Conventionβ
Use names/descriptions to encode priority:
[CRITICAL] Payment Processor[NORMAL] Inventory Sync[LOW] Analytics Updater
Or use description field:
{
"name": "Payment Processor",
"description": "Priority: CRITICAL. Processes customer payments."
}
Coordination Endpointβ
System Health Monitor (1-minute interval)
- Checks overall system load (CPU, memory, queue depths)
- Returns recommendations for priority levels to pause
Response Body:
{
"system_load_pct": 85,
"recommendations": {
"active_priority_levels": ["CRITICAL", "NORMAL"],
"paused_priority_levels": ["LOW"],
"reason": "System load at 85% - pausing low-priority work"
}
}
AI Logic for Each Endpointβ
When AI analyzes any endpoint:
- Calls
get_sibling_latest_responses() - Finds System Health Monitor response
- Extracts endpoint's priority from name/description
- Checks if priority in
paused_priority_levels - If yes:
pause_until(calculated_time, reason="System load high") - If no: No action (or resume if paused)
Key Pointsβ
- Single health monitor coordinates entire tier
- Priority encoded in metadata (names/descriptions)
- AI interprets recommendations and applies to individual endpoints
- Centralized policy (health monitor) with distributed execution (AI per endpoint)
Pattern 5: Cross-Job Coordination (Interdependent Jobs)β
Scenario: Endpoints in different jobs need to coordinate (e.g., upstream service health affects downstream consumers). This is the pattern for interdependent HTTP jobs that span separate job containers.
Challengeβ
get_sibling_latest_responses() only works within the same job. Cross-job coordination requires a different approach.
Solution: Shared State Endpointβ
The downstream endpoint checks upstream health directly and embeds the result in its own response body. The AI reads the embedded status and reacts accordingly.
Complete API Setupβ
Since these endpoints are in different jobs, they can't use get_sibling_latest_responses(). Instead, the downstream endpoint checks upstream health and embeds the status in its own response body.
# Create Job 1: Upstream Service
curl -X POST https://cronicorn.com/api/jobs \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Upstream Service", "description": "Monitors upstream API health" }'
# Returns: { "id": "job_upstream", ... }
# Add upstream health check endpoint
curl -X POST https://cronicorn.com/api/jobs/job_upstream/endpoints \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "upstream-health",
"url": "https://upstream.example.com/health",
"method": "GET",
"baselineIntervalMs": 60000,
"timeoutMs": 10000,
"description": "Monitors upstream service health. Response includes service_status field used by downstream consumers in other jobs."
}'
# Create Job 2: Downstream Consumers (separate job)
curl -X POST https://cronicorn.com/api/jobs \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Downstream Consumers", "description": "Processes data from upstream, embeds upstream health in responses for cross-job coordination" }'
# Returns: { "id": "job_downstream", ... }
# Add data processor endpoint
curl -X POST https://cronicorn.com/api/jobs/job_downstream/endpoints \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "data-processor",
"url": "https://downstream.example.com/process",
"method": "POST",
"baselineIntervalMs": 300000,
"minIntervalMs": 60000,
"timeoutMs": 60000,
"description": "Processes data from upstream service. Response body includes upstream_status from upstream health check. When upstream_status is unavailable, pause for 15 minutes to wait for recovery. When upstream_status returns to healthy, resume immediately."
}'
# Add analytics pipeline endpoint
curl -X POST https://cronicorn.com/api/jobs/job_downstream/endpoints \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "analytics-pipeline",
"url": "https://downstream.example.com/analytics",
"method": "POST",
"baselineIntervalMs": 600000,
"minIntervalMs": 60000,
"timeoutMs": 60000,
"description": "Runs analytics on upstream data. Response includes upstream_status. When upstream_status is unavailable, pause for 30 minutes. Resume when upstream_status is healthy."
}'
# Cross-job coordination works through embedded status in response bodies.
# Each downstream endpoint checks upstream health in its own HTTP call
# and includes the result in its response for the AI to interpret.
How It Worksβ
Job 1: "Upstream Service"
- Endpoint: Health Check (1-minute interval)
- Returns:
{ "service_status": "healthy", "last_check": "..." }
Job 2: "Downstream Consumers"
- Endpoint 1: Data Processor
- Endpoint 2: Analytics Pipeline
Configure Downstream endpoints to call Upstream Service's health endpoint and include the result in their response body:
Data Processor Response:
{
"processed_count": 100,
"upstream_health": {
"status": "healthy",
"checked_at": "2025-11-02T14:30:00Z"
}
}
When upstream goes down:
{
"processed_count": 0,
"error": "Upstream service unavailable",
"upstream_health": {
"status": "unavailable",
"checked_at": "2025-11-02T14:35:00Z"
}
}
AI Logicβ
When AI analyzes Data Processor:
- Calls
get_latest_response() - Sees:
{ "upstream_health": { "status": "unavailable" } } - Calls
pause_until(now + 15_minutes, reason="Upstream service unavailable")
What the AI Does: Step-by-Stepβ
Step 1: Upstream is healthy
- AI analyzes data-processor β calls
get_latest_response() - Sees
{ "processed_count": 100, "upstream_health": { "status": "healthy" } } - Reads description: "When upstream_status returns to healthy, resume immediately"
- No action needed β maintains baseline
Step 2: Upstream goes down
- AI analyzes data-processor β calls
get_latest_response() - Sees
{ "processed_count": 0, "upstream_health": { "status": "unavailable" } } - Reads description: "When upstream_status is unavailable, pause for 15 minutes"
- Calls
pause_until(now + 15 minutes, reason="Upstream unavailable") - AI analyzes analytics-pipeline β same pattern, pauses for 30 minutes (per its description)
Step 3: Upstream recovers
- AI analyzes data-processor β calls
get_latest_response() - Sees
{ "processed_count": 50, "upstream_health": { "status": "healthy" } } - Reads description: "When upstream_status returns to healthy, resume immediately"
- Calls
pause_until(null, reason="Upstream recovered")
What Happens When Both Jobs' AI Analyzes Simultaneouslyβ
No conflicts occur because each AI session only modifies its own endpoint:
- The AI analyzing upstream-health can only modify upstream-health's schedule
- The AI analyzing data-processor can only modify data-processor's schedule
- Both sessions read response bodies as snapshots β no locking or mutual modification
- Even if both run at the exact same moment, their actions are independent and non-conflicting
This is by design: the conflict resolution architecture ensures that independent analysis sessions cannot create contradictory scheduling decisions.
Practical Example: Three Endpoints Across Two Interdependent Jobsβ
Job 1: "Data Ingestion" β Pulls data from external sources
curl -X POST https://cronicorn.com/api/jobs \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Data Ingestion", "description": "Ingests data from external APIs" }'
# Endpoint: fetch-orders (runs every 5 minutes)
curl -X POST https://cronicorn.com/api/jobs/JOB1_ID/endpoints \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "fetch-orders",
"url": "https://api.example.com/orders/recent",
"method": "GET",
"baselineIntervalMs": 300000,
"minIntervalMs": 60000,
"description": "Fetches recent orders. Response includes new_order_count and ingestion_status."
}'
Job 2: "Order Processing" β Processes ingested orders (separate team, separate job)
curl -X POST https://cronicorn.com/api/jobs \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Order Processing", "description": "Processes ingested orders, checks ingestion status" }'
# Endpoint: process-orders (checks ingestion status via its own response body)
curl -X POST https://cronicorn.com/api/jobs/JOB2_ID/endpoints \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "process-orders",
"url": "https://downstream.example.com/process",
"method": "POST",
"baselineIntervalMs": 300000,
"minIntervalMs": 60000,
"description": "Processes ingested orders. Response includes ingestion_status from upstream. When ingestion_status is stale (no new data for 15 minutes), relax to 10-minute interval. When ingestion_status is active with high volume, tighten to 1 minute."
}'
# Endpoint: generate-reports (depends on processed orders)
curl -X POST https://cronicorn.com/api/jobs/JOB2_ID/endpoints \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "generate-reports",
"url": "https://downstream.example.com/reports",
"method": "POST",
"baselineIntervalMs": 600000,
"description": "Generates reports from processed orders. Check process-orders sibling: only generate when orders_processed_count > 0 in latest response. If no new data, maintain baseline."
}'
In this setup, process-orders and generate-reports are siblings (same job), so the AI coordinates them via get_sibling_latest_responses(). The cross-job dependency (Job 1 β Job 2) is handled by embedding ingestion status in the downstream response body.
fetch-orders response body (Job 1):
{
"new_order_count": 150,
"ingestion_status": "active",
"last_order_id": "ord_98765",
"ingested_at": "2026-02-03T14:30:00Z",
"timestamp": "2026-02-03T14:30:00Z"
}
process-orders response body (Job 2 β embeds upstream status):
{
"orders_processed_count": 120,
"orders_pending": 30,
"ingestion_status": "active",
"ingestion_checked_at": "2026-02-03T14:31:00Z",
"processing_rate_per_min": 40,
"status": "processing",
"timestamp": "2026-02-03T14:31:00Z"
}
generate-reports response body (Job 2 β reads sibling process-orders):
{
"reports_generated": 3,
"last_report_batch": "2026-02-03",
"orders_available": 120,
"status": "complete",
"timestamp": "2026-02-03T14:35:00Z"
}
When ingestion stalls (fetch-orders returns no new data):
{
"new_order_count": 0,
"ingestion_status": "stale",
"last_order_id": "ord_98765",
"stale_since": "2026-02-03T14:30:00Z",
"timestamp": "2026-02-03T14:45:00Z"
}
The AI reads ingestion_status: "stale" from the process-orders response, matches it against the description ("When ingestion_status is stale, relax to 10-minute interval"), and calls propose_interval(600000).
Verifying cross-job coordination is working:
# Check AI analysis sessions for the downstream endpoint
curl -H "x-api-key: YOUR_API_KEY" \
"https://cronicorn.com/api/endpoints/PROCESS_ORDERS_EP_ID/analysis-sessions?limit=5"
Look for reasoning entries that mention ingestion_status β this confirms the AI is reading the embedded upstream status from the response body and acting on it.
Key Pointsβ
- Endpoints embed external dependency status in their response bodies
- AI reacts to dependency signals
- Works across jobs (no sibling query needed)
- Endpoint is responsible for checking upstream status
- No conflicts: each AI session only modifies its own endpoint
Common Coordination Signalsβ
Here are standard signal patterns you can reuse:
Readiness Signalsβ
{
"ready_for_processing": true,
"data_available": true,
"batch_ready": true
}
Dependency Statusβ
{
"dependency_status": "healthy" | "degraded" | "unavailable",
"upstream_healthy": true
}
Action Requestsβ
{
"warm_cache_needed": true,
"scale_up_needed": true,
"send_alert": true
}
Cooldown Trackingβ
{
"last_action_at": "2025-11-02T14:30:00Z",
"cooldown_minutes": 60,
"action_type": "cache_warm" | "alert_sent" | "scaled"
}
Load/Priority Signalsβ
{
"load_status": "normal" | "elevated" | "high" | "critical",
"pause_priorities": ["LOW", "NORMAL"],
"active_priorities": ["CRITICAL"]
}
Best Practicesβ
- Be explicit: Use clear signal names (
ready_for_loadnotready) - Include timestamps: Enable cooldown calculations and staleness checks
- Provide context: Add
reasonfields explaining state - Use batch IDs: Enable idempotency in pipelines
- Keep signals simple: AI reads truncated bodies (1000 chars)
- Document protocols: Comment what signals mean in endpoint descriptions
- Test coordination: Verify AI picks up signals by checking analysis sessions
Debugging Coordinationβ
When coordination isn't working:
- Check sibling responses: Does
get_sibling_latest_responses()return expected data? - Review AI sessions: What did AI see? What reasoning did it provide?
- Verify signal format: Are field names consistent?
- Check timing: AI analyzes every 5 minutesβcoordination isn't instant
- Look for errors: Did endpoint fail to write expected signal?
Key Takeawaysβ
- Coordination via response bodies: Write signals, other endpoints read them
- AI interprets signals: Uses
get_sibling_latest_responses()andget_latest_response() - Eventual consistency: Coordination happens within minutes (AI analysis cycle)
- Flexible protocols: You define what signals mean
- Priority systems: Use names/descriptions to encode metadata
- Cooldowns prevent duplicates: Track
last_action_atin response bodies - Cross-job coordination: Embed external status checks in responses
These patterns give you the building blocks for complex workflows. Mix and match based on your needs.
See Alsoβ
- How AI Adaptation Works - AI tools including sibling queries
- Configuration and Constraints - Endpoint configuration
- Use Cases - Real-world examples
- Technical Reference - Response body patterns