Skip to main content

Tools 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. Write operations (POST/PATCH/DELETE) require owner, admin, or editor role.

This page covers the Tool shell CRUD only. The rest of the Tool Catalog (Phase 4) domain lives alongside it:

PageWhat it covers
versions.mdxCommit immutable versions; description vs changelog vs source.
aliases.mdxThe production and staging pointers, and promoting between versions.
sync.mdxOne-call reconcile for tools defined in code — what @acrux.tool uses.
resolve.mdxBatch name → model-ready function definition, plus executorType.
execute.mdxRunning an http executor server-side.
analytics.mdxPer-tool call counts, latency, and error rates.
Building a tool from code?

Reach for sync.mdx and resolve.mdx rather than this page. Creating the shell here and then committing a version separately is the dashboard's path; a decorated function does both in one call and stays idempotent across deploys.

Readiness fields​

Every tool in a list or single fetch carries five read-only fields saying whether it can actually be called. POST /tools creates a shell — a name with no version — and a shell resolves to nothing, so a prompt bound to one runs with that tool missing and says so nowhere. These fields are how a caller tells the two states apart in one request, without a versions fetch per tool.

FieldMeaning
callabletrue once a version exists and production points at one. This is the resolvable state — the one an unqualified tool_ref needs.
versionCountCommitted versions. 0 means the tool is a name only.
latestVersionNumberHighest committed version, or null.
executorTypeclient or http — who runs the call — read from the version production serves, not the newest one. null when not callable.
aliasesEvery alias with the version it points at, production first.

A tool that is not callable has one of two causes, and they need different fixes: versionCount: 0 means commit a version (POST /tools/:id/versions), while a non-zero count with no production entry in aliases means promote one (POST /tools/:id/aliases/production/promote).

Reading these fields needs SDK 0.15.0

The HTTP response carries all five fields today — every JSON example on this page, including the curl responses, is real. Both SDKs read them from 0.15.0 onward (npm i @acruxcoreai/sdk@latest, pip install -U acruxcore).

On 0.14.0 and earlier the two SDKs fail differently. In TypeScript, hub.tools.get() and hub.tools.list() return the raw response object, so the fields are there at runtime and only the compiled type is missing them — tool.callable is a type error unless you cast. In Python they are worse than untyped: ToolDetail.from_dict builds the dataclass field by field, so the five fields are dropped and never reach the object at all. On an older SDK, call the HTTP endpoint directly, as the curl examples below do.


POST /api/v1/tools​

Creates a new tool shell (no versions/aliases yet — commit a version separately). name must match ^[a-zA-Z0-9_-]{1,64}$ (kept LLM-function-name-safe for OpenAI/Anthropic/Gemini tool-calling compatibility).

The name must also be free within the team: POST /tools/sync and every tool_ref find a tool by name, so two active tools sharing one would make resolution arbitrary. Soft-deleting a tool releases its name for reuse, and two different teams may each hold the same name.

curl -X POST $ACRUXCORE_BASE_URL/tools \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"docs_get_weather","description":"Fetch current weather for a city."}'

Response (status 201):

{
"id": "d41a0bd2-0a55-4e73-a1e2-f9d7d8d84e58",
"name": "docs_get_weather",
"description": "Fetch current weather for a city.",
"teamId": "8d3ceb9b-39d0-463b-8b91-aa2ef20ac9ba",
"createdBy": "18c76b52-ee0d-4001-be8e-c29976488fbb",
"createdAt": "2026-09-13T21:50:39.977Z",
"callable": false,
"versionCount": 0,
"latestVersionNumber": null,
"executorType": null,
"aliases": []
}

The tool is not callable yet — POST /tools writes the shell only. Commit its first version with POST /tools/:id/versions, which also creates the production and staging aliases.

Response (status 400) — name fails the safe-name pattern:

{"error":{"code":"VALIDATION_ERROR","message":"name must match ^[a-zA-Z0-9_-]{1,64}$"}}

Response (status 409) — an active tool in this team already holds the name. PATCH /tools/:id returns the same error when a rename would collide:

{"error":{"code":"TOOL_NAME_TAKEN","message":"A tool named 'docs_get_weather' already exists in this team."}}

GET /api/v1/tools​

Returns a paginated list of active (non-deleted) tools for the current team. Supports ?search=, ?page=, ?limit=.

curl $ACRUXCORE_BASE_URL/tools \
-H "Authorization: Bearer $ACRUXCORE_API_KEY"

Response (status 200):

{
"data": [
{
"id": "d41a0bd2-0a55-4e73-a1e2-f9d7d8d84e58",
"name": "docs_get_weather",
"description": "Fetch current weather for a city.",
"teamId": "8d3ceb9b-39d0-463b-8b91-aa2ef20ac9ba",
"createdBy": "18c76b52-ee0d-4001-be8e-c29976488fbb",
"createdAt": "2026-09-13T21:50:39.977Z",
"callable": false,
"versionCount": 0,
"latestVersionNumber": null,
"executorType": null,
"aliases": []
}
],
"total": 1,
"page": 1,
"limit": 20
}

GET /api/v1/tools/:id​

Fetches a single active tool by ID.

curl $ACRUXCORE_BASE_URL/tools/d41a0bd2-0a55-4e73-a1e2-f9d7d8d84e58 \
-H "Authorization: Bearer $ACRUXCORE_API_KEY"

Response (status 200):

{
"id": "d41a0bd2-0a55-4e73-a1e2-f9d7d8d84e58",
"name": "docs_get_weather",
"description": "Fetch current weather for a city.",
"teamId": "8d3ceb9b-39d0-463b-8b91-aa2ef20ac9ba",
"createdBy": "18c76b52-ee0d-4001-be8e-c29976488fbb",
"createdAt": "2026-09-13T21:50:39.977Z",
"callable": true,
"versionCount": 1,
"latestVersionNumber": 1,
"executorType": "http",
"aliases": [
{ "alias": "production", "versionNumber": 1 },
{ "alias": "staging", "versionNumber": 1 }
]
}

This is the same tool after one http version was committed: callable is now true, and both aliases point at v1.

Response (status 404) — tool not found, deleted, or belongs to another team:

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

PATCH /api/v1/tools/:id​

Partially updates a tool's name and/or description. At least one field is required.

curl -X PATCH $ACRUXCORE_BASE_URL/tools/d41a0bd2-0a55-4e73-a1e2-f9d7d8d84e58 \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"description":"Fetch current weather for a city (v2)."}'

Response (status 200):

{
"id": "d41a0bd2-0a55-4e73-a1e2-f9d7d8d84e58",
"name": "docs_get_weather",
"description": "Fetch current weather for a city (v2).",
"teamId": "8d3ceb9b-39d0-463b-8b91-aa2ef20ac9ba",
"createdBy": "18c76b52-ee0d-4001-be8e-c29976488fbb",
"createdAt": "2026-09-13T21:50:39.977Z",
"callable": true,
"versionCount": 1,
"latestVersionNumber": 1,
"executorType": "http",
"aliases": [
{ "alias": "production", "versionNumber": 1 },
{ "alias": "staging", "versionNumber": 1 }
]
}

Response (status 400) — neither name nor description provided:

{"error":{"code":"VALIDATION_ERROR","message":"At least one of name or description must be provided."}}

DELETE /api/v1/tools/:id​

Soft-deletes a tool (deleted_at = now()). Version and alias rows are preserved but the tool no longer appears in list/get, and a subsequent GET returns 404.

curl -X DELETE $ACRUXCORE_BASE_URL/tools/d41a0bd2-0a55-4e73-a1e2-f9d7d8d84e58 \
-H "Authorization: Bearer $ACRUXCORE_API_KEY"

Response (status 204) — no body.

Subsequent GET returns 404:

curl $ACRUXCORE_BASE_URL/tools/d41a0bd2-0a55-4e73-a1e2-f9d7d8d84e58 \
-H "Authorization: Bearer $ACRUXCORE_API_KEY"

Response (status 404):

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