Skip to main content

Manage a tool's lifecycle via the SDK

What you'll build: a tool created, versioned, listed, updated, and deleted entirely through the API or SDK — no dashboard click anywhere in the flow.

Build and attach a tool covers the common case: you write an @acrux.tool-decorated function, call hub.tools.sync(...), and the platform reconciles the catalog to match your code. That path assumes the tool's definition lives in your codebase. Sometimes it doesn't — a tool defined by an external system, or a management UI your team is building on top of the catalog, needs to create tool shells and commit versions directly, without a decorated function to sync from. That's what hub.tools.create, .commitVersion, .update, and friends are for. Everything below works the same over raw curl or through either SDK.

1. Create a tool shell

A tool starts as a shell: just a name and an optional description, with no schema or executor yet. name must match ^[a-zA-Z0-9_-]{1,64}$ and be unique within your team.

curl -X POST "$ACRUXCORE_BASE_URL/tools" \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"order_status_1785860323","description":"Looks up the status of a customer order by id."}'
{
"id": "41fd848e-ad01-43bb-bb3c-44869616d321",
"name": "order_status_1785860323",
"description": "Looks up the status of a customer order by id.",
"teamId": "221e37aa-9a14-4a7f-ae10-2982247e6d38",
"createdBy": "b085d017-cf51-43f4-adb3-a7640337651c",
"createdAt": "2026-08-04T16:18:43.202Z"
}

2. Commit a version with an http executor

A shell has nothing to call yet. Committing a version gives it a parametersSchema (the JSON Schema the model reads to decide how to call the tool) and an executor — here, an http executor that points at a public, no-auth endpoint (https://httpbin.org/get) so the example is runnable as-is. The first version committed for a tool auto-creates both the production and staging aliases pointing at it.

curl -X POST "$ACRUXCORE_BASE_URL/tools/41fd848e-ad01-43bb-bb3c-44869616d321/versions" \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"description": "Looks up the status of a customer order by id.",
"parametersSchema": {"type":"object","properties":{"order_id":{"type":"string","description":"The order id, e.g. \"ORD-1234\"."}},"required":["order_id"]},
"executor": {"type":"http","url":"https://httpbin.org/get","method":"GET","headers":[],"query":[{"name":"order_id","value":"{{order_id}}"}],"argMapping":[{"arg":"order_id","in":"query"}]}
}'
{
"id": "f3c068c4-e161-4ed8-9ccd-e27c6e51d07e",
"toolId": "41fd848e-ad01-43bb-bb3c-44869616d321",
"versionNumber": 1,
"description": "Looks up the status of a customer order by id.",
"changelog": null,
"source": "api",
"parametersSchema": {
"type": "object",
"required": ["order_id"],
"properties": { "order_id": { "type": "string", "description": "The order id, e.g. \"ORD-1234\"." } }
},
"executor": {
"url": "https://httpbin.org/get",
"type": "http",
"query": [{ "name": "order_id", "value": "{{order_id}}" }],
"method": "GET",
"headers": [],
"argMapping": [{ "in": "query", "arg": "order_id" }]
},
"createdBy": "b085d017-cf51-43f4-adb3-a7640337651c",
"createdAt": "2026-08-04T16:18:52.604Z",
"aliases": [
{ "id": "10186eee-2f2a-419a-8f01-e2478467ea1d", "alias": "production", "versionId": "f3c068c4-e161-4ed8-9ccd-e27c6e51d07e", "versionNumber": 1, "updatedAt": "2026-08-04T16:18:52.613Z" },
{ "id": "f46a02f5-6065-4ea9-a229-8331605da355", "alias": "staging", "versionId": "f3c068c4-e161-4ed8-9ccd-e27c6e51d07e", "versionNumber": 1, "updatedAt": "2026-08-04T16:18:52.613Z" }
]
}
source: 'code' is reserved for sync

This endpoint accepts source: 'dashboard' or 'api' (it defaults to 'api' if omitted). Sending 'code' is rejected — that value means "derived from a decorated function" and is only writable by POST /tools/sync, so a direct commit can't forge code-ownership the dashboard would otherwise trust:

curl -X POST "$ACRUXCORE_BASE_URL/tools/41fd848e-ad01-43bb-bb3c-44869616d321/versions" \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"source":"code","parametersSchema":{"type":"object","properties":{"order_id":{"type":"string"}},"required":["order_id"]},"executor":{"type":"client"}}'
{"error":{"code":"VALIDATION_ERROR","message":"Invalid enum value. Expected 'dashboard' | 'api', received 'code'"}}

description vs. changelog vs. source

Three fields are easy to mix up on a commit, and mixing them up changes what the model actually does:

FieldWho reads itEffect
descriptionThe model. It decides whether to call the tool from this text.Changing it changes the model's behaviour.
changelogYour team, in the dashboard's version list.None — the model never sees it.
sourceThe dashboard and the audit log.Records who authored the version, so the dashboard can warn before a deploy overwrites a hand-made edit.

If you commit a changelog with no description, the response carries a warnings array saying so — a release note is not what the model reads, and the warning exists so that omission doesn't go unnoticed.

3. List versions

List items omit parametersSchema/executor to keep pages small — fetch a specific version (next step) for those.

curl "$ACRUXCORE_BASE_URL/tools/41fd848e-ad01-43bb-bb3c-44869616d321/versions" \
-H "Authorization: Bearer $ACRUXCORE_API_KEY"
{
"data": [
{
"id": "f3c068c4-e161-4ed8-9ccd-e27c6e51d07e",
"toolId": "41fd848e-ad01-43bb-bb3c-44869616d321",
"versionNumber": 1,
"description": "Looks up the status of a customer order by id.",
"changelog": null,
"source": "api",
"createdBy": "b085d017-cf51-43f4-adb3-a7640337651c",
"createdAt": "2026-08-04T16:18:52.604Z"
}
],
"total": 1,
"page": 1,
"limit": 20
}

4. Get a specific version

Fetching one version by number returns the full parametersSchema and executor that a list entry leaves out.

curl "$ACRUXCORE_BASE_URL/tools/41fd848e-ad01-43bb-bb3c-44869616d321/versions/1" \
-H "Authorization: Bearer $ACRUXCORE_API_KEY"
{
"id": "f3c068c4-e161-4ed8-9ccd-e27c6e51d07e",
"toolId": "41fd848e-ad01-43bb-bb3c-44869616d321",
"versionNumber": 1,
"description": "Looks up the status of a customer order by id.",
"changelog": null,
"source": "api",
"parametersSchema": {
"type": "object",
"required": ["order_id"],
"properties": { "order_id": { "type": "string", "description": "The order id, e.g. \"ORD-1234\"." } }
},
"executor": {
"url": "https://httpbin.org/get",
"type": "http",
"query": [{ "name": "order_id", "value": "{{order_id}}" }],
"method": "GET",
"headers": [],
"argMapping": [{ "in": "query", "arg": "order_id" }]
},
"createdBy": "b085d017-cf51-43f4-adb3-a7640337651c",
"createdAt": "2026-08-04T16:18:52.604Z"
}

5. Update the shell's description

Updating a tool only touches its name/description — versions are immutable and unaffected by renaming the shell they belong to.

curl -X PATCH "$ACRUXCORE_BASE_URL/tools/41fd848e-ad01-43bb-bb3c-44869616d321" \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"description":"Looks up the status of a customer order by id. Now includes shipping carrier."}'
{
"id": "41fd848e-ad01-43bb-bb3c-44869616d321",
"name": "order_status_1785860323",
"description": "Looks up the status of a customer order by id. Now includes shipping carrier.",
"teamId": "221e37aa-9a14-4a7f-ae10-2982247e6d38",
"createdBy": "b085d017-cf51-43f4-adb3-a7640337651c",
"createdAt": "2026-08-04T16:18:43.202Z"
}

6. List tools

curl "$ACRUXCORE_BASE_URL/tools?search=order_status" \
-H "Authorization: Bearer $ACRUXCORE_API_KEY"
{
"data": [
{
"id": "41fd848e-ad01-43bb-bb3c-44869616d321",
"name": "order_status_1785860323",
"description": "Looks up the status of a customer order by id. Now includes shipping carrier.",
"teamId": "221e37aa-9a14-4a7f-ae10-2982247e6d38",
"createdBy": "b085d017-cf51-43f4-adb3-a7640337651c",
"createdAt": "2026-08-04T16:18:43.202Z"
}
],
"total": 1,
"page": 1,
"limit": 20
}

7. Delete the tool

Deleting is a soft delete: the tool stops appearing in list/get, but its versions and aliases are preserved (just unreachable) rather than removed.

curl -X DELETE "$ACRUXCORE_BASE_URL/tools/41fd848e-ad01-43bb-bb3c-44869616d321" \
-H "Authorization: Bearer $ACRUXCORE_API_KEY"
# 204 No Content

curl "$ACRUXCORE_BASE_URL/tools/41fd848e-ad01-43bb-bb3c-44869616d321" \
-H "Authorization: Bearer $ACRUXCORE_API_KEY"
{"error":{"code":"NOT_FOUND","message":"Tool not found."}}
Runnable end-to-end scripts

A fuller version of this walkthrough — three commits, an alias promotion, and analytics — is one runnable script per language:

Each needs only pip install acruxcore / npm install @acruxcoreai/sdk plus ACRUXCORE_API_KEY/ACRUXCORE_BASE_URL — no monorepo checkout required.

What's next