Execute Component
Execute any component in the library standalone — no workflow required. Useful for one-off operations, testing integrations, or building your own orchestration.
Endpoint
POST /v1/sdk/components
Auth: X-Api-Key
Rate limit multiplier: 2×
TypeScript SDK — CallBuilder
The caller-sdk exposes a CallBuilder pattern. workspace.call() validates inputs synchronously and returns a builder. Choose your delivery model by calling .execute() or .promise().
import { WorkspaceClient, ComponentModule } from 'caller-sdk';
const workspace = new WorkspaceClient({ apiKey: process.env.WR_API_KEY! });
const builder = workspace.call(ComponentModule.GET_EVM_ACCOUNT_BALANCE, {
jsonRpcUrl: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY',
tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
account: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
});
// Delivery option A: wait for result (SSE stream)
const output = await builder.promise();
console.log(output.balance); // "1000000"
// Delivery option B: fire and get execution record
const execution = await builder.execute();
console.log(execution.id, execution.status);
Validation errors are thrown synchronously from workspace.call() — before .execute() or .promise() is called and before any network request is made.
.execute(options?) — fire and return
Submits the component and returns the ExecuteComponentResponse immediately. The execution may still be CREATED or EXECUTING when this resolves.
const execution = await workspace.call(ComponentModule.RANDOM_UUID, {}).execute();
// execution.completed may be false — use .id to poll or stream
ExecuteOptions
interface ExecuteOptions {
/**
* Number of execution attempts on failure (1–3).
* Each attempt is retried with exponential backoff by the backend.
* @default 1
*/
attempts?: number;
/**
* Wait up to this many milliseconds for the component to complete.
* If it finishes within the window, the response includes the output
* inline (`completed: true`). Max: 15000.
*/
waitForMs?: number;
/**
* HTTPS URL to receive the completion webhook.
* The payload is the full `ExecuteComponentResponse`.
*/
callbackUrl?: string;
/**
* HMAC-SHA256 signing key for the callback.
* The signature is sent in the `X-WR-Signature` header.
*/
callbackSecret?: string;
/**
* Custom HTTP headers added to the callback request.
*/
callbackHeaders?: Record<string, string>;
}
Example — webhook delivery
await workspace.call(ComponentModule.WAIT_FOR_EVM_TRANSACTION, {
jsonRpcUrl: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY',
transactionHash: '0x1234...',
}).execute({
callbackUrl: 'https://your-app.com/webhooks/wr',
callbackSecret: process.env.WR_CALLBACK_SECRET,
callbackHeaders: { 'X-Source': 'whiterabbit' },
attempts: 3,
});
The callback payload is delivered with these headers:
X-WR-Signature: hmac-sha256-v1=<hex>(ifcallbackSecretis set)X-WR-Execution-Id: <uuid>- Any custom headers from
callbackHeaders
.promise(options?) — wait for result
Submits the component then opens an SSE stream and resolves with the typed component output once the execution completes. Closes the stream automatically on COMPLETED or FAILED.
const result = await workspace.call(ComponentModule.GET_EVM_ACCOUNT_BALANCE, {
jsonRpcUrl: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY',
tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
account: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
}).promise();
console.log(result.balance); // "1000000"
.promise() uses SSE — not HTTP long-polling. The connection opens once, the server pushes a single event on completion, and the connection closes. See Streaming for the underlying protocol.
PromiseOptions
interface PromiseOptions {
/**
* Number of execution attempts on failure (1–3).
* @default 1
*/
attempts?: number;
/**
* Maximum time to wait for the result in milliseconds.
* If the execution doesn't complete within this window, the promise
* rejects with a `CallerSDKError` with message "Execution timed out".
* @default 60_000
*/
timeoutMs?: number;
}
Example — with timeout
try {
const result = await workspace.call(ComponentModule.WAIT_FOR_EVM_TRANSACTION, {
jsonRpcUrl: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY',
transactionHash: '0xabc...',
}).promise({ timeoutMs: 120_000, attempts: 2 });
console.log('Confirmed:', result);
} catch (err) {
if (err instanceof CallerSDKError && err.message === 'Execution timed out') {
console.warn('Transaction not confirmed within 2 minutes');
}
}
Request body (REST)
interface ExecuteComponentRequest {
module: string; // Component module name (e.g. "RANDOM_UUID")
input: object; // Component inputs (varies per component)
config: object; // Component config (varies per component)
// Execution options
attempts?: number; // 1–3, default: 1
waitForMs?: number; // 0–15000 ms
callbackUrl?: string; // HTTPS webhook URL
callbackSecret?: string; // HMAC-SHA256 signing key
callbackHeaders?: Record<string, string>;
}
module
The component module identifier. See Component Library for all available modules.
{ "module": "RANDOM_UUID" }
{ "module": "GET_EVM_ACCOUNT_BALANCE" }
{ "module": "SIGN_WITH_KEY_SHARE" }
input and config
Each component defines its own input and config schemas. Find them in the Component Library. Fields marked secret are redacted from logs.
{
"module": "GET_EVM_ACCOUNT_BALANCE",
"input": {
"jsonRpcUrl": "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY",
"tokenAddress": "0x0000000000000000000000000000000000000000",
"account": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
},
"config": {}
}
Response
interface ExecuteComponentResponse {
id: string;
module: string;
status: 'CREATED' | 'EXECUTING' | 'COMPLETED' | 'FAILED';
completed: boolean;
output?: unknown; // present when COMPLETED
error?: unknown; // present when FAILED
totalUsage: number; // credits consumed
callback: {
url: string | null;
signed: boolean;
signatureAlgorithm: 'hmac-sha256-v1' | null;
headerNames: string[];
lastAttemptAt?: string | null;
deliveredAt?: string | null;
attemptCount: number;
lastError?: unknown | null;
};
createdAt: string;
updatedAt: string;
}
Examples
Instant component (REST)
Direct REST calls require X-Sdk-Timestamp and X-Sdk-Signature headers in addition to the API key. See Authentication → for the signing implementation. The TypeScript SDK handles this automatically.
import { createPrivateKey, sign } from 'crypto';
const signingKey = createPrivateKey({
key: Buffer.from(process.env.WR_API_SECRET!, 'base64'),
format: 'der',
type: 'pkcs8',
});
const body = { module: 'RANDOM_UUID', input: {}, config: {}, waitForMs: 5000 };
const timestamp = Math.floor(Date.now() / 1000);
const message = `POST|/v1/sdk/components|${timestamp}|${JSON.stringify(body)}`;
const signature = sign(null, Buffer.from(message), signingKey).toString('base64');
const res = await fetch('https://api.whiterabbit.app/v1/sdk/components', {
method: 'POST',
headers: {
'X-Api-Key': process.env.WR_API_KEY!,
'X-Sdk-Timestamp': String(timestamp),
'X-Sdk-Signature': signature,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const { output, totalUsage } = await res.json();
console.log(output.uuid); // "a4c2e8f1-..."
SDK — promise pattern
import { WorkspaceClient, ComponentModule, CallerSDKError } from 'caller-sdk';
const workspace = new WorkspaceClient({ apiKey: process.env.WR_API_KEY! });
try {
// Inputs validated synchronously before request
const result = await workspace
.call(ComponentModule.GET_EVM_ACCOUNT_BALANCE, {
jsonRpcUrl: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY',
tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
account: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
})
.promise();
console.log(result.balance); // fully typed
} catch (err) {
if (err instanceof CallerSDKError) {
console.error(err.message, err.details);
}
}
SDK — execute pattern
import { WorkspaceClient, ComponentModule } from 'caller-sdk';
const workspace = new WorkspaceClient({ apiKey: process.env.WR_API_KEY! });
// Fire-and-forget: get execution ID, handle result via webhook
const execution = await workspace
.call(ComponentModule.WAIT_FOR_EVM_TRANSACTION, {
jsonRpcUrl: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY',
transactionHash: '0x1234...',
})
.execute({
callbackUrl: 'https://your-app.com/webhooks/wr',
});
console.log(`Tracking execution ${execution.id}`); // status: "CREATED"
Get execution status
GET /v1/sdk/components/executions/:executionId
Fetch the current state of any execution by ID. Use this when you've submitted a job with .execute() and want to check it later.
import { WorkspaceClient, ComponentModule } from 'caller-sdk';
const workspace = new WorkspaceClient({ apiKey: process.env.WR_API_KEY! });
// 1. Submit without waiting
const execution = await workspace
.call(ComponentModule.WAIT_FOR_EVM_TRANSACTION, {
jsonRpcUrl: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY',
transactionHash: '0x1234...',
})
.execute();
console.log('Tracking:', execution.id); // status: "CREATED"
// 2. Check status later
const latest = await workspace.execution.get(execution.id);
if (latest.completed) {
console.log('Output:', latest.output);
} else if (latest.status === 'FAILED') {
console.error('Error:', latest.error);
} else {
console.log('Still running:', latest.status);
}
For real-time updates without polling, use workspace.execution.stream() or .promise(). See below.
Stream execution status
GET /v1/sdk/components/executions/:executionId/stream
Subscribe to live SSE updates for any execution by ID. The stream closes automatically when the execution reaches COMPLETED or FAILED.
import { WorkspaceClient, ComponentModule } from 'caller-sdk';
const workspace = new WorkspaceClient({ apiKey: process.env.WR_API_KEY! });
// 1. Submit fire-and-forget
const execution = await workspace
.call(ComponentModule.WAIT_FOR_EVM_TRANSACTION, {
jsonRpcUrl: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY',
transactionHash: '0x1234...',
})
.execute();
// 2. Stream status updates
const sub = workspace.execution.stream(execution.id, {
onUpdate(event) {
console.log(event.status, event.output);
// stream closes automatically on COMPLETED / FAILED
},
onError(err) {
console.error('Stream error:', err.message);
},
});
// Cancel early if needed
// sub.close();
workspace.execution.stream() is the lower-level primitive. Use .promise() on workspace.call() if you just want to await the typed output — it uses the same SSE stream internally.
Redeliver a failed callback
POST /v1/sdk/components/executions/:executionId/replay-callback
Re-delivers the completion webhook if the original delivery failed. Idempotent — safe to call multiple times.
import { WorkspaceClient, ComponentModule } from 'caller-sdk';
const workspace = new WorkspaceClient({ apiKey: process.env.WR_API_KEY! });
// 1. Submit with a webhook
const execution = await workspace
.call(ComponentModule.BROADCAST_EVM_TRANSACTION, {
jsonRpcUrl: 'https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY',
signedTransaction: '0x...',
})
.execute({
callbackUrl: 'https://your-app.com/webhooks/wr',
callbackSecret: process.env.WR_CALLBACK_SECRET,
});
// 2. If the webhook wasn't delivered, retry it
await workspace.webhook.redeliver(execution.id);
workspace.webhook.redeliver() only works when callbackUrl was set at execution time.