
# Brugerdefinerede funktioner

Brugerdefinerede funktioner gør det muligt for din AI-bot at oprette forbindelse til andre systemer under live-samtaler. I stedet for at sige "Jeg tjekker det og vender tilbage til dig", kan botten slå en ordrestatus op, tjekke lagerbeholdningen eller oprette en post i dit CRM (customer relationship management-system - den software, du bruger til at spore kundeemner og kunder, såsom HubSpot eller Salesforce) - alt sammen i realtid, mens kunden venter.


---

## Brugerdefinerede funktioner vs. Webhooks

Dette er det mest almindelige punkt for forvirring, så det er værd at få afklaret, før du bygger noget.

| | Webhooks | Brugerdefinerede funktioner |
|---|----------|------------------|
| **Retning** | Envejs (send og glem) | Tovejs (kald og vent) |
| **Hvad botten gør** | Sender en meddelelse, når noget sker, og fortsætter derefter. | Kalder ud, **pauser, venter på svaret** og bruger det, der kommer tilbage, til at fortsætte samtalen. |
| **Synlighed for samtalen** | Resultatet er usynligt for botten — den ser aldrig, hvad der skete. | Svaret føres direkte tilbage til AI'en, så botten kan citere det, ræsonnere omkring det og svare kunden med det. |
| **Bedst til** | Logning af hændelser, synkronisering af data til et CRM, udløsning af eksterne automatiseringer (Zapier, Make, n8n). | Alt, hvor botten har brug for et **svar**, før den kan svare — live-opslag, realtidspriser, indholdsgenerering på farten. |

**Hvornår skal man vælge hvad:** Hvis du bare har brug for at *fortælle* et andet system, at der er sket noget, skal du bruge en webhook - en automatiseret envejsbesked sendt til et andet system (se **Indstillinger → Integrationer → Webhooks**). Hvis botten skal *lære* noget fra et andet system, før den fortsætter samtalen, skal du bruge en brugerdefineret funktion.

---

## Eksempler på hvad brugerdefinerede funktioner muliggør

Fordi svaret føres tilbage i samtalen, åbner brugerdefinerede funktioner op for ting, som webhooks simpelthen ikke kan:

- **Live Shopify- eller WooCommerce-lagertjek** — Før botten giver kunden et tilbud, tjekker den lagerbeholdningen i realtid og svarer "Ja, vi har 12 på lager i størrelse M" i stedet for "lad mig tjekke det og vende tilbage til dig."
- **Dynamisk prissætning fra et Google Sheet** — Dit salgsteam opdaterer priserne i et ark; botten læser den nyeste række midt i samtalen og oplyser den aktuelle pris, uden at nogen behøver at røre ved AI-konfigurationen.
- **Voice-AI callback-agent** — Når botten kvalificerer et lead, udløser den en stemmeagent (f.eks. en ElevenLabs-drevet opkalder) til at ringe til leadet tilbage inden for få minutter, og bekræfter over for kunden: "fint, forvent et opkald inden for de næste 5 minutter."
- **Brugerdefineret tilbuds-PDF, genereret og sendt via e-mail midt i chatten** — Botten indsamler krav, kalder din tilbudsbygger, får et PDF-link tilbage og fortæller kunden: "Jeg har lige sendt dit tilbud via e-mail — tjek din indbakke."

---

## Hvad kan brugerdefinerede funktioner gøre?

Tænk på brugerdefinerede funktioner som noget, der giver din bot superkræfter ud over bare at chatte. Her er eksempler fra den virkelige verden:

- **Ordresporing** - En kunde spørger "Hvor er min ordre?", og botten tjekker dit e-handelssystem og svarer med forsendelsesstatus og sporingslink
- **Lagertjek** - "Har I denne i størrelse 10?" Botten tjekker dit lagersystem og giver et svar i realtid
- **CRM-opdateringer** - Når botten kvalificerer et lead, opretter eller opdaterer den automatisk en post i HubSpot, Salesforce eller et hvilket som helst andet CRM
- **Tilbudsgenerering** - Botten indsamler kundens krav og genererer et personligt tilbud fra dit prissystem
- **Booking** - Botten opretter en aftale i dit eksterne bookingsystem
- **Rabatverificering** - "Er denne rabatkode gyldig?" Botten tjekker og bekræfter
- **Kontoopslag** - En tilbagevendende kunde genkendes automatisk, og deres kontooplysninger hentes frem

**Kunden ser aldrig, hvad der foregår bag kulisserne.** De oplever blot en bot, der kan besvare deres spørgsmål med rigtig, opdateret information.

---

## Hvordan brugerdefinerede funktioner fungerer (den enkle version)

Her er hvad der sker, når en brugerdefineret funktion udløses under en samtale:

1. **Kunden spørger om noget**, der kræver realtidsdata (f.eks. "Hvor er min ordre?")
2. **Botten genkender**, at den skal bruge en brugerdefineret funktion for at svare
3. **Botten indsamler** eventuel manglende information fra kunden (f.eks. "Hvad er dit ordrenummer?")
4. **Platformen sender en anmodning** til dit system (din hjemmeside, CRM eller et andet værktøj) med de relevante detaljer
5. **Dit system svarer** med dataene (f.eks. ordrestatus, trackingnummer, leveringsdato)
6. **Botten læser svaret** og formulerer et naturligt svar: "Din ordre ORD-4582 er blevet afsendt og forventes at ankomme senest fredag!"

### Hvad et brugerdefineret funktionskald koster

Hvert brugerdefineret funktionskald faktureres baseret på din agents AI-kvalitetsniveau:

| AI-kvalitetsniveau | Credits pr. brugerdefineret funktionskald | Med din egen Anthropic-nøgle (BYOK) tilsluttet |
|---|---|---|
| Pro | 1 credit | 0 credits — kører på din nøgle |
| Economy (udgået) | 0,5 credits | 0 credits — kører på din nøgle |
| Max | 0,25 credits | stadig 0,25 credits, faktureres selv med din egen nøgle tilsluttet, da Max kører på vores egen model |
| Mini | 0,15 credits | stadig 0,15 credits, faktureres selv med din egen nøgle tilsluttet, da Mini kører på vores egen model |

---

## Opsætning af en brugerdefineret funktion (trin for trin)

1. I hovedsidepanelet under **AI Studio** skal du klikke på **Custom Functions**.


2. Klik på den grønne **+ Add Function** (eller **New function**) knap øverst til højre.


Listen over brugerdefinerede funktioner viser en tabel med følgende kolonner:

| Kolonne | Hvad den viser |
|--------|--------------|
| **Navn** | Funktionens navn (f.eks. `check_order_status`) |
| **Beskrivelse** | Et kort resumé af, hvad funktionen gør (afkortes til 50 tegn i tabellen) |
| **Metode** | Den anvendte HTTP-metode, vist som et farvet badge: GET (blå), POST (grøn), PUT (orange), DELETE (rød) |
| **Oprettet** | Datoen for, hvornår funktionen blev oprettet |

Dette gør det nemt at overskue dine funktioner og finde den, du skal bruge.

### Trin 1: Giv den et navn og en beskrivelse


| Felt | Hvad skal indtastes | Eksempel |
|-------|--------------|---------|
| **Navn** | Et kort navn med bogstaver, tal og understregninger | `check_order_status` |
| **Beskrivelse** | Forklar hvad denne funktion gør (AI'en læser dette for at beslutte, hvornår den skal bruges) | "Slår den aktuelle status for en kundes ordre op ved hjælp af deres ordrenummer" |
| **Formål (AI-handling)** | Fortæl AI'en præcis hvornår og hvordan denne funktion skal bruges | "Brug denne, når en kunde spørger om deres ordrestatus, forsendelse eller levering. Spørg først efter deres ordrenummer." |

**Tip:** Vær meget specifik i beskrivelsen og formålet. Jo tydeligere du er omkring, hvornår funktionen skal bruges, jo mere pålideligt vil botten bruge den på det rigtige tidspunkt.

### Trin 2: Opsæt forbindelsen

Du skal fortælle appen, hvor anmodningen skal sendes hen:

| Felt | Hvad skal indtastes | Eksempel |
|-------|--------------|---------|
| **URL** | Webadressen på dit systems endpoint (den specifikke adresse på dit system, der modtager anmodningen og sender data tilbage) | `https://api.yourstore.com/v1/orders/status` |
| **Metode** | Typen af anmodning, der skal sendes | Se muligheder nedenfor |

**Hvilken metode skal man vælge:**

| Metode | Hvornår skal den bruges |
|--------|---------------|
| **GET** | Opslag af information (ordrestatus, lagerbeholdning, kontooplysninger) |
| **POST** | Oprettelse af nye poster (support-tickets, leads, bookinger) eller komplekse opslag |
| **PUT** | Fuldstændig opdatering af en eksisterende post |
| **PATCH** | Opdatering af en del af en eksisterende post |
| **DELETE** | Sletning af en post |

Hvis du er i tvivl om, hvilken du skal bruge, så tjek med din udvikler eller dokumentationen for det system, du opretter forbindelse til. **GET** (til opslag) og **POST** (til oprettelse af poster) er de mest almindelige.

### Trin 3: Tilføj godkendelsesoverskrifter (Authentication Headers)

De fleste systemer kræver godkendelse for at acceptere anmodninger. Tilføj alle påkrævede overskrifter:

| Overskrift | Eksempelværdi |
|--------|--------------|
| `Authorization` | `Bearer your-api-key-here` |
| `Content-Type` | `application/json` |

**Sikkerhedstip:** Brug en dedikeret API-nøgle med begrænsede tilladelser. Brug ikke legitimationsoplysninger på administratorniveau.

**Hvor finder du API-nøgler:** Tjek indstillingerne eller udviklersektionen i det system, du opretter forbindelse til (f.eks. dit CRM, din e-handelsplatform eller dit bookingsystem).

### Trin 4: Definer input (Hvad botten sender)

Inputparametre er de stykker information, som botten indsamler fra samtalen og sender til dit system.

For hver parameter angiver du:

| Egenskab | Hvad det betyder |
|----------|--------------|
| **Navn** | Parameternavnet (skal matche det, dit system forventer) |
| **Type** | Hvilken type data der er tale om (tekst, tal, sandt/falsk osv.) |
| **Beskrivelse** | Fortæl AI'en, hvad denne information er, og hvor den kan findes i samtalen |
| **Påkrævet** | Hvis sat til Ja, vil botten bede kunden om denne information, før den fortsætter |

**Tilgængelige parametertyper:**

| Type | Hvad det betyder |
|------|--------------|
| **string** | Tekst (navne, ordrenumre, adresser) |
| **number** | En numerisk værdi (antal, pris) |
| **boolean** | Sand eller falsk (ja/nej-værdier) |
| **array** | En liste over elementer. Sendes som en rigtig JSON-liste — i **Kør test** kan du indtaste den som `[8624]`, `["a", "b"]` eller blot kommasepareret (`8624, 8625`), og den konverteres for dig. Hvis dit API er strengt omkring, hvad listen indeholder — f.eks. kun tal — skal du indstille den valgfrie **Elementtype** ved siden af typen, så konverteres hver værdi på listen til denne. |
| **query_param** | Tekst, der sendes som en URL-parameter i stedet for i anmodningens brødtekst. Brug denne, når dit API forventer data i URL'en (f.eks. `?order_id=123`). |

Hver parameter har også et valgfrit felt til **Sti for anmodningstekst** (Request body path). Normalt sendes en parameter som et felt på øverste niveau i anmodningsteksten (eller som en forespørgselsstrengværdi for `query_param`-typen). Hvis dit slutpunkt forventer, at den er indlejret i stedet — f.eks. `{"order": {"id": "ORD-123"}}` — skal du indstille stien til `order.id`, så indlejrer platformen værdien der for dig.


**Eksempel: Til et opslag af ordrestatus kan du definere:**

- **order_number** (string, påkrævet): "Kundens ordrenummer. Starter normalt med ORD- efterfulgt af cifre. Spørg kunden om dette, hvis de ikke har nævnt det."
- **email** (string, valgfri): "Kundens e-mailadresse til yderligere bekræftelse. Kun nødvendig, hvis ordrenummeret alene ikke finder et match."

### Hvad dit system modtager automatisk

Udover de inputparametre, du definerer, inkluderer platformen automatisk systemdata med hver anmodning. Dit endpoint modtager dette i et `system`-felt:

| Systemfelt | Hvad det indeholder |
|-------------|----------------|
| `system.contactId` | Platform-ID'et for kontakten i samtalen |
| `system.campaignId` | Kampagne-ID'et, som samtalen tilhører |
| `system.userId` | Dit bruger-ID |
| `system.channel` | Beskedkanalen (f.eks. `"whatsapp"`, `"instagram"`) |
| `system.contact` | Den fulde kontaktpost (navn, telefon, e-mail, tags osv.) |
| `system.campaign` | Kampagnekonfigurationen |
| `system.test` | `true` hvis dette er en test, `false` for live-samtaler |

Dette er nyttigt, hvis dit system har brug for at identificere kontakten, tjekke hvilken kampagne der udløste funktionen, eller opføre sig anderledes under test.

> **Har du ikke brug for systemdataene?** Slå **Spring systemdata over**-kontakten til i funktionsbyggeren. Botten vil derefter kun sende de inputparametre, du har defineret — ingen kontakt- eller kampagnedata. Brug dette, hvis dit endpoint afviser uventede felter, eller hvis du blot ønsker en mere slank payload.

### Trin 5: Test det, og lad derefter botten læse svaret

Du behøver normalt slet ikke at mappe svarfelter. Når dit slutpunkt svarer, læser botten hele JSON-svaret og bruger din funktions **Beskrivelse** og **Formål (AI-handling)** — plus hver parameters egen beskrivelse — til at finde ud af, hvad der er vigtigt, og præsentere det naturligt. En klar beskrivelse af selve funktionen ("Henter den aktuelle status for en kundeordre inklusive forsendelsesoplysninger og sporing") gør mere af arbejdet her, end felt-for-felt-mapping ville gøre.

Hvis dit slutpunkt returnerer et stort svar, og du kun ønsker, at botten skal se et par specifikke værdier, skal du åbne sektionen **Svar-mapping** (som er skjult som standard, lige over Test). Hver række vælger ét felt på øverste niveau fra svaret: **Svarfelt** er feltnavnet i din API's JSON-svar, og **Outputfelt** er det navn, botten modtager det under. Med mindst én udfyldt række modtager botten kun dine mappede værdier i stedet for hele svarteksten. Lad sektionen være tom for at bevare standardopførslen med fuldt svar.


Før du gemmer, skal du bruge **Test**-sektionen nederst i editoren til at sende anmodningen præcis som konfigureret og se det faktiske svar uden at forlade appen:


Det svar, du ser her, er slutpunktets rå svar. Hvis du har konfigureret **Svarsmapping** ovenfor, modtager botten i en rigtig chat kun de mappede felter — testen viser altid det fulde rå svar, så du kan se, hvad der er tilgængeligt at mappe. Hvis noget ser forkert ud (uventede feltnavne, ekstra indlejring), skal du rette det på dit slutpunkt eller justere din mapping.

---

## Tildeling af funktioner til en agent

Når du har oprettet en brugerdefineret funktion, skal du fortælle hver agent, hvilke funktioner den må bruge:

1. Åbn [Agenten](../ai-agents/ai-agents.md) under **AI Studio → AI-agenter**.
2. Gå til fanen **AI-evner**. (For en kampagne, der stadig har sine egne AI-indstillinger direkte i stedet for gennem en separat agent, vises den samme liste i stedet på kampagnens eget **AI-evner**-trin.)
3. Du vil se en liste over alle de brugerdefinerede funktioner, du har oprettet. Slå hver funktion til, som du vil have, at denne agents bot skal kunne kalde.
4. Klik på **Gem ændringer** nederst. Valg træder først i kraft, når de er gemt.


Kun tildelte funktioner er tilgængelige for botten for den pågældende agent. Dette forhindrer botten i ved et uheld at bruge funktioner, der ikke er relevante.

---

## Test af dine brugerdefinerede funktioner

Før du går live, bør du teste grundigt:

1. **Kør den indbyggede test** - Brug **Test**-sektionen inde i funktionseditoren (se ovenfor) til et hurtigt tjek uden at forlade appen — udfyld realistiske værdier og klik på Kør test.
2. **Test dit systems endpoint direkte** - For den fulde tjekliste nedenfor graver et dedikeret værktøj som Postman (eller din udvikler) dybere end en enkelt Kør test.
3. **Test i Prøv det** - Simuler en samtale, hvor kunden spørger om noget, der bør udløse funktionen.
4. **Tjek svaret** - Sørg for, at botten korrekt læser og præsenterer dataene.
5. **Test fejlscenarier** - Hvad sker der, hvis kunden giver et ugyldigt ordrenummer? Hvad hvis dit system er midlertidigt nede?

### Når testen returnerer 401 eller 403

En 401 eller 403 betyder, at dit endpoint modtog anmodningen og afviste den. Det afslørende tegn er, at **intet dukker op i dine egne logfiler** — de fleste værktøjer afviser et uautoriseret kald, før de overhovedet starter workflowet, så der er intet at se fra din side, og det ser ud som om, anmodningen aldrig ankom.

Dette skyldes næsten altid en uoverensstemmelse i godkendelsen: dit endpoint ønsker én type legitimationsoplysninger, og funktionen sender en anden. Kontroller, at den header, du tilføjede i [Trin 3](#step-3-add-authentication-headers), er præcis den, dit system forventer.

Den mest almindelige version af dette er en webhook beskyttet med **Basic Auth** (n8n, Make og de fleste selvhostede værktøjer tilbyder dette som et afkrydsningsfelt på selve webhooken), mens funktionen sender en brugerdefineret hemmelig header som `X-My-Secret`. Basic Auth accepterer kun en `Authorization`-header, så en brugerdefineret header ignoreres, og kaldet afvises. Du har to muligheder:

- **Slå Basic Auth fra** på webhooken, og tjek i stedet din brugerdefinerede header inde i workflowet.
- **Behold Basic Auth slået til**, og tilføj en `Authorization`-header til funktionen, hvis værdi er ordet `Basic` efterfulgt af din base64-kodede `username:password`.

Begge dele virker — sørg blot for, at begge parter er enige.

### Når testen returnerer 404

Endpoint-URL'en er forkert, eller workflowet er ikke publiceret. Specifikt i n8n har hver webhook en separat **Test**-URL og **Production**-URL, og test-URL'en lytter kun, mens du har editoren åben. Kopier Production-URL'en og sørg for, at workflowet er aktivt.

### Se fejl i Try Out og Chats

Når AI'en kalder en brugerdefineret funktion under en samtale, og kaldet fejler — forkerte legitimationsoplysninger, endpoint nede, timeout — viser samtalen det nu: en rød **"(funktionsnavn) fejlede"**-markør vises i tråden, både i agentens **Try Out**-fane og i rigtige samtaler i **Chats**. Klik på markøren for at udvide detaljerne: statuskoden, som dit endpoint returnerede, og dens svartekst, hvilket normalt er nok til at fortælle dig præcis, hvad der skal rettes (en `401` med en "unauthorized"-besked betyder, at godkendelsesoverskriften er forkert, en timeout betyder, at dit endpoint tog længere tid end 30 sekunder).

Succesfulde kald viser også en markør — klik på den for at se, hvad dit endpoint faktisk sendte tilbage. Dette er den hurtigste måde at debugge en integration fra ende til anden: hav testsamtalen i Try Out, og klik derefter på funktionsmarkøren for at se det faktiske resultat af anmodningen uden at forlade siden.

---

## Komplet eksempel: Opslag af ordrestatus

Her er et fuldt konfigureret eksempel, som du kan bruge som skabelon:

**Grundlæggende oplysninger:**
- **Navn:** `check_order_status`
- **Beskrivelse:** "Henter den aktuelle status for en kundeordre, inklusive forsendelsesoplysninger og sporing"
- **AI-handling:** "Kald denne, når en kunde spørger om deres ordrestatus, hvor deres pakke er, eller hvornår den ankommer. Bed altid om ordrenummeret først."

**Forbindelse:**
- **URL:** `https://api.mystore.com/orders/lookup`
- **Metode:** POST

**Headere:**
- `Authorization`: `Bearer sk_live_abc123`
- `Content-Type`: `application/json`

**Inputparametre:**
- `order_number` (tekst, påkrævet): "Ordrenummeret oplyst af kunden"
- `customer_email` (tekst, valgfri): "Kundens e-mail til yderligere bekræftelse"

**Ingen svar-mapping nødvendig** — med en klar beskrivelse og AI-handling allerede indstillet ovenfor, læser botten den JSON, dit endpoint returnerer (status, sporingsnummer, leveringsdato, varer), og finder ud af, hvordan den skal tale om det.

**Hvordan samtalen ser ud:**

> **Kunde:** Hej, hvor er min ordre?
>
> **Bot:** Hej! Jeg vil meget gerne tjekke din ordre. Kan du dele dit ordrenummer med mig?
>
> **Kunde:** Det er ORD-78234
>
> **Bot:** Lad mig slå det op for dig...
>
> Din ordre ORD-78234 er blevet afsendt og er på vej! Den forventede leveringsdato er 10. marts. Du kan spore din pakke her: https://tracking.example.com/1Z999AA1
>
> Er der andet, jeg kan hjælpe dig med?

---

## Komplet eksempel: Shopify-ordrestatus

Hvis butikken kører på Shopify, behøver du ikke en udvikler til at bygge et opslagsslutpunkt — Shopifys eget system kan besvare ordrespørgsmål direkte. (For spørgsmål om produkter og lager i en Shopify-butik behøver du slet ikke en tilpasset funktion: forbind i stedet butikkens indbyggede server — se [Forbind en Shopify-butik](mcp-servers.md#ready-made-example-connect-a-shopify-store).)

**Opret først et adgangstoken i Shopify.** Shopify ændrede dette i løbet af 2026: apps kan ikke længere oprettes inde i Shopify-administratoren, og den nye app-skærm giver dig et **Client ID** og en **Client secret** i stedet for et færdigt token. Trinene nedenfor omdanner disse til et permanent token. Sæt ti minutter af til det, én gang pr. butik. (Hvis butikken allerede har en ældre app oprettet på den gamle måde, vil dens eksisterende token fortsat fungere – spring direkte til den brugerdefinerede funktion nedenfor.)

1. Gå til Shopify Dev Dashboard på [dev.shopify.com](https://dev.shopify.com), åbn din organisation, og klik på **Apps → Create app**. Giv den et navn som f.eks. `Order lookup`.
2. Giv appen tilladelsen **read_orders**, udgiv en version, og installer appen i butikken.
3. Åbn appens **Settings** og tilføj butikkens egen webadresse (f.eks. `https://www.yourstore.com/`) til de tilladte omdirigerings-URL'er (redirect URLs). Gem.
4. Stadig under **Settings**, kopier **Client ID** og **Client secret**.
5. I en browser, hvor du er logget ind på den pågældende butiks Shopify-admin, skal du åbne adressen nedenfor og erstatte butiksnavnet, klient-ID'et og omdirigeringsadressen med dine egne:
   `https://YOUR-STORE.myshopify.com/admin/oauth/authorize?client_id=YOUR-CLIENT-ID&scope=read_orders&redirect_uri=https://www.yourstore.com/&state=12345`
   Godkend skærmen, der vises. Browseren lander på din omdirigeringsadresse, og adresselinjen indeholder nu `code=` efterfulgt af en lang værdi — kopier denne værdi. Den er kun gyldig i et par minutter, så gå direkte til næste trin.
6. Ombyt denne kode med tokenet, hvilket du kan gøre inde i <span data-t="appName">Your AI Connector</span>. I editoren til brugerdefinerede funktioner skal du indstille **Method** til POST og **URL** til `https://YOUR-STORE.myshopify.com/admin/oauth/access_token`, tilføje tre tekst-inputparametre navngivet `client_id`, `client_secret` og `code`, klik derefter på **Test**, udfyld de tre værdier, og kør den. Svaret indeholder `access_token` — det er dit permanente token. Kopier det et sikkert sted hen, ryd derefter editoren og opsæt den rigtige funktion nedenfor.

**Opsæt derefter den tilpassede funktion:**

**Grundlæggende oplysninger:**
- **Navn:** `check_shopify_order`
- **Beskrivelse:** "Slår en ordre op i butikkens Shopify-system og returnerer dens status, sporing og varer"
- **AI-handling:** "Kald denne, når en kunde spørger om deres ordrestatus eller levering. Bed altid om ordrenummeret først."

**Forbindelse:**
- **URL:** `https://YOUR-STORE.myshopify.com/admin/api/2026-01/orders.json?status=any` — erstat `YOUR-STORE` med butikkens `.myshopify.com`-navn (denne adresse bruger det tekniske Shopify-domæne, ikke butikkens brugerdefinerede domæne)
- **Metode:** GET

**Headere:**
- `X-Shopify-Access-Token`: `shpat_...` (tokenet fra før)

**Inputparametre:**
- `name` (query_param, påkrævet): "Kundens ordrenummer præcis som det fremgår af deres ordrebekræftelse, inklusive #-tegnet — for eksempel #1001. Bed kunden om det, hvis de ikke har nævnt det."

**Ingen svar-mapping nødvendig** — botten læser den returnerede ordre (betalingsstatus, leveringsstatus, sporing, varer) og svarer naturligt.

**Godt at vide:** et token oprettet på denne måde kan se ordrer fra de **sidste 60 dage** — rigeligt til daglige supportspørgsmål, men ikke en fuld ordrehistorik.

---

## Komplet eksempel: Book en aftale

**Grundlæggende oplysninger:**
- **Navn:** `create_booking`
- **Beskrivelse:** "Opretter en ny aftale i vores bookingsystem"
- **AI-handling:** "Brug denne efter at have bekræftet dato, tid og kontaktoplysninger med kunden. Kald den ikke, før kunden eksplicit bekræfter, at de ønsker at booke."

**Forbindelse:**
- **URL:** `https://booking.mycompany.com/api/appointments`
- **Metode:** POST

**Inputparametre:**
- `date` (tekst, påkrævet): "Aftaledato i formatet ÅÅÅÅ-MM-DD"
- `time` (tekst, påkrævet): "Aftaletidspunkt i formatet TT:MM"
- `name` (tekst, påkrævet): "Kundens fulde navn"
- `phone` (tekst, påkrævet): "Kundens telefonnummer"
- `service_type` (tekst, påkrævet): "Typen af service, der bookes"

---

## Komplet eksempel: Tilføj en nyhedsbrevsabonnent til dit CRM

Et meget almindeligt mønster: botten er færdig med at svare, tilbyder dit nyhedsbrev, kontakten svarer med sin e-mailadresse, og den adresse skal lande direkte i dit e-mailværktøj. De fleste CRM-systemer (FluentCRM, ActiveCampaign, MailerLite, Brevo og andre) accepterer et simpelt POST-kald til netop dette, så der er ikke brug for en automatiseringsplatform imellem.

Dette eksempel bruger **FluentCRM** på WordPress. Strukturen er den samme for ethvert andet værktøj, der giver dig et "indgående webhook" eller et "opret abonnent"-endepunkt.

**Først skal du hente URL'en fra dit CRM.** I WordPress skal du åbne **FluentCRM → Indstillinger → Indgående webhooks** og oprette et webhook. Vælg den liste, de tags og den abonnementsstatus, som nye kontakter skal have, og kopiér derefter den webhook-URL, der genereres. Alt, hvad du indstiller her, anvendes automatisk, så botten skal kun sende e-mailadressen.

**Opsæt derefter den tilpassede funktion:**

**Grundlæggende oplysninger:**
- **Navn:** `add_newsletter_subscriber`
- **Beskrivelse:** "Tilføjer en person til vores nyhedsbrevsliste ved hjælp af den e-mailadresse, de opgav i chatten"
- **AI-handling:** "Brug denne i det øjeblik, kontakten accepterer at abonnere på nyhedsbrevet og opgiver sin e-mailadresse. Kald den ikke, før de rent faktisk har opgivet en adresse, og kald den ikke to gange for den samme person."

**Forbindelse:**
- **URL:** den webhook-URL, du kopierede fra dit CRM
- **Metode:** POST

**Inputparametre:**
- `email` (streng, påkrævet): "Den e-mailadresse, som kontakten opgav i samtalen"
- `first_name` (streng, valgfri): "Kontaktens fornavn, hvis de har nævnt det"

**Spring systemdata over:** slå denne **til**. Dit CRM har kun brug for felterne ovenfor, og en mindre nyttelast undgår fejl fra værktøjer, der afviser uventede felter.

**Svarsmapping:** ikke nødvendig her. Der behøver ikke komme noget retur, for at botten kan fortsætte.

**Glem ikke at aktivere funktionen for den Agent, der kører samtalen** (se [Tildeling af funktioner til en agent](#assigning-functions-to-an-agent)). Dette er den mest almindelige årsag til, at en korrekt bygget funktion aldrig aktiveres.

::: tip
**Tip:** botten har også et indbygget **Opdater kontakt-e-mail**-værktøj, som gemmer adressen på kontaktposten inde i platformen. Det er adskilt fra denne funktion og er nyttigt at bruge sammen med den — det indbyggede værktøj holder din egen kontaktpost komplet, mens den brugerdefinerede funktion sender adressen videre til dit CRM.
:::


---

## Tips til pålidelige brugerdefinerede funktioner

1. **Sørg for, at gentagne anmodninger er sikre.** Hvis den samme anmodning ved et uheld sendes to gange, bør den ikke oprette dublerede poster. Netværksproblemer kan lejlighedsvis forårsage dette.

2. **Returnér klare fejlmeddelelser.** Hvis noget går galt på dit systems side, skal du returnere en menneskeligt læsbar fejl. Botten vil videreformidle den til kunden på en elegant måde.

3. **Hold svartiderne under 10 sekunder.** Hvis dit system tager længere tid, bør du overveje først at returnere en hurtig bekræftelse.

4. **Håndter udløbne eller ugyldige legitimationsoplysninger.** Hvis din API-nøgle udløber, skal du sørge for, at fejlmeddelelsen er klar, så botten ved, at den skal give besked til et menneske i stedet for at prøve igen.

5. **Skriv detaljerede beskrivelser.** AI'en bruger dine beskrivelser til at finde ud af, hvornår funktionen skal kaldes, og hvordan den rette information skal udtrækkes fra samtalen. Vage beskrivelser fører til fejl.

6. **Test med rigtige samtaler.** Try Out er fantastisk til indledende test, men overvåg dine første par live-samtaler for at sikre, at alt fungerer med rigtige kundehenvendelser.

7. **Hold logfiler på din side.** Bed din udvikler om at logge de anmodninger, der kommer fra appen, så du hurtigt kan fejlfinde eventuelle problemer.

8. **Brug en offentlig endelig URL.** Din funktions-URL skal være en offentlig webadresse (HTTP/HTTPS). Interne adresser, localhost og adresser på private netværk afvises af sikkerhedshensyn, og platformen følger ikke omdirigeringer — peg funktionen direkte mod den endelige URL, ikke mod en, der omdirigerer til den.

---

## Udførelsesgrænser

Hver brugerdefineret funktion har et valgfrit afsnit om **Udførelsesgrænser** nederst i editoren. Det styrer, hvor ofte AI'en må køre funktionen, og om et tidligere resultat kan genbruges. Alt her er valgfrit — lad det hele stå tomt, så opfører funktionen sig præcis som før.


**Skrivebeskyttet funktion.** Slå denne til, hvis din funktion kun *læser* data — et opslag af aktiekurser, et pristjek, en søgning på ordrestatus — og aldrig opretter eller ændrer noget. Når et midlertidigt netværksproblem afbryder AI'en midt i et svar, kan platformen sikkert forsøge samtalen igen i stedet for at efterlade kunden uden svar. Aktivér den kun, hvis funktionen aldrig skriver noget: en funktion, der opretter poster, skal forblive slået fra, så et nyt forsøg aldrig ved et uheld kan køre den to gange.

**Brug cachelagret resultat ved gentagne kald.** Når AI'en kalder funktionen igen med de samme input (for eksempel hvis kunden stiller det samme spørgsmål to gange), genbruges det forrige resultat i stedet for at kalde dit endepunkt igen. Cachelagrede resultater gemmes i op til 24 timer, og et kald med *forskellige* input går altid direkte til dit endepunkt.

**Maks. kørsler pr. samtale.** En fast grænse for, hvor mange gange funktionen må køre inden for én samtale. Sæt den til 1 for funktioner, der kun bør køre én gang pr. chat — generering af et tilbud, udløsning af et tilbagekald, start af en automatisering. Når grænsen er nået, får AI'en besked om, at funktionen allerede er kørt, og får det seneste resultat, så den stadig kan svare kunden i stedet for at forblive tavs.

**Maks. kørsler pr. tidsvindue.** En hastighedsbegrænsning over tid: for eksempel højst 5 kørsler inden for 60 minutter. Nyttigt for funktioner, der kalder betalte tredjepartstjenester eller udløser tungere automatiseringer. Begge felter skal udfyldes sammen (et antal kørsler og et vindue i minutter, op til 7 dage).

Et par ting, du bør vide:

- Grænser tæller kun **succesfulde** kørsler. Et kald, der fejlede på dit endepunkts side, bruger ikke af budgettet.
- Når en kørsel blokeres af en grænse, bliver kunden aldrig ladt i stikken — AI'en får besked om hvorfor og arbejder videre med den information, den allerede har.
- Grænser gælder overalt, hvor funktionen kører: almindelige chats på alle kanaler og funktioner styret af en automatisering. Test-samtaler i "Prøv" tælles ikke med og er ikke begrænsede.

---

## Indbyggede bot-værktøjer

Ud over de brugerdefinerede funktioner, du selv bygger, leveres platformen med et bibliotek af præbyggede værktøjer, som AI-botten kan bruge under en samtale. Disse dækker de mest almindelige opgaver, en bot skal udføre — at give besked til en kollega, booke en aftale, tilføje et tag til en kontakt, søge på din hjemmeside, planlægge en opfølgning og meget mere — så du ikke behøver at bygge dem fra bunden.

**Botten beslutter, hvornår den skal bruge hvert værktøj** baseret på, hvad der sker i samtalen, og hvordan din agent (og dens tilknyttede kampagne) er konfigureret. De fleste af disse værktøjer aktiveres automatisk, når den relaterede funktion er aktiveret (for eksempel bliver bookingværktøjerne først tilgængelige, når du forbinder en kalender og aktiverer bookinger).

**Credit-omkostning:** Hvert værktøjskald faktureres baseret på din agents AI-kvalitetsniveau, og brugerdefinerede funktioner, som du selv bygger, faktureres på samme måde:

| AI-kvalitetsniveau | Credits pr. værktøjskald | Med din egen Anthropic-nøgle (BYOK) tilsluttet |
|---|---|---|
| Pro | 1 credit | 0 credits — kører på din nøgle |
| Economy (udgået) | 0,5 credits | 0 credits — kører på din nøgle |
| Max | 0,25 credits | stadig 0,25 credits, faktureres selv med din egen nøgle tilsluttet, da Max kører på vores egen model |
| Mini | 0,15 credits | stadig 0,15 credits, faktureres selv med din egen nøgle tilsluttet, da Mini kører på vores egen model |

### Team- og opgaveværktøjer

| Værktøj | Hvad det gør | Hvornår botten bruger det |
|------|--------------|----------------------|
| **Advar et teammedlem** | Sætter botten på pause for denne kontakt og sender en e-mail til dit team om, at et menneske er nødvendigt. Chatten markeres, så en kollega kan tage over. | Når kunden beder om et menneske, er frustreret eller spørger om noget, som botten ikke må eller kan svare på. |
| **Opret en opgave** | Opretter en ny opgave på din opgavetavle, eventuelt linket til kontakten og samtalen. Botten fortsætter med at svare som normalt — opgaven er blot en note til dit team om at følge op. | Til ikke-hastende emner som et ønske om en funktion, en mersalgsmulighed eller et opkald, som teamet bør håndtere senere. |
| **Foreslå en FAQ-opdatering** | Når botten støder på et spørgsmål, den ikke kan svare godt på, opretter den en opgave, der beder dit team om at tilføje et svar til vidensbasen. | Når en kontakt spørger om noget, der ikke er dækket af dine eksisterende FAQ'er — så hullet bliver rettet til næste gang. |
| **Tilføj kontekst til et FAQ-forslag** | Hvis en anden kontakt senere stiller et lignende spørgsmål med en anden vinkel, tilføjer botten denne kontekst til det eksisterende FAQ-forslag i stedet for at oprette en dubletopgave. | Automatisk — holder din opgaveliste ren, når flere personer rejser det samme videnshul. |

### Kontaktværktøjer

| Værktøj | Hvad det gør | Hvornår botten bruger det |
|------|--------------|----------------------|
| **Tagging** | Kører automatisk efter hvert bot-svar — det er ikke et værktøj, som den kundevendte bot beslutter at kalde. Systemet gennemgår den seneste samtale og anvender relevante tags, hvor det genbruger dine eksisterende tags, hvor det er muligt (og opretter kun et nyt, når det er nødvendigt). | Automatisk — når samtalen afslører noget, der er værd at segmentere på, såsom interesse, hensigt, lead-kvalitet eller sprog. |
| **Opdater kontaktnavn** | Gemmer kontaktens for- og/eller efternavn, når de deler det. | Når kunden præsenterer sig selv eller retter et navn. |
| **Opdater kontaktemail** | Gemmer kontaktens e-mailadresse, når de deler den. | Når kunden oplyser en e-mail — til nyhedsbreve, kvitteringer, opslag af konti osv. |

### Værktøjer til aftaler og booking

Disse værktøjer er kun tilgængelige, når bookinger er aktiveret på den kampagne, der er linket til din agent, og en kalenderbegivenhedstype er konfigureret.

| Værktøj | Hvad det gør | Hvornår botten bruger det |
|------|--------------|----------------------|
| **Tjek ledige tider** | Slår op, hvilke tider der er ledige i din tilknyttede kalender for en given dato eller datointerval. | Når kunden ønsker at booke, og botten skal tilbyde reel tilgængelighed. |
| **Book en aftale** | Opretter aftalen i din kalender og bekræfter bookingen over for kunden. | Efter kunden har bekræftet en specifik dato og tid. |
| **Flyt en aftale** | Ændrer tidspunktet for en eksisterende aftale til en ny dato og tid. | Når kunden beder om at ændre tidspunkt. |
| **Annuller en aftale** | Annullerer en eksisterende aftale. | Når kunden beder om at annullere. |
| **Slå aftaler op** | Henter en kontakts eksisterende aftaler, så botten ved, hvad der allerede er booket. | Når kunden spørger "hvornår er min aftale?" eller før den tilbyder at ændre tidspunkt. |

### Videns- og webværktøjer

| Værktøj | Hvad det gør | Hvornår botten bruger det |
|------|--------------|----------------------|
| **Søg på dit websted** | Scanner de URL'er, du har tilføjet til kampagnens liste over dynamiske URL'er, for at finde produktsider, artikler eller andet indhold, der besvarer kundens spørgsmål. Kun tilgængelig, når **AI-websøgning** er slået til, og du har tilføjet mindst én dynamisk URL. Hvis AI-websøgning er slået fra, kan botten ikke læse sider eller links – heller ikke dem, kunden indsætter i chatten. | Når kunden spørger om noget, der sandsynligvis findes på dit websted – produkter, priser, lokationer, politikker. |
| **Tjek et link** | Læser indholdet af en specifik URL, så botten kan besvare spørgsmål om den side. Kun tilgængelig, når **AI-websøgning** er slået til, og du har tilføjet mindst én dynamisk URL. Hvis AI-websøgning er slået fra, kan botten ikke læse sider eller links – heller ikke dem, kunden indsætter i chatten. | Når kunden deler et link eller spørger om en specifik side på dit websted. |
| **Søg på nettet** | Udfører en offentlig Google-søgning og returnerer de bedste resultater, så botten kan besvare spørgsmål uden for dit eget indhold. | Når kunden spørger om noget generelt (f.eks. rutevejledninger, offentlig information), som ikke findes i din vidensbase. Bruges kun, hvis websøgning er aktiveret. |

### Opfølgningsværktøjer

Disse værktøjer kræver, at opfølgninger er aktiveret på den kampagne, der er knyttet til din Agent.

| Værktøj | Hvad det gør | Hvornår botten bruger det |
|------|--------------|----------------------|
| **Planlæg en smart opfølgning** | Planlægger en intelligent opfølgningsbesked ved hjælp af din opfølgningssekvens – vælger den rette skabelon og timing baseret på samtalen. | Når kunden bliver stille eller beder botten om at "vende tilbage senere". |
| **Planlæg en opfølgning** | Planlægger en grundlæggende opfølgning på et bestemt tidspunkt. | Når botten har brug for at skubbe samtalen videre på et defineret tidspunkt. |

### Kører til brugerdefinerede funktioner

| Værktøj | Hvad det gør | Hvornår botten bruger det |
|------|--------------|----------------------|
| **Kør en brugerdefineret funktion** | Udfører en af de brugerdefinerede funktioner, du har bygget og tildelt Agenten (se resten af denne side). | Når kundens anmodning matcher formålet med en af dine brugerdefinerede funktioner. |

### Værktøjer til restaurantbooking (Zenchef og Formitable)

Disse værktøjer er kun tilgængelige, når en Zenchef- eller Formitable-integration er tilsluttet. De lader botten administrere restaurantreservationer fra start til slut.

| Værktøj | Hvad det gør | Hvornår botten bruger det |
|------|--------------|----------------------|
| **Tjek restauranttilgængelighed** | Slår ledige reservationstider op for en given dato, antal personer og (valgfrit) siddeområde. | Når en gæst beder om at booke et bord. |
| **Opret en restaurantbooking** | Opretter en ny reservation. | Efter gæsten har bekræftet et specifikt tidspunkt. |
| **Opdater en restaurantbooking** | Ændrer dato, tid, antal personer eller noter på en eksisterende reservation. | Når gæsten beder om at ændre sin booking. |
| **Annuller eller skift bookingstatus** | Annullerer en reservation eller opdaterer dens status (f.eks. bekræftet, udeblevet). | Når gæsten annullerer, eller når botten skal markere en statusændring. |
| **Søg i bookinger** | Finder eksisterende reservationer, der matcher kriterier som navn, e-mail eller dato. | Når en tilbagevendende gæst spørger om en eksisterende reservation. |
| **Opdater gæsteprofil** | Opdaterer gæstens profil i restaurantsystemet (præferencer, noter, kontaktoplysninger). | Når gæsten deler kostpræferencer, et nyt telefonnummer eller andre profiloplysninger. |
| **Vis restaurantprodukter** | Henter listen over menuer, faste menuer eller tilvalg, der er tilgængelige for booking. | Når gæsten spørger "hvilke faste menuer har I?" eller botten skal vedhæfte en menu til en reservation. |

### Aktivering og deaktivering af værktøjer

De fleste værktøjer styres under fanen **AI-evner** på Agenten (eller kampagnens **AI-evner**-trin, hvis du arbejder ud fra en stadig klassisk kampagne):

- **Bookingværktøjer** aktiveres, når du aktiverer bookinger og forbinder en kalender – dette forbliver en indstilling pr. kampagne indtil videre, med et link direkte til den kampagnes trin fra Agentens egen AI-evner-fane
- **Opfølgningsværktøjer** aktiveres, når du aktiverer opfølgninger
- **Restaurantværktøjer** aktiveres, når du forbinder en Zenchef- eller Formitable-konto
- **Websøgning** har sin egen kontakt på fanen **FAQ & Viden**
- **Opgaveværktøjer** kan slås fra pr. Agent med kontakten **Tillad AI at oprette opgaver** (de er slået til som standard; den kontodækkende opgavekontakt under **Indstillinger → Profil → Funktioner** slår hele opgavesystemet fra overalt)
- **Kontakt-opdateringsværktøjer** styres under den samme **AI-evner**-fane – om AI'en må omdøbe kontakter eller gemme yderligere indsamlet information på dem
- **Alarmværktøjer** er altid tilgængelige; **tagging** kører automatisk efter hvert bottensvar (det er ikke et værktøj, botten vælger at kalde)

Hvis du vil have botten til at stoppe med at bruge et specifikt indbygget værktøj, er den reneste måde at deaktivere den underliggende funktion (deaktiver f.eks. bookinger for at slå alle bookingværktøjer fra).

---

## Funktioner administreret af en automatisering

Nogle poster på din side for tilpassede funktioner kan have et **Administreret af automatisering**-badge. Disse er ikke oprettet her — de kommer fra en automatisering med en **AI Agent-funktion**-udløser, som giver din agent en evne, hvis trin du opbygger visuelt på automatiseringslærredet i stedet for at pege på en ekstern webadresse.

En administreret funktion passes for dig: dens navn, beskrivelse og felter følger altid det, der er indstillet på automatiseringens trigger, så den kan ikke redigeres eller slettes fra denne side — brug dens **Åbn automatisering**-link og skift selve automatiseringen. Du kan dog stadig vælge, hvilke agenter der skal have den på normal vis: på en agents **AI-evner**-fane vises den sammen med agentens andre evner med en almindelig til/fra-knap (hvis dens automatisering er sat på pause, angiver rækken dette — evnen bliver aktiv, når automatiseringen tændes). Alt andet ved den fungerer ligesom enhver anden brugerdefineret funktion: AI'en beslutter, hvornår den skal kaldes, indsamler de detaljer, du har defineret, og kan bruge automatiseringens svar i den samme samtale.

Hvis du skal vælge mellem de to: peg en almindelig brugerdefineret funktion mod et system, der allerede har en adresse, der kan kaldes; byg en automatisering med en AI Agent Function-udløser, når arbejdet er noget, du hellere vil samle af trin — slå noget op i et regneark eller en database, forgrene baseret på en betingelse, oprette poster — uden at køre din egen server. Se [Automatiseringer](../automations/automations.md#letting-your-ai-agent-call-an-automation).

---

## Plan-krav

Brugerdefinerede funktioner er tilgængelige på planer, der inkluderer funktionen for brugerdefinerede funktioner. Tjek dit abonnement for at bekræfte tilgængelighed.

---

## Næste skridt

- [Forbind MCP-servere til din bot](mcp-servers.md) — en færdig pakke af værktøjer i stedet for én funktion ad gangen.
- [AI-agenter](../ai-agents/ai-agents.md) — hovedsiden for den AI Studio-gruppe, som brugerdefinerede funktioner hører til, og hvor brugerdefinerede funktioner tildeles en bot.
