> ## 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.

# Strumenti funzione

> Consenti ai tuoi agenti AI di chiamare API esterne durante le conversazioni

Gli strumenti funzione consentono ai tuoi agenti vocali AI di invocare API esterne durante le telefonate. Usali per cercare dati dei clienti, verificare la disponibilità, fissare 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 agli 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

<Note>
  Gli strumenti funzione sono il percorso per usare le tue API. ThunderPhone offre anche
  strumenti gestiti dalla piattaforma che non richiedono endpoint:
  [connessioni app](/it/guides/connect-apps) (HubSpot, Salesforce, Slack,
  Google Calendar, Google Sheets, Cal.com),
  [connessioni API](/it/guides/api-connections) e
  [server MCP](/it/guides/mcp-servers).
</Note>

***

## Schema dello strumento

Ogni strumento segue questa struttura:

```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"
    }
  }
}
```

### Definizione della funzione

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

### Configurazione dell'endpoint

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

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

***

## Due modalità 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                        | **Argomenti dello strumento senza wrapper**                                | Involucro `telephony.tool` / `web.tool`                                                               |
| Intestazioni                 | `endpoint.headers` + `X-ThunderPhone-Call-ID` + `X-ThunderPhone-Signature` | `Content-Type` + `X-ThunderPhone-Signature`                                                           |
| Chiave di firma              | Segreto del webhook dell'organizzazione                                    | Segreto del webhook dell'organizzazione                                                               |

Entrambe le modalità sono **bloccanti**: l'AI attende il risultato a metà
frase, con un timeout di **20 s**. Mantieni rapidi gli handler. Puoi usarle
insieme: in una chiamata la cui organizzazione ha un URL webhook, gli strumenti con un
`endpoint` vengono chiamati direttamente e gli altri usano il webhook come fallback.

## Chiamate dirette agli endpoint

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

### Intestazioni della richiesta

```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
```

Le intestazioni personalizzate di `endpoint.headers` sono sempre incluse
testualmente, insieme a due intestazioni nello spazio dei nomi ThunderPhone:

* `X-ThunderPhone-Signature` — HMAC-SHA256 dei byte esatti del corpo della
  richiesta, con chiave il tuo **segreto webhook dell'organizzazione**
* `X-ThunderPhone-Call-ID` — L'ID della chiamata corrente

`Content-Type: application/json` viene impostato a meno che `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 da
  [`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 degli 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>

### Corpo della richiesta

Per `POST` / `PUT` / `PATCH`, il corpo contiene **solo** gli argomenti dello
strumento (senza wrapper), serializzati in modo canonico (chiavi ordinate,
separatori compatti):

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

Per `GET` / `DELETE`, gli argomenti vengono inviati come **parametri di query**
e il corpo è vuoto — la firma viene quindi calcolata sulla stringa di byte
vuota. Vedi
[Verifica le firme webhook](/it/guides/verify-webhook-signatures).

### Risposta

Restituisci una risposta JSON con il risultato dello strumento:

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

La risposta viene formattata e fornita all'AI per proseguire 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) inviate agli endpoint webhook dopo
l'esecuzione, questa richiesta **è** l'esecuzione — la tua risposta HTTP è il
risultato dello strumento.

```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` 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 sul corpo non elaborato,
come ogni altro webhook.

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

***

## Verifica della firma

Le chiamate dirette agli strumenti sono firmate nello stesso modo dei webhook:

* HMAC-SHA256 sugli byte esatti del corpo della richiesta (il JSON canonico —
  chiavi ordinate, nessuno spazio aggiuntivo)
* Con la chiave segreta webhook della tua organizzazione
* Gli strumenti `GET` / `DELETE` firmano la stringa di byte vuota

<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>

Le procedure complete — incluso il caso del corpo vuoto e l'avvertenza sull'assenza di un segreto —
sono disponibili in [Verifica le firme webhook](/it/guides/verify-webhook-signatures).

***

## Esempio: flusso di prenotazione completo

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

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

***

## Best practice

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

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

  <Accordion title="Mantieni le risposte concise">
    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="Connessioni 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 helper di verifica per webhook e chiamate degli strumenti.
  </Card>
</CardGroup>
