---
title: "Byg en værktøjsintegration (API)"
description: "Lad din agent kalde dine API'er midt i samtalen — søg i en database, opret en sag, slå en ordre op."
---

En **værktøjsintegration** er et genanvendeligt HTTP-endepunkt, som en agent kan
kalde under et opkald. Du giver ThunderPhone en JSON-skema-beskrivelse
af værktøjet samt en endepunkt-URL; agenten afgør ud fra samtalen, hvornår det skal kaldes,
og ThunderPhone foretager den udgående HTTP-anmodning fra sine servere og
returnerer svaret til agenten.

<Note>
  Dashboardet dækker de fleste værktøjsbehov uden dette API: **Forbindelser
  → Apps** forbinder Slack, HubSpot, Salesforce, Google Calendar,
  Google Sheets og Cal.com med få OAuth-klik; **Forbindelser →
  API'er** omdanner ethvert HTTP-API til en agenthandling (indsæt en cURL-kommando,
  så udarbejder en AI-guide værktøjet med en indbygget Testanmodning); og
  **Forbindelser → MCP** tilføjer MCP-servere. Se
  [Forbindelser](/da/guides/concepts). Denne vejledning dækker det underliggende
  API bag API-grænsefladen.
</Note>

Denne vejledning gennemgår opbygningen af et værktøj til vejr-opslag fra start til slut.

## Et værktøjs opbygning

To dele:

1. **Skemaet** — en funktionsdefinition i OpenAI-stil
   (`{type: "function", function: {name, description, parameters}}`)
   der fortæller LLM'en, hvad værktøjet gør, og hvilke argumenter det tager.
2. **Endepunktet** — den URL, ThunderPhones servere kalder, når
   LLM'en beslutter at bruge værktøjet. Anmodningen er en JSON POST med
   LLM'ens valgte argumenter som brødtekst.

## 1. Vælg en editor

<CardGroup cols={2}>
  <Card title="Dashboard" icon="window-maximize">
    Åbn **Forbindelser → API'er**, opret eller rediger API-forbindelsen,
    skift parametereditoren til **JSON**, og tilføj formatet der.
  </Card>
  <Card title="Integrations-API" icon="plug">
    Opret specifikationen med `POST /v1/integrations`, eller opdater den med
    `PATCH /v1/integrations/{id}`.
  </Card>
</CardGroup>

Begge veje opretter en gemt integration. Tilknyt integrationen til
agenten, når du har gemt den. Agents API'et har intet skrivbart indlejret `tools`-
felt. Denne vejledning bruger integrations-API-ruten.

## 2. Opret integrationen

```bash
curl -X POST https://api.thunderphone.com/v1/integrations \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "display_name": "Weather API",
    "spec": {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Return the current weather for a zip code.",
        "parameters": {
          "type": "object",
          "properties": {
            "zip": { "type": "string", "description": "5-digit US ZIP code" }
          },
          "required": ["zip"]
        }
      }
    },
    "endpoint_url":    "https://api.example.com/weather",
    "endpoint_method": "GET",
    "headers": [
      { "key": "X-Api-Key", "value": "your-provider-key" }
    ]
  }'
```

Gem det returnerede `id` (en UUID). Headerværdier er kun skrivbare: Svar viser kun headernavne, og en senere `PATCH` kan sende en header uden en `value` for at bevare den gemte værdi.

<Tip>
  Brug reel indsats på `description` for værktøjet og for hver
  parameter. LLM'en bruger disse strenge under kørsel til at afgøre,
  om og hvordan værktøjet skal kaldes. Vage beskrivelser → vage
  værktøjskald.
</Tip>

### Angiv `format: "email"` på adresseparametre

En parameter, der modtager en e-mailadresse, skal angive det i sit
skema:

```json
"email": { "type": "string", "format": "email", "description": "The caller's email address" }
```

`format` er mere end et hint. For et e-mailskema, der kan opløses,
trimmer ThunderPhone værdien, skriver domænet med små bogstaver, ændrer
de selvstændige engelske ord `at`, `dot`, `underscore`, `dash` og
`hyphen` til deres tegn og fjerner mellemrum direkte omkring `@`, `.`,
`_` og `-`, før dit endepunkt kaldes. Ordene har samme betydning,
uanset om transskriptionen allerede indeholder et bogstaveligt `@`:
`"john dot smith at gmail dot com"` bliver til
`john.smith@gmail.com`.

Alle andre interne mellemrum afvises i stedet for at blive sat sammen
stiltiende. Talte separatorord er kun engelske; ikke-engelske eller
ikke-genkendte former med mellemrum afvises sikkert. Gyldige
internationaliserede domæner og SMTPUTF8-lokale dele accepteres.
Punycode-input forbliver punycode, og Unicode-domæneinput forbliver
Unicode efter parsernormalisering, så dit API modtager den
konventionelle repræsentation, som opkalderen angav. Hvis den endelige
værdi er ugyldig, bliver værktøjet **ikke kaldt**. Agenten modtager
`invalid_email_argument`, som fortæller den,
at den skal bekræfte stavningen med opkalderen og sende den
bogstavelige adresse igen.

En udeladt valgfri e-mail er urørt. `null`, en tom streng eller en
streng, der kun indeholder mellemrum, er også urørt, når egenskaben er
valgfri eller kan være null; de samme værdier afvises for en påkrævet
e-mail, der ikke kan være null.

Lokale skemareferencer som `#/$defs/email` og
`#/definitions/email` samt `anyOf`, `oneOf` og `allOf` inspiceres
med grænser for cyklusser og dybde. En ikke-lokal eller uopløselig
`$ref` er en kendt begrænsning i håndhævelsen og sendes videre uændret,
ligesom et kald, hvis værktøjssnapshot ikke har et brugbart skema. Hold
e-mailskemaer lokale, når du har brug for, at kontrollen anvendes.

Parametre uden et håndhævet e-mailformat sendes videre præcis, som
modellen producerede dem.

Understøttede formater er `date-time`, `time`, `date`, `duration`,
`email`, `hostname`, `ipv4`, `ipv6` og `uuid`; kun `email`
normaliseres og håndhæves i dag.

## 3. Test endepunktet i sandbox

Før du knytter integrationen til en agent, skal du sende en signeret
anmodning fra ThunderPhones servere for at bekræfte forbindelsen:

```bash
curl -X POST https://api.thunderphone.com/v1/integrations/test-request \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url":    "https://api.example.com/weather?zip=94110",
    "method": "GET",
    "headers": { "X-Api-Key": "your-provider-key" }
  }'
```

```json Response
{
  "ok": true,
  "status": 200,
  "elapsed_ms": 187,
  "response_headers": { "content-type": "application/json" },
  "response_preview": "{\"temperature_f\": 64, ...}"
}
```

Denne test styrker også ThunderPhones SSRF-beskyttelse — anmodninger til
localhost eller private IP-områder returnerer `400 code=url_not_allowed`.

## 4. Knyt integrationen til en agent

Knyt den via `integration_ids`, når du opretter eller opdaterer en agent:

```bash
curl -X PATCH https://api.thunderphone.com/v1/agents/12 \
  -H "Authorization: Bearer sk_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "integration_ids": ["f9b5a1a4-..."]
  }'
```

Du kan knytte mange integrationer til én agent. Agentens prompt kan
referere til dem ved navn — "brug `get_weather`, når opkalderen spørger
om vejrforhold" — eller den kan finde dem implicit ud fra
skemabeskrivelserne.

## 5. Implementer endpointet

Når agenten kalder værktøjet, sender ThunderPhone en signeret POST til
din `endpoint_url`:

```
POST /weather HTTP/1.1
Host: api.example.com
X-Api-Key: your-provider-key
X-ThunderPhone-Signature-V2: t=<unix seconds>,v1=<HMAC-SHA256 hex>
X-ThunderPhone-Signature: <HMAC-SHA256 hex>
X-ThunderPhone-Call-ID: 987654321
Content-Type: application/json

{"zip": "94110"}
```

Din server svarer med JSON, som sendes tilbage til LLM'en:

```json
{"temperature_f": 64, "condition": "Partly cloudy", "wind_mph": 8}
```

LLM'en indlæser svaret og giver opkalderen en mundtlig opsummering i
almindeligt sprog.

<Warning>
  Signaturen beregnes over den rå anmodningstekst med den samme
  `secret` som dit webhook-endpoint. **Bekræft den** — værktøjsendpoints
  er tilgængelige fra internettet og er udsat for de samme risici for
  forfalskning som webhooks. Se
  [Bekræft webhook-signaturer](/da/guides/verify-webhook-signatures).
</Warning>

## 6. Test forløbet

Start en [mikrofonsession](/api-reference/mic-sessions) mod agenten,
og stil det spørgsmål, dit værktøj håndterer ("Hvordan er vejret i
94110?"). Opkaldets transskription viser hele processen:

```json
{
  "call_id": 987654321,
  "transcripts": [
    { "role": "user",
      "content": "What's the weather in 94110?" },
    { "role": "tool_call",
      "content": "{\"tool_call\": \"get_weather\", \"arguments\": {\"zip\": \"94110\"}}" },
    { "role": "tool_response",
      "content": "{\"tool_name\": \"get_weather\", \"response\": {\"temperature_f\": 64, \"condition\": \"Partly cloudy\"}}" },
    { "role": "agent",
      "content": "It's 64 degrees and partly cloudy." }
  ]
}
```

Du kan hente dette via
[`GET /v1/calls/{call_id}/transcript`](/api-reference/calls#get-transcript);
den rå hændelsesstrøm med tidsangivelser og lydforskydninger for hver
post findes på
[`GET /v1/calls/{call_id}/history`](/api-reference/calls#get-history).

I opkaldslister og analysetjenester rapporterer ThunderPhone nu testopkald separat fra reel trafik. Browser Talk-tests og simuleringer kan genbruge et rigtigt telefonnummer og kan ligne indgående opkald, men de tæller ikke med i samlede produktionsopkald, vurderinger eller problemmetriker.

## Almindelige faldgruber

<AccordionGroup>
  <Accordion title="Agenten kalder aldrig værktøjet">
    LLM'en træffer beslutningen ud fra værktøjets beskrivelse. Hvis opkalderens
    spørgsmål ikke matcher beskrivelsen, kalder modellen ikke værktøjet. Gør
    beskrivelsen mere præcis ved at tilføje almindelige synonymer og formuleringer,
    eller nævn det eksplicit i agentens prompt ("Når opkalderen spørger om vejret,
    skal du bruge `get_weather`.").
  </Accordion>

  <Accordion title="Værktøjet returnerer for mange data">
    Svar over 6 kB afkortes i transskriptionsforhåndsvisningen. Returner kun
    de felter, LLM'en har brug for — ikke hele din række.
  </Accordion>

  <Accordion title="Tidsudløb">
    Værktøjsendpoints har en standardtimeout på 10 sekunder. Hvis du har brug
    for længere tid, skal du håndtere det asynkront: Returner `{"status": "pending", "request_id": "..."}`
    og vis resultatet via et separat værktøjskald.
  </Accordion>

  <Accordion title="Versionering">
    Hver `PATCH` af en integration opretter en ny revision. Undersøg
    [`GET /v1/integrations/{id}/versions`](/api-reference/integrations#version-history)
    for at se, hvem der ændrede hvad. Hvis du ødelægger et værktøjs skema, kan du
    rulle tilbage manuelt ved at PATCH'e et ældre øjebliksbillede ind igen.
  </Accordion>
</AccordionGroup>

---

## Næste trin

<CardGroup cols={2}>
  <Card title="Reference til integrationer" icon="plug" href="/api-reference/integrations">
    CRUD, overførsel, versionshistorik.
  </Card>
  <Card title="Specifikation for funktionsværktøjer" icon="screwdriver-wrench" href="/da/tools/overview">
    Komplet JSON-skema-grammatik og kontrakten for signerede endepunkter.
  </Card>
  <Card title="Bekræft signaturer" icon="shield-check" href="/da/guides/verify-webhook-signatures">
    Anvend mønsteret for webhook-signaturer på værktøjsendepunkter.
  </Card>
  <Card title="API til transskription + historik" icon="phone" href="/api-reference/calls">
    Undersøg hele forløbet for et værktøjskald.
  </Card>
</CardGroup>
