Transport

Webhook push vs. worker pull. How the handoff crosses the boundary.

Overview

Transport is how the handoff crosses the boundary. It is the mechanism by which CueAPI delivers a cue's declared intent to your agent. A cue is the intent. An execution is the handoff boundary. Transport is the path across it. CueAPI supports two transport modes:

Webhook (push)

CueAPI sends a signed HTTP POST to your callback URL. Default transport.

Worker (pull)

Your local daemon polls CueAPI for pending executions and runs handler scripts.

Webhook transport

The default. CueAPI POSTs a signed payload to your callback URL when the cue fires.

json
{
  "name": "daily-sync",
  "schedule": {"cron": "0 9 * * *"},
  "callback": {
    "url": "https://api.yourapp.com/webhook",
    "method": "POST"
  }
}

Requirements:

  • Public HTTPS URL (HTTP allowed in development only)
  • Returns 2xx for success
  • Must respond within 30 seconds

What CueAPI sends:

POST https://api.yourapp.com/webhook
X-CueAPI-Signature: v1=abc123...
X-CueAPI-Timestamp: 1710340800
X-CueAPI-Cue-Id: cue_abc123def456
X-CueAPI-Execution-Id: exec-uuid
Content-Type: application/json

{"task": "daily-sync", "kind": "scheduled_task", ...}

Worker transport

For machines without a public URL. A daemon on your machine polls for executions and runs local handler scripts.

json
{
  "name": "draft-linkedin",
  "schedule": {"cron": "0 9 * * 1-5"},
  "transport": "worker",
  "payload": {
    "task": "draft-linkedin",
    "kind": "agent_turn"
  }
}

How it works:

  1. CueAPI creates executions in pending status
  2. Your worker daemon polls GET /v1/executions/claimable
  3. Worker claims an execution via POST /v1/executions/{id}/claim
  4. Worker runs the matched handler script
  5. Worker reports the outcome via POST /v1/executions/{id}/outcome

Requirements:

  • cueapi-worker daemon running on your machine, or a custom polling script
  • YAML config mapping payload.task to handler scripts (if using the daemon)
  • API key for authentication

Worker loop in JavaScript

If you prefer to write your own worker instead of using the daemon:

javascript
const API = "https://api.cueapi.ai/v1";
const headers = {
  "Authorization": "Bearer cue_sk_YOUR_KEY",
  "Content-Type": "application/json",
};
 
async function workerLoop() {
  while (true) {
    // 1. Poll for claimable executions
    const poll = await fetch(
      `${API}/executions/claimable?task=my-task`,
      { headers }
    );
    const { executions } = await poll.json();
 
    for (const exec of executions) {
      // 2. Claim the execution
      const claim = await fetch(
        `${API}/executions/${exec.id}/claim`,
        { method: "POST", headers }
      );
      if (!claim.ok) continue; // already claimed
 
      // 3. Do the work
      let success = true;
      let result = "";
      try {
        result = await doWork(exec.payload);
      } catch (err) {
        success = false;
        result = err.message;
      }
 
      // 4. Report outcome
      await fetch(`${API}/executions/${exec.id}/outcome`, {
        method: "POST",
        headers,
        body: JSON.stringify({
          success,
          result,
          ...(success ? {} : { error: result }),
        }),
      });
    }
 
    // Wait before next poll
    await new Promise((r) => setTimeout(r, 5000));
  }
}

Choosing a transport

ConsiderationWebhookWorker
Public URL requiredYesNo
Runs on local machineNoYes
Handler languageAny (HTTP endpoint)Any (shell command)
LatencyImmediatePolling interval (5s default)
RetriesAutomatic (3 attempts)Manual via outcome reporting
SigningHMAC-SHA256N/A (API key auth)
Best forCloud services, APIsLocal scripts, AI agents

Specifying transport

Set transport at cue creation:

json
{
  "name": "my-cue",
  "schedule": {"cron": "0 9 * * *"},
  "transport": "worker",
  "payload": {"task": "my-handler"}
}

You can also nest transport inside callback:

json
{
  "callback": {"transport": "worker"}
}

Info

Webhook cues require a callback URL. Worker cues do not need a callback URL, but can optionally have one.

How do I know if my agent ran successfully?
Ctrl+K