Make Your Agent Async

Enable async execution with webhooks.

When to Use Async Execution

Agentfield supports both synchronous and asynchronous execution patterns. Choose based on your workload characteristics:

FactorUse Sync (/execute/)Use Async (/execute/async/)
Duration< 90 secondsAny duration (no timeout)
ResponseNeed immediate resultCan wait or use webhooks
WorkflowSequential dependenciesFire-and-forget, batch jobs
ConnectionCan hold HTTP openAvoid blocking connections
Use CaseQuick translations, validationsLLM reasoning, data processing, multi-step workflows

Rule of thumb: If it involves LLM reasoning or takes > 10 seconds, use async.

Three Execution Patterns

Pattern 1: Synchronous - Immediate Results

Use for quick operations where you need the result before proceeding.

curl -X POST http://localhost:8080/api/v1/execute/hello-world.greet \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "name": "Alice"
    }
  }'

Response (HTTP 200, blocks until complete):

{
  "execution_id": "exec_abc123",
  "run_id": "run_xyz789",
  "status": "succeeded",
  "result": {
    "greeting": "Hello, Alice!"
  },
  "duration_ms": 450,
  "finished_at": "2025-01-15T10:30:00Z"
}

Timeout: 90 seconds. Use async for longer tasks.

Use for long-running tasks. Your endpoint receives the result when complete.

curl -X POST http://localhost:8080/api/v1/execute/async/research-agent.deep_analysis \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "topic": "quantum computing applications"
    },
    "webhook": {
      "url": "https://your-app.com/api/webhooks/agentfield",
      "secret": "your-webhook-secret-key",
      "headers": {
        "X-Custom-ID": "request-123"
      }
    }
  }'

Immediate Response (HTTP 202):

{
  "execution_id": "exec_abc123",
  "run_id": "run_xyz789",
  "status": "queued",
  "target": "research-agent.deep_analysis",
  "webhook_registered": true
}

Your Webhook Receives (when complete):

{
  "event": "execution.completed",
  "execution_id": "exec_abc123",
  "status": "succeeded",
  "result": {
    "analysis": "Quantum computing enables...",
    "sources": [...]
  },
  "duration_ms": 45000
}

Webhook Handler Example (Python/Flask):

import hmac
import hashlib
from flask import request, jsonify

@app.route('/api/webhooks/agentfield', methods=['POST'])
def handle_agentfield_webhook():
    # Verify signature
    signature = request.headers.get('X-AgentField-Signature')
    payload = request.get_data()

    expected_sig = "sha256=" + hmac.new(
        b"your-webhook-secret-key",
        payload,
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature, expected_sig):
        return jsonify({"error": "invalid signature"}), 401

    # Process result
    data = request.json
    execution_id = data["execution_id"]
    result = data["result"]

    # Your business logic here
    process_research_analysis(execution_id, result)

    return jsonify({"received": True}), 200

Why webhooks?

  • No polling overhead
  • Instant notification
  • Scales to thousands of concurrent executions
  • Production-grade (retries, signatures, delivery tracking)

See Webhooks Documentation for security best practices.

Pattern 3: Async + Polling - Simple Integration

Use when you can't expose a webhook endpoint (firewalls, local development).

# 1. Start execution
curl -X POST http://localhost:8080/api/v1/execute/async/doc-processor.analyze \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "document_url": "https://example.com/report.pdf"
    }
  }'

Response:

{
  "execution_id": "exec_abc123",
  "status": "queued"
}
# 2. Poll for status (every 2-5 seconds)
curl http://localhost:8080/api/v1/executions/exec_abc123

While Running:

{
  "execution_id": "exec_abc123",
  "status": "running",
  "started_at": "2025-01-15T10:29:00Z"
}

When Complete:

{
  "execution_id": "exec_abc123",
  "status": "succeeded",
  "result": {
    "summary": "Document contains...",
    "entities": [...]
  },
  "duration_ms": 12000,
  "completed_at": "2025-01-15T10:29:12Z"
}

Polling with Exponential Backoff (JavaScript):

async function pollExecution(executionId, maxAttempts = 60) {
  let attempt = 0;
  let delay = 1000; // Start with 1s

  while (attempt < maxAttempts) {
    const response = await fetch(
      `http://localhost:8080/api/v1/executions/${executionId}`
    );
    const data = await response.json();

    if (data.status === 'succeeded') {
      return data.result;
    }

    if (data.status === 'failed') {
      throw new Error(data.error);
    }

    // Still running, wait and retry
    await new Promise(resolve => setTimeout(resolve, delay));
    delay = Math.min(delay * 1.5, 10000); // Cap at 10s
    attempt++;
  }

  throw new Error('Execution timeout');
}

// Usage
const result = await pollExecution('exec_abc123');

Batch Status Check:

curl -X POST http://localhost:8080/api/v1/executions/batch-status \
  -H "Content-Type: application/json" \
  -d '{
    "execution_ids": ["exec_1", "exec_2", "exec_3"]
  }'

Response:

{
  "exec_1": {"status": "succeeded", "result": {...}},
  "exec_2": {"status": "running"},
  "exec_3": {"status": "failed", "error": "timeout"}
}

Error Handling

All execution patterns return consistent status values:

StatusMeaningAction
queuedWaiting in worker queuePoll or wait for webhook
runningCurrently executingPoll or wait for webhook
succeededCompleted successfullyUse result field
failedExecution errorCheck error field
timeoutExceeded time limitRetry or redesign
cancelledManually stoppedNo result available

Sync Execution Errors:

{
  "error": "Agent timeout",
  "details": "Execution exceeded 90 second limit",
  "execution_id": "exec_abc123"
}

Async Webhook Failure Event:

{
  "event": "execution.failed",
  "execution_id": "exec_abc123",
  "status": "failed",
  "error": "LLM API rate limit exceeded",
  "timestamp": "2025-01-15T10:30:00Z"
}

Webhook Delivery Tracking:

curl http://localhost:8080/api/v1/executions/exec_abc123
{
  "execution_id": "exec_abc123",
  "status": "succeeded",
  "webhook_registered": true,
  "webhook_events": [
    {
      "attempt": 1,
      "http_status": 500,
      "timestamp": "2025-01-15T10:30:00Z"
    },
    {
      "attempt": 2,
      "http_status": 200,
      "timestamp": "2025-01-15T10:30:05Z"
    }
  ]
}

Webhooks retry up to 5 times with exponential backoff. Check webhook_events if you're not receiving callbacks.

Quick Start Checklist

  1. Choose your pattern:

    • Fast task? → Sync
    • Long task + production? → Async + webhooks
    • Long task + development? → Async + polling
  2. For webhooks:

    • Expose HTTPS endpoint
    • Verify HMAC-SHA256 signature
    • Return 200 OK quickly (process async internally)
    • Handle both execution.completed and execution.failed events
  3. For polling:

    • Use exponential backoff (start 1s, max 10s)
    • Check batch status for multiple executions
    • Set reasonable timeout (e.g., 60 attempts = ~10 minutes)
  4. Error handling:

    • Check status field
    • Handle failed and timeout states
    • Log execution_id for debugging

Next Steps