Version: 1.0.0
Category: API Patterns
Purpose: Guide for AI agents on implementing multi-step API workflows and parallel request patterns
This pattern provides strategies for implementing complex workflows that involve multiple API calls, parallel requests, and state management. It helps AI agents coordinate multiple operations efficiently and handle dependencies between API calls.
Key Principles:
Use Case: Operations that depend on previous results
Structure:
Step 1 → Step 2 → Step 3 → Step 4
Implementation:
{
"pattern": "linear-chain",
"steps": [
{
"step": 1,
"action": "Get player",
"endpoint": "GET /api/player/{id}",
"output": "player"
},
{
"step": 2,
"action": "Get player planets",
"endpoint": "GET /api/player/{id}/planets",
"dependsOn": ["player.id"],
"output": "planets"
},
{
"step": 3,
"action": "Get planet structs",
"endpoint": "GET /api/planet/{planetId}/structs",
"dependsOn": ["planets[0].id"],
"output": "structs"
}
]
}
Code Example:
async function linearChainWorkflow(playerId) {
// Step 1: Get player
const player = await getPlayer(playerId);
// Step 2: Get player planets (depends on player)
const planets = await getPlayerPlanets(player.id);
// Step 3: Get planet structs (depends on planets)
const structs = await getPlanetStructs(planets[0].id);
return { player, planets, structs };
}
Use Case: Operations with conditional branching
Structure:
Step 1 → Condition Check → Branch A or Branch B
Implementation:
{
"pattern": "conditional-chain",
"steps": [
{
"step": 1,
"action": "Get player",
"output": "player"
},
{
"step": 2,
"action": "Check condition",
"condition": "player.guildId !== null",
"ifTrue": "step3a",
"ifFalse": "step3b"
},
{
"step": "3a",
"action": "Get guild info",
"endpoint": "GET /api/guild/{guildId}",
"dependsOn": ["player.guildId"]
},
{
"step": "3b",
"action": "Create guild or skip"
}
]
}
Code Example:
async function conditionalChainWorkflow(playerId) {
const player = await getPlayer(playerId);
if (player.guildId) {
const guild = await getGuild(player.guildId);
return { player, guild };
} else {
// No guild, skip guild step
return { player };
}
}
Use Case: Operations that may need retries
Structure:
Step 1 → (Retry if needed) → Step 2 → (Retry if needed) → Step 3
Implementation:
{
"pattern": "retry-chain",
"steps": [
{
"step": 1,
"action": "Get player",
"retry": {
"enabled": true,
"maxRetries": 3,
"strategy": "exponential-backoff"
}
},
{
"step": 2,
"action": "Get planets",
"retry": {
"enabled": true,
"maxRetries": 3
}
}
]
}
Code Example:
async function retryChainWorkflow(playerId) {
const player = await retryWithBackoff(
() => getPlayer(playerId),
3
);
const planets = await retryWithBackoff(
() => getPlayerPlanets(player.id),
3
);
return { player, planets };
}
Use Case: Multiple independent operations
Structure:
Step 1 ─┐
Step 2 ─┼→ Combine Results
Step 3 ─┘
Implementation:
{
"pattern": "independent-parallel",
"steps": [
{
"step": 1,
"action": "Get player",
"endpoint": "GET /api/player/{id}",
"parallel": true
},
{
"step": 2,
"action": "Get guild",
"endpoint": "GET /api/guild/{id}",
"parallel": true,
"independent": true
},
{
"step": 3,
"action": "Get planet",
"endpoint": "GET /api/planet/{id}",
"parallel": true,
"independent": true
}
],
"combine": "After all parallel steps complete"
}
Code Example:
async function independentParallelWorkflow(playerId, guildId, planetId) {
// Execute all requests in parallel
const [player, guild, planet] = await Promise.all([
getPlayer(playerId),
getGuild(guildId),
getPlanet(planetId)
]);
return { player, guild, planet };
}
Use Case: Some operations independent, some dependent
Structure:
Step 1 → Step 2 ─┐
├→ Step 4
Step 3 ──────────┘
Implementation:
{
"pattern": "parallel-with-dependency",
"steps": [
{
"step": 1,
"action": "Get player",
"output": "player"
},
{
"step": 2,
"action": "Get player planets",
"dependsOn": ["player.id"],
"output": "planets"
},
{
"step": 3,
"action": "Get guild",
"dependsOn": ["player.guildId"],
"parallel": true
},
{
"step": 4,
"action": "Combine results",
"dependsOn": ["planets", "guild"]
}
]
}
Code Example:
async function parallelWithDependencyWorkflow(playerId) {
// Step 1: Sequential
const player = await getPlayer(playerId);
// Steps 2 and 3: Parallel (both depend on player)
const [planets, guild] = await Promise.all([
getPlayerPlanets(player.id),
player.guildId ? getGuild(player.guildId) : null
]);
return { player, planets, guild };
}
Use Case: Multiple similar operations
Structure:
Batch: [Item1, Item2, Item3, ...] → Parallel Requests → Combine Results
Implementation:
{
"pattern": "batch-parallel",
"steps": [
{
"step": 1,
"action": "Get list of IDs",
"output": "ids"
},
{
"step": 2,
"action": "Fetch each item in parallel",
"batch": true,
"items": "ids",
"operation": "GET /api/player/{id}",
"parallel": true
}
]
}
Code Example:
async function batchParallelWorkflow(playerIds) {
// Fetch all players in parallel
const players = await Promise.all(
playerIds.map(id => getPlayer(id))
);
return players;
}
Use Case: Track state across workflow steps
Implementation:
{
"pattern": "workflow-state",
"state": {
"workflowId": "unique-workflow-id",
"step": 1,
"completed": [],
"pending": [2, 3, 4],
"failed": [],
"data": {}
}
}
Code Example:
class WorkflowState {
constructor(workflowId) {
this.workflowId = workflowId;
this.step = 1;
this.completed = [];
this.pending = [];
this.failed = [];
this.data = {};
}
markComplete(step, result) {
this.completed.push(step);
this.data[step] = result;
this.step++;
}
markFailed(step, error) {
this.failed.push({ step, error });
}
}
Use Case: Resume workflow from checkpoint
Implementation:
{
"pattern": "checkpoint",
"checkpoints": [
{
"step": 1,
"checkpoint": "player-loaded",
"data": "player"
},
{
"step": 2,
"checkpoint": "planets-loaded",
"data": "planets"
}
],
"resume": "From last checkpoint"
}
Code Example:
async function resumeFromCheckpoint(checkpoint) {
if (checkpoint === 'player-loaded') {
// Resume from step 2
const planets = await getPlayerPlanets(checkpoint.data.player.id);
return { player: checkpoint.data.player, planets };
}
// ... other checkpoints
}
Strategy: Stop workflow on first error
Implementation:
{
"errorHandling": "fail-fast",
"behavior": "Stop workflow on first error",
"useWhen": "Critical workflow, errors are fatal"
}
Code Example:
async function failFastWorkflow() {
try {
const player = await getPlayer(playerId);
const planets = await getPlayerPlanets(player.id);
return { player, planets };
} catch (error) {
// Stop immediately on error
throw error;
}
}
Strategy: Continue workflow, collect errors
Implementation:
{
"errorHandling": "continue-on-error",
"behavior": "Continue workflow, collect errors",
"useWhen": "Non-critical steps, partial results acceptable"
}
Code Example:
async function continueOnErrorWorkflow() {
const errors = [];
const results = {};
try {
results.player = await getPlayer(playerId);
} catch (error) {
errors.push({ step: 'player', error });
}
try {
results.planets = await getPlayerPlanets(results.player?.id);
} catch (error) {
errors.push({ step: 'planets', error });
}
return { results, errors };
}
Strategy: Retry failed steps, use fallback if retry fails
Implementation:
{
"errorHandling": "retry-with-fallback",
"behavior": "Retry failed steps, use fallback",
"fallback": "Use cached data or default values"
}
Code Example:
async function retryWithFallbackWorkflow(playerId) {
let player;
try {
player = await retryWithBackoff(() => getPlayer(playerId), 3);
} catch (error) {
// Fallback to cached data
player = await getCachedPlayer(playerId);
}
return { player };
}
Pattern: Linear Chain
{
"workflow": "get-player-and-planets",
"steps": [
{
"step": 1,
"action": "GET /api/player/{id}",
"output": "player"
},
{
"step": 2,
"action": "GET /api/player/{id}/planets",
"dependsOn": ["player.id"],
"output": "planets"
}
]
}
Pattern: Parallel with Dependency
{
"workflow": "get-player-guild-planet",
"steps": [
{
"step": 1,
"action": "GET /api/player/{id}",
"output": "player"
},
{
"step": 2,
"action": "GET /api/guild/{guildId}",
"dependsOn": ["player.guildId"],
"parallel": true
},
{
"step": 3,
"action": "GET /api/planet/{planetId}",
"dependsOn": ["player.planetId"],
"parallel": true
}
]
}
Pattern: Batch Parallel
{
"workflow": "batch-fetch-players",
"steps": [
{
"step": 1,
"action": "GET /api/guild/{id}/members",
"output": "memberIds"
},
{
"step": 2,
"action": "GET /api/player/{id}",
"batch": true,
"items": "memberIds",
"parallel": true
}
]
}
Do:
Don’t:
Do:
Don’t:
Do:
Don’t:
Do:
Don’t:
Do:
Don’t:
Pattern: Retry failed steps in workflow
{
"integration": {
"workflow": "linear-chain",
"retry": "exponential-backoff",
"strategy": "Retry each step on failure"
}
}
Reference: patterns/retry-strategies.md
Pattern: Cache workflow results
{
"integration": {
"workflow": "get-player-and-planets",
"cache": "Cache final result",
"strategy": "Reuse cached result if available"
}
}
Reference: patterns/caching.md
Pattern: Respect rate limits in parallel workflows
{
"integration": {
"workflow": "batch-parallel",
"rateLimit": "Throttle parallel requests",
"strategy": "Limit concurrent requests to stay within rate limit"
}
}
Reference: patterns/rate-limiting.md
patterns/retry-strategies.md - Retry patterns for failed requestspatterns/caching.md - Caching strategiespatterns/rate-limiting.md - Rate limit handlingprotocols/error-handling.md - Error handling protocolexamples/workflows/ - Complete workflow examplesPattern Version: 1.0.0 - January 2025