Source: https://docs.cumuluslabs.io/sdk-reference/runtime
Content version: 5cce2092

# Calls and results reference

Prepare a call command, persist it, then send it with placeCall. Use the returned run_id to wait for completion or artifacts. The helpers keep timeouts and reconnects bounded; they do not change the published agent or assign numbers implicitly. assignIntegration is the separate operation for applying phone number bindings.

Import from `@cumulus/sdk/runtime`. This page documents SDK 0.3.4. Follow the [calls and results guide](/docs/sdk-runtime) for a worked workflow.

## Start with a working example {#example}

```typescript
import { Cumulus } from "@cumulus/sdk";
import {
  prepareCall, placeCall, waitUntilEnded, waitForRecording, waitForAnalysis,
  paginate, watchRunEvents, type PreparedCall,
} from "@cumulus/sdk/runtime";

export async function callAgent(client: Cumulus, agentId: string, to: string,
  persistCommand: (command: PreparedCall) => Promise<void>) {
  const command = prepareCall({ channel: "phone", agent_id: agentId, to,
    test: true }); // A test phone run still dials the supplied destination.
  await persistCommand(command);
  const run = await placeCall(client, command);
  const ended = await waitUntilEnded(client, run.run_id, { timeoutMs: 120_000 });
  return { runId: ended.run_id, state: ended.state, outcome: ended.outcome };
}

export async function artifacts(client: Cumulus, runId: string) {
  const recording = await waitForRecording(client, runId);
  const analysis = await waitForAnalysis(client, runId);
  return { recording, analysis };
}

export async function listAllRuns(client: Cumulus) {
  const runs = [];
  for await (const run of paginate(cursor =>
    client.runs.list({ query: { limit: 100, ...(cursor ? { cursor } : {}) } }))) {
    runs.push(run);
  }
  return runs;
}

export async function follow(client: Cumulus, runId: string,
  consume: (event: unknown) => Promise<void>) {
  for await (const event of watchRunEvents(client, runId, { maxReconnects: 3 })) {
    await consume(event);
  }
}
```

## Prepare and start a call {#prepare-and-start-a-call}

### prepareCall {#preparecall}

Validate and freeze a phone RunsCreateRequest without sending it. Default idempotencyKey is a new UUID; an explicit key must be 8–255 printable non-space ASCII characters. Persist the PreparedCall so retries reuse the same body and key.

Related types and helpers: [PreparedCall](/docs/sdk-reference/runtime#preparedcall).

```typescript-declaration
/** Pure preparation. Persist this command before the first send; test:true still dials. */
export declare function prepareCall(body: CallBody, options?: {
    idempotencyKey?: string;
}): PreparedCall;
```

### placeCall {#placecall}

Verify the prepared body digest and send runs.create with the saved request key. Return the admitted run. This is the point where a real phone call can be started.

`placeCall(client, command)` · Optional arguments: `signal`.

Resolves to the [created run](/docs/reference/runs.create#responses), including its `run_id` and current `state`.

Use `Awaited<ReturnType<typeof placeCall>>` for the exact inferred result type in your application. [Full TypeScript declaration](/docs-assets/sdk-reference/declarations/runtime-placeCall.txt).

Related types and helpers: [Cumulus](/docs/sdk-reference/client#cumulus) · [PreparedCall](/docs/sdk-reference/runtime#preparedcall) · [components](/docs/sdk-reference/client#components).

## Wait for results and artifacts {#wait-for-results-and-artifacts}

### waitUntilEnded {#waituntilended}

Poll runs.get until completed, failed, cancelled, rejected or expired. Return the terminal run; completion alone does not imply a successful business outcome.

`waitUntilEnded(client, runId)` · Optional arguments: `options`.

Resolves to the [run](/docs/reference/runs.get#responses) after its state becomes terminal.

Use `Awaited<ReturnType<typeof waitUntilEnded>>` for the exact inferred result type in your application. [Full TypeScript declaration](/docs-assets/sdk-reference/declarations/runtime-waitUntilEnded.txt).

Related types and helpers: [Cumulus](/docs/sdk-reference/client#cumulus) · [WaitOptions](/docs/sdk-reference/runtime#waitoptions) · [components](/docs/sdk-reference/client#components).

### waitForRecording {#waitforrecording}

Poll runs.recording until its state is ready, partial, failed, not_required or expired. Inspect the returned state before using the artifact URL.

Related types and helpers: [Cumulus](/docs/sdk-reference/client#cumulus) · [WaitOptions](/docs/sdk-reference/runtime#waitoptions) · [components](/docs/sdk-reference/client#components).

```typescript-declaration
export declare function waitForRecording(client: Cumulus, runId: string, options?: WaitOptions): Promise<{
    run_id: string;
    state: "pending" | "recording" | "processing" | "ready" | "partial" | "failed" | "not_required" | "expired";
    url: string | null;
    url_expires_at: string | null;
    media_url: string | null;
    media_url_expires_at: string | null;
    content: components["schemas"]["ContentRef"] | null;
}>;
```

### waitForAnalysis {#waitforanalysis}

Poll runs.analysis while its state is pending, running or processing, then return the settled response. Inspect its state and content before using the analysis.

Related types and helpers: [Cumulus](/docs/sdk-reference/client#cumulus) · [WaitOptions](/docs/sdk-reference/runtime#waitoptions) · [components](/docs/sdk-reference/client#components).

```typescript-declaration
export declare function waitForAnalysis(client: Cumulus, runId: string, options?: WaitOptions): Promise<{
    run_id: string;
    state: "pending" | "running" | "completed" | "failed" | "not_requested";
    evaluation_id: string | null;
    disposition: string | null;
    successful: boolean | null;
    fields: {
        [key: string]: unknown;
    };
    model: components["schemas"]["ModelIdentity"] | null;
    completed_at: string | null;
}>;
```

## Read pages and events {#read-pages-and-events}

### paginate {#paginate}

Iterate items from a cursor-based list callback until next_cursor is null. Repeated cursors throw DefinitionError instead of looping indefinitely.

```typescript-declaration
export declare function paginate<T>(page: (cursor?: string) => Promise<{
    items: T[];
    next_cursor: string | null;
}>): AsyncGenerator<T>;
```

### watchRunEvents {#watchrunevents}

Yield durable events, reconnect from the last consumed frame, and stop after the run reaches a terminal state. Defaults: five reconnects and 500 ms between attempts. onCursor runs after the consumer resumes from the yielded event; persist it after durable processing and deduplicate event_id.

Related types and helpers: [Cumulus](/docs/sdk-reference/client#cumulus) · [SemanticEvent](/docs/sdk-reference/client#semanticevent).

```typescript-declaration
/** Reconnect from the last consumed SSE frame. Persist onCursor after durable handling; deduplicate by event_id. */
export declare function watchRunEvents(client: Cumulus, runId: string, options?: {
    signal?: AbortSignal;
    lastEventId?: string;
    maxReconnects?: number;
    reconnectMs?: number;
    onCursor?: (cursor: string) => void;
}): AsyncGenerator<SemanticEvent>;
```

## Assign phone numbers {#assign-phone-numbers}

### assignIntegration {#assignintegration}

Apply inbound and outbound number aliases to the receipt’s agent. Require a publication and verify it is the current head before updating numbers. Missing number bindings fail before their update; routing changes are explicit and independent of publication.

Related types and helpers: [Cumulus](/docs/sdk-reference/client#cumulus) · [IntegrationDefinition](/docs/sdk-reference/agent#integrationdefinition) · [EnvironmentBindings](/docs/sdk-reference/agent#environmentbindings) · [ApplyReceipt](/docs/sdk-reference/agent#applyreceipt).

```typescript-declaration
/** Explicit routing only. The API pins the current head, so verify it matches the receipt first. */
export declare function assignIntegration(client: Cumulus, integration: IntegrationDefinition, bindings: EnvironmentBindings, receipt: ApplyReceipt): Promise<{
    number_id: string;
    e164: string;
    trunk_id: string;
    nickname: string | null;
    inbound_agent_id: string | null;
    outbound_agent_id: string | null;
    inbound_agent_version: number | null;
    outbound_agent_version: number | null;
    state: "pending" | "ready";
    created_at: string;
}[]>;
```

## Handle wait timeouts {#handle-wait-timeouts}

### WaitTimeout {#waittimeout}

A bounded wait expired. code is wait_timeout; runId and phase identify what was being awaited. The run continues independently.

```typescript-declaration
export declare class WaitTimeout extends Error {
    readonly runId: string;
    readonly phase: string;
    readonly code = "wait_timeout";
    constructor(runId: string, phase: string);
}
```

## Types and declarations {#types}

### PreparedCall {#preparedcall}

Serializable phone call command: schema version, validated body, request key and canonical digest. Store it before sending.

```typescript-declaration
export interface PreparedCall {
    schemaVersion: 1;
    body: CallBody;
    idempotencyKey: string;
    digest: string;
}
```

### WaitOptions {#waitoptions}

timeoutMs defaults to 120,000 and pollMs to 1,000. timeoutMs must be positive; pollMs must be at least 10. signal cancels waiting. Reaching the timeout does not cancel the run.

```typescript-declaration
export interface WaitOptions {
    timeoutMs?: number;
    pollMs?: number;
    signal?: AbortSignal;
}
```

## Supporting types {#supporting-types}

These local type expressions appear in the signatures above. They are not separate package exports.

```typescript-declaration
type CallBody = Extract<components["schemas"]["RunsCreateRequest"], {
    channel: "phone";
}>;
```

## Export index {#exports}

- **Prepare and start a call:** [prepareCall](#preparecall) · [placeCall](#placecall)
- **Wait for results and artifacts:** [waitUntilEnded](#waituntilended) · [waitForRecording](#waitforrecording) · [waitForAnalysis](#waitforanalysis)
- **Read pages and events:** [paginate](#paginate) · [watchRunEvents](#watchrunevents)
- **Assign phone numbers:** [assignIntegration](#assignintegration)
- **Handle wait timeouts:** [WaitTimeout](#waittimeout)
- **Types:** [PreparedCall](#preparedcall) · [WaitOptions](#waitoptions)
