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

JSON
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

JSON
GET /pools?tenant_id=tenant-1

Add Resource

JSON
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

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

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

Delete Resource

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

Response: 204 No Content

List Resources

JSON
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

JSON
[
  {
    "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

JSON
{
  "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.