Your Restate handler can call other handlers in three ways:
To call a service from an external application, see the HTTP, Kafka, or SDK Clients documentation.

Request-response calls

To call a Restate handler and wait for its result:
// To call a Service:
svcResponse, err := restate.Service[string](ctx, "MyService", "MyHandler").
  Request("Hi")
if err != nil {
  return err
}

// To call a Virtual Object:
objResponse, err := restate.Object[string](ctx, "MyObject", "Mary", "MyHandler").
  Request("Hi")
if err != nil {
  return err
}

// To call a Workflow:
// `run` handler — can only be called once per workflow ID
wfResponse, err := restate.Workflow[bool](ctx, "MyWorkflow", "my-workflow-id", "Run").
  Request("Hi")
if err != nil {
  return err
}
// Other handlers can be called anytime within workflow retention
status, err := restate.Workflow[restate.Void](ctx, "MyWorkflow", "my-workflow-id", "GetStatus").
  Request("Hi again")
if err != nil {
  return err
}
Request-response calls between exclusive handlers 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 and unblock deadlocked invocations.

Sending messages

To send a message to another Restate handler without waiting for a response:
// To message a Service:
restate.ServiceSend(ctx, "MyService", "MyHandler").Send("Hi")

// To message a Virtual Object:
restate.ObjectSend(ctx, "MyObject", "Mary", "MyHandler").Send("Hi")

// To message a Workflow:
// `run` handler — can only be called once per workflow ID
restate.WorkflowSend(ctx, "MyWorkflow", "my-workflow-id", "Run").
  Send("Hi")
// Other handlers can be called anytime within workflow retention
restate.WorkflowSend(ctx, "MyWorkflow", "my-workflow-id", "InteractWithWorkflow").
  Send("Hi again")
Restate handles message delivery and retries, so the handler can complete and return without waiting for the message to be processed.
Calls to a Virtual Object execute in order of arrival, serially. Example:
restate.ObjectSend(ctx, "MyService", "Mary", "MyHandler").Send("I'm call A")
restate.ObjectSend(ctx, "MyService", "Mary", "MyHandler").Send("I'm call B")
Call A is guaranteed to execute before B. However, other invocations may interleave between A and B.

Delayed messages

To send a message after a delay:
// To message a Service with a delay:
restate.ServiceSend(ctx, "MyService", "MyHandler").
  Send("Hi", restate.WithDelay(5*time.Hour))

// To message a Virtual Object with a delay:
restate.ObjectSend(ctx, "MyObject", "Mary", "MyHandler").
  Send("Hi", restate.WithDelay(5*time.Hour))

// To message a Workflow with a delay:
restate.WorkflowSend(ctx, "MyWorkflow", "my-workflow-id", "Run").
  Send("Hi", restate.WithDelay(5*time.Hour))
Learn how this is different from sleeping and then sending a message.

Using an idempotency key

To prevent duplicate executions of the same call, add an idempotency key:
restate.
  ServiceSend(ctx, "MyService", "MyHandler").
  // Send attaching idempotency key
  Send("Hi", restate.WithIdempotencyKey("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:
// Execute the request and retrieve the invocation id
invocationId := restate.
  ServiceSend(ctx, "MyService", "MyHandler").
  // Optional: send attaching idempotency key
  Send("Hi", restate.WithIdempotencyKey("my-idempotency-key")).
  GetInvocationId()

// Later re-attach to the request
response, err := restate.AttachInvocation[string](ctx, invocationId).Response()
  • With an idempotency key: Wait for completion and retrieve the result.
  • Without an idempotency key: Can only wait, not retrieve the result.

Cancel an invocation

To cancel a running handler:
// Execute the request and retrieve the invocation id
invocationId := restate.
  ServiceSend(ctx, "MyService", "MyHandler").
  Send("Hi").
  GetInvocationId()

// I don't need this invocation anymore, let me just cancel it
restate.CancelInvocation(ctx, invocationId)

See also

  • SDK Clients: Call Restate services from external applications
  • Error Handling: Handle failures and terminal errors in service calls
  • Durable Timers: Implement timeouts for your service calls
  • Sagas: Roll back or compensate for canceled service calls.