REST Endpoints
A pipeline can publish an HTTP endpoint. An ERP posts an order, an MES asks for line status, a label printer requests the next job — each arrives as a message that runs your graph and returns whatever you build.
Published routes are served by the RestGateway on port 8090, under the project's slug:
http(s)://<host>:8090/gateway/<project-slug><URL pattern>
A route published as /api/v1/orders in the project plant-a is called at
/gateway/plant-a/api/v1/orders. The node's editor shows the full path under the URL pattern.
Building one
REST API (ingress) → Python (validate) → MongoDB (insert) → HTTP Response
- REST API node in
ingressmode defines the method and URL pattern. - Middle nodes do the work.
- HTTP Response sends the reply. Every ingress path must reach one, or the caller waits for the timeout.
Route configuration
| Setting | Default | Purpose |
|---|---|---|
| Method | GET | GET, POST, PUT, DELETE, PATCH, or * for any. |
| URL pattern | /api/v1/example | The route's path. No :id placeholders — see Matching. |
| Timeout | 30 s | How long the caller waits before the gateway gives up. |
| Auth | Public (No Auth) on a new node | API Key (X-API-Key) or Public (No Auth). |
| Required scopes | (none) | Capability scopes the caller's key must hold, e.g. tag:write,db:read. |
| Allowed IPs | (any) | Comma-separated CIDR allow-list. |
| Rate limit | 0 (off) | Requests per second. |
| Burst limit | 0 | Burst size above the sustained rate. |
| Max payload size | 1 MB | Request body cap. |
| CORS enabled | false | Allow browser cross-origin calls. |
| Allowed origins | * | Origins permitted when CORS is on. |
Matching
A request is matched, in order, to the route with the same method and path; then to a route whose
method is *; then to the route whose path the request's path begins with. So
/api/v1/orders/SO-1042 reaches a route published as /api/v1/orders. There are no path
parameters: pass identifiers in the query string, or read the full request path from the message's
origin (below). A trailing slash is ignored.
Choose the authentication deliberately
A new REST API node starts Public (No Auth), and the editor says so in an amber warning: anyone who can reach it can invoke its pipeline. Set API Key (X-API-Key) for anything outside a test bench. Behind that choice the gateway fails closed: a route whose auth setting is anything other than public or a valid API key is refused.
API keys
Keys are project-scoped credentials. Create them where they are used: in a REST API ingress node, set the authentication to API Key (X-API-Key) and use Project API Keys → Create Key. Listing, creating and revoking keys requires Access Settings.
- The key value is shown once, at creation. Only a hash is stored.
- Revocation is immediate.
- Give each consumer its own key, so one can be revoked without disrupting the others.
→ API keys
Scopes
A key carries capability scopes; a route can require some. A key without a required scope is rejected even though it is otherwise valid.
Key "mes-integration" scopes: db:read, tag:read
Route POST /api/v1/setpoint requires: tag:write → rejected
Route GET /api/v1/status requires: tag:read → allowed
Scopes let one key serve several routes at a bounded level of privilege.
Defence in depth
Layer the controls; each is cheap and independent.
| Control | Stops |
|---|---|
| API key | Unauthenticated callers |
| Scopes | An authenticated caller doing more than it should |
| IP allow-list | Calls from outside the expected network |
| Rate limit + burst | Runaway clients and brute-force attempts |
| Max payload size | Memory-exhaustion attempts |
| CORS off | Browser-based cross-origin abuse |
An integration endpoint for a known MES should have: an API key, the narrowest scopes, the MES's CIDR, a rate limit slightly above its real traffic, and CORS off.
Calling an endpoint
curl -X POST https://qubiq.example.com:8090/gateway/plant-a/api/v1/orders \
-H "X-API-Key: <key>" \
-H "Content-Type: application/json" \
-d '{"orderId":"SO-1042","line":"L1","qty":500}'
Reading request data
The request arrives as the message payload:
| Field | Holds |
|---|---|
params | The query string, one value per name: ?line=L1 → {"line": "L1"} |
body | A JSON body, parsed. Any other body arrives as text |
form · files | A form post's fields, and its uploaded files (name, size, type and the content as base64) |
The request line and headers are in the message's origin: message["origin"]["ctx"] holds method,
path (which includes the project slug), query, headers and client_ip, the caller address the
gateway trusts.
def transform(payload, message):
order = payload.get("body") or {}
if not order.get("orderId"):
return {"http_status": 400, "error": "orderId is required"}
system.db.runNamedQuery("insert_order", {
"id": order["orderId"],
"line": order.get("line"),
"qty": order.get("qty", 0),
})
return {"http_status": 201, "accepted": order["orderId"]}
The HTTP Response node after it takes the status from http_status and
sends the rest of the payload as the body; an error field answers {"error": "…"} instead.
Credentials never reach pipeline code: Authorization, Cookie, X-Api-Key, X-Auth-Token and
Proxy-Authorization are removed from the headers, and so are X-Forwarded-For and X-Real-Ip,
which a caller can forge.
Outbound calls (egress)
The same node in egress mode calls out: post an alarm to a webhook, fetch a work order, push
a report to an external API.
| Setting | Purpose |
|---|---|
| URL, method | The target |
| Static headers | Added to every request (e.g. an authorization header) |
| Timeout | Give up after this long |
Egress calls are not buffered by store-and-forward: a call that fails, fails, and nothing replays it later. When the event must not be lost, write it somewhere durable first.
Operational notes
- Port 8090 must be reachable by the caller; it is separate from the UI port.
- The pipeline must be running. A stopped pipeline's routes return an error.
- Version your URLs (
/api/v1/...). Changing a pipeline's response shape breaks callers exactly as changing any API does. - Return meaningful status codes from the HTTP Response node. A 200 carrying an error message is hostile to whoever integrates with you at 03:00.
Troubleshooting
| Symptom | Check |
|---|---|
| 404 | The /gateway/<project-slug> prefix is missing or wrong, the pipeline is stopped, or no route matches the method and path. |
| 401 | Missing, unknown or revoked API key. |
| 403 | Missing required scope, or source IP outside the allow-list. |
| 413 | Body exceeds max payload size. |
| 429 | Rate limit hit — raise it or fix the client. |
| 504 after the route timeout | No HTTP Response node was reached on that path. |
| 503 No pipeline handler available | The route is known but no running pipeline is listening behind it. |
| CORS error in a browser | CORS disabled, or the origin is not in the allowed list. |
Next
→ Designer