---
title: "Instrumente de funcții"
description: "Oferiți agenților dumneavoastră AI instrumente de funcții care apelează API-uri externe în timpul conversației — preiau date despre clienți, programează întâlniri, actualizează înregistrări — cu parametri tipizați."
---

Instrumentele de funcție le permit agenților dumneavoastră vocali cu AI să invoce API-uri externe în timpul apelurilor telefonice. Folosiți-le pentru a căuta date despre clienți, a verifica disponibilitatea, a programa întâlniri sau a efectua orice acțiune acceptată de backendul dumneavoastră.

## Cum funcționează

1. Definiți instrumente cu o schemă (ce argumente acceptă instrumentul)
2. Furnizați o configurație `endpoint` (unde ThunderPhone apelează API-ul dumneavoastră) — sau omiteți-o pentru a primi apelurile instrumentelor pe webhookul organizației dumneavoastră
3. În timpul unui apel, AI-ul decide când să utilizeze un instrument pe baza conversației
4. ThunderPhone apelează endpointul dumneavoastră cu argumentele instrumentului
5. Răspunsul API-ului dumneavoastră este transmis înapoi AI-ului pentru a continua conversația

| Capacitate | Unde rulează | Configurare |
| --- | --- | --- |
| [Instrumente integrate](/ro/guides/built-in-tools) | ThunderPhone | Instrucțiuni în prompt; unele instrumente necesită și o setare a agentului |
| [Conexiuni de aplicații](/ro/guides/connect-apps) | ThunderPhone și furnizorul conectat | Conectați contul și atașați acțiunile aprobate |
| [Conexiuni API](/ro/guides/api-connections) și instrumente de funcție | API-ul dumneavoastră HTTP | Definiți endpointul și schema sau primiți apeluri de funcție printr-un webhook |
| [Servere MCP](/ro/guides/mcp-servers) | Un server MCP la distanță | Adăugați serverul, descoperiți instrumentele acestuia și atașați-l la agent |

---

## Schema instrumentului

Fiecare instrument respectă această structură:

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

### Configurarea instrumentului

| Câmp | Tip | Obligatoriu | Descriere |
|-------|------|----------|-------------|
| `timeout` | number | Nu | Durata maximă de execuție în secunde (implicit: `20`, maxim: `180`) |

### Definiția funcției

| Câmp | Tip | Obligatoriu | Descriere |
|-------|------|----------|-------------|
| `name` | string | Da | Identificator unic pentru instrument |
| `description` | string | Da | Explică AI-ului când să utilizeze acest instrument |
| `parameters` | object | Da | Schemă JSON pentru argumentele instrumentului |

### Configurarea endpointului

| Câmp | Tip | Obligatoriu | Descriere |
|-------|------|----------|-------------|
| `url` | string | Da | URL-ul endpointului API-ului dumneavoastră |
| `method` | string | Nu | Metodă HTTP (implicit: `POST`) |
| `headers` | object | Nu | Anteturi personalizate de inclus |

<Note>
  Configurația `endpoint` **nu** este trimisă modelului AI — este utilizată doar de ThunderPhone pentru a executa apelul instrumentului.
</Note>

---

## Două căi de invocare

Solicitarea primită de serverul dumneavoastră depinde de existența unui
`endpoint` pentru instrument:

| | Instrument **cu** `endpoint` | Instrument **fără** `endpoint` |
|---|---|---|
| Unde ajunge solicitarea | Direct la `endpoint.url` | La [adresa URL webhook moștenită](/api-reference/organizations#legacy-single-url-webhook) a organizației dumneavoastră |
| Corp | Formatul de solicitare selectat (argumente simple în mod implicit) | Anvelopa `telephony.tool` / `web.tool` |
| Antete | `endpoint.headers` + `X-ThunderPhone-Call-ID` + `X-ThunderPhone-Signature-V2` + `X-ThunderPhone-Signature` | `Content-Type` + `X-ThunderPhone-Signature-V2` + `X-ThunderPhone-Signature` |
| Cheie de semnare | Secretul webhook al organizației | Secretul webhook al organizației |

Ambele căi sunt **blocante** — AI-ul așteaptă rezultatul în mijlocul
propoziției. Timpul de expirare implicit este de **20 s**; setați
`timeout` la nivelul superior al instrumentului pentru a permite o execuție mai lungă, până la
maximul platformei de **180 s**. Mențineți handlerele rapide. Puteți utiliza o combinație:
într-un apel al cărui organizație are un URL de webhook, instrumentele cu un `endpoint` sunt
apelate direct, iar celelalte revin la webhook.

## Apeluri directe către endpoint

Când AI-ul invocă un instrument care are un `endpoint`, ThunderPhone trimite
o solicitare către URL-ul dumneavoastră:

### Antete de solicitare

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

Antetele personalizate din `endpoint.headers` sunt incluse întotdeauna
literal, împreună cu aceste antete din spațiul de nume ThunderPhone:

- `X-ThunderPhone-Signature-V2` — HMAC-SHA256 cu marcaj temporal pentru
  metodă, cale, interogare și corp, utilizând **secretul webhook al organizației**.
  Verificați-o și respingeți cererile mai vechi de 5 minute; consultați
  [Verificarea semnăturilor webhook](/ro/guides/verify-webhook-signatures)
- `X-ThunderPhone-Signature` — HMAC-SHA256 moștenit al octeților exacți
  ai corpului cererii, cu aceeași cheie, încă trimis pentru validatoarele existente
- `X-ThunderPhone-Call-ID` — ID-ul apelului curent

`Content-Type: application/json` este setat, cu excepția cazului în care
`endpoint.headers` îl suprascrie — un `Content-Type` personalizat are prioritate.

<Warning>
  Semnătura este generată cu secretul webhook la nivel de organizație din
  [`GET /v1/webhook`](/api-reference/organizations#legacy-single-url-webhook).
  Dacă organizația dumneavoastră nu a configurat niciodată webhookul legacy, nu există
  niciun secret, iar apelurile instrumentelor includ **doar**
  `X-ThunderPhone-Call-ID` — un handler care eșuează strict la lipsa unei semnături le-ar
  respinge.
  Configurați webhookul legacy pentru a obține un secret sau introduceți propriul
  secret partajat în `endpoint.headers`.
</Warning>

### Formatul solicitării

Fiecare conexiune API are o setare `request_format`. Alegeți `vapi` sau
`retell` atunci când un instrument importat trebuie să continue să apeleze un endpoint creat pentru
acea platformă. Conexiunile noi și cele existente folosesc implicit `standard`.

Pentru `POST` / `PUT` / `PATCH`, ThunderPhone serializează canonic
anvelopa selectată (chei sortate, separatori compacți). Ambele semnături
acoperă exact acești octeți.

#### Standard

`standard` trimite doar argumentele instrumentului, fără un înveliș. Aceasta este opțiunea
implicită și păstrează corpul original al solicitării ThunderPhone octet cu octet:

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

#### Vapi

`vapi` trimite [mesajul de server `tool-calls` al Vapi](https://docs.vapi.ai/tools/custom-tools)
în forma documentată de Vapi. `call.id` este ID-ul apelului ThunderPhone. ID-ul
`id` al apelului instrumentului este unic pentru invocare și este reutilizat pentru a corela
rezultatul endpointului. Apelul apare atât în `toolCallList`, cât și în
`toolWithToolCallList`, cu `function.arguments` ca obiect 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" }
          }
        }
      }
    ]
  }
}
```

Câmpurile exclusiv Vapi, precum `assistant`, `artifact` și obiectul complet `call`,
nu sunt trimise.

Returnați anvelopa de răspuns Vapi cu ID-ul corespunzător al apelului instrumentului:

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

ThunderPhone oferă AI-ului șirul `result` neîmpachetat. Acceptă și o
valoare JSON pentru compatibilitate cu endpointurile care returnează deja una. Un
`error` în loc de `result` este transmis AI-ului ca eroare a instrumentului. Dacă
endpointul returnează o anvelopă Vapi incorectă sau necorespunzătoare, ThunderPhone înregistrează
un avertisment și revine la analiza standard a răspunsului, în loc să eșueze
apelul.

#### Retell

`retell` trimite solicitarea documentată pentru o [funcție personalizată
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" }
}
```

Obiectul `call` include doar `call_id`. ThunderPhone nu include
câmpuri specifice Retell pe care nu le poate furniza corect la expediere, precum
`agent_id`, `transcript`, `transcript_object`, `metadata` sau variabile dinamice.
Retell acceptă orice corp de răspuns; ThunderPhone îl transmite
folosind gestionarea standard a răspunsurilor.

Pentru `GET` / `DELETE`, argumentele sunt trimise ca **parametri de interogare**
iar corpul este gol indiferent de `request_format`. Semnătura V2
acoperă șirul de interogare așa cum este trimis; semnătura veche este calculată pe
șirul gol de octeți. Consultați
[Verificarea semnăturilor webhook](/ro/guides/verify-webhook-signatures).

### Răspuns

Returnați un răspuns JSON cu rezultatul instrumentului:

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

Răspunsul este formatat și furnizat AI-ului pentru a continua
conversația. Răspunsurile non-JSON sunt împachetate ca `{"data": "<text>"}`;
expirările și erorile de conexiune sunt raportate AI-ului ca erori, astfel încât
agentul își poate cere scuze și poate continua, în loc să rămână blocat.

## Distribuire în modul webhook

Instrumentele **fără** un `endpoint` sunt distribuite către URL-ul webhook legacy
al organizației dumneavoastră ca o solicitare semnată `telephony.tool` (apeluri telefonice) sau `web.tool`
(apeluri web). Spre deosebire de [notificările de audit](/ro/webhooks/events)
livrate către endpointurile webhook după execuție, această solicitare **este**
execuția — răspunsul HTTP reprezintă rezultatul instrumentului.

```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` conține `origin_domain` în loc de `from_number` /
`to_number`. Răspundeți cu rezultatul instrumentului ca JSON — același contract de răspuns
ca pentru apelurile directe către endpoint. Cererea este semnată cu secretul webhook al organizației,
cu ambele antete de semnătură, la fel ca orice alt webhook.

<Note>
  [Endpointurile webhook](/ro/webhooks/endpoints) abonate primesc suplimentar
  o **notificare** neblocantă `telephony.tool` / `web.tool`
  **după** executarea fiecărui instrument (indiferent de calea care l-a executat), inclusiv
  răspunsul instrumentului — util pentru pistele de audit. Consultați
  [catalogul de evenimente](/ro/webhooks/events).
</Note>

---

## Verificarea semnăturii

Apelurile directe de instrumente sunt semnate la fel ca webhookurile, folosind secretul webhook al organizației dumneavoastră:

- `X-ThunderPhone-Signature-V2` semnează marcajul temporal, metoda, calea și
  interogarea așa cum sunt trimise, precum și octeții exacți ai corpului. Respingeți solicitările al căror
  marcaj temporal diferă cu mai mult de 5 minute de ceasul dumneavoastră.
- `X-ThunderPhone-Signature` (moștenit) semnează numai octeții corpului,
  șirul gol de octeți pentru `GET` / `DELETE`. Este trimis în continuare, nemodificat.

Funcțiile auxiliare SDK verifică antetul V2, inclusiv fereastra de 5 minute:

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

Rețetele complete — șirul semnat, un exemplu detaliat, cod fără
SDK, cazul `GET` / `DELETE` și avertismentul privind absența unui secret — sunt în [Verificați semnăturile webhookurilor](/ro/guides/verify-webhook-signatures).

---

## Exemplu: flux complet de programare

Iată un set de instrumente pentru un sistem complet de programări:

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

---

## Practici recomandate

<AccordionGroup>
  <Accordion title="Scrieți descrieri clare">
    Câmpul `description` ajută AI-ul să înțeleagă **când** să utilizeze instrumentul. Specificați clar ce face și când este potrivit să fie utilizat.
  </Accordion>

  <Accordion title="Gestionați erorile cu eleganță">
    Returnați mesaje de eroare pe care AI-ul le poate înțelege: `{"error": "No slots available for that date"}` în locul erorilor 500 generice.
  </Accordion>

  <Accordion title="Mențineți răspunsurile concise">
    Returnați doar ceea ce AI-ul are nevoie pentru a continua conversația. Încărcăturile utile mari încetinesc timpii de răspuns.
  </Accordion>

  <Accordion title="Utilizați cu discernământ câmpurile obligatorii">
    Marcați câmpurile drept `required` numai atunci când este cu adevărat necesar. AI-ul va solicita utilizatorului informațiile obligatorii înainte de a apela instrumentul.
  </Accordion>
</AccordionGroup>

---

## Resurse asociate

<CardGroup cols={2}>
  <Card title="Instrumente integrate" icon="wrench" href="/ro/guides/built-in-tools">
    Inițiați acțiuni de apel gestionate de platformă fără a defini un endpoint.
  </Card>
  <Card title="Conexiuni de aplicații" icon="plug" href="/ro/guides/connect-apps">
    Instrumente gestionate de platformă pentru HubSpot, Salesforce, Slack, Google
    Calendar, Google Sheets și Cal.com — nu este necesar niciun endpoint.
  </Card>
  <Card title="Servere MCP" icon="server" href="/ro/guides/mcp-servers">
    Atașați un server MCP și permiteți agentului să îi apeleze instrumentele.
  </Card>
  <Card title="Conexiuni API" icon="code" href="/ro/guides/api-connections">
    Integrări REST reutilizabile pe care le puteți atașa agenților.
  </Card>
  <Card title="Verificați semnăturile webhook" icon="shield-check" href="/ro/guides/verify-webhook-signatures">
    Un singur utilitar de verificare pentru webhookuri și apeluri de instrumente.
  </Card>
</CardGroup>
