What is CueAPI
CueAPI is a scheduling API for AI agents. Register a cue (cron or one-time), and CueAPI fires a webhook or queues an execution for your local worker at the right time. Every delivery includes HMAC-SHA256 signatures, automatic retries, and execution history.
Base URL: https://api.cueapi.ai
Auth: Bearer cue_sk_... (SHA-256 hashed, cached in Redis, DB fallback)
Docs: https://docs.cueapi.ai
Dashboard: https://cueapi.ai
Reliability
| Property | Detail |
|---|
| Delivery guarantee | At-least-once. Consumers must be idempotent. |
| Retries | 3 attempts, exponential backoff (default 1, 2, 4 minutes) |
| Polling interval | 5 seconds |
| Webhook timeout | 30 seconds |
| Stale recovery | Executions stuck in "delivering" > 5 min recovered automatically |
| Worker lease recovery | Reset when BOTH worker heartbeat stale (3 min) AND claim expired (15 min) |
| Poller leader election | Redis SET NX EX 30, standby takes over within 30s |
| Auth degradation | Redis down → falls through to PostgreSQL |
| Rate limit degradation | Redis down → rate limiting skipped, requests allowed |
| Data persistence | PostgreSQL (primary), Redis (cache + rate limits) |
| Health endpoint | GET /health — full diagnostics (services, poller, workers, queue) |
| Status endpoint | GET /status — lightweight for uptime monitors |
Security
| Property | Detail |
|---|
| Webhook signing | HMAC-SHA256 with per-user secrets (whsec_ prefix) |
| Signature format | X-CueAPI-Signature: v1={hex_digest} |
| Replay protection | X-CueAPI-Timestamp header, 5-minute tolerance |
| Secret management | GET /v1/auth/webhook-secret, POST /v1/auth/webhook-secret/regenerate |
| API key storage | SHA-256 hashed, never stored in plaintext |
| SSRF protection | DNS resolution + IP range blocking (private, loopback, link-local, metadata) |
| Transport security | HTTPS required in production |
| Rate limiting | Sliding window (Redis sorted set), per-API-key, tier-aware |
| Comparison | Constant-time HMAC comparison |
Mental model
Webhook transport:
REGISTER --> FIRE --> DELIVER --> REPORT
POST /v1/cues poller webhook POST POST /v1/executions/{id}/outcome
Worker transport:
REGISTER --> QUEUE --> PULL+RUN --> REPORT
POST /v1/cues poller GET /v1/executions/claimable POST /v1/executions/{id}/outcome
POST /v1/executions/{id}/claimKey concepts
- Cue: A registered schedule. Has status (active/paused/completed/failed), schedule (cron or once), transport (webhook or worker), payload (JSON).
- Execution: A single fire of a cue. Status: pending → delivering → success/failed. Retries go through: retrying → retry_ready → delivering.
- Outcome: What the handler reports back. Write-once. Contains success (bool), result, error, metadata.
- Transport: webhook (push to URL) or worker (pull from API).
- Delivery guarantee: at-least-once. Use execution_id as idempotency/dedup key.
- Two status layers: delivery_status (did the HTTP call succeed?) and outcome_status (did the handler logic succeed?).
Idempotency warning
CueAPI guarantees at-least-once delivery. Your handler may receive the same
execution more than once. Use the X-CueAPI-Execution-Id header as a
deduplication key. Store processed execution IDs and skip duplicates.
All endpoints (22)
| Method | Path | Description |
|---|
| POST | /v1/auth/register | Register new user, get API key |
| GET | /v1/auth/me | Current user info + usage stats |
| POST | /v1/auth/key/regenerate | Regenerate API key, revoke old |
| POST | /v1/auth/device-code | Create device code for login |
| POST | /v1/auth/device-code/poll | Poll device code status |
| POST | /v1/auth/device-code/submit-email | Submit email for magic link |
| GET | /v1/auth/webhook-secret | Get webhook signing secret |
| POST | /v1/auth/webhook-secret/regenerate | Rotate webhook secret |
| POST | /v1/cues | Create a cue |
| GET | /v1/cues | List cues (filter by status, pagination) |
| GET | /v1/cues/{cue_id} | Get cue detail + last 10 executions |
| PATCH | /v1/cues/{cue_id} | Update cue (pause/resume/reschedule) |
| DELETE | /v1/cues/{cue_id} | Delete a cue |
| GET | /v1/executions/claimable | List pending worker-transport executions |
| POST | /v1/executions/{id}/claim | Claim a worker execution |
| POST | /v1/executions/{id}/outcome | Report execution outcome (write-once) |
| GET | /v1/usage | Usage stats (plan, cues, executions, rate limit) |
| POST | /v1/billing/checkout | Create Stripe checkout session |
| POST | /v1/billing/portal | Create Stripe customer portal session |
| POST | /v1/worker/heartbeat | Register/update worker with handlers |
| GET | /health | Full system diagnostics |
| GET | /status | Lightweight status check |
Rate limits per plan
| Plan | Rate limit | Cues | Executions/month |
|---|
| Free | 60 req/min | 10 | 300 |
| Pro ($9.99/mo) | 200 req/min | 100 | 5,000 |
| Scale ($49/mo) | 500 req/min | 500 | 50,000 |
Execution payload schema (webhook delivery)
Headers:
X-CueAPI-Signature: v1={hmac_sha256_hex}
X-CueAPI-Timestamp: {unix_epoch}
X-CueAPI-Cue-Id: {cue_id}
X-CueAPI-Execution-Id: {execution_id}
X-CueAPI-Scheduled-For: {iso8601}
X-CueAPI-Attempt: {int}
Content-Type: application/json
Authorization: Bearer {cue_api_key}Body:
The cue's payload JSON (whatever you set when creating the cue).
Webhook secret
Retrieve: GET /v1/auth/webhook-secret
Rotate: POST /v1/auth/webhook-secret/regenerate
Format: whsec_ + 64 hex chars
Rotation: Old secret instantly revoked on rotation.
Error codes
Error format:
{ "error": { "code": "string", "message": "string", "status": int } }| Code | HTTP |
|---|
| invalid_schedule | 400 |
| invalid_payload_size | 400 |
| invalid_callback_url | 400 |
| invalid_plan | 400 |
| invalid_interval | 400 |
| no_stripe_customer | 400 |
| invalid_device_code | 400 |
| device_code_expired | 400 |
| invalid_status | 400 |
| device_code_exists | 409 |
| device_code_not_found | 404 |
| validation_error | 400 |
| invalid_api_key | 401 |
| cue_limit_exceeded | 403 |
| cue_not_found | 404 |
| rate_limit_exceeded | 429 |
| internal_error | 500 |
Retry policy
| Property | Detail |
|---|
| Max attempts | 3 (configurable per cue via retry_max_attempts) |
| Backoff | Exponential, default [1, 2, 4] minutes (configurable via retry_backoff_minutes) |
| Webhook timeout | 30 seconds |
| Success | HTTP 2xx |
| Failure | HTTP 4xx/5xx or timeout |
| After exhaustion | One-time cues → status "failed". Recurring cues → stay "active". |
Webhook verification code
Python
import hmac, hashlib, time
def verify_webhook(payload_bytes, signature_header, timestamp_header, secret):
# Check timestamp freshness (5 min tolerance)
if abs(time.time() - int(timestamp_header)) > 300:
raise ValueError("Timestamp too old")
# Reconstruct signed content
signed_content = f"{timestamp_header}.{payload_bytes.decode()}"
expected = "v1=" + hmac.new(
secret.encode(), signed_content.encode(), hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature_header, expected):
raise ValueError("Invalid signature")Node.js
const crypto = require('crypto');
function verifyWebhook(payloadBuffer, signatureHeader, timestampHeader, secret) {
if (Math.abs(Date.now() / 1000 - parseInt(timestampHeader)) > 300) {
throw new Error('Timestamp too old');
}
const signedContent = `${timestampHeader}.${payloadBuffer.toString()}`;
const expected = 'v1=' + crypto
.createHmac('sha256', secret)
.update(signedContent)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected))) {
throw new Error('Invalid signature');
}
}Quick start
1. Register: POST /v1/auth/register {"email": "you@example.com"} → get api_key
2. Get secret: GET /v1/auth/webhook-secret → whsec_...
3. Create cue: POST /v1/cues with schedule + callback
4. Verify: Check your webhook endpoint receives the signed payloadWorker pull flow
1. Heartbeat: POST /v1/worker/heartbeat with worker_id + handlers list
2. Poll: GET /v1/executions/claimable?task=your_task
3. Claim: POST /v1/executions/{id}/claim with worker_id
4. Run: Execute handler logic
5. Report: POST /v1/executions/{id}/outcome with {success, result/error, metadata}Install
pip install cueapi # CLI
pip install cueapi-worker # Local daemon
No SDK needed — direct HTTP API.