GreenPT Docs

How runs work

The phases of a Mangrove run, why each run is one exchange, and how to read its stream.

The flow of a run

The general flow for using GreenPT's Mangrove is:

  1. Send your question to Mangrove — you get back an agent ID.
  2. Hold on to that ID. It is how you follow the run's progress and read its answer.
  3. Mangrove works through the task and answers. If it needs something only you can provide, it says so and stops.
  4. Want to take it further? Send another question — that starts a new run.
  5. Repeat :-)

Inside a run

Every run moves through the same phases, and the event stream shows them as numbered steps:

  1. A forced step, when the request carried something to read. A recording is transcribed, and an image is looked at, before the model sees your question — it never has to decide to do either. A document URL is the exception: the model calls document_parse itself, for the reason in Case 2.
  2. The agentic loop, where the model calls whichever tools it decides it needs, over at most 20 rounds.
  3. A final check, where the model is asked whether its answer is complete and whether a tool it has not used would close a gap. It either uses that tool and answers again, or restates the answer it already had.

The final check is why a run emits its answer twice — see Reading a stream without duplicating the answer.

One run, one exchange

Step 4 is worth spelling out, because it is where Mangrove differs from a chat endpoint you may be used to: a run is a single exchange, not a conversation. Every run starts from a blank slate and sees only the question you send it. Nothing on our side threads two runs together, and no run can be resumed once it has finished — including one that finished by asking you for something (see Handling awaiting_input).

The agent ID does not change that. It keeps the run readable until the expiresAt timestamp the run reports, so you can poll it, stream it, or come back for the answer later. Reading a run is not resuming it.

Asking a follow-up

A follow-up is a new run, so carry whatever context it needs into the question — most simply, by quoting the part of the previous answer you want built on:

curl -X POST https://api.greenpt.ai/v1/agents/runs \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "question": "Earlier I was told: \"EU grid carbon intensity averaged 158 gCO2e/kWh in 2026, down 16% from 189 gCO2e/kWh in 2025.\" How does that compare with 2024, and what drove the difference?"
  }'

Quoting is not only a convenience — it is what keeps a follow-up cheap. Mangrove, handed the earlier finding, does not have to go looking for it again; asked "and how does that compare with 2024?" on its own, it has to rediscover the whole thing, and you pay for that work a second time (see Billing).

Saved values are a different mechanism

Asking Mangrove to remember something — "save this figure as grid_intensity_2026" — is not conversation history. It writes a value under a name, scoped to your account, that you can ask for by name on any later run, and Mangrove decides to reach for it the same way it decides to search the web. Use it for figures you want to compare across runs; quote the previous answer for the thread of a discussion.

Reading a stream without duplicating the answer

The last step of every run is the final check described in Inside a run, and it asks the model to restate its answer in full. So a run emits its answer at least twice: once when the model first produces it, once more from the check. Concatenating every text event gives you the answer two or three times over.

Two ways to read a stream correctly:

  • Use done.finalText. It is the last message the model produced, which is the answer after any correction the check made. Treat text as progress to display while the run is working, not as the thing to keep.
  • Reset on step_start. If you are rendering text as it arrives, clear what you have buffered at each new step. Each step's text then stands on its own, and the final check replaces the draft rather than appending to it.

Polling sidesteps this entirely: result on a succeeded run is the same string as finalText.

Handling awaiting_input

A run that lacks something it needs ends with an input_required event instead of tool_call/text/done — for example, a question that asks to summarize a meeting recording without an audioUrl and without enough question text to be a pasted transcript, where Mangrove asks for the recording rather than guessing:

event: input_required
data: {"type":"input_required","message":"I can analyze a meeting recording, but I need the audio first. Create a new run with an audioUrl pointing to the recording, or include the transcript text in the question.","expected":[{"name":"audioUrl","description":"URL of the meeting recording to transcribe"}]}

There is no resume: start a new run carrying the field named in expected.

On this page