Skip to main content

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:


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);
tip

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> (if callbackSecret is 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"
info

.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)

Request signing required

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);
}
tip

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();
info

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);
note

workspace.webhook.redeliver() only works when callbackUrl was set at execution time.