Integraties

E-mail versturen vanuit OpenAI function calling

Definieer Wesender als OpenAI-tool zodat GPT-4o zelf besluit wanneer en naar wie er gemaild wordt.

Agent-framework Webhooks

Vereisten

  • Wesender-account met geverifieerd verzenddomein
  • API-sleutel (WESENDER_API_KEY)
  • OpenAI API-sleutel (OPENAI_API_KEY)
  • Node.js 18 of nieuwer

Stap 1: Installeer de packages

Installeer de OpenAI SDK en de Wesender SDK.

npm install openai @wesender/node

Stap 2: Definieer het tool-schema

Beschrijf de send_email-functie als een OpenAI-tool. Het model gebruikt de description en parameters om te beslissen wanneer en hoe het de tool aanroept.

import OpenAI from "openai"

const tools: OpenAI.Chat.ChatCompletionTool[] = [
  {
    type: "function",
    function: {
      name: "send_email",
      description: "Verstuur een e-mail via Wesender",
      parameters: {
        type: "object",
        properties: {
          to:      { type: "string", description: "Ontvanger-e-mailadres" },
          subject: { type: "string", description: "Onderwerpregel" },
          html:    { type: "string", description: "HTML-inhoud van het bericht" },
        },
        required: ["to", "subject", "html"],
      },
    },
  },
]

Stap 3: Schrijf de tool-handler

Verwerk de tool-aanroep en stuur de e-mail via Wesender. Het resultaat geef je terug als tool-bericht zodat het model de uitkomst kent.

import { Wesender } from "@wesender/node"

const emailClient = new Wesender(process.env.WESENDER_API_KEY!)

async function handleToolCall(
  toolCallId: string,
  args: { to: string; subject: string; html: string }
): Promise<OpenAI.Chat.ChatCompletionToolMessageParam> {
  try {
    await emailClient.emails.send({
      from: "agent@jouwdomein.nl",
      to: args.to,
      subject: args.subject,
      html: args.html,
    })
    return { role: "tool", tool_call_id: toolCallId, content: "E-mail verstuurd" }
  } catch (err) {
    return { role: "tool", tool_call_id: toolCallId, content: "Fout: " + String(err) }
  }
}

Stap 4: Bouw de agent-loop

Roep het model aan in een loop totdat het stopt met tool-aanroepen. Dit patroon werkt met alle modellen die de tools-parameter ondersteunen.

const openai = new OpenAI()

async function runAgent(userPrompt: string): Promise<string> {
  const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [
    { role: "user", content: userPrompt },
  ]

  while (true) {
    const response = await openai.chat.completions.create({
      model: "gpt-4o",
      messages,
      tools,
      tool_choice: "auto",
    })

    const choice = response.choices[0]
    messages.push(choice.message)

    if (choice.finish_reason === "stop") return choice.message.content ?? ""

    for (const call of choice.message.tool_calls ?? []) {
      if (call.function.name === "send_email") {
        const args = JSON.parse(call.function.arguments)
        messages.push(await handleToolCall(call.id, args))
      }
    }
  }
}

Volledig voorbeeld

Hier is de volledige implementatie in agent.ts:

import OpenAI from "openai"
import { Wesender } from "@wesender/node"

const openai = new OpenAI()
const emailClient = new Wesender(process.env.WESENDER_API_KEY!)

// Tool-schema
const tools: OpenAI.Chat.ChatCompletionTool[] = [
  {
    type: "function",
    function: {
      name: "send_email",
      description: "Verstuur een transactionele e-mail via Wesender",
      parameters: {
        type: "object",
        properties: {
          to:      { type: "string", description: "Ontvanger-e-mailadres" },
          subject: { type: "string", description: "Onderwerpregel" },
          html:    { type: "string", description: "HTML-inhoud van het bericht" },
        },
        required: ["to", "subject", "html"],
      },
    },
  },
]

// Tool-handler
async function handleToolCall(
  toolCallId: string,
  name: string,
  rawArgs: string
): Promise<OpenAI.Chat.ChatCompletionToolMessageParam> {
  if (name !== "send_email") {
    return { role: "tool", tool_call_id: toolCallId, content: "Onbekend tool" }
  }
  try {
    const args = JSON.parse(rawArgs) as { to: string; subject: string; html: string }
    await emailClient.emails.send({
      from: "agent@jouwdomein.nl",
      to: args.to,
      subject: args.subject,
      html: args.html,
    })
    return { role: "tool", tool_call_id: toolCallId, content: "E-mail verstuurd" }
  } catch (err) {
    return { role: "tool", tool_call_id: toolCallId, content: "Fout: " + String(err) }
  }
}

// Agent-loop
async function runAgent(userPrompt: string): Promise<string> {
  const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [
    { role: "user", content: userPrompt },
  ]

  while (true) {
    const response = await openai.chat.completions.create({
      model: "gpt-4o",
      messages,
      tools,
      tool_choice: "auto",
    })

    const choice = response.choices[0]
    messages.push(choice.message)

    if (choice.finish_reason === "stop") return choice.message.content ?? ""

    for (const call of choice.message.tool_calls ?? []) {
      messages.push(await handleToolCall(call.id, call.function.name, call.function.arguments))
    }
  }
}

// Voorbeeld
const output = await runAgent("Stuur een welkomstmail naar jan@jouwdomein.nl")
console.log(output)

Bezorgstatussen via webhooks

OpenAI function calling kan bezorg-, bounce- en klachtsignalen ontvangen via webhooks. Configureer een webhook-endpoint in je OpenAI function calling-workflow dat POST-verzoeken van Wesender accepteert. Zie Webhooks instellen voor de volledige configuratiestappen en het payload-formaat.

Veelgestelde vragen

Werkt dit ook met Anthropic Claude-modellen?

Ja, maar dan gebruik je de Anthropic SDK in plaats van openai. Het patroon is vergelijkbaar: definieer tools als JSON-schema, verwerk tool_use-blokken in de response en stuur tool_result-blokken terug. De Wesender-handler blijft hetzelfde.

Hoe beperk ik wat het model mag mailen?

Voeg validatie toe in de handler vóór de emailClient.emails.send()-aanroep. Controleer het ontvangeradres op een toegestane lijst, filter onderwerpen of begrens het HTML-formaat. Het model heeft geen directe toegang tot de API, alleen via jouw handler.

Kan ik bijlagen meesturen via function calling?

Ja. Voeg een attachments-parameter toe aan het tool-schema en verwerk die in de handler. Geef bijlagen base64-gecodeerd mee in de attachments-array van de Wesender API-aanroep.

Volgende stappen