Skip to content

Resource Pools & Rotation

Distribute work across multiple resources — API keys, email accounts, proxy servers, or any rate-limited service. Built-in rotation strategies, daily caps, and warmup ramps ensure even distribution without overloading any single resource.

Why Resource Pools

Many workflows interact with rate-limited external services. Without rotation, a single API key or mail server becomes a bottleneck:

  • Email deliverability — rotate across SMTP accounts to avoid sender reputation damage
  • API rate limits — distribute calls across multiple keys to stay under per-key quotas
  • Proxy rotation — cycle through IPs to avoid blocks during scraping
  • Load balancing — spread traffic across servers with weight-based distribution
  • New resource onboarding — warmup ramps prevent new accounts from hitting limits on day one

Core Concepts

ConceptDescription
PoolA named collection of resources with a rotation strategy. Tenant-scoped.
ResourceA single unit in a pool (e.g., one API key, one mail server). Has a unique resource_key.
StrategyHow the next resource is selected: round_robin, weighted, or random.
Daily CapMaximum assignments per day per resource. Resets at midnight. 0 = unlimited.
WarmupGradual daily cap increase over N days for newly added resources.
AssignmentResult of requesting a resource: Assigned(key), Exhausted, or Empty.

Rotation Strategies

round_robin — Default

Assigns resources in order, cycling through all enabled resources with capacity. Tracks position via round_robin_index. Skips resources that have hit their daily cap.

weighted

Assigns proportional to each resource's weight. A resource with weight 3 receives ~3x more assignments than weight 1. Respects daily caps — exhausted resources are excluded from the weighted selection.

random

Random uniform selection among enabled resources with remaining capacity. Useful when distribution doesn't need to be perfectly even.

Assignment results: When all resources hit their daily cap, assignment returns Exhausted. If no resources are enabled, it returns Empty. Workflows should handle these cases (retry later or fail).

Warmup Ramps

New email accounts or freshly provisioned API keys often have lower limits initially. Warmup ramps gradually increase the daily cap from a starting value to the full cap over a configurable number of days.

How It Works

The effective daily cap is linearly interpolated:

effective_cap = warmup_start_cap + (daily_cap - warmup_start_cap) * days_active / warmup_days

Example: warmup_start_cap=10, daily_cap=100, warmup_days=10

Day 0:  cap = 10
Day 3:  cap = 10 + (90 * 3 / 10) = 37
Day 5:  cap = 10 + (90 * 5 / 10) = 55
Day 10: cap = 100 (warmup complete)
Day 15: cap = 100 (stays at full)

Configuration

FieldTypeDescription
warmup_startdate (YYYY-MM-DD)When the warmup period begins
warmup_daysu32Number of days to ramp from start cap to full cap. 0 = no warmup.
warmup_start_capu32Starting daily cap on day 0 of warmup

Pool API

Create Pool

POST /pools

{
  "tenant_id": "tenant-1",
  "name": "Email Senders",
  "strategy": "round_robin"
}

Response (201):
{
  "id": "550e8400-...",
  "tenant_id": "tenant-1",
  "name": "Email Senders",
  "strategy": "round_robin",
  "round_robin_index": 0,
  "created_at": "2024-01-15T10:00:00Z",
  "updated_at": "2024-01-15T10:00:00Z"
}

List Pools

GET /pools?tenant_id=tenant-1

Add Resource

POST /pools/{pool_id}/resources

{
  "resource_key": "smtp-account-1",
  "name": "Primary SMTP",
  "weight": 2,
  "daily_cap": 500,
  "warmup_start": "2024-01-15",
  "warmup_days": 14,
  "warmup_start_cap": 50
}

Response (201):
{
  "id": "...",
  "pool_id": "...",
  "resource_key": "smtp-account-1",
  "name": "Primary SMTP",
  "weight": 2,
  "enabled": true,
  "daily_cap": 500,
  "daily_usage": 0,
  "daily_usage_date": null,
  "warmup_start": "2024-01-15",
  "warmup_days": 14,
  "warmup_start_cap": 50,
  "created_at": "2024-01-15T10:00:00Z"
}

Update Resource

PUT /pools/{pool_id}/resources/{resource_id}

{
  "enabled": false,
  "daily_cap": 1000
}

Delete Resource

DELETE /pools/{pool_id}/resources/{resource_id}

Response: 204 No Content

List Resources

GET /pools/{pool_id}/resources

Response: Array of PoolResource objects with current daily_usage

Validation: resource_key andname must be 1-255 characters. weight must be at least 1.

Using Pools in Workflows

Workflows request a resource from a pool before executing rate-limited steps. The assigned resource_key is used to look up the corresponding credential or configuration.

Example: Email Rotation

[
  {
    "id": "get-sender",
    "type": "pool_assign",
    "params": {
      "pool_id": "email-senders-pool-id"
    }
  },
  {
    "id": "send-email",
    "type": "http_request",
    "params": {
      "url": "https://api.sendgrid.com/v3/mail/send",
      "headers": {
        "Authorization": "Bearer credentials://{{steps.get-sender.output.resource_key}}"
      },
      "body": {
        "from": "{{steps.get-sender.output.resource_key}}@example.com",
        "to": "{{context.recipient}}",
        "subject": "Hello"
      }
    }
  }
]

Handling Exhaustion

{
  "id": "get-api-key",
  "type": "pool_assign",
  "params": { "pool_id": "openai-keys-pool" },
  "retry": {
    "max_attempts": 3,
    "backoff": "exponential",
    "initial_delay_ms": 60000
  }
}

When all resources are exhausted, the step fails with a transient error. A retry policy with delay gives resources time to reset at midnight.

Production Patterns

Multi-Provider LLM Pool

Create a pool with weighted resources: OpenAI (weight: 3, cap: 10000), Anthropic (weight: 2, cap: 5000), Groq (weight: 1, cap: 2000). The engine distributes calls proportionally while respecting per-provider rate limits.

SMTP Warmup

Add new mail accounts with warmup_start_cap: 20,daily_cap: 500, warmup_days: 30. Over a month, sending volume gradually increases, building sender reputation without triggering spam filters.

Graceful Degradation

Disable a resource via PATCH enabled: false when it's experiencing issues. The pool automatically routes to remaining resources. Re-enable when resolved — no workflow changes needed.

Capacity Monitoring

Poll GET /pools/{id}/resources to checkdaily_usage vs daily_cap across resources. Alert when total remaining capacity drops below threshold.