# Quickstart

> Install 10xGraph, build a support agent in Python, serve it with the API server, then call it with curl and the TypeScript client.

Source: https://10xgraph.com/docs/get-started/first-agent
Last updated: 2026-10-06

This page builds a working support agent with two tools, runs it from a Python script, serves it with the 10xGraph API server, then calls it with curl and from TypeScript. You need Python 3.12 or newer and a Google API key.

## Install the packages

```bash
pip install "10xgraph[google-genai]" 10xgraph-api
```

`10xgraph` is the framework, `10xgraph-api` adds the server and the `10xgraph` command, and the `google-genai` extra adds the model provider used below. [Installation](/docs/get-started/installation) covers uv, other extras and provider API keys. The Google provider reads `GEMINI_API_KEY` or `GOOGLE_API_KEY`:

```bash
export GOOGLE_API_KEY="your-key"
```

## Build and run the agent

1. **Write the agent**

   Create `agent.py`. A tool is a plain Python function. The docstring and type hints become the schema the model sees.

   ```python title="agent.py"
   from tenxgraph.prebuilt.agent import ReactAgent
   from tenxgraph.storage.checkpointer import InMemoryCheckpointer

   def lookup_order(order_id: str) -> dict:
       """Look up an order by id and return its status and total."""
       return {"order_id": order_id, "status": "delivered", "total": 59.0}

   def refund_order(order_id: str, amount: float) -> str:
       """Refund an order. This moves money, so it must run once per request."""
       return f"Refunded {amount:.2f} for order {order_id}"

   agent = ReactAgent(
       model="google/gemini-2.5-flash",
       provider="google",
       system_prompt=[{"role": "system", "content": "You are a concise support agent for an online shop."}],
       tools=[lookup_order, refund_order],
   )

   app = agent.compile(checkpointer=InMemoryCheckpointer())
   ```

   `compile()` returns a `CompiledGraph`. That object, named `app` here, is what you run and what the server loads.

2. **Run it from Python**

   Add a runner next to the agent.

   ```python title="run.py"
   from tenxgraph.core.state import Message

   from agent import app

   result = app.invoke(
       {"messages": [Message.text_message("Where is order 1042?")]},
       config={"thread_id": "quickstart-1"},
   )
   print(result["messages"][-1].text())
   ```

   ```bash
   python run.py
   ```

   The model decides to call `lookup_order`, the tool node runs it, and the agent node writes the final answer. Ask it to refund an order and it calls `refund_order` the same way.

3. **Add the config file**

   The server finds your graph through `10xgraph.json`. The `agent` value is `module:variable`.

   ```json title="10xgraph.json"
   {
     "agent": "agent:app",
     "env": ".env",
     "auth": null
   }
   ```

4. **Serve it**

   ```bash
   10xgraph api
   ```

   The server listens on `127.0.0.1:8000` by default. Use `--host 0.0.0.0` to accept outside connections and `--port` to change the port.

5. **Call it with curl**

   ```bash
   curl -X POST http://127.0.0.1:8000/v1/graph/invoke \
     -H "Content-Type: application/json" \
     -d '{
       "messages": [
         {"role": "user", "content": [{"type": "text", "text": "Where is order 1042?"}]}
       ],
       "config": {"thread_id": "quickstart-2"}
     }'
   ```

   The reply is wrapped in a `data` object, next to a `metadata` object with a request id and timestamp. The assistant message is the last item in `data.messages`.

## Call it from TypeScript

`10xgraph-client` is a typed client for the endpoints `10xgraph api` exposes: graph execution, threads, long-term memory and file uploads. It needs Node.js 18 or newer. With the server still running:

```bash
npm install 10xgraph-client
```

```typescript title="client.ts"
import { AgentFlowClient, Message } from "10xgraph-client";

const client = new AgentFlowClient({ baseUrl: "http://127.0.0.1:8000" });

const result = await client.invoke(
  [Message.text_message("Where is order 1042?")],
  {
    config: { thread_id: "quickstart-3" },
    recursion_limit: 10,
  }
);

console.log(result.messages.at(-1)?.text());
```

If the server has auth enabled, pass `auth: bearerAuth("your-api-token")` to the constructor. The client also exports `basicAuth(username, password)` and `headerAuth(name, value)`.

To stream the reply, iterate `client.stream(...)`. It calls `POST /v1/graph/stream`:

```typescript
import { StreamEventType } from "10xgraph-client";

const stream = client.stream(
  [Message.text_message("Refund order 1042 for 59.00.")],
  { config: { thread_id: "quickstart-4" } }
);

for await (const chunk of stream) {
  if (chunk.event === StreamEventType.MESSAGE && chunk.message) {
    process.stdout.write(chunk.message.text());
  }
}
```

| Topic | Guide |
|---|---|
| Client setup, auth, config options | [Create a client](/docs/how-to/client/create-client) |
| Invoke, stream, WebSocket, partial results | [Invoke an agent](/docs/how-to/client/invoke-agent) |
| Streaming responses in depth | [Stream responses](/docs/how-to/client/stream-responses) |
| Thread state, messages, history | [Manage threads](/docs/how-to/client/manage-threads) |
| Long-term memory store and search | [Use memory API](/docs/how-to/client/use-memory-api) |
| File uploads and multimodal messages | [Upload files](/docs/how-to/client/upload-files) |
| Remote tools from the client side | [Register remote tools](/docs/how-to/client/register-remote-tools) |

## What does the request body accept?

The invoke endpoint validates the body against `GraphInputSchema`:

| Field | Default | Meaning |
|---|---|---|
| `messages` | `[]` | Messages to process. Required unless you send `resume`. |
| `config` | none | Run settings. `thread_id` selects the conversation. |
| `initial_state` | none | Initial values for your state fields. |
| `recursion_limit` | 25 | Step cap for the run. Allowed range is 1 to 100. |
| `response_granularity` | `low` | `low` returns messages, `partial` adds context and summary, `full` adds state. |
| `resume` | none | Answer for a thread paused by an interrupt. |

Message content is a list of typed blocks, so a text message is `[{"type": "text", "text": "..."}]`.

> **Always send a thread_id**
>
> If you leave `thread_id` out, the server generates a fresh one for every call. The run works, but you cannot continue or stop that conversation because nobody knows its id.

> **InMemoryCheckpointer forgets on restart**
>
> `InMemoryCheckpointer` keeps threads in process memory and loses them when the server stops. It is right for a first run. For anything that must survive a restart, read [Memory: hot and cold](/docs/concepts/memory).

> **Why refund_order matters**
>
> A refund must not run twice if the process dies mid-run. With a checkpointer, 10xGraph records each finished tool call and skips it on resume. `InMemoryCheckpointer` only protects within one process, so use `PgCheckpointer` for crash recovery. See [Replay-safe tools](/docs/concepts/replay-safe-tools).

## Where to go next

- [Create a client](https://10xgraph.com/docs/how-to/client/create-client): Client options, auth and configuration.
- [Project structure](https://10xgraph.com/docs/get-started/project-structure): What 10xgraph init generates for a production project.
- [StateGraph](https://10xgraph.com/docs/concepts/state-graph): Nodes, edges and routing, the layer ReactAgent is built on.
- [Memory: hot and cold](https://10xgraph.com/docs/concepts/memory): Persist threads with Redis and PostgreSQL.
- [Replay-safe tools](https://10xgraph.com/docs/concepts/replay-safe-tools): How a resumed run skips tools that already finished.
- [Add JWT auth](https://10xgraph.com/docs/how-to/api-cli/add-auth): Protect the endpoints before you deploy.

## Frequently asked questions

### Do I need ReactAgent, or should I write a StateGraph myself?

Start with ReactAgent. It builds the standard reason-and-act graph for you (one agent node, one tool node, a conditional edge between them) and returns a normal compiled graph. Move to StateGraph when you need custom routing or several agents.

### Can I use OpenAI or Anthropic instead of Google?

Yes. Change the model string and the provider argument, and install the matching extra. The graph, the tools and the API server stay the same.

### Which Node.js version does the TypeScript client need?

Node.js 18 or newer. Install it with npm install 10xgraph-client and point AgentFlowClient at the baseUrl of your running 10xgraph api server.

### How do I authenticate the TypeScript client?

Pass an auth option to the AgentFlowClient constructor, for example bearerAuth("your-token"). The client also exports basicAuth(username, password) and headerAuth(name, value).

### Why does the second curl call remember the first one?

Both calls send the same thread_id, and the compiled graph has a checkpointer. The checkpointer stores the conversation per thread, so the next call on that thread continues it.
