Transformations and Routing
A bin can have routes. A route has conditions, an optional transformation and an action. Routes run in order for each request. Use them to split one stream between destinations, to drop noise, and to change a body before your server sees it.
Use it
- Open a bin you own and add its destinations.
- Open Routes on the bin page (the link is in the Destinations panel).
- Write the conditions, select the action, and add a transformation if you need one.
- Press Test with the latest request. The test shows which route matched and the exact request that a destination would get. Nothing is sent.
- Press Add route.
Actions
| Action | Result |
|---|---|
| Forward | The request goes to the destinations of the route, with the usual retries. The run stops. |
| Drop | The request is stored and not forwarded. The run stops. |
| Transform and continue | The transformation is kept and the next route is checked against the changed request. |
A bin without routes sends every request to every destination. A bin with routes sends only what a route forwards: a request that matches no route is stored and not sent. Add a last route with no conditions if you want a default.
Conditions
One condition per line. All lines must match. An empty box matches every request.
body.type starts charge.refund header:x-github-event is push body.data.object.amount > 5000 query:source is not test path ends /orders body.customer missing verify is ok
Fields: method, path, verify, header:<name>, query:<name>, body.<dotted.path>. Operators: is, is not, contains, starts, ends, exists, missing, >, <, >=, <=.
Transformations
JavaScript. One function. It gets req with method, path, query, headers and body (parsed when the body is JSON). Return req. Return null to drop the request.
(req) => {
// keep three fields, add one header
req.body = { id: req.body.id, type: req.body.type, amount: req.body.data?.object?.amount };
req.headers["x-source"] = "webhooks.xyz";
return req;
}
JSONata. One expression over the body. The result is the new body. $headers, $path, $method and $query are available.
{ "id": id, "type": type, "amount": data.object.amount }
- The code runs in a sandbox: no network, no files, no timers. It has 100 ms and 16 MB.
- If a transformation fails, the request is stored and not forwarded. The route shows the error and you get one inbox note an hour for that route. Fix the route, then replay.
- Retries and replays send the same transformed request. The Private Relay agent gets the transformed request too.
- A changed body that is an object is sent as JSON with
Content-Type: application/json.
Limits
| Free | Dev | Team | Business | |
|---|---|---|---|---|
| Routes per bin | 5 | 10 | 25 | 50 |
API and MCP
GET /api/v1/bins/{id}/routes
POST /api/v1/bins/{id}/routes {"name", "conditions", "action", "destination_ids", "transform_kind", "transform_code"}
POST /api/v1/bins/{id}/routes/test {"request_id"?, "draft"?: {…a route…}}
DELETE /api/v1/bins/{id}/routes/{route_id}
MCP tools: list_routes, add_route, test_route, delete_route.