Skip to main content

Tool Execute API

All endpoints verified working via curl. Document updated only after curl confirmation.

Accepts either a session cookie (connect.sid) or a Bearer API key. Requires owner, admin, or editor role.

Only a version whose executor.type is "http" can be executed server-side — a "client" executor means the customer's own app runs the tool, and this endpoint returns 422 for it. The target version is resolved as: explicit versionNumber in the body → named alias in the body → the production alias by default.

Arguments are validated against the version's parametersSchema before anything is sent. A wrong type, a value outside an enum, a number outside minimum/maximum, a string outside minLength/maxLength, and an undeclared property when the schema sets additionalProperties: false each stop the call and come back as 200 with an error naming the first thing that did not match — a recorded tool failure, not an exception, so a model can read the reason and try again instead of ending the run. A missing required key is the one case that returns 400. Keywords outside that list (pattern, anyOf, $ref, …) are ignored rather than rejected, so an unsupported keyword never turns into a false rejection.

Arguments reach the outgoing request in two ways. {{arg.NAME}} inside a query value, a headers value or the url is replaced with the argument NAME at call time. A requestTransform shapes the request body from the whole arguments object. A value carrying no placeholder stays fixed.

A placeholder in the url is percent-encoded when it is filled in, so the value stays inside the one path segment or query value you wrote it into. A tool whose argument legitimately spans two path segments needs one {{arg.NAME}} per segment. Header and query values are not encoded here — query values are encoded by the URL builder, and a header value is not a URL.

On top of that encoding, a value substituted into the url may not contain .. or be a lone ., because a relative path reference changes which resource is addressed no matter how it is encoded. Such a call is refused before any request goes out, with the same 200-and-error shape as a schema violation. Ordinary dotted values are unaffected: report.pdf, 1.2.3 and [email protected] all reach the upstream as written.

The outgoing HTTP call goes through an SSRF guard (safeFetch): only public http(s) URLs are reachable, so testing this endpoint against a real target requires a real public URL (this doc uses https://httpbin.org) — pointing it at localhost/127.0.0.1 is rejected by design, even in dev. A response body over 1 MB fails the call rather than being truncated — a half-read body is not a result worth handing to a model. The count is bytes on the wire, so a non-ASCII body is measured the same way as an ASCII one.


POST /api/v1/tools/:id/execute​

Executes the version behind the default ("production") alias.

curl -X POST $ACRUXCORE_BASE_URL/tools/8662ca24-69f2-4431-a7f4-82e9abc77ff1/execute \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"arguments":{"city":"Berlin"}}'

Response (status 200):

{
"result": {
"args": {"city": "placeholder"},
"headers": {"Content-Type": "application/json", "Host": "httpbin.org", "X-Amzn-Trace-Id": "Root=1-6a5316bb-2818b2c824233ba1133c94a1"},
"origin": "39.40.215.16",
"url": "https://httpbin.org/get?city=placeholder"
},
"status": 200,
"latencyMs": 874,
"toolVersionId": "b40e86ed-9dfd-44c4-bf86-038e4964be22"
}

Pin to an explicit version number instead of the default alias.

curl -X POST $ACRUXCORE_BASE_URL/tools/8662ca24-69f2-4431-a7f4-82e9abc77ff1/execute \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"arguments":{"city":"Berlin"},"versionNumber":1}'

Response (status 200) — the version carries a failureWhen predicate and the predicate declared a failure. The HTTP status is still 200 and nothing threw: error is a field to read, not an exception to catch. The span this call wrote is red and carries httpStatus: 200 at the same time. error.type is the classification, one of six fixed values; the slug the predicate returned is on the span as errorCode. See versions.mdx for the predicate that produced this.

{
"result": {"generationtime_ms": 0.22029877},
"status": 200,
"latencyMs": 521,
"toolVersionId": "2bbeb8d3-9162-4e57-90f2-6d55f35e3faa",
"error": {
"type": "tool_declared",
"message": "Open-Meteo has no coordinates for that city."
}
}

The same tool called with a city that exists returns no error key at all — the field is absent on a healthy call, not null:

{
"result": {
"results": [
{
"id": 1283240,
"name": "Kathmandu",
"latitude": 27.70169,
"longitude": 85.3206,
"elevation": 1317,
"feature_code": "PPLC",
"country_code": "NP",
"admin1_id": 12095449,
"admin2_id": 12095484,
"timezone": "Asia/Kathmandu",
"population": 1442271,
"country_id": 1282988,
"country": "Nepal",
"admin1": "Bagmati Province",
"admin2": "Kathmandu"
}
],
"generationtime_ms": 0.24616718
},
"status": 200,
"latencyMs": 638,
"toolVersionId": "2bbeb8d3-9162-4e57-90f2-6d55f35e3faa"
}

Response (status 400) — missing required argument (per parametersSchema.required):

{"error":{"code":"VALIDATION_ERROR","message":"Missing required argument: city"}}

Arguments that do not match the schema​

Every response below came from one tool whose parametersSchema is:

{
"type": "object",
"additionalProperties": false,
"properties": {
"q": {"type": "string", "maxLength": 20},
"limit": {"type": "integer", "minimum": 1, "maximum": 10},
"sort": {"type": "string", "enum": ["asc", "desc"]}
},
"required": ["q"]
}
curl -X POST $ACRUXCORE_BASE_URL/tools/$TOOL_ID/execute \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"arguments":{"q":"ok","limit":"lots"}}'

Each request and the exact body it returned:

argumentsStatuserror.message
{"q":"ok","limit":3,"sort":"asc"}200—
{"q":"ok","limit":"lots"}200arguments.limit should be integer, got string
{"q":"ok","sort":"DROP TABLE"}200arguments.sort should be one of "asc", "desc"
{"q":"ok","limit":100000}200arguments.limit should be <= 10, got 100000
{"q":"<100 characters>"}200arguments.q should be at most 20 characters, got 100
{"q":"ok","evil":"x"}200arguments does not allow the property 'evil'
{}400Missing required argument: q

A refused call returns a null result beside the error, and nothing reaches the upstream:

{"result":null,"status":0,"latencyMs":0,"toolVersionId":"…","error":{"type":"transport","message":"arguments.limit should be <= 10, got 100000"}}

The one 400, for a missing required argument:

{"error":{"code":"VALIDATION_ERROR","message":"Missing required argument: q"}}

A schema that declares no constraints stays permissive: with {"type":"object","properties":{}} and no additionalProperties, any argument is accepted.

Response (status 404) — tool, version, or alias not found:

{"error":{"code":"NOT_FOUND","message":"Tool not found."}}

Response (status 422, code NOT_EXECUTABLE) — resolved version has a "client" executor (no server-side call to make):

{"error":{"code":"NOT_EXECUTABLE","message":"This tool has no server-side executor."}}