Tool Versions API
All endpoints verified working via curl. Document updated only after curl confirmation.
All endpoints accept either a session cookie (connect.sid) or a Bearer API key.
Committing a version requires owner, admin, or editor role; reads require any
authenticated role.
A version's executor is a discriminated union: {"type":"client"} (the
customer's own app runs the tool — nothing to validate) or a declarative
{"type":"http", ...} executor that the platform itself can call (see
execute.mdx). For an http executor, commit-time validation deep-checks
requestTransform/responseTransform JS syntax, {{secret.NAME}} references,
and that url resolves to a public (non-private/loopback) address.
The first version committed for a tool (versionNumber === 1) auto-creates
production and staging aliases pointing at it; later commits do not move
any alias — use aliases.mdx's promote endpoint for that.
description, changelog, and source
These three fields are easy to confuse, and getting them wrong changes what the model does:
| Field | Who reads it | Effect |
|---|---|---|
description | The model. It decides whether to call the tool from this text. | Changing it changes the model's behaviour. |
changelog | Your team, in the dashboard's version list. | None. The model never sees it. |
source | The dashboard and the audit log. | Records who authored the version, so the dashboard can warn before a deploy overwrites a hand edit. |
source is one of code, dashboard, or api, and defaults to api on this
endpoint. code cannot be claimed here — it means "derived from a decorated
function" and is writable only by sync.mdx. Without that
restriction a hand-rolled call could forge code ownership and make the dashboard
warn about an edit that no deploy would ever supersede.
If you send a changelog and no description, the response carries a warnings
array saying so — a release note is not what the model reads. See the example below.
POST /api/v1/tools/:id/versions
- curl
- TypeScript
- Python
curl -X POST $ACRUXCORE_BASE_URL/tools/50c99ce0-cda1-4b49-81bc-d06e091fbcc5/versions \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"description": "v1 - calls httpbin GET",
"parametersSchema": {"type":"object","properties":{"city":{"type":"string"}},"required":["city"]},
"executor": {
"type": "http",
"url": "https://httpbin.org/get",
"method": "GET",
"headers": [],
"query": [{"name":"city","value":"placeholder"}],
"argMapping": []
}
}'
const version = await hub.tools.commitVersion('50c99ce0-cda1-4b49-81bc-d06e091fbcc5', {
description: 'v1 - calls httpbin GET',
parametersSchema: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] },
executor: {
type: 'http',
url: 'https://httpbin.org/get',
method: 'GET',
headers: [],
query: [{ name: 'city', value: 'placeholder' }],
argMapping: [],
},
});
version = hub.tools.commit_version(
'50c99ce0-cda1-4b49-81bc-d06e091fbcc5',
parameters_schema={'type': 'object', 'properties': {'city': {'type': 'string'}}, 'required': ['city']},
executor={
'type': 'http',
'url': 'https://httpbin.org/get',
'method': 'GET',
'headers': [],
'query': [{'name': 'city', 'value': 'placeholder'}],
'argMapping': [],
},
description='v1 - calls httpbin GET',
)
Response (status 201) — first commit: includes auto-created aliases:
{
"id": "b48008a0-d162-48cd-8ebe-e4b399dce756",
"toolId": "50c99ce0-cda1-4b49-81bc-d06e091fbcc5",
"versionNumber": 1,
"description": "v1 - calls httpbin GET",
"changelog": null,
"source": "api",
"parametersSchema": {"type":"object","required":["city"],"properties":{"city":{"type":"string"}}},
"executor": {"url":"https://httpbin.org/get","type":"http","query":[{"name":"city","value":"placeholder"}],"method":"GET","headers":[],"argMapping":[]},
"createdBy": "5b8d5fb8-a8da-4afc-9d5c-20e7c088d6e2",
"createdAt": "2026-07-27T16:21:16.535Z",
"aliases": [
{"id":"5a068857-05d1-435c-802b-983c2b7ca653","alias":"production","versionId":"b48008a0-d162-48cd-8ebe-e4b399dce756","versionNumber":1,"updatedAt":"2026-07-27T16:21:16.538Z"},
{"id":"9bce7731-321a-4cbd-9889-794357af6d9f","alias":"staging","versionId":"b48008a0-d162-48cd-8ebe-e4b399dce756","versionNumber":1,"updatedAt":"2026-07-27T16:21:16.538Z"}
]
}
A subsequent commit (v2) — no aliases field in the response:
curl -X POST $ACRUXCORE_BASE_URL/tools/50c99ce0-cda1-4b49-81bc-d06e091fbcc5/versions \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"description": "v2 - POST variant",
"parametersSchema": {"type":"object","properties":{"city":{"type":"string"}},"required":["city"]},
"executor": {"type":"http","url":"https://httpbin.org/post","method":"POST","headers":[],"query":[],"argMapping":[]}
}'
Response (status 201):
{
"id": "c1cafe54-edba-4200-b1ef-0a749c2f5b75",
"toolId": "50c99ce0-cda1-4b49-81bc-d06e091fbcc5",
"versionNumber": 2,
"description": "v2 - POST variant",
"changelog": null,
"source": "api",
"parametersSchema": {"type":"object","required":["city"],"properties":{"city":{"type":"string"}}},
"executor": {"url":"https://httpbin.org/post","type":"http","query":[],"method":"POST","headers":[],"argMapping":[]},
"createdBy": "5b8d5fb8-a8da-4afc-9d5c-20e7c088d6e2",
"createdAt": "2026-07-27T16:21:16.560Z"
}
A commit carrying a changelog but no description succeeds, and warns:
curl -X POST $ACRUXCORE_BASE_URL/tools/4d9b768c-8ccb-414d-86ab-c629b3cafff5/versions \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"changelog": "switched to the read replica",
"parametersSchema": {"type":"object","properties":{"table":{"type":"string"},"schema":{"type":"string"}},"required":["table"]},
"executor": {"type":"client"},
"source": "dashboard"
}'
Response (status 201):
{
"id": "9d05c296-bdc2-4d8a-b0eb-a15e8a7e91ae",
"toolId": "4d9b768c-8ccb-414d-86ab-c629b3cafff5",
"versionNumber": 3,
"description": null,
"changelog": "switched to the read replica",
"source": "dashboard",
"parametersSchema": {"type":"object","required":["table"],"properties":{"table":{"type":"string"},"schema":{"type":"string"}}},
"executor": {"type":"client"},
"createdBy": "5b8d5fb8-a8da-4afc-9d5c-20e7c088d6e2",
"createdAt": "2026-07-27T16:17:32.389Z",
"warnings": ["This version has a changelog but no description, so the model will read the tool-level description instead. `description` is what the model reads; `changelog` is a note for your team."]
}
warnings is present only when there is something to warn about — it is absent, not
empty, on a clean commit.
Response (status 400) — http executor's url is not a public address (SSRF guard, defense-in-depth):
{"error":{"code":"VALIDATION_ERROR","message":"Blocked address: 127.0.0.1"}}
Response (status 404) — tool does not exist or belongs to another team:
{"error":{"code":"NOT_FOUND","message":"Tool not found."}}
GET /api/v1/tools/:id/versions
Lists all versions for a tool, newest first. parametersSchema/executor are
omitted from list items (fetch a specific version for those), but changelog and
source are included — they are what the dashboard's version list shows.
- curl
- TypeScript
- Python
curl $ACRUXCORE_BASE_URL/tools/4d9b768c-8ccb-414d-86ab-c629b3cafff5/versions \
-H "Authorization: Bearer $ACRUXCORE_API_KEY"
const versions = await hub.tools.listVersions('4d9b768c-8ccb-414d-86ab-c629b3cafff5');
versions = hub.tools.list_versions('4d9b768c-8ccb-414d-86ab-c629b3cafff5')
Response (status 200) — v1 came from a decorated function with no docstring, v2 and v3 were committed by hand:
{
"data": [
{
"id": "9d05c296-bdc2-4d8a-b0eb-a15e8a7e91ae",
"toolId": "4d9b768c-8ccb-414d-86ab-c629b3cafff5",
"versionNumber": 3,
"description": null,
"changelog": "switched to the read replica",
"source": "dashboard",
"createdBy": "5b8d5fb8-a8da-4afc-9d5c-20e7c088d6e2",
"createdAt": "2026-07-27T16:17:32.389Z"
},
{
"id": "11e7c6b4-c112-41d5-adcb-1d96921b3bea",
"toolId": "4d9b768c-8ccb-414d-86ab-c629b3cafff5",
"versionNumber": 2,
"description": "Count the rows in a table. Use for quick size checks, not for analytics.",
"changelog": null,
"source": "dashboard",
"createdBy": "5b8d5fb8-a8da-4afc-9d5c-20e7c088d6e2",
"createdAt": "2026-07-27T16:17:19.400Z"
},
{
"id": "b1f4bb07-5eb6-48f7-87b8-70ad2b1f3f80",
"toolId": "4d9b768c-8ccb-414d-86ab-c629b3cafff5",
"versionNumber": 1,
"description": null,
"changelog": null,
"source": "code",
"createdBy": "5b8d5fb8-a8da-4afc-9d5c-20e7c088d6e2",
"createdAt": "2026-07-27T16:17:19.355Z"
}
],
"total": 3,
"page": 1,
"limit": 20
}
GET /api/v1/tools/:id/versions/:version_number
Fetches a specific version, including its full parametersSchema and executor.
- curl
- TypeScript
- Python
curl $ACRUXCORE_BASE_URL/tools/4d9b768c-8ccb-414d-86ab-c629b3cafff5/versions/2 \
-H "Authorization: Bearer $ACRUXCORE_API_KEY"
const version = await hub.tools.getVersion('4d9b768c-8ccb-414d-86ab-c629b3cafff5', 2);
version = hub.tools.get_version('4d9b768c-8ccb-414d-86ab-c629b3cafff5', 2)
Response (status 200):
{
"id": "11e7c6b4-c112-41d5-adcb-1d96921b3bea",
"toolId": "4d9b768c-8ccb-414d-86ab-c629b3cafff5",
"versionNumber": 2,
"description": "Count the rows in a table. Use for quick size checks, not for analytics.",
"changelog": null,
"source": "dashboard",
"parametersSchema": {"type":"object","required":["table"],"properties":{"table":{"type":"string"}}},
"executor": {"type":"client"},
"createdBy": "5b8d5fb8-a8da-4afc-9d5c-20e7c088d6e2",
"createdAt": "2026-07-27T16:17:19.400Z"
}
Response (status 404) — version number does not exist for this tool:
{"error":{"code":"NOT_FOUND","message":"Version 99 not found for this tool."}}