Skip to main content

API Orchestration Playbook

Call multiple APIs, handle auth, retries, and transform responses. Build API workflows that just work.

Basic Pattern

ensemble: api-workflow

agents:
  # Call APIs in sequence or parallel
  - name: api-1
    agent: fetcher
    inputs:
      url: https://api1.example.com/data

  - name: api-2
    agent: fetcher
    inputs:
      url: https://api2.example.com/data
      body: ${api-1.output.body}

  # Transform and combine
  - name: combine
    agent: transformer
    inputs:
      data:
        source1: ${api-1.output.body}
        source2: ${api-2.output.body}

OAuth Flow

ensemble: oauth-api-call

agents:
  # Get access token
  - name: get-token
    agent: fetcher
    inputs:
      url: https://auth.example.com/oauth/token
      method: POST
      body:
        grant_type: client_credentials
        client_id: ${env.CLIENT_ID}
        client_secret: ${env.CLIENT_SECRET}
    cache:
      ttl: 3600
      key: oauth-token

  # Use token
  - name: api-call
    agent: fetcher
    inputs:
      url: https://api.example.com/data
      headers:
        Authorization: Bearer ${get-token.output.body.access_token}

Parallel API Aggregation

ensemble: aggregate-apis

agents:
  # Call multiple APIs in parallel
  - name: weather
    agent: fetcher
    inputs:
      url: https://api.weather.com/current?city=${input.city}

  - name: news
    agent: fetcher
    inputs:
      url: https://api.news.com/local?city=${input.city}

  - name: events
    agent: fetcher
    inputs:
      url: https://api.events.com/upcoming?city=${input.city}

  # Combine results
  - name: combine
    operation: code
    config:
      code: |
        return {
          city: ${input.city},
          weather: ${weather.output.body},
          news: ${news.output.body.articles}.slice(0, 5),
          events: ${events.output.body.events}.slice(0, 10)
        };

Webhook Handling

ensemble: process-webhook

agents:
  # Validate webhook signature
  - name: validate
    operation: code
    config:
      code: |
        const crypto = require('crypto');
        const signature = crypto
          .createHmac('sha256', ${env.WEBHOOK_SECRET})
          .update(JSON.stringify(${input.body}))
          .digest('hex');

        return {
          valid: signature === ${input.headers['x-signature']}
        };

  # Process if valid
  - name: process
    condition: ${validate.output.valid}
    operation: code
    config:
      code: |
        // Process webhook data
        return { processed: true };

  # Forward to internal API
  - name: forward
    condition: ${process.executed}
    agent: fetcher
    inputs:
      url: ${env.INTERNAL_API_URL}
      method: POST
      body: ${input.body}

Rate Limiting

ensemble: rate-limited-api

agents:
  # Check rate limit
  - name: check-limit
    operation: storage
    config:
      type: kv
      action: get
      key: rate-limit-${input.user_id}

  # Allow if under limit
  - name: allow
    condition: ${!check-limit.output || check-limit.output.count < 100}
    agent: fetcher
    inputs:
      url: https://api.example.com/data

  # Update counter
  - name: update-limit
    condition: ${allow.executed}
    operation: storage
    config:
      type: kv
      action: put
      key: rate-limit-${input.user_id}
      value:
        count: ${(check-limit.output?.count || 0) + 1}
        reset_at: ${Date.now() + 3600000}
      ttl: 3600

  # Reject if over limit
  - name: reject
    condition: ${check-limit.output && check-limit.output.count >= 100}
    operation: code
    config:
      code: |
        return {
          error: 'Rate limit exceeded',
          retry_after: ${check-limit.output.reset_at}
        };

Circuit Breaker

ensemble: circuit-breaker

agents:
  # Check circuit state
  - name: check-circuit
    operation: storage
    config:
      type: kv
      action: get
      key: circuit-${input.service}

  # Call if circuit closed
  - name: call-api
    condition: ${!check-circuit.output || check-circuit.output.state !== 'open'}
    agent: fetcher
    inputs:
      url: ${input.url}
    retry:
      maxAttempts: 3

  # Open circuit on failure
  - name: open-circuit
    condition: ${call-api.failed}
    operation: storage
    config:
      type: kv
      action: put
      key: circuit-${input.service}
      value:
        state: open
        opened_at: ${Date.now()}
      ttl: 60

  # Use fallback if circuit open
  - name: fallback
    condition: ${check-circuit.output?.state === 'open'}
    operation: code
    config:
      code: |
        return {
          error: 'Service unavailable',
          fallback: true
        };

Next Steps