---
title: "Strumenti di funzione"
description: "Fornisci ai tuoi agenti AI strumenti di funzione che chiamano API esterne durante la conversazione — recuperano dati dei clienti, fissano appuntamenti, aggiornano record — con parametri tipizzati."
---

Gli strumenti funzione consentono ai tuoi agenti AI di invocare API esterne durante le chiamate telefoniche. Usali per cercare dati dei clienti, verificare la disponibilità, prenotare appuntamenti o eseguire qualsiasi azione supportata dal tuo backend.

## Come funziona

1. Definisci gli strumenti con uno schema (quali argomenti accetta lo strumento)
2. Fornisci una configurazione `endpoint` (dove ThunderPhone chiama la tua API) oppure omettila per ricevere le chiamate degli strumenti sul webhook della tua organizzazione
3. Durante una chiamata, l'AI decide quando usare uno strumento in base alla conversazione
4. ThunderPhone chiama il tuo endpoint con gli argomenti dello strumento
5. La risposta della tua API viene restituita all'AI per continuare la conversazione

| Capacità | Dove viene eseguito | Configurazione |
| --- | --- | --- |
| [Strumenti integrati](/it/guides/built-in-tools) | ThunderPhone | Istruzioni nel prompt; alcuni strumenti richiedono anche un'impostazione dell'agente |
| [Connessioni app](/it/guides/connect-apps) | ThunderPhone e il provider connesso | Connetti l'account e associa le azioni approvate |
| [Connessioni API](/it/guides/api-connections) e strumenti funzione | La tua API HTTP | Definisci l'endpoint e lo schema, oppure ricevi le chiamate funzione tramite webhook |
| [Server MCP](/it/guides/mcp-servers) | Un server MCP remoto | Aggiungi il server, rileva i suoi strumenti e associalo all'agente |

---

## Schema dello strumento

Ogni strumento segue questa struttura:

```json
{
  "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"
    }
  },
  "timeout": 120
}
```

### Configurazione dello strumento

| Campo | Tipo | Obbligatorio | Descrizione |
|-------|------|----------|-------------|
| `timeout` | numero | No | Tempo massimo di esecuzione in secondi (predefinito: `20`, massimo: `180`) |

### Definizione della funzione

| Campo | Tipo | Obbligatorio | Descrizione |
|-------|------|----------|-------------|
| `name` | stringa | Sì | Identificatore univoco dello strumento |
| `description` | stringa | Sì | Spiega all'AI quando usare questo strumento |
| `parameters` | oggetto | Sì | Schema JSON per gli argomenti dello strumento |

### Configurazione dell'endpoint

| Campo | Tipo | Obbligatorio | Descrizione |
|-------|------|----------|-------------|
| `url` | stringa | Sì | URL dell'endpoint della tua API |
| `method` | stringa | No | Metodo HTTP (predefinito: `POST`) |
| `headers` | oggetto | No | Intestazioni personalizzate da includere |

<Note>
  La configurazione `endpoint` **non** viene inviata al modello AI: viene usata solo da ThunderPhone per eseguire la chiamata dello strumento.
</Note>

---

## Due percorsi di invocazione

La richiesta ricevuta dal tuo server dipende dal fatto che lo strumento abbia un
`endpoint`:

| | Strumento **con** `endpoint` | Strumento **senza** `endpoint` |
|---|---|---|
| Destinazione della richiesta | Direttamente a `endpoint.url` | [URL webhook legacy](/api-reference/organizations#legacy-single-url-webhook) della tua organizzazione |
| Corpo | Formato della richiesta selezionato (argomenti senza wrapper per impostazione predefinita) | Involucro `telephony.tool` / `web.tool` |
| Intestazioni | `endpoint.headers` + `X-ThunderPhone-Call-ID` + `X-ThunderPhone-Signature-V2` + `X-ThunderPhone-Signature` | `Content-Type` + `X-ThunderPhone-Signature-V2` + `X-ThunderPhone-Signature` |
| Chiave di firma | Segreto webhook dell'organizzazione | Segreto webhook dell'organizzazione |

Entrambi i percorsi sono **bloccanti**: l'AI attende il
risultato a metà frase. Il timeout predefinito è **20 s**; imposta il
`timeout` di primo livello dello strumento per consentire un'esecuzione più lunga, fino al
massimo della piattaforma di **180 s**. Mantieni gli handler rapidi. Puoi combinarli:
in una chiamata la cui organizzazione dispone di un URL webhook, gli strumenti con un `endpoint` vengono
chiamati direttamente e gli altri usano il webhook.

## Chiamate dirette agli endpoint

Quando l'AI richiama uno strumento con un `endpoint`, ThunderPhone invia
una richiesta al tuo URL:

### Intestazioni della richiesta

```http
POST /appointments/search HTTP/1.1
Host: api.example.com
Content-Type: application/json
X-ThunderPhone-Signature-V2: t=1767225600,v1=5f2c...
X-ThunderPhone-Signature: abc123...
X-ThunderPhone-Call-ID: 987654321
X-Api-Key: your-api-key
```

Le intestazioni personalizzate di `endpoint.headers` vengono sempre incluse
alla lettera, insieme a queste intestazioni con namespace ThunderPhone:

- `X-ThunderPhone-Signature-V2` — HMAC-SHA256 con timestamp su
  metodo, percorso, query e corpo, con chiave nel tuo **segreto webhook dell'organizzazione**.
  Verifica questa firma e rifiuta le richieste più vecchie di 5 minuti; consulta
  [Verificare le firme webhook](/it/guides/verify-webhook-signatures)
- `X-ThunderPhone-Signature` — HMAC-SHA256 legacy dei byte esatti del
  corpo della richiesta, stessa chiave, ancora inviata per i verificatori esistenti
- `X-ThunderPhone-Call-ID` — L'ID della chiamata corrente

`Content-Type: application/json` viene impostato a meno che il tuo `endpoint.headers`
non lo sovrascriva — un `Content-Type` personalizzato ha la precedenza.

<Warning>
  La firma usa come chiave il segreto webhook a livello di organizzazione di
  [`GET /v1/webhook`](/api-reference/organizations#legacy-single-url-webhook).
  Se la tua organizzazione non ha mai configurato il webhook legacy, non esiste
  alcun segreto e le chiamate agli strumenti includono **solo**
  `X-ThunderPhone-Call-ID` — un gestore che fallisce rigidamente in assenza di
  firma le rifiuterebbe.
  Configura il webhook legacy per ottenere un segreto oppure inserisci il tuo
  segreto condiviso in `endpoint.headers`.
</Warning>

### Formato della richiesta

Ogni connessione API ha un'impostazione `request_format`. Scegli `vapi` o
`retell` quando uno strumento importato deve continuare a chiamare un endpoint creato per
quella piattaforma. Le connessioni nuove ed esistenti usano `standard` per impostazione predefinita.

Per `POST` / `PUT` / `PATCH`, ThunderPhone serializza in modo canonico
l'involucro selezionato (chiavi ordinate, separatori compatti). Entrambe le firme
coprono questi byte esatti.

#### Standard

`standard` invia solo gli argomenti dello strumento, senza wrapper. Questo è il comportamento
predefinito e preserva il corpo della richiesta originale di ThunderPhone byte per byte:

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

#### Vapi

`vapi` invia il [messaggio server `tool-calls`](https://docs.vapi.ai/tools/custom-tools)
di Vapi nella forma documentata da Vapi. Il `call.id` è l'ID della chiamata ThunderPhone. L'`id`
della chiamata allo strumento è univoco per l'invocazione e viene riutilizzato per associare il
risultato dell'endpoint. La chiamata appare sia in `toolCallList` sia in
`toolWithToolCallList`, con `function.arguments` come oggetto JSON:

```json
{
  "message": {
    "type": "tool-calls",
    "timestamp": 1725000000123,
    "call": { "id": "987654321" },
    "toolCallList": [
      {
        "id": "call_00112233445566778899aabbccddeeff",
        "type": "function",
        "function": {
          "name": "search_appointments",
          "arguments": { "date": "2025-01-02", "service": "consultation" }
        }
      }
    ],
    "toolWithToolCallList": [
      {
        "type": "function",
        "function": { "name": "search_appointments" },
        "toolCall": {
          "id": "call_00112233445566778899aabbccddeeff",
          "type": "function",
          "function": {
            "name": "search_appointments",
            "arguments": { "date": "2025-01-02", "service": "consultation" }
          }
        }
      }
    ]
  }
}
```

I campi esclusivi di Vapi come `assistant`, `artifact` e l'oggetto `call` completo
non vengono inviati.

Restituisci l'involucro di risposta di Vapi con l'ID della chiamata allo strumento corrispondente:

```json
{
  "results": [
    {
      "toolCallId": "call_00112233445566778899aabbccddeeff",
      "result": "{\"available_slots\":[\"9:00 AM\",\"2:00 PM\"]}"
    }
  ]
}
```

ThunderPhone fornisce all'IA la stringa `result` senza involucro. Accetta anche un
valore JSON per la compatibilità con endpoint che ne restituiscono già uno. Un
`error` al posto di `result` viene restituito all'IA come errore dello strumento. Se l'
endpoint restituisce un involucro Vapi non valido o non corrispondente, ThunderPhone registra
un avviso e passa all'analisi standard della risposta invece di interrompere
la chiamata.

#### Retell

`retell` invia la richiesta documentata per una [funzione personalizzata
Retell](https://docs.retellai.com/build/single-multi-prompt/custom-function):

```json
{
  "name": "search_appointments",
  "call": { "call_id": "987654321" },
  "args": { "date": "2025-01-02", "service": "consultation" }
}
```

L'oggetto `call` include solo `call_id`. ThunderPhone non include
campi specifici di Retell che non può fornire correttamente al momento dell'invio, come
`agent_id`, `transcript`, `transcript_object`, `metadata` o variabili
dinamiche. Retell accetta qualsiasi corpo della risposta; ThunderPhone lo inoltra
utilizzando la gestione standard della risposta.

Per `GET` / `DELETE`, gli argomenti vengono inviati come **parametri di query**
e il corpo è vuoto indipendentemente da `request_format`. La firma V2
copre la stringa di query così come inviata; la firma legacy viene calcolata sulla
stringa di byte vuota. Consulta
[Verifica le firme dei webhook](/it/guides/verify-webhook-signatures).

### Risposta

Restituisci una risposta JSON con il risultato dello strumento:

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

La risposta viene formattata e fornita all'AI per continuare la
conversazione. Le risposte non JSON vengono racchiuse in `{"data": "<text>"}`;
i timeout e gli errori di connessione vengono segnalati all'AI come errori,
così l'agente può scusarsi e proseguire anziché bloccarsi.

## Instradamento in modalità webhook

Gli strumenti **senza** un `endpoint` vengono instradati all'URL webhook legacy
della tua organizzazione come richiesta firmata `telephony.tool` (chiamate
telefoniche) o `web.tool` (chiamate web). A differenza delle
[notifiche di audit](/it/webhooks/events) recapitate agli endpoint webhook dopo
l'esecuzione, questa richiesta **è** l'esecuzione — la tua risposta HTTP è il
risultato dello strumento.

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

`web.tool` include `origin_domain` invece di `from_number` /
`to_number`. Rispondi con il risultato dello strumento in formato JSON — lo stesso contratto di risposta delle chiamate dirette agli endpoint. La richiesta è firmata con il segreto webhook dell'organizzazione, con entrambe le intestazioni della firma, come qualsiasi altro webhook.

<Note>
  Gli [endpoint webhook](/it/webhooks/endpoints) sottoscritti ricevono inoltre una
  **notifica non bloccante dopo** l'esecuzione di ogni strumento `telephony.tool` /
  `web.tool` (indipendentemente dal percorso che lo ha eseguito), inclusa la
  risposta dello strumento — utile per le tracce di audit. Vedi il
  [catalogo degli eventi](/it/webhooks/events).
</Note>

---

## Verifica della firma

Le chiamate dirette agli strumenti sono firmate come i webhook, usando il segreto webhook della tua organizzazione:

- `X-ThunderPhone-Signature-V2` firma il timestamp, il metodo, il percorso e la
  query così come inviati, nonché i byte esatti del corpo. Rifiuta le richieste il cui
  timestamp differisce di più di 5 minuti dal tuo orologio.
- `X-ThunderPhone-Signature` (legacy) firma solo i byte del corpo, la
  stringa di byte vuota per `GET` / `DELETE`. Viene ancora inviata, invariata.

Gli helper dell'SDK verificano l'header V2, inclusa la finestra di 5 minuti:

<CodeGroup>
```python Python
import json

from fastapi import FastAPI, HTTPException, Request
from thunderphone import verify_signature

app = FastAPI()

@app.post("/appointments/search")
async def search_appointments(request: Request):
    body = await request.body()
    query = request.scope.get("query_string", b"").decode("latin-1")
    path = request.url.path + (f"?{query}" if query else "")

    if not verify_signature(
        request.headers.get("X-ThunderPhone-Signature-V2"),
        secret=WEBHOOK_SECRET,
        method=request.method,
        path=path,
        body=body,
    ):
        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
import express from "express";
import { verifySignature } from "@thunderphone/sdk";

app.post('/appointments/search', express.raw({type: 'application/json'}), (req, res) => {
  const ok = verifySignature({
    header: req.header('X-ThunderPhone-Signature-V2'),
    secret: WEBHOOK_SECRET,
    method: req.method,
    path: req.originalUrl,
    body: req.body,
  });
  if (!ok) 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>

Le procedure complete — la stringa firmata, un esempio pratico, il codice senza
SDK, il caso `GET` / `DELETE` e l'avvertenza in assenza di segreto — sono disponibili in [Verificare le firme dei webhook](/it/guides/verify-webhook-signatures).

---

## Esempio: flusso di prenotazione completo

Ecco un insieme di strumenti per un sistema completo di prenotazione appuntamenti:

```json
{
  "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" }
      }
    }
  ]
}
```

---

## Best practice

<AccordionGroup>
  <Accordion title="Scrivi descrizioni chiare">
    Il campo `description` aiuta l'IA a capire **quando** usare lo strumento. Specifica con precisione cosa fa e quando è appropriato utilizzarlo.
  </Accordion>

  <Accordion title="Gestisci gli errori in modo efficace">
    Restituisci messaggi di errore comprensibili per l'IA: `{"error": "No slots available for that date"}` anziché errori 500 generici.
  </Accordion>

  <Accordion title="Mantieni concise le risposte">
    Restituisci solo ciò di cui l'IA ha bisogno per continuare la conversazione. Payload di grandi dimensioni rallentano i tempi di risposta.
  </Accordion>

  <Accordion title="Usa con criterio i campi obbligatori">
    Contrassegna i campi come `required` solo quando è davvero necessario. L'IA chiederà all'utente le informazioni obbligatorie prima di chiamare lo strumento.
  </Accordion>
</AccordionGroup>

---

## Correlati

<CardGroup cols={2}>
  <Card title="Strumenti integrati" icon="wrench" href="/it/guides/built-in-tools">
    Attiva azioni di chiamata gestite dalla piattaforma senza definire un endpoint.
  </Card>
  <Card title="Connessioni alle app" icon="plug" href="/it/guides/connect-apps">
    Strumenti gestiti dalla piattaforma per HubSpot, Salesforce, Slack, Google
    Calendar, Google Sheets e Cal.com — nessun endpoint richiesto.
  </Card>
  <Card title="Server MCP" icon="server" href="/it/guides/mcp-servers">
    Collega un server MCP e consenti all'agente di chiamare i suoi strumenti.
  </Card>
  <Card title="Connessioni API" icon="code" href="/it/guides/api-connections">
    Integrazioni REST riutilizzabili che puoi collegare agli agenti.
  </Card>
  <Card title="Verifica le firme dei webhook" icon="shield-check" href="/it/guides/verify-webhook-signatures">
    Un unico strumento di verifica per webhook e chiamate degli strumenti.
  </Card>
</CardGroup>
