---
title: "Drive It Over HTTP"
description: "Talk to the dispatcher over eve's stable HTTP session API: start a durable session, stream the NDJSON event log, and send a follow-up to that session."
canonical_url: "https://vercel.com/academy/building-agents-with-eve/drive-it-over-http"
md_url: "https://vercel.com/academy/building-agents-with-eve/drive-it-over-http.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-08-29T02:28:26.092Z"
content_type: "lesson"
course: "building-agents-with-eve"
course_title: "Building Agents with eve"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# Drive It Over HTTP

# Drive It Over HTTP

The TUI (terminal user interface) is a comfortable place to test an agent, but no customer is ever going to open your terminal. A real dispatcher gets reached over the wire: from a web page, a Slack message, a phone. Before we wire up any of those surfaces, it's worth seeing the thing they all sit on top of.

Every eve app exposes the same HTTP session API, regardless of the interface built on top. The TUI, Slack, and dashboard are all clients of the routes that start sessions, stream events, and send follow-ups.

So let's skip the surfaces for a second and talk to the dispatcher the way every surface does, with plain `curl`.

## Outcome

You start a durable session with the dispatcher over HTTP, stream its reply as events, and send a follow-up to the same session.

## Hands-on exercise

With `npx eve dev` running, open a second terminal. There's nothing to write this lesson, you're learning the contract every channel speaks, so we drive it by hand.

**Start a session.** Send the customer's first message:

```bash
curl -X POST http://127.0.0.1:2000/eve/v1/session \
  -H 'content-type: application/json' \
  -d '{"message":"what does a basic tune-up cost?"}'
```

The response should look like this:

```json
{"ok":true,"sessionId":"wrun_01...","status":"accepted"}
```

`accepted` means eve started the turn asynchronously. It is not the dispatcher's answer. Copy the **`sessionId`** from this response; you use it to stream the answer and to address the next message to this conversation.

If zsh prints `%` immediately after the closing brace, that is just your shell prompt touching curl's newline-free JSON output; it is not part of the response.

\*\*Note: Accepted is a handoff, not the answer\*\*

The first request returns quickly with a session handle while the durable turn runs in the background. Seeing a response with `status: "accepted"` is expected. Attach to the stream with the returned `sessionId` to read what the dispatcher says.

**Stream the session.** Point at the stream route with the id from the JSON response. `-N` tells curl not to buffer the events:

```bash
curl -N http://127.0.0.1:2000/eve/v1/session/<sessionId>/stream
```

You get newline-delimited JSON, one event per line: the turn starting, the `lookup_service` call going out, its result coming back, the assistant's text, and the turn completing. The TUI renders this raw event log as the `⚙` lines you saw earlier.

**Send a follow-up.** When the session is waiting for you (`session.waiting`), post the next message to that same session ID:

```bash
curl -X POST http://127.0.0.1:2000/eve/v1/session/<sessionId> \
  -H 'content-type: application/json' \
  -d '{"message":"and how long does it take?"}'
```

The dispatcher retained the session history, so it already knows you're asking about the tune-up.

## Try It

The first `POST` returns something like this:

```text
{ "ok": true, "sessionId": "wrun_01...", "status": "accepted" }
```

And the stream prints the turn as it happens:

```text
{"type":"session.started"}
{"type":"turn.started"}
{"type":"actions.requested","data":{"calls":[{"name":"lookup_service","input":{"query":"tune-up"}}]}}
{"type":"action.result","data":{"result":[{"name":"Basic Tune-Up","price":"$65.00","estMinutes":60}]}}
{"type":"message.completed","data":{"finishReason":"stop"}}
{"type":"turn.completed"}
{"type":"session.waiting"}
```

That `session.waiting` at the end is the agent telling you it's done with this turn and ready for the next message. The same durable machinery will let a turn park for human approval in Section 3 and pick up days later.

\*\*Note: Send one turn at a time\*\*

For predictable ordering, wait for `session.waiting` before sending the next message to the same session. Fire two messages at once and you're racing the runtime, which is rarely what you want in a chat.

Getting a `401`? The dev server accepts local requests, but make sure you're hitting `127.0.0.1`/`localhost` and not a deployed URL. Getting a `404` from the stream or follow-up route? Check that you copied the complete `sessionId` from the first response.

## Done-When

- [ ] `POST /eve/v1/session` returns `status: "accepted"` and a `sessionId`.
- [ ] Streaming the session prints NDJSON events, including the `lookup_service` call and `session.waiting`.
- [ ] A follow-up `POST` to that session ID continues the conversation without repeating context.
- [ ] You can explain why the first `POST` returns a handle instead of the assistant's answer.

## Solution

There's no code to write here. The agent exposed these routes when you ran it, and every channel uses the same contract:

| Route                            | What It Does                                                       |
| -------------------------------- | ------------------------------------------------------------------ |
| `POST /eve/v1/session`           | Start a durable session; returns an accepted status + `sessionId`. |
| `GET /eve/v1/session/:id/stream` | Stream the run as NDJSON events (reconnectable).                   |
| `POST /eve/v1/session/:id`       | Send a follow-up to the same durable session.                      |

Hold onto this picture. In Section 4 you'll put a web dashboard and Slack in front of the dispatcher, and neither one invents a new way to talk to it. `useEveAgent` calls these same routes from the browser; the Slack channel calls them from a webhook.


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
