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

PropertyDetail
Delivery guaranteeAt-least-once. Consumers must be idempotent.
Retries3 attempts, exponential backoff (default 1, 2, 4 minutes)
Polling interval5 seconds
Webhook timeout30 seconds
Stale recoveryExecutions stuck in "delivering" > 5 min recovered automatically
Worker lease recoveryReset when BOTH worker heartbeat stale (3 min) AND claim expired (15 min)
Poller leader electionRedis SET NX EX 30, standby takes over within 30s
Auth degradationRedis down → falls through to PostgreSQL
Rate limit degradationRedis down → rate limiting skipped, requests allowed
Data persistencePostgreSQL (primary), Redis (cache + rate limits)
Health endpointGET /health — full diagnostics (services, poller, workers, queue)
Status endpointGET /status — lightweight for uptime monitors

Security

PropertyDetail
Webhook signingHMAC-SHA256 with per-user secrets (whsec_ prefix)
Signature formatX-CueAPI-Signature: v1={hex_digest}
Replay protectionX-CueAPI-Timestamp header, 5-minute tolerance
Secret managementGET /v1/auth/webhook-secret, POST /v1/auth/webhook-secret/regenerate
API key storageSHA-256 hashed, never stored in plaintext
SSRF protectionDNS resolution + IP range blocking (private, loopback, link-local, metadata)
Transport securityHTTPS required in production
Rate limitingSliding window (Redis sorted set), per-API-key, tier-aware
ComparisonConstant-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}/claim

Key concepts

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)

MethodPathDescription
POST/v1/auth/registerRegister new user, get API key
GET/v1/auth/meCurrent user info + usage stats
POST/v1/auth/key/regenerateRegenerate API key, revoke old
POST/v1/auth/device-codeCreate device code for login
POST/v1/auth/device-code/pollPoll device code status
POST/v1/auth/device-code/submit-emailSubmit email for magic link
GET/v1/auth/webhook-secretGet webhook signing secret
POST/v1/auth/webhook-secret/regenerateRotate webhook secret
POST/v1/cuesCreate a cue
GET/v1/cuesList 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/claimableList pending worker-transport executions
POST/v1/executions/{id}/claimClaim a worker execution
POST/v1/executions/{id}/outcomeReport execution outcome (write-once)
GET/v1/usageUsage stats (plan, cues, executions, rate limit)
POST/v1/billing/checkoutCreate Stripe checkout session
POST/v1/billing/portalCreate Stripe customer portal session
POST/v1/worker/heartbeatRegister/update worker with handlers
GET/healthFull system diagnostics
GET/statusLightweight status check

Rate limits per plan

PlanRate limitCuesExecutions/month
Free60 req/min10300
Pro ($9.99/mo)200 req/min1005,000
Scale ($49/mo)500 req/min50050,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 } }
CodeHTTP
invalid_schedule400
invalid_payload_size400
invalid_callback_url400
invalid_plan400
invalid_interval400
no_stripe_customer400
invalid_device_code400
device_code_expired400
invalid_status400
device_code_exists409
device_code_not_found404
validation_error400
invalid_api_key401
cue_limit_exceeded403
cue_not_found404
rate_limit_exceeded429
internal_error500

Retry policy

PropertyDetail
Max attempts3 (configurable per cue via retry_max_attempts)
BackoffExponential, default [1, 2, 4] minutes (configurable via retry_backoff_minutes)
Webhook timeout30 seconds
SuccessHTTP 2xx
FailureHTTP 4xx/5xx or timeout
After exhaustionOne-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 payload

Worker 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.