Skip to main content
This guide explains how to invoke Restate services from frontend applications using the Restate SDK clients or plain HTTP requests.

Calling agents from the frontend

You can call your agents in two ways:
  1. Via the SDK clients (type-safe, recommended). Supported for TypeScript, Python, Java, Go.
  2. Via plain HTTP requests (works anywhere in any language).
Restate supports calling your agents via:
  • Request-response: short operations where the user is waiting (< few seconds)
  • Fire-and-forget: longer operations where you’ll receive updates via a separate channel (pubsub/webhooks) or want to retrieve the result later
  • Scheduled/delayed calls: schedules a request to an agent with a delay

Example: TypeScript SDK client

Install the Restate SDK clients package:
npm install @restatedev/restate-sdk-clients
Export the type definition of your agent:
export type MyAgent = typeof myAgent;
Create a client connection to the Restate ingress, then use the client for your service type to send a request:
// import * as clients from "@restatedev/restate-sdk-clients";
// import {MyAgent} from "./my_agent";

const ingress = clients.connect({ url: "http://localhost:8080" });

// Send a message without waiting for completion
const handle = await ingress
  .serviceSendClient<MyAgent>({ name: "my-agent" })
  .run({ message: "How are you?" });
Consult the client SDK docs to learn about calling Virtual Objects or doing other types of calls.

Example: Next.js API Route

You can use the SDK clients to send requests from your NextJS app to Restate services. Here’s an example of an API route that calls a Restate service:
app/agent/[topic]/api/route.ts
import { NextRequest } from "next/server";
import * as clients from "@restatedev/restate-sdk-clients";
import { Agent } from "@/restate/services/agent";

export async function POST(
  request: NextRequest,
  { params }: { params: Promise<{ topic: string }> },
) {
  const { topic } = await params;
  const { message } = await request.json();
  const ingressUrl = process.env.INGRESS_URL || "http://localhost:8080";

  const ingress = clients.connect({ url: ingressUrl });

  await ingress
    .serviceSendClient<Agent>({ name: "agent" })
    .chat({ prompt: message, topic });

  return Response.json({ ok: true });
}
Check out the full NextJS example here.

Using HTTP requests

You can also call Restate services using plain HTTP requests without the SDK. For example, in JavaScript:
// Request-response
const response = await fetch("http://localhost:8080/my-agent/run", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ message: "How are you?" }),
});
const result = await response.json();

// Fire-and-forget: add /send to the URL
await fetch("http://localhost:8080/my-agent/run/send", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ message: "How are you?" }),
});

Request deduplication / idempotency

You can let Restate deduplicate requests by adding an idempotency key to the header of your request.
await fetch("http://localhost:8080/my-agent/run/send", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "idempotency-key": "abc-123",
  },
  body: JSON.stringify("How are you?"),
});
This can help with implementing retries and avoiding rage clicking to lead to many invocations.

Re-attach to executions

Restate lets you re-attach to ongoing executions. This is useful to retrieve the response from another processs, for example after a crash or as part of your application logic.idempotency There are three ways to do this:
  1. If you use an idempotency key, then sending the same request with the same idempotency key will re-attach you to the original invocation.
  2. If you have the invocation ID, then you can use that to attach to it:
// start the invocation
const handle = await fetch("http://localhost:8080/agent/run/send", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify("How are you?"),
});

// retrieve the invocationId
const { invocationId, status } = await handle.json();

// retrieve the result
const result = await fetch(
  `http://localhost:8080/restate/invocation/${invocationId}/attach`,
  { method: "GET" }
);
await result.json();
  1. If you started the invocation with an idempotency key, then you can use that to attach to it. This does not start the invocation as opposed to point 1:
// start the invocation with idempotency key
const idempotencyKey = "abc-123";
const result = await fetch(
  `http://localhost:8080/restate/invocation/agent/run/${idempotencyKey}/attach`,
  { method: "GET" }
);
await result.json();
Consult the HTTP requests page for more information.