> ## Documentation Index
> Fetch the complete documentation index at: https://restate-6d46e1dc-mintlify-a831f9d6.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Service Communication

> Call other services from your handler.

Your Restate handler can call other handlers in three ways:

* **[Request-response calls](#request-response-calls)**: Call and wait for a response
* **[One-way messages](#sending-messages)**: Send a message and continue
* **[Delayed messages](#delayed-messages)**: Send a message after a delay

<Info>
  To call a service from an external application, see the [HTTP](/services/invocation/http), [Kafka](/services/invocation/kafka), or [SDK Clients](/services/invocation/clients/typescript-sdk) documentation.
</Info>

<Info>[Learn how Restate how it works](/foundations/key-concepts#resilient-communication)</Info>

## Request-response calls

To call a Restate handler and wait for its result:

```ts {"CODE_LOAD::ts/src/develop/service_communication.ts#request_response"}  theme={null}
// To call a Service:
const svcResponse = await ctx.serviceClient(myService).myHandler("Hi");

// To call a Virtual Object:
const objResponse = await ctx
  .objectClient(myObject, "Mary")
  .myHandler("Hi");

// To call a Workflow:
// `run` handler — can only be called once per workflow ID
const wfResponse = await ctx
  .workflowClient(myWorkflow, "wf-id")
  .run("Hi");
// Other handlers can be called anytime within workflow retention
const result = await ctx
  .workflowClient(myWorkflow, "wf-id")
  .interactWithWorkflow();
```

Use generic calls when you don't have the service definition or need dynamic service names:

```ts {"CODE_LOAD::ts/src/develop/service_communication.ts#generic_call"}  theme={null}
const response = await ctx.genericCall({
  service: "MyObject",
  method: "myHandler",
  parameter: "Hi",
  key: "Mary", // drop this for Service calls
  inputSerde: restate.serde.json,
  outputSerde: restate.serde.json,
});
```

<Accordion title="Workflow retention">
  After a workflow's run handler completes, other handlers can still be called for up to 24 hours (default).
  Update this via the [service configuration](/services/configuration).
</Accordion>

<Info>
  Request-response calls between [exclusive handlers](/foundations/handlers#handler-behavior) of Virtual Objects may lead to deadlocks:

  * Cross deadlock: A → B and B → A (same keys).
  * Cycle deadlock: A → B → C → A.

  Use the UI or CLI to [cancel](/services/invocation/managing-invocations#cancel) and unblock deadlocked invocations.
</Info>

## Sending messages

To send a message to another Restate handler without waiting for a response:

```ts {"CODE_LOAD::ts/src/develop/service_communication.ts#one_way"}  theme={null}
// To message a Service:
ctx.serviceSendClient(myService).myHandler("Hi");

// To message a Virtual Object:
ctx.objectSendClient(myObject, "Mary").myHandler("Hi");

// To message a Workflow:
// `run` handler — can only be called once per workflow ID
ctx.workflowSendClient(myWorkflow, "wf-id").run("Hi");
// Other handlers can be called anytime within workflow retention
ctx.workflowSendClient(myWorkflow, "wf-id").interactWithWorkflow();
```

Restate handles message delivery and retries, so the handler can complete and return without waiting for the message to be processed.

Use generic send when you don't have the service definition:

```ts {"CODE_LOAD::ts/src/develop/service_communication.ts#generic_send"}  theme={null}
ctx.genericSend({
  service: "MyService",
  method: "myHandler",
  parameter: "Hi",
  inputSerde: restate.serde.json,
});
```

<Info>
  Calls to a Virtual Object execute in order of arrival, serially.
  Example:

  ```typescript {"CODE_LOAD::ts/src/develop/service_communication.ts#ordering"}  theme={null}
  ctx.objectSendClient(myObject, "Mary").myHandler("I'm call A");
  ctx.objectSendClient(myObject, "Mary").myHandler("I'm call B");
  ```

  Call A is guaranteed to execute before B. However, other invocations may interleave between A and B.
</Info>

## Delayed messages

To send a message after a delay:

```ts {"CODE_LOAD::ts/src/develop/service_communication.ts#delayed"}  theme={null}
// To message a Service with a delay:
ctx
  .serviceSendClient(myService)
  .myHandler("Hi", restate.rpc.sendOpts({ delay: { hours: 5 } }));

// To message a Virtual Object with a delay:
ctx
  .objectSendClient(myObject, "Mary")
  .myHandler("Hi", restate.rpc.sendOpts({ delay: { hours: 5 } }));

// To message a Workflow with a delay:
ctx
  .workflowSendClient(myWorkflow, "Mary")
  .run("Hi", restate.rpc.sendOpts({ delay: { hours: 5 } }));
```

Use generic send with a delay when you don't have the service definition:

```ts {"CODE_LOAD::ts/src/develop/service_communication.ts#generic_delayed"}  theme={null}
ctx.genericSend({
  service: "MyService",
  method: "myHandler",
  parameter: "Hi",
  inputSerde: restate.serde.json,
  delay: { seconds: 5 },
});
```

<Info>
  Learn [how this is different](/develop/ts/durable-timers#scheduling-async-tasks) from sleeping and then sending a message.
</Info>

## Using an idempotency key

To prevent duplicate executions of the same call, add an idempotency key:

```ts {"CODE_LOAD::ts/src/develop/service_communication.ts#idempotency_key"}  theme={null}
// For request-response
const response = await ctx.serviceClient(myService).myHandler(
  "Hi",
  restate.rpc.opts({
    idempotencyKey: "my-idempotency-key",
  })
);
// For sending a message
ctx.serviceSendClient(myService).myHandler(
  "Hi",
  restate.rpc.sendOpts({
    idempotencyKey: "my-idempotency-key",
  })
);
```

Restate automatically deduplicates calls made during the same handler execution, so there's no need to provide an idempotency key in that case.
However, if multiple handlers might call the same service independently, you can use an idempotency key to ensure deduplication across those calls.

## Attach to an invocation

To wait for or get the result of a previously sent message:

```ts {"CODE_LOAD::ts/src/develop/service_communication.ts#attach"}  theme={null}
const handle = ctx.serviceSendClient(myService).myHandler(
  "Hi",
  restate.rpc.sendOpts({
    idempotencyKey: "my-idempotency-key",
  })
);
const invocationId = await handle.invocationId;

// Later...
const response = ctx.attach(invocationId);
```

* With an idempotency key: Wait for completion and retrieve the result.
* Without an idempotency key: Can only wait, not retrieve the result.

## Signals

Signals let invocations communicate directly with each other: one invocation waits for a named signal, and any other handler can resolve or reject it by referencing the target invocation's ID.

**Waiting for a signal** (inside the invocation that wants to pause):

```typescript theme={null}
// Suspend until another handler sends a signal named "approved"
const approved = await ctx.signal<boolean>("approved");
```

**Sending a signal** (from another handler that knows the target invocation's ID):

```typescript theme={null}
// Get a reference to the target invocation
const target = ctx.invocation(targetInvocationId);

// Resolve the signal with a value — wakes the waiting handler
target.signal<boolean>("approved").resolve(true);

// Or reject it — wakes the waiting handler with a terminal error
target.signal("approved").reject("Request denied");
```

The target invocation's ID is available via `ctx.request().id`. Pass it to other handlers using state, awakeables, or any other mechanism you prefer.

Each invocation can wait on multiple distinct signal names at the same time. Signals are scoped to the invocation — the same name on different invocations is independent.

<Note>
  Signals require restate-server 1.7 with `RESTATE_EXPERIMENTAL_ENABLE_PROTOCOL_V7=true` enabled.
</Note>

## InvocationReference

`ctx.invocation(invocationId)` returns an `InvocationReference` that lets you interact with any other invocation by its ID:

```typescript theme={null}
const ref = ctx.invocation(targetInvocationId);

// Send a signal
ref.signal<boolean>("approved").resolve(true);

// Cancel the target invocation
ref.cancel();

// Attach and wait for the target invocation's result
const result = await ref.attach<string>();
```

## Cancel an invocation

To cancel a running handler, use `InvocationReference`:

```typescript theme={null}
ctx.invocation(targetInvocationId).cancel();
```

Or using the legacy `ctx.cancel()` (deprecated):

```ts {"CODE_LOAD::ts/src/develop/service_communication.ts#cancel"}  theme={null}
const handle = ctx.serviceSendClient(myService).myHandler("Hi");
const invocationId = await handle.invocationId;

// Cancel the invocation
ctx.cancel(invocationId);
```

## Advanced: sharing service type definitions

When calling services, you need access to their type definitions for type safety. Here are four approaches to share service definitions without exposing implementation details:

<AccordionGroup>
  <Accordion title="Option 1: Export type-only service definition">
    Export only the service type information from where you define your service:

    ```typescript {"CODE_LOAD::ts/src/develop/my_service.ts#api_export"}  theme={null}
    export const myService = restate.service({
      name: "MyService",
      handlers: {
        myHandler: async (ctx: restate.Context, greeting: string) => {
          return `${greeting}!`;
        },
      },
    });

    export type MyService = typeof myService;
    ```

    Import and use this definition in calling handlers:

    ```ts {"CODE_LOAD::ts/src/develop/service_communication.ts#export_definition"}  theme={null}
    const response = await ctx
      .serviceClient<MyService>({ name: "MyService" })
      .myHandler("Hi");
    ```
  </Accordion>

  <Accordion title="Option 2: Define service interface manually">
    Create a TypeScript interface matching the service's handler signatures. Useful when the service is in a different language or when you can't import the type definition:

    ```ts {"CODE_LOAD::ts/src/develop/service_communication.ts#interface"}  theme={null}
    interface MyService {
      myHandler(ctx: unknown, greeting: string): Promise<string>;
    }
    const response = await ctx
      .serviceClient<MyService>({ name: "MyService" })
      .myHandler("Hi");
    ```
  </Accordion>

  <Accordion title="Option 3: Import as dev dependency">
    When your service is published as a separate package, import it as a dev dependency to access its types:

    ```ts {"CODE_LOAD::ts/src/develop/service_communication.ts#dev_dependency"}  theme={null}
    // import type { MyService } from "my-service-package";

    const response = await ctx
      .serviceClient<MyService>({ name: "MyService" })
      .myHandler("Hi");
    ```
  </Accordion>
</AccordionGroup>

## See also

* **[SDK Clients](/develop/ts/service-communication)**: Call Restate services from external applications
* **[Error Handling](/develop/ts/error-handling)**: Handle failures and terminal errors in service calls
* **[Durable Timers](/develop/ts/durable-timers)**: Implement timeouts for your service calls
* **[Serialization](/develop/ts/serialization)**: Customize how data is serialized between services
* **[Sagas](/guides/sagas)**: Roll back or compensate for canceled service calls.
