HTTP Transitions – Building a Weather Pipeline with Agentic Nets
In my Petri-net based runtime (Agentic Nets), agentic processes are composed from transition “building blocks”. One of the most versatile is the HTTP transition – it lets you call external APIs, transform data with templates, and route results to different places based on success or failure.
In this article, I’ll show how HTTP transitions work using a concrete example: the Weather Net, a two-stage pipeline that transforms a city name into real-time weather data using the Open-Meteo APIs.
The first video is just the usage, to see the complete video which is about entirely creating te complete net by using Claude Code is the second one.
The Weather Net – from city name to weather data
The Weather Net contains five places and two transitions:
- Places:
- city-request – incoming city name tokens (e.g.,
{"city": "Stuttgart"}). - geocoded-city – intermediate tokens containing latitude/longitude coordinates.
- weather-response – final weather data (temperature, humidity, wind speed).
- geocode-error – errors from the geocoding stage.
- weather-error – errors from the weather fetch stage.
- city-request – incoming city name tokens (e.g.,
- Transitions:
- t-geocode – an HTTP transition that converts a city name to coordinates via the Open-Meteo Geocoding API.
- t-fetch-weather – an HTTP transition that fetches weather data for those coordinates via the Open-Meteo Forecast API.
The flow looks like this:
city-request → t-geocode → geocoded-city → t-fetch-weather → weather-response
Let’s look at both HTTP transitions in detail.
HTTP transition: “t-geocode”
The first stage takes a city name and calls the Open-Meteo Geocoding API to get latitude and longitude coordinates. Here’s the inscription:
{
"id": "t-geocode",
"kind": "http",
"presets": {
"input": {
"placeId": "city-request",
"host": "default@localhost:8080",
"arcql": "FROM $ LIMIT 1",
"take": "FIRST",
"consume": true
}
},
"postsets": {
"success": {
"placeId": "geocoded-city",
"host": "default@localhost:8080"
},
"error": {
"placeId": "geocode-error",
"host": "default@localhost:8080"
}
},
"action": {
"type": "http",
"method": "GET",
"url": "https://geocoding-api.open-meteo.com/v1/search?name=${input.data.city}&count=1",
"headers": {
"Accept": "application/json"
}
},
"emit": [
{"to": "success", "from": "@response.json", "when": "success"},
{"to": "error", "from": "@response", "when": "error"}
],
"mode": "SINGLE"
}
Template interpolation: dynamic URLs from token data
The key feature here is template interpolation in the URL:
https://geocoding-api.open-meteo.com/v1/search?name=${input.data.city}&count=1
The ${input.data.city} placeholder is replaced at runtime with the actual city name from the input token. If the token contains {"city": "Stuttgart"}, the URL becomes:
https://geocoding-api.open-meteo.com/v1/search?name=Stuttgart&count=1
This is how HTTP transitions become dynamic – the same inscription can handle any city, driven entirely by the data in the input token.
Emit: routing success and error responses
The emit rules define where results go:
{"to": "success", "from": "@response.json", "when": "success"}– on success, the JSON response goes to geocoded-city.{"to": "error", "from": "@response", "when": "error"}– on error, the full response goes to geocode-error.
The geocoding API returns coordinates in this format:
{
"results": [
{
"name": "Stuttgart",
"latitude": 48.78,
"longitude": 9.18,
"country": "Germany",
"admin1": "Baden-Württemberg"
}
]
}
This entire response becomes a token in geocoded-city, ready for the next stage.
HTTP transition: “t-fetch-weather”
The second stage takes the geocoded coordinates and fetches the current weather. Here’s the inscription:
{
"id": "t-fetch-weather",
"kind": "http",
"presets": {
"input": {
"placeId": "geocoded-city",
"host": "default@localhost:8080",
"arcql": "FROM $ LIMIT 1",
"take": "FIRST",
"consume": true
}
},
"postsets": {
"success": {
"placeId": "weather-response",
"host": "default@localhost:8080"
},
"error": {
"placeId": "weather-error",
"host": "default@localhost:8080"
}
},
"action": {
"type": "http",
"method": "GET",
"url": "https://api.open-meteo.com/v1/forecast?latitude=${input.data.results[0].latitude}&longitude=${input.data.results[0].longitude}¤t=temperature_2m,relative_humidity_2m,wind_speed_10m,weather_code",
"headers": {
"Accept": "application/json"
}
},
"emit": [
{"to": "success", "from": "@response.json", "when": "success"},
{"to": "error", "from": "@response", "when": "error"}
],
"mode": "SINGLE"
}
Chaining data through the pipeline
Notice how this transition reads from the output of the previous one. The URL template:
latitude=${input.data.results[0].latitude}&longitude=${input.data.results[0].longitude}
This accesses nested data from the geocoding response. The path input.data.results[0].latitude navigates into the JSON structure to extract exactly the values we need.
This is the power of token-based pipelines: each transition consumes structured data and produces structured data, with full visibility into what flows between stages.
The complete flow in action
Here’s what happens when we drop a token into city-request:
1. Input token (city-request):
{"city": "Stuttgart"}
2. After t-geocode (geocoded-city):
{
"results": [{
"name": "Stuttgart",
"latitude": 48.78,
"longitude": 9.18,
"country": "Germany"
}]
}
3. After t-fetch-weather (weather-response):
{
"latitude": 48.78,
"longitude": 9.18,
"current": {
"temperature_2m": 3.2,
"relative_humidity_2m": 80,
"wind_speed_10m": 2.7,
"weather_code": 3
}
}
From a simple city name, we now have real-time weather data – temperature, humidity, wind speed, and weather conditions.
Results: weather data for Baden-Württemberg
During the session, we collected weather data for three cities:
| City | Temp | Humidity | Wind | Weather |
|---|---|---|---|---|
| Pforzheim | 3.5°C | 78% | 9.0 km/h | Overcast |
| Karlsruhe | 4.2°C | 79% | 9.4 km/h | Overcast |
| Stuttgart | 3.2°C | 80% | 2.7 km/h | Overcast |
All cities showing similar overcast winter weather conditions – typical for December in Baden-Württemberg!
HTTP transitions as a reusable pattern
The Weather Net demonstrates a pattern that appears again and again in Agentic Nets:
- HTTP transitions – call external APIs with dynamic URLs built from token data.
- Template interpolation – use
${...}syntax to inject token values into URLs, headers, and request bodies. - Pipeline chaining – output tokens from one transition become input tokens for the next.
- Error routing – separate postsets for success and error cases keep the happy path clean.
Once you have this pattern, weather is just one use case. The same approach can drive:
- Payment processing – validate order → charge payment → confirm shipment.
- Data enrichment – lookup customer → fetch credit score → determine eligibility.
- Multi-API orchestration – call service A → transform data → call service B → aggregate results.
Building with Claude Code
This entire Weather Net was built live using Claude Code. Starting from an empty Petri net, Claude:
- Created the visual PNML elements (5 places, 2 transitions, 6 arcs) via the Workspace API.
- Set up the runtime infrastructure via the Runtime API.
- Defined both HTTP transition inscriptions with proper template interpolation.
- Added test tokens and verified the complete pipeline.
The entire session took about 10 minutes – from empty canvas to working weather pipeline. That’s the power of combining AI-assisted development with a well-designed runtime: Claude understands the APIs, and the Petri net makes the agentic process explicit and inspectable.
In upcoming posts, I’ll show how to combine HTTP transitions with Agent and Command transitions to build more complex agentic processes – including human-in-the-loop approvals and LLM-driven decision making.