---
title: "Create custom tool"
method: POST
path: "/v1/custom-tools"
tags: ["custom_tools"]
---

# Create custom tool

`POST /v1/custom-tools`

Creates a custom code tool. Requires a token with write access. See GET /v1/custom-tools/runtimes for the code contract.

## Request body

- object — Request body for creating a custom code tool. Requires a token with write access.
  - `workspace_id` string, required
  - `name` string, required — Unique tool name within the workspace. Lowercase kebab-case (e.g. `lookup-order-status`), at most 64 characters. This is how the agent references the tool.
  - `description` string
  - `language` 'python' | 'javascript', required — Runtime the tool's code is executed in. Determines the entrypoint signature and which dependencies are available (see GET /v1/custom-tools/runtimes).
  - `code` string, required — The tool's source code. Must define a single entrypoint named `main`. Python: `def main(**params):` — parameters arrive as keyword args. Return a dict containing a string `message` key, optionally a `rawData` key with structured data. JavaScript: `async function main(params) { ... }` — parameters arrive as a single object. Return an object containing a string `message` key, optionally a `rawData` key. Writes statically validate that the code defines `main` and includes a return statement; runtime execution validates the returned object. Standard library modules are always importable. A curated set of third-party packages installs automatically on first import. Query GET /v1/custom-tools/runtimes for the exact signature, return contract, and available dependencies per language.
  - `parameters` object — Parameters the tool accepts, keyed by parameter name. Each becomes a key in the `params` object/dict passed to `main`.
  - `execution_message_description` string — Short status line shown to the user while the tool runs, e.g. `Looking up order status`.
  - `enable_globally` boolean — When true, the tool is available to every agent in the workspace without per-agent enablement.

## Response `201`

Custom tool created

- object — Single custom code tool response.
  - `data` object, required
    - `id` string, required
    - `workspace_id` string, required
    - `name` string, required
    - `description` string, required
    - `language` 'python' | 'javascript', required — Runtime the tool's code is executed in. Determines the entrypoint signature and which dependencies are available (see GET /v1/custom-tools/runtimes).
    - `code` string, required
    - `parameters` object, required — Parameters the tool accepts, keyed by parameter name. Each becomes a key in the `params` object/dict passed to `main`.
    - `execution_message_description` string, nullable, required
    - `enable_globally` boolean, required
    - `validated` boolean, required
    - `version` number, required
    - `created_at` string, required

## Other responses

- `400` — Invalid request
- `401` — Unauthorized
- `403` — Forbidden
- `409` — Conflict
- `429` — Rate limit exceeded

---

[API](https://skmtc.net/conduit/apis/conduit-api.md) · [All operations](https://skmtc.net/conduit/apis/conduit-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/conduit/conduit-api/versions/3ad7e17f96ad/schema)
