> ## Documentation Index
> Fetch the complete documentation index at: https://docs.thunderphone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 함수 도구

> 대화 중 AI 에이전트가 외부 API를 호출하도록 설정합니다

함수 도구를 사용하면 AI 에이전트가 통화 중에 외부 API를 호출할 수 있습니다. 이를 사용하여 고객 데이터를 조회하고, 예약 가능 여부를 확인하고, 약속을 예약하거나, 백엔드가 지원하는 모든 작업을 수행할 수 있습니다.

## 작동 방식

1. 스키마(도구가 허용하는 인수)를 사용하여 도구를 정의합니다
2. `endpoint` 구성(ThunderPhone이 API를 호출하는 위치)을 제공합니다. 또는 이를 생략하여 조직 웹훅에서 도구 호출을 수신합니다
3. 통화 중 AI는 대화를 기반으로 도구를 사용할 시점을 결정합니다
4. ThunderPhone은 도구 인수와 함께 엔드포인트를 호출합니다
5. API 응답이 AI에 다시 전달되어 대화를 계속합니다

<Note>
  함수 도구는 자체 API를 사용하는 방식입니다. ThunderPhone은 엔드포인트가 필요 없는 플랫폼 관리형 도구도
  제공합니다:
  [앱 연결](/ko/guides/connect-apps) (HubSpot, Salesforce, Slack,
  Google Calendar, Google Sheets, Cal.com),
  [API 연결](/ko/guides/api-connections), 그리고
  [MCP 서버](/ko/guides/mcp-servers).
</Note>

***

## 도구 스키마

각 도구는 다음 구조를 따릅니다:

```json theme={null}
{
  "type": "function",
  "function": {
    "name": "search_appointments",
    "description": "Find available appointment slots for a given date",
    "parameters": {
      "type": "object",
      "properties": {
        "date": {
          "type": "string",
          "description": "Date in YYYY-MM-DD format"
        },
        "service": {
          "type": "string",
          "description": "Type of service (e.g., 'consultation', 'follow-up')"
        }
      },
      "required": ["date"]
    }
  },
  "endpoint": {
    "url": "https://api.example.com/appointments/search",
    "method": "POST",
    "headers": {
      "X-Api-Key": "your-api-key"
    }
  }
}
```

### 함수 정의

| 필드            | 유형     | 필수 여부 | 설명                          |
| ------------- | ------ | ----- | --------------------------- |
| `name`        | string | 예     | 도구의 고유 식별자                  |
| `description` | string | 예     | 이 도구를 사용해야 하는 시점을 AI에 설명합니다 |
| `parameters`  | object | 예     | 도구 인수의 JSON 스키마             |

### 엔드포인트 구성

| 필드        | 유형     | 필수 여부 | 설명                    |
| --------- | ------ | ----- | --------------------- |
| `url`     | string | 예     | API 엔드포인트 URL         |
| `method`  | string | 아니요   | HTTP 메서드(기본값: `POST`) |
| `headers` | object | 아니요   | 포함할 사용자 지정 헤더         |

<Note>
  `endpoint` 구성은 AI 모델로 전송되지 **않습니다**. ThunderPhone이 도구 호출을 실행하는 데만 사용됩니다.
</Note>

***

## 두 가지 호출 경로

서버가 수신하는 요청은 도구에 `endpoint`가 있는지에 따라 달라집니다:

|          | `endpoint`가 **있는** 도구                                                      | `endpoint`가 **없는** 도구                                                    |
| -------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| 요청 전송 위치 | `endpoint.url`로 직접 전송                                                      | 조직의 [레거시 웹훅 URL](/api-reference/organizations#legacy-single-url-webhook) |
| 본문       | **도구 인수만 포함**                                                              | `telephony.tool` / `web.tool` 래퍼                                         |
| 헤더       | `endpoint.headers` + `X-ThunderPhone-Call-ID` + `X-ThunderPhone-Signature` | `Content-Type` + `X-ThunderPhone-Signature`                              |
| 서명 키     | 조직 웹훅 시크릿                                                                  | 조직 웹훅 시크릿                                                                |

두 경로 모두 **차단 방식**입니다. AI는 문장 중간에 결과를 기다리며,
**20초** 제한 시간이 적용됩니다. 핸들러를 빠르게 유지하십시오. 혼합하여 사용해도 됩니다:
조직에 웹훅 URL이 있는 통화에서는 `endpoint`가 있는 도구가
직접 호출되고, 나머지는 웹훅으로 대체됩니다.

## 직접 엔드포인트 호출

AI가 `endpoint`가 있는 도구를 호출하면 ThunderPhone이
URL로 요청을 전송합니다.

### 요청 헤더

```http theme={null}
POST /appointments/search HTTP/1.1
Host: api.example.com
Content-Type: application/json
X-ThunderPhone-Signature: abc123...
X-ThunderPhone-Call-ID: 987654321
X-Api-Key: your-api-key
```

`endpoint.headers`의 커스텀 헤더는 항상 원문 그대로 포함되며,
ThunderPhone 네임스페이스 헤더 두 개가 추가됩니다.

* `X-ThunderPhone-Signature` — 정확한 요청 본문 바이트에 대해
  **조직 웹훅 시크릿**을 키로 사용한 HMAC-SHA256
* `X-ThunderPhone-Call-ID` — 현재 통화 ID

`endpoint.headers`에서 재정의하지 않는 한 `Content-Type: application/json`이
설정됩니다. 커스텀 `Content-Type`이 우선합니다.

<Warning>
  서명은 [`GET /v1/webhook`](/api-reference/organizations#legacy-single-url-webhook)의
  조직 수준 웹훅 시크릿을 키로 사용합니다.
  조직에서 레거시 웹훅을 구성한 적이 없다면 시크릿이 없으므로
  도구 호출에는 `X-ThunderPhone-Call-ID`만 포함됩니다. 누락된 서명에서
  즉시 실패하는 핸들러는 이러한 호출을 거부합니다.
  시크릿을 받으려면 레거시 웹훅을 구성하거나, 자체 공유 시크릿을
  `endpoint.headers`에 추가합니다.
</Warning>

### 요청 본문

`POST` / `PUT` / `PATCH`의 경우 본문에는 래퍼 없이 도구 인수만
정규화된 형식(키 정렬, 압축 구분자)으로 직렬화되어 포함됩니다.

```json theme={null}
{"date":"2025-01-02","service":"consultation"}
```

`GET` / `DELETE`의 경우 인수는 **쿼리 매개변수**로 전송되고
본문은 비어 있습니다. 이 경우 서명은 빈 바이트 문자열을 기준으로
계산됩니다. [웹훅 서명 확인](/ko/guides/verify-webhook-signatures)을
참조하세요.

### 응답

도구 결과가 포함된 JSON 응답을 반환합니다.

```json theme={null}
{
  "available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"],
  "timezone": "America/Los_Angeles"
}
```

응답은 형식화되어 AI에 제공되며, AI는 대화를 계속 진행합니다.
JSON이 아닌 응답은 `{"data": "<text>"}`로 래핑됩니다. 시간 초과 및
연결 실패는 AI에 오류로 보고되므로 에이전트가 사과하고 중단하지
않고 다음 단계로 진행할 수 있습니다.

## 웹훅 모드 디스패치

`endpoint`가 **없는** 도구는 조직의 레거시 웹훅 URL로 서명된
`telephony.tool`(전화 통화) 또는 `web.tool`(웹 통화) 요청으로
디스패치됩니다. 실행 후 웹훅 엔드포인트에 전달되는
[감사 알림](/ko/webhooks/events)과 달리, 이 요청 자체가 **실행**입니다.
HTTP 응답이 도구 결과가 됩니다.

```json theme={null}
{
  "type": "telephony.tool",
  "data": {
    "call_id": 987654321,
    "tool_name": "search_appointments",
    "arguments": { "date": "2026-04-21" },
    "from_number": "+14155550199",
    "to_number": "+15551234567"
  }
}
```

`web.tool`은 `from_number` / `to_number` 대신 `origin_domain`을
포함합니다. 직접 엔드포인트 호출과 동일한 응답 계약에 따라 도구 결과를
JSON으로 응답합니다. 요청은 다른 모든 웹훅과 마찬가지로 원시 본문에 대해
조직 웹훅 시크릿으로 서명됩니다.

<Note>
  구독한 [웹훅 엔드포인트](/ko/webhooks/endpoints)는 각 도구 실행 후
  실행된 경로와 관계없이 차단되지 않는 `telephony.tool` / `web.tool`
  **알림도 수신합니다**. 이 알림에는 도구 응답이 포함되어 감사 추적에
  유용합니다. [이벤트 카탈로그](/ko/webhooks/events)를
  참조하세요.
</Note>

***

## 서명 검증

직접 도구 호출은 웹훅과 동일한 방식으로 서명됩니다.

* 정확한 요청 본문 바이트(정규 JSON — 키 정렬, 추가 공백 없음)에 대한 HMAC-SHA256
* 조직의 웹훅 시크릿으로 키 지정
* `GET` / `DELETE` 도구는 빈 바이트 문자열에 서명

<CodeGroup>
  ```python Python theme={null}
  import hmac
  import hashlib

  def verify_tool_call(body: bytes, signature: str, secret: str) -> bool:
      expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
      return hmac.compare_digest(expected, signature)

  @app.post("/appointments/search")
  async def search_appointments(request: Request):
      body = await request.body()
      signature = request.headers.get("X-ThunderPhone-Signature", "")

      if not verify_tool_call(body, signature, WEBHOOK_SECRET):
          raise HTTPException(status_code=401)

      data = json.loads(body)
      date = data["date"]

      # Look up availability
      slots = await get_available_slots(date)

      return {"available_slots": slots}
  ```

  ```javascript Node.js theme={null}
  app.post('/appointments/search', express.raw({type: 'application/json'}), (req, res) => {
    const signature = req.headers['x-thunderphone-signature'] || '';
    const expected = crypto
      .createHmac('sha256', WEBHOOK_SECRET)
      .update(req.body)
      .digest('hex');

    if (!signature ||
        signature.length !== expected.length ||
        !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
      return res.status(401).send('Invalid signature');
    }

    const { date, service } = JSON.parse(req.body);

    // Look up availability
    const slots = getAvailableSlots(date, service);

    res.json({ available_slots: slots });
  });
  ```
</CodeGroup>

빈 본문 사례와 시크릿이 없는 경우의 주의 사항을 포함한 전체 예제는 [웹훅 서명 검증](/ko/guides/verify-webhook-signatures)에서 확인할 수 있습니다.

***

## 예시: 전체 예약 흐름

완전한 예약 시스템을 위한 도구 세트는 다음과 같습니다.

```json theme={null}
{
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "search_appointments",
        "description": "Find available appointment slots",
        "parameters": {
          "type": "object",
          "properties": {
            "date": { "type": "string", "description": "YYYY-MM-DD" },
            "service": { "type": "string" }
          },
          "required": ["date"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/search",
        "method": "POST",
        "headers": { "X-Api-Key": "key" }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "book_appointment",
        "description": "Book an appointment at a specific time",
        "parameters": {
          "type": "object",
          "properties": {
            "date": { "type": "string", "description": "YYYY-MM-DD" },
            "time": { "type": "string", "description": "HH:MM format" },
            "customer_name": { "type": "string" },
            "customer_phone": { "type": "string" }
          },
          "required": ["date", "time", "customer_name"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/book",
        "method": "POST",
        "headers": { "X-Api-Key": "key" }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "cancel_appointment",
        "description": "Cancel an existing appointment",
        "parameters": {
          "type": "object",
          "properties": {
            "confirmation_number": { "type": "string" }
          },
          "required": ["confirmation_number"]
        }
      },
      "endpoint": {
        "url": "https://api.example.com/appointments/cancel",
        "method": "POST",
        "headers": { "X-Api-Key": "key" }
      }
    }
  ]
}
```

***

## 모범 사례

<AccordionGroup>
  <Accordion title="명확한 설명 작성">
    `description` 필드는 AI가 도구를 **언제** 사용해야 하는지 이해하도록 돕습니다. 도구의 기능과 적절한 사용 시점을 구체적으로 설명합니다.
  </Accordion>

  <Accordion title="오류를 자연스럽게 처리">
    일반적인 500 오류 대신 AI가 이해할 수 있는 오류 메시지(예: `{"error": "No slots available for that date"}`)를 반환합니다.
  </Accordion>

  <Accordion title="응답을 간결하게 유지">
    AI가 대화를 계속하는 데 필요한 정보만 반환합니다. 큰 페이로드는 응답 시간을 늦춥니다.
  </Accordion>

  <Accordion title="필수 필드를 신중하게 사용">
    반드시 필요한 경우에만 필드를 `required`로 표시합니다. AI는 도구를 호출하기 전에 사용자에게 필수 정보를 요청합니다.
  </Accordion>
</AccordionGroup>

***

## 관련 문서

<CardGroup cols={2}>
  <Card title="앱 연결" icon="plug" href="/ko/guides/connect-apps">
    HubSpot, Salesforce, Slack, Google
    Calendar, Google Sheets, Cal.com을 위한 플랫폼 관리형 도구입니다. 엔드포인트가 필요하지 않습니다.
  </Card>

  <Card title="MCP 서버" icon="server" href="/ko/guides/mcp-servers">
    MCP 서버를 연결하고 에이전트가 해당 도구를 호출하도록 합니다.
  </Card>

  <Card title="API 연결" icon="code" href="/ko/guides/api-connections">
    에이전트에 연결할 수 있는 재사용 가능한 REST 통합입니다.
  </Card>

  <Card title="웹훅 서명 검증" icon="shield-check" href="/ko/guides/verify-webhook-signatures">
    웹훅 및 도구 호출을 위한 단일 검증 도우미입니다.
  </Card>
</CardGroup>
