# Add a custom tool

- **OpenAPI Version:** `3.2.0`
- **API Version:** `3.1.0`
- **Contact:** Syndai <https://syndai.ai>

## Contents

**Operations**

- [POST /api/v1/external/coding/tools](#scalar-operation-post-apiv1externalcodingtools)

**Schemas**

- [CustomToolCreateRequest](#scalar-schema-customtoolcreaterequest)
- [CustomToolManualClient](#scalar-schema-customtoolmanualclient)
- [CustomToolResponse](#scalar-schema-customtoolresponse)
- [CustomToolRead](#scalar-schema-customtoolread)
- [CustomToolOAuthRead](#scalar-schema-customtooloauthread)
- [ProblemDetail](#scalar-schema-problemdetail)
- [ExternalErrorCode](#scalar-schema-externalerrorcode)

<a id="scalar-context-global-servers"></a>

## Servers

- **URL:** `https://api.syndai.ai`

<a id="scalar-context-global-authentication"></a>

## Authentication

- **CustomerApiKey**: HTTP bearer (`API Key`)

  Customer API key sent as a bearer token

## Tags

### Tools

Custom tools your runs may call: skills and MCP servers, workspace-wide or for one project. Add, update, rescan or delete a tool. Each is safety-scanned, and runs skip a flagged tool until someone acknowledges the flag.

## Operations

<a id="scalar-operation-post-apiv1externalcodingtools"></a>

### Add a custom tool

- **Method:** `POST`
- **Path:** `/api/v1/external/coding/tools`
- **Operation ID:** `createTool`
- **Tags:** Tools

Add a skill or MCP server, workspace-wide or for one project. Its content is safety-scanned: a flagged tool is still created but runs skip it until someone acknowledges the flag in the portal. An MCP server using OAuth stays pending until it is connected in the portal. Requires the `coding:tools:write` scope.

**Servers:** [Inherited servers](#scalar-context-global-servers)

**Authentication:** [Global authentication](#scalar-context-global-authentication)

#### Query parameters

- **`customer_id`**: `string | null`, format: `uuid`

#### Request body

**Required:** `true`

**Content type:** `application/json`

[CustomToolCreateRequest](#scalar-schema-customtoolcreaterequest)

<a id="scalar-example-1"></a>

**Generated example:**

```json
{
  "auth_mode": "oauth",
  "config": {
    "additionalProperty": "anything"
  },
  "description": "",
  "kind": "mcp_server",
  "manual_client": {
    "client_id": "",
    "client_secret": ""
  },
  "name": "",
  "repository_id": ""
}
```

#### Responses

##### 201 Document created, URL follows

**Content type:** `application/json`

[CustomToolResponse](#scalar-schema-customtoolresponse)

<a id="scalar-example-2"></a>

**Generated example:**

```json
{
  "tool": {
    "auth_mode": "token",
    "auth_status": "pending",
    "config": {
      "additionalProperty": "anything"
    },
    "description": "",
    "description_hint": "",
    "enabled": true,
    "has_token": true,
    "id": "",
    "kind": "mcp_server",
    "name": "",
    "not_ready_reason": "",
    "oauth": {
      "connected_at": "",
      "connected_by_left": true,
      "connected_by_name": "",
      "issuer_host": "",
      "redirect_uri": "",
      "requested_scopes": [
        ""
      ],
      "scopes": [
        ""
      ]
    },
    "repository_id": "",
    "run_ready": true,
    "scan_reason": "",
    "scan_status": "scanned",
    "scanned_at": ""
  }
}
```

##### 400, 401, 403, 404

- `400` Bad request syntax or unsupported method
- `401` Invalid or missing customer API key
- `403` Insufficient scope or forbidden customer access
- `404` Resource not found

**Content type:** `application/problem+json`

[ProblemDetail](#scalar-schema-problemdetail)

RFC 7807 problem detail returned for public CaaS errors.

<a id="scalar-example-3"></a>

**Generated example:**

```json
{
  "code": "BUDGET_EXHAUSTED",
  "detail": null,
  "existing_run_id": null,
  "instance": null,
  "status": 1,
  "title": "",
  "type": "about:blank",
  "additionalProperty": "anything"
}
```

##### 429 Rate limit exceeded

**Headers:**

- **`Retry-After`**

  Seconds to wait before retrying the request

  `integer`

**Content type:** `application/problem+json`

[ProblemDetail](#scalar-schema-problemdetail)

RFC 7807 problem detail returned for public CaaS errors.

[Generated example](#scalar-example-3)

##### default Unexpected error

**Content type:** `application/problem+json`

[ProblemDetail](#scalar-schema-problemdetail)

<a id="scalar-example-4"></a>

**Generated example:**

```json
null
```

## Schemas

<a id="scalar-schema-customtoolcreaterequest"></a>

### CustomToolCreateRequest

**Type:** `object`

- **`config` (required)**: `object`
- **`description` (required)**: `string`
- **`kind` (required)**: `string`, possible values: `"mcp_server", "skill"`
- **`name` (required)**: `string`
- **`auth_mode`**: `null | string`, possible values: `"oauth", "token", "none", null`
- **`manual_client`**: [CustomToolManualClient](#scalar-schema-customtoolmanualclient) | `null`
- **`repository_id`**: `string | null`, format: `uuid`

<a id="scalar-example-5"></a>

**Generated example:**

```json
{
  "auth_mode": "oauth",
  "config": {
    "additionalProperty": "anything"
  },
  "description": "",
  "kind": "mcp_server",
  "manual_client": {
    "client_id": "",
    "client_secret": ""
  },
  "name": "",
  "repository_id": ""
}
```

<a id="scalar-schema-customtoolmanualclient"></a>

### CustomToolManualClient

**Type:** `object`

- **`client_id` (required)**: `string`
- **`client_secret`**: `string | null`, format: `password`

<a id="scalar-example-6"></a>

**Generated example:**

```json
{
  "client_id": "",
  "client_secret": ""
}
```

<a id="scalar-schema-customtoolresponse"></a>

### CustomToolResponse

**Type:** `object`

- **`tool` (required)**: [CustomToolRead](#scalar-schema-customtoolread)

<a id="scalar-example-7"></a>

**Generated example:**

```json
{
  "tool": {
    "auth_mode": "token",
    "auth_status": "pending",
    "config": {
      "additionalProperty": "anything"
    },
    "description": "",
    "description_hint": "",
    "enabled": true,
    "has_token": true,
    "id": "",
    "kind": "mcp_server",
    "name": "",
    "not_ready_reason": "",
    "oauth": {
      "connected_at": "",
      "connected_by_left": true,
      "connected_by_name": "",
      "issuer_host": "",
      "redirect_uri": "",
      "requested_scopes": [
        ""
      ],
      "scopes": [
        ""
      ]
    },
    "repository_id": "",
    "run_ready": true,
    "scan_reason": "",
    "scan_status": "scanned",
    "scanned_at": ""
  }
}
```

<a id="scalar-schema-customtoolread"></a>

### CustomToolRead

**Type:** `object`

- **`config` (required)**: `object`
- **`description` (required)**: `string`
- **`enabled` (required)**: `boolean`
- **`has_token` (required)**: `boolean`
- **`id` (required)**: `string`
- **`kind` (required)**: `string`, possible values: `"mcp_server", "skill"`
- **`name` (required)**: `string`
- **`run_ready` (required)**: `boolean`
- **`scan_status` (required)**: `string`, possible values: `"scanned", "unscanned", "flagged", "acknowledged"`
- **`auth_mode`**: `string`, possible values: `"oauth", "token", "none"`, default: `"token"`
- **`auth_status`**: `null | string`, possible values: `"pending", "connected", "reconnect", "more_access", null`
- **`description_hint`**: `string | null`
- **`not_ready_reason`**: `string | null`
- **`oauth`**: [CustomToolOAuthRead](#scalar-schema-customtooloauthread) | `null`
- **`repository_id`**: `string | null`
- **`scan_reason`**: `string | null`
- **`scanned_at`**: `string | null`

<a id="scalar-example-8"></a>

**Generated example:**

```json
{
  "auth_mode": "token",
  "auth_status": "pending",
  "config": {
    "additionalProperty": "anything"
  },
  "description": "",
  "description_hint": "",
  "enabled": true,
  "has_token": true,
  "id": "",
  "kind": "mcp_server",
  "name": "",
  "not_ready_reason": "",
  "oauth": {
    "connected_at": "",
    "connected_by_left": true,
    "connected_by_name": "",
    "issuer_host": "",
    "redirect_uri": "",
    "requested_scopes": [
      ""
    ],
    "scopes": [
      ""
    ]
  },
  "repository_id": "",
  "run_ready": true,
  "scan_reason": "",
  "scan_status": "scanned",
  "scanned_at": ""
}
```

<a id="scalar-schema-customtooloauthread"></a>

### CustomToolOAuthRead

**Type:** `object`

- **`connected_by_left` (required)**: `boolean`
- **`requested_scopes` (required)**: `array of string`
- **`scopes` (required)**: `array of string`
- **`connected_at`**: `string | null`
- **`connected_by_name`**: `string | null`
- **`issuer_host`**: `string | null`
- **`redirect_uri`**: `string | null`

<a id="scalar-example-9"></a>

**Generated example:**

```json
{
  "connected_at": "",
  "connected_by_left": true,
  "connected_by_name": "",
  "issuer_host": "",
  "redirect_uri": "",
  "requested_scopes": [
    ""
  ],
  "scopes": [
    ""
  ]
}
```

<a id="scalar-schema-problemdetail"></a>

### ProblemDetail

**Type:** `object`

RFC 7807 problem detail returned for public CaaS errors.

- **`code` (required)**: `string`, possible values: `"BUDGET_EXHAUSTED", "QUOTA_EXCEEDED", "RUN_CONCURRENCY_LIMIT", "CUSTOMER_DISABLED", "REPO_NOT_BOUND", "IDEMPOTENCY_REPLAY", "CONFLICT", "MISSION_BUSY_RETRY", "HITL_REQUEST_CONFLICT", "HITL_REQUEST_SETTLED", "RUN_TERMINATED_BEFORE_RESPONSE", "INVALID_POLICY", "INVALID_SCOPE", "RATE_LIMITED", "INSTALLATION_INCOMPLETE", "SUBSCRIPTION_USAGE_UNAVAILABLE", "INVALID_TOKEN", "ROLE_REQUIRED", "PLAN_UPGRADE_REQUIRED", "EMAIL_MISMATCH", "EXPIRED_TOKEN", "NOT_FOUND", "OVERSCOPE_ACK_REQUIRED", "VERIFY_FAILED", "PUBLISH_IN_FLIGHT", "PUBLISH_UNSUPPORTED_REPO", "PUBLICATION_NOT_LIVE", "NO_APP_DATA", "FEATURE_REQUESTS_NEEDS_MEMBERS", "EMPTY_UPDATE", "BAD_CURSOR", "HUMAN_APPROVAL_REQUIRED", "PROTECTED_PATHS_MANAGED_ONLY", "RUN_IN_FLIGHT", "TEXT_EDIT_REFUSED", "CLI_UPGRADE_REQUIRED", "PRODUCTION_STEP_UP_REQUIRED", "PRICES_UPDATING", "GITHUB_UNAVAILABLE", "PUBLISH_NOT_SET_UP", "VERSION_NOT_DEPLOYED", "INTERNAL_ERROR"`

  Stable error codes surfaced to public CaaS clients.
- **`status` (required)**: `integer`
- **`title` (required)**: `string`
- **`detail`**: `string | null`, default: `null`
- **`existing_run_id`**: `string | null`, default: `null`
- **`instance`**: `string | null`, default: `null`
- **`type`**: `string`, default: `"about:blank"`

<a id="scalar-example-10"></a>

**Generated example:**

```json
{
  "code": "BUDGET_EXHAUSTED",
  "detail": null,
  "existing_run_id": null,
  "instance": null,
  "status": 1,
  "title": "",
  "type": "about:blank",
  "additionalProperty": "anything"
}
```

<a id="scalar-schema-externalerrorcode"></a>

### ExternalErrorCode

**Type:** `string`, possible values: `"BUDGET_EXHAUSTED", "QUOTA_EXCEEDED", "RUN_CONCURRENCY_LIMIT", "CUSTOMER_DISABLED", "REPO_NOT_BOUND", "IDEMPOTENCY_REPLAY", "CONFLICT", "MISSION_BUSY_RETRY", "HITL_REQUEST_CONFLICT", "HITL_REQUEST_SETTLED", "RUN_TERMINATED_BEFORE_RESPONSE", "INVALID_POLICY", "INVALID_SCOPE", "RATE_LIMITED", "INSTALLATION_INCOMPLETE", "SUBSCRIPTION_USAGE_UNAVAILABLE", "INVALID_TOKEN", "ROLE_REQUIRED", "PLAN_UPGRADE_REQUIRED", "EMAIL_MISMATCH", "EXPIRED_TOKEN", "NOT_FOUND", "OVERSCOPE_ACK_REQUIRED", "VERIFY_FAILED", "PUBLISH_IN_FLIGHT", "PUBLISH_UNSUPPORTED_REPO", "PUBLICATION_NOT_LIVE", "NO_APP_DATA", "FEATURE_REQUESTS_NEEDS_MEMBERS", "EMPTY_UPDATE", "BAD_CURSOR", "HUMAN_APPROVAL_REQUIRED", "PROTECTED_PATHS_MANAGED_ONLY", "RUN_IN_FLIGHT", "TEXT_EDIT_REFUSED", "CLI_UPGRADE_REQUIRED", "PRODUCTION_STEP_UP_REQUIRED", "PRICES_UPDATING", "GITHUB_UNAVAILABLE", "PUBLISH_NOT_SET_UP", "VERSION_NOT_DEPLOYED", "INTERNAL_ERROR"`

Stable error codes surfaced to public CaaS clients.
