openapi: 3.0.3
info:
  title: naturali.ai — Tools API
  version: 1.0.0
  description: >
    Tools, scoped to a project (API.md §5 — Agents, Tools). A tool is an external
    capability an agent can call: an HTTP endpoint (`http`) or an MCP server
    (`mcp`). Each is a thin surface onto a runtime tool. What the tool *is* lives
    on the runtime (the source of truth) and is read from there when shaping
    responses — naturali stores nothing of its own about it — except auth
    headers, which are write-only and never returned (only `has_headers` is,
    the same way a provider's credentials are handled). Attach tools to an agent
    with the agent's `tool_bindings` (§5).
  contact:
    name: naturali.ai
    url: https://naturali.ai
servers:
  - url: '{baseUrl}'
    description: Host of your naturali.ai deployment; every path carries the /v1 prefix.
    variables:
      baseUrl:
        description: Base host URL.
        default: https://api.naturali.ai
tags:
  - name: Tools
    description: Create and manage a project's tools (http / mcp).
security:
  - bearerAuth: []
paths:
  /v1/projects/{project_id}/tools:
    parameters:
      - $ref: '#/components/parameters/ProjectId'
    get:
      tags: [Tools]
      summary: List tools
      description: Lists the tools registered in the project.
      operationId: listTools
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: A page of tools.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ToolList'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    post:
      tags: [Tools]
      summary: Create a tool
      description: >
        Create an http or mcp tool in the project. See ToolCreate for the fields
        each type takes. Any auth headers are stored write-only and never
        returned.
      operationId: createTool
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ToolCreate'
      responses:
        '201':
          description: Tool created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Tool'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '502':
          $ref: '#/components/responses/UpstreamUnavailable'
  /v1/projects/{project_id}/tools/{tool_id}:
    parameters:
      - $ref: '#/components/parameters/ProjectId'
      - $ref: '#/components/parameters/ToolId'
    get:
      tags: [Tools]
      summary: Get a tool
      operationId: getTool
      responses:
        '200':
          description: Tool details.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Tool'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags: [Tools]
      summary: Update a tool
      description: >
        Change the name, description, parameters, or type-specific config
        (incl. rotating auth headers). The tool `type` is immutable. At least
        one field is required.
      operationId: updateTool
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ToolUpdate'
      responses:
        '200':
          description: Tool updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Tool'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '502':
          $ref: '#/components/responses/UpstreamUnavailable'
    delete:
      tags: [Tools]
      summary: Delete a tool
      description: >
        Deletes the backing runtime tool. Returns 409 if the tool is still attached
        to an agent (detach it first).
      operationId: deleteTool
      responses:
        '204':
          description: Tool deleted.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '502':
          $ref: '#/components/responses/UpstreamUnavailable'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: A naturali API key (nat_sk_…) or a session JWT.
  parameters:
    Limit:
      name: limit
      in: query
      required: false
      description: Maximum items per page — an integer from 1 to 100 (default 20).
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
    Cursor:
      name: cursor
      in: query
      required: false
      description: Opaque pagination cursor from a previous response's next_cursor.
      schema:
        type: string
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: Client-supplied key to make this mutating POST idempotent.
      schema:
        type: string
    ProjectId:
      name: project_id
      in: path
      required: true
      description: Project public ID (proj_ prefix).
      schema:
        type: string
        example: proj_V1StGXR8Z5jdHi6B
    ToolId:
      name: tool_id
      in: path
      required: true
      description: Tool public ID (tool_ prefix).
      schema:
        type: string
        example: tool_V1StGXR8Z5jdHi6B
  responses:
    Unauthorized:
      description: Missing or invalid credentials.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    BadRequest:
      description: The request was malformed or failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: The resource does not exist (existence is not leaked).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Conflict:
      description: The request conflicts with the resource's current state.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    UpstreamUnavailable:
      description: The upstream runtime could not complete the operation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  schemas:
    HttpExecute:
      type: object
      description: >
        Execution config for http tools. `url` is required and may carry
        `{param}` placeholders filled from the tool arguments at call time.
      required: [url]
      properties:
        url:
          type: string
          example: https://api.example.com/weather
        method:
          type: string
          description: Defaults to POST.
          example: GET
        body_mode:
          type: string
          enum: [json, multipart]
          description: Request body encoding. Defaults to json.
          example: json
        headers:
          type: object
          additionalProperties:
            type: string
          description: >
            Write-only. Auth headers sent to the target (e.g. Authorization).
            Accepted on write, never returned — reads report only `has_headers`.


            A value may carry a `{{context:<key>}}` reference, resolved per call
            from the caller's `tool_context` (see the `tool_context` field on
            starting an orchestration run). That is how a per-user credential
            lands in the header the target already expects — `Authorization:
            "Bearer {{context:ocaToken}}"` — instead of only in the prefixed
            `X-Naturali-Context-<key>` header. A reference is legal in `headers`
            and nowhere else, `url` included: a context value comes from the
            caller, so it must not be able to steer the outbound request. A key
            missing from the `tool_context` at call time fails the tool call
            rather than sending an empty credential.
          example:
            Authorization: Bearer {{context:ocaToken}}
    McpConfig:
      type: object
      description: MCP server config.
      required: [url]
      properties:
        url:
          type: string
          example: https://mcp.example.com/sse
        headers:
          type: object
          additionalProperties:
            type: string
          description: >
            Write-only. Auth headers sent to the MCP server. Accepted on write,
            never returned — reads report only `has_headers`.


            As with an http tool's headers, a value may carry a
            `{{context:<key>}}` reference — `Authorization: "Bearer
            {{context:ocaToken}}"` — resolved per call from the caller's
            `tool_context`, so an MCP server can read a per-user credential from
            the standard bearer header it already expects.
          example:
            Authorization: Bearer {{context:ocaToken}}
    Tool:
      type: object
      properties:
        id:
          type: string
          description: Public tool ID (tool_ prefix) — the runtime tool id.
          example: tool_V1StGXR8Z5jdHi6B
        project_id:
          type: string
          example: proj_V1StGXR8Z5jdHi6B
        name:
          type: string
          example: get_weather
        description:
          type: string
          nullable: true
          description: What the tool does (sent to the model).
          example: Look up the current weather for a city.
        type:
          type: string
          enum: [http, mcp]
          example: http
        parameters:
          type: object
          nullable: true
          description: JSON Schema for the tool's input.
          additionalProperties: true
          example:
            type: object
            properties:
              city:
                type: string
        execute:
          type: object
          nullable: true
          additionalProperties: true
          description: >
            http tools only — execution config (url, method, body_mode; see
            HttpExecute), minus the write-only headers.
        output_mapping:
          type: object
          nullable: true
          additionalProperties: true
          description: http tools only — JSON Logic mapping applied to the raw result.
        preset_parameters:
          type: object
          nullable: true
          additionalProperties: true
          description: http tools only — fixed parameters merged into every call.
        mcp:
          type: object
          nullable: true
          additionalProperties: true
          description: >
            mcp tools only — server config (url; see McpConfig), minus the
            write-only headers.
        actions:
          type: array
          nullable: true
          items:
            type: string
          description: mcp tools only — allowlist of MCP tool names to expose (null = all).
        denied_actions:
          type: array
          nullable: true
          items:
            type: string
          description: mcp tools only — denylist of MCP tool names to hide.
        context_keys:
          type: array
          nullable: true
          items:
            type: string
          description: >-
            Which `tool_context` keys may reach this tool as
            `X-Naturali-Context-<key>` headers. `null` forwards every key the
            caller supplied; a list forwards only those, and `[]` forwards none.
            Key *names* are returned — the values never are.
          example: [ocaToken]
        has_headers:
          type: boolean
          description: Whether auth headers are on file (their values are never returned).
          example: true
        created_at:
          type: string
          format: date-time
          example: '2026-07-18T00:00:00.000Z'
        updated_at:
          type: string
          format: date-time
          example: '2026-07-18T00:00:00.000Z'
      required:
        - id
        - project_id
        - name
        - description
        - type
        - parameters
        - context_keys
        - has_headers
        - created_at
        - updated_at
    ToolCreate:
      type: object
      description: >
        Two types, selected by `type` (default `http`).

        `http`: supply `execute` (with a `url`), and optionally `parameters`,
        `output_mapping`, `preset_parameters`.

        `mcp`: supply `mcp` (with a `url`), and optionally `actions` /
        `denied_actions` to scope the server surface.

        Auth headers on `execute`/`mcp` are stored write-only and never returned.
      required:
        - name
      properties:
        name:
          type: string
          example: get_weather
        type:
          type: string
          enum: [http, mcp]
          default: http
          example: http
        description:
          type: string
          example: Look up the current weather for a city.
        parameters:
          type: object
          additionalProperties: true
          description: JSON Schema for the tool's input.
          example:
            type: object
            properties:
              city:
                type: string
        execute:
          $ref: '#/components/schemas/HttpExecute'
        output_mapping:
          type: object
          additionalProperties: true
          description: http only — JSON Logic mapping applied to the raw result.
        preset_parameters:
          type: object
          additionalProperties: true
          description: http only — fixed parameters merged into every call.
        mcp:
          $ref: '#/components/schemas/McpConfig'
        actions:
          type: array
          items:
            type: string
          description: mcp only — allowlist of MCP tool names to expose.
        denied_actions:
          type: array
          items:
            type: string
          description: mcp only — denylist of MCP tool names to hide.
        context_keys:
          type: array
          nullable: true
          items:
            type: string
          description: >-
            Allowlist of `tool_context` keys forwarded to this tool as
            `X-Naturali-Context-<key>` headers. Omit (or `null`) to forward every
            key the caller supplied; `[]` forwards none. Set it on the tools that
            need a given credential and the credential stops egressing to the
            rest of the agent's tool set — a key a tool consumes through a
            `{{context:<key>}}` reference in its own `headers` is substituted
            either way, since the tool declared that header itself.
          example: [ocaToken]
    ToolUpdate:
      type: object
      description: At least one field must be present. The tool `type` is immutable.
      minProperties: 1
      properties:
        name:
          type: string
          example: renamed_tool
        description:
          type: string
          example: Updated description.
        parameters:
          type: object
          additionalProperties: true
        execute:
          $ref: '#/components/schemas/HttpExecute'
        output_mapping:
          type: object
          additionalProperties: true
        preset_parameters:
          type: object
          additionalProperties: true
        mcp:
          $ref: '#/components/schemas/McpConfig'
        actions:
          type: array
          items:
            type: string
        denied_actions:
          type: array
          items:
            type: string
        context_keys:
          type: array
          nullable: true
          items:
            type: string
          description: >-
            Replace the allowlist of `tool_context` keys forwarded to this tool.
            `null` clears it (every key is forwarded again) — unlike omitting the
            field, which leaves the current allowlist alone.
          example: [ocaToken]
    ToolList:
      type: object
      required: [data, next_cursor]
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Tool'
        next_cursor:
          type: string
          nullable: true
          description: Cursor for the next page, or null at the end.
          example: null
    ErrorResponse:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message]
          properties:
            code:
              type: string
              example: upstream_unavailable
            message:
              type: string
              example: Could not create the tool on the upstream runtime.
            details:
              type: object
              additionalProperties: true
              description: Optional structured context for the error.
