Bridge Runtime
Understanding how Orderly executes bridges helps you write more reliable integrations.
Execution Flow
When a bridge task is triggered (by schedule, API call, or webhook):
- Load — The bridge module is loaded and its manifest is validated
- Initialize —
bridge.initialize(config)is called with the bridge’s stored credentials - Execute —
bridge.executeTask(taskName, context, input)runs the task - Record — The result is saved to
task_executionswith status, metrics, and timing - Cleanup —
bridge.destroy()is called if implemented
Task Queuing
Tasks are queued via Upstash QStash for reliable async execution:
- Tasks are deduplicated by bridge + task name
- Failed tasks are retried with exponential backoff
- Execution timeout is 5 minutes per task
- Concurrent tasks per bridge are limited to prevent rate limiting
Error Handling
When a task fails, return error details in the result:
return {
success: false,
error: {
code: 'API_ERROR',
message: 'Platform returned 429 Too Many Requests',
retryable: true,
details: { statusCode: 429, retryAfter: 60 },
},
};Error Codes
Use meaningful error codes:
| Code | When to Use |
|---|---|
AUTH_ERROR | Invalid credentials or expired token |
API_ERROR | Platform API returned an error |
RATE_LIMITED | Too many requests |
NOT_FOUND | Requested resource doesn’t exist |
VALIDATION_ERROR | Invalid input data |
TIMEOUT | Request timed out |
UNKNOWN_TASK | Task name not recognized |
Retryable Errors
Set retryable: true for transient errors (rate limits, timeouts, 5xx responses). Orderly will automatically retry the task with backoff.
Set retryable: false for permanent errors (invalid credentials, missing resources). These won’t be retried.
Logging
Use the provided logger in the task context:
context.logger.info('Fetching orders', { page: 1, limit: 100 });
context.logger.warn('Rate limited, backing off', { retryAfter: 60 });
context.logger.error('API call failed', error, { endpoint: '/orders' });Logs are associated with the task execution and viewable in the portal.
Storage
Use context.storage for persistent data between executions:
// Store a cursor with 24-hour TTL
await context.storage.set('lastCursor', cursor, 86400);
// Retrieve it next time
const lastCursor = await context.storage.get<string>('lastCursor');Storage is scoped to the bridge instance — different instances of the same bridge type have separate storage.