Build a REST API
Serve HTTP endpoints with routing, path params, and response shaping.
This guide builds a JSON API on the http connector: one endpoint that routes
on the HTTP method, reads path and query parameters, captures headers, and
shapes its response. It follows
samples/http-orders.yaml.

Declare the server
The http connector owns the listener; every flow that sources from it
registers a route on the same server. Variables declared under env: resolve
as OS environment, then .env file, then the default.
env:
- name: HTTP_HOST
default: 0.0.0.0
- name: HTTP_PORT
default: "8080"
- name: HTTP_BASE_PATH
default: /api/v1
connectors:
- name: api
type: http
settings:
host: ${HTTP_HOST}
port: ${HTTP_PORT} # an exact ${VAR} keeps its type -> int 8080
basePath: ${HTTP_BASE_PATH}
keepAlive: true
requestTimeout: 5s # how long a handler waits for the flow to finish
readTimeout: 10s
writeTimeout: 10s
idleTimeout: 60sRegister a route
A flow with an http source becomes an endpoint. A path parameter such as
{id} becomes vars.id. Headers listed in headers: are copied into vars,
e.g. vars["X-Tenant"]. correlationIdHeader propagates a request ID through
the flow as correlationID, and maxBodyBytes caps the request body.
workers and buffer set how many messages run in parallel and how many can
queue before the source blocks.
flows:
- name: orders-api
workers: 8
buffer: 128
source:
connector: api
type: http
settings:
path: /orders/{id} # {id} -> vars.id
correlationIdHeader: X-Request-Id
headers: [X-Tenant] # captured as vars["X-Tenant"]
timeout: 5s
maxBodyBytes: 1048576 # 1 MiBThe URL is the base path plus the route: POST /api/v1/orders/42 triggers this
flow with vars.id == "42" and vars.method == "POST".
Read query parameters
vars.query is always a map (possibly empty); guard optional parameters with
has():
process:
- type: set-variable
name: resolve-currency
settings:
name: currency
value: 'has(vars.query.currency) ? vars.query.currency : "USD"'Route on the HTTP method
The source stores the method in vars.method and a switch block routes on
it. Each case runs its own processor chain; default catches everything else.
- type: switch
name: route-by-method
cases:
- when: 'vars.method == "POST"'
process:
- type: flow-ref
name: enrich-sync
settings:
flow: enrich-order # delegate to another flow, wait for its result
- when: 'vars.method == "GET"'
process:
- type: set-payload
name: lookup-order
settings:
value: '{"orderId": vars.id, "currency": vars.currency, "status": "found"}'
default:
process:
- type: set-payload
name: method-not-supported
settings:
value: '{"error": "method " + vars.method + " not supported"}'The POST case delegates to a second flow with flow-ref; the called flow's
result folds back into the message (see Composing Flows).
The sample's enrich-order flow builds the response from the body, the captured
header, and the correlation ID:
- name: enrich-order
process:
- type: set-payload
name: normalize-order
settings:
value: >
{
"orderId": vars.id,
"tenant": vars["X-Tenant"],
"item": body.item,
"amount": body.amount,
"currency": vars.currency,
"requestId": correlationID
}
- type: if
name: priority-check
condition: 'body.amount >= 1000.0'
then:
process:
- type: set-variable
settings: { name: priority, value: '"high"' }
else:
process:
- type: set-variable
settings: { name: priority, value: '"normal"' }
- type: set-payload
name: wrap-response
settings:
value: '{"order": body, "priority": vars.priority, "status": "accepted"}'Shape the response
The body at the end of the chain is serialized as the JSON response. The status
code defaults to 200; set the httpStatus variable to override it, for example
a 405 from the default case:
- type: set-variable
settings: { name: httpStatus, value: "405" }Run it
bin/octo run --config samples/http-orders.yamlCreate an order:
curl -s -X POST localhost:8080/api/v1/orders/42 \
-H 'X-Tenant: acme' -H 'X-Request-Id: req-1' \
-d '{"item":"widget","amount":1500}'{
"order": {
"orderId": "42",
"tenant": "acme",
"item": "widget",
"amount": 1500,
"currency": "USD",
"requestId": "req-1"
},
"priority": "high",
"status": "accepted"
}Read one back with a query parameter:
curl -s 'localhost:8080/api/v1/orders/42?currency=EUR'{"orderId": "42", "currency": "EUR", "status": "found"}Override a declared env var at startup with
HTTP_PORT=9090 bin/octo run --config samples/http-orders.yaml, or put it in a
./.env file.
Where to go next
- Database CRUD: back these endpoints with SQL.
- Validation and Auth: reject bad input and require JWTs.
- Composing Flows: the
flow-refpattern in depth. - HTTP connector reference: every setting on the connector and source.