GreenPT Docs

Carbon Aware

Let a Mangrove run wait for the cleanest hour in a window you grant, and read what was decided.

A run that does not need its answer straight away can wait for cleaner electricity. Send greenShiftWindowHours (a whole number from 1 to 24) with the JSON body, and the run may start up to that many hours late. When it is created, Mangrove looks up the carbon intensity forecast for the grid its models run on and holds the run until the cleanest start in that window.

Send a window

curl -X POST https://api.greenpt.ai/v1/agents/runs \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "question": "Summarise the EU grid carbon intensity news from this week",
    "greenShiftWindowHours": 8
  }'
const response = await fetch('https://api.greenpt.ai/v1/agents/runs', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'Bearer YOUR_API_KEY',
  },
  body: JSON.stringify({
    question: 'Summarise the EU grid carbon intensity news from this week',
    greenShiftWindowHours: 8,
  }),
});

const { agentId, greenShift } = await response.json();
if (greenShift.outcome === 'held') {
  console.log(`${agentId} starts at ${greenShift.plannedStart}, ${greenShift.savingPct}% cleaner`);
} else {
  console.log(`${agentId} started now (${greenShift.outcome})`);
}
import requests

response = requests.post(
    "https://api.greenpt.ai/v1/agents/runs",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "question": "Summarise the EU grid carbon intensity news from this week",
        "greenShiftWindowHours": 8,
    },
)

run = response.json()
shift = run["greenShift"]
if shift["outcome"] == "held":
    print(f"{run['agentId']} starts at {shift['plannedStart']}, {shift['savingPct']}% cleaner")
else:
    print(f"{run['agentId']} started now ({shift['outcome']})")

The response for a run that was held:

{
  "agentId": "7d41c2e9-0b3a-4f6e-a1d8-2c9e5b7f3a10",
  "status": "queued",
  "greenShift": {
    "version": 1,
    "outcome": "held",
    "windowHours": 8,
    "plannedStart": "2026-07-27T14:00:00.000Z",
    "gco2Kwh": 41.2,
    "ifRunNowGco2Kwh": 58.7,
    "savingPct": 29.8,
    "kind": "forecast"
  }
}

Outcomes

outcome says what was decided:

outcomeMeaning
heldWaiting saves enough. The run starts at plannedStart.
run_nowStarting now is cleanest, or waiting would save too little to be worth the delay. The run starts at once.
unavailableNo forecast could be read. The run starts at once; a carbon check never delays or fails a run.

Fields

FieldTypeDescription
versionnumberShape of this object, 1.
windowHoursnumberThe window you granted.
plannedStartstring (ISO 8601)When the run starts. Equal to the creation time unless outcome is held.
gco2KwhnumberCarbon intensity over the planned run, in gCO₂e/kWh. Absent when outcome is unavailable.
ifRunNowGco2KwhnumberCarbon intensity had the run started at once, in gCO₂e/kWh. Absent when the current hour has no figure.
savingPctnumberHow much cleaner the planned start is, in percent. Present when outcome is held.
kindstringHow certain the least certain hour in the window is: measured, day_ahead or forecast.

Good to know

  • A held run reports queued until it starts. Nothing changes before plannedStart, so check back after that time rather than polling it.
  • The same greenShift object is on Get a run and on the run list, so you can show when a run will start.
  • expiresAt counts from plannedStart, so a held run stays readable for 24 hours after it starts.
  • The window is a JSON field only; a multipart/form-data upload always starts at once.

On this page