Kostenlos testen
Handbuch

Eigene API bauen — Tools per API-Key aufrufen

Tools als authentifizierte REST-API freigeben, API-Keys verwalten, OpenAPI-Spec abrufen. Für Server-zu-Server-Anbindungen an Datenbanken, Sheets und eigene Logik.

Eigene API bauen

Mit API-Keys wird dein Workspace zu einer REST-API für deine Bestandssysteme. Du schreibst ein Tool (oder lässt den Builder eins schreiben), gibst es für die API frei und legst im Backend einen Key an. Andere Systeme — Shop, ERP, Zapier, n8n, ein Partner — rufen das Tool dann per HTTP auf.

Typische Fälle:

  • eine Bestandsdatenbank (Shop, CMS, Eigenbau) lesend als API bereitstellen → Rezept
  • ein Google Sheet als kleine Datenbank mit API nutzen → Rezept
  • eigene Fachlogik (Preisberechnung, Verfügbarkeit, Lead-Anlage) für andere Systeme öffnen

Die Plattform kümmert sich um Authentifizierung, Freigabe, Rate-Limit, Parameter-Prüfung, Logging und die OpenAPI-Spec. Die Fachlogik bleibt in deinem Tool.

Abgrenzung zur Public Tool API: Die Public Tool API ist für anonyme Aufrufe aus dem Browser (z. B. Landingpage mit Token-Link). Die API-Key-API ist für Server-zu-Server mit Geheimnis. Beide Freigaben sind unabhängig voneinander.

Nicht pollen — Webhooks: Soll dein System erfahren, wann etwas passiert (neue Inbox-Nachricht, Tool fertig, Scheduled Task fehlgeschlagen), statt regelmäßig /api/v1 abzufragen, richte einen ausgehenden Webhook ein. Umgekehrt kann ein fremdes System über einen eingehenden Webhook ein Tool auslösen, ohne einen API-Key zu verwalten — Tools mit external_api sind dafür automatisch wählbar. Beispiel mit n8n: Rezept.

In drei Schritten

1. Tool freigeben — in der Tool-Definition:

'getKunde' => [
    'description' => 'Liefert Stammdaten eines Kunden per Kundennummer.',
    'internal' => true,
    'external_api' => true,               // für API-Keys freigeben
    'parameters' => [
        'type' => 'object',
        'properties' => [
            'kundennummer' => ['type' => 'string', 'pattern' => '^K[0-9]{5}$'],
        ],
        'required' => ['kundennummer'],
    ],
    'handler' => function (array $results, array $args, array $ctx): array {
        // …
    },
],

2. Key anlegen — Backend → Verwaltung → API-Keys → „Neuer API-Key". Name vergeben, Tools anhaken, Limit setzen. Der Key (tm_live_…) wird genau einmal angezeigt. TaskMonkey speichert nur einen Hash; ein verlorener Key lässt sich nicht wiederherstellen, nur ersetzen.

3. Aufrufen:

curl -X POST https://app.taskmonkey.de/api/v1/tools/getKunde \
  -H 'Authorization: Bearer tm_live_…' \
  -H 'Content-Type: application/json' \
  -d '{"args":{"kundennummer":"K10001"}}'
{"success":true,"found":true,"kunde":{"kundennummer":"K10001","firma":"Gärtnerei Blum","ort":"Bremen","umsatz_vorjahr":48210.5}}

Freigabe: zwei Schlüssel, beide nötig

Ein Aufruf geht nur durch, wenn beides zutrifft:

  1. Das Tool trägt 'external_api' => true (genau true, nicht 1 oder "yes").
  2. Der Key hat das Tool in seiner Liste.

Fehlt eins, antwortet die API mit 403 „Tool nicht verfuegbar" — dieselbe Antwort, egal ob das Tool nicht existiert, nicht freigegeben ist oder nicht im Key steht. So lässt sich die Tool-Liste nicht von außen abtasten.

Weitere Regeln:

  • Der Workspace kommt aus dem Key, nicht aus der URL. Ein Key kann nie Tools eines anderen Workspaces erreichen.
  • Tools mit async: true sind über die API nicht aufrufbar.
  • public_api öffnet ein Tool nicht für Keys und umgekehrt.
  • Der Builder setzt external_api, external_api_write und public_api nur nach einer eigenen Rückfrage im Chat. Technisch lehnt writeTenantConfig jede Änderung ab, die eines dieser Flags neu auf true setzt, solange der Aufruf nicht confirm_exposure=true trägt.

Felder in der Tool-Definition

Feld Pflicht Bedeutung
external_api ja true gibt das Tool für API-Keys frei
external_api_write nein true erlaubt Schreibzugriffe (siehe Nur lesend)
external_api_description nein Beschreibung für Entwickler in der OpenAPI-Spec (statt der LLM-orientierten description)
external_api_response nein JSON Schema der Antwort, landet in der OpenAPI-Spec
parameters empfohlen JSON Schema der Args — wird vor dem Aufruf geprüft

Parameter werden geprüft und gefiltert

Bevor das Tool läuft, gleicht die API die args mit dem parameters-Schema ab:

  • Nicht deklarierte Parameter werden verworfen. Viele Handler lassen sich über Args umkonfigurieren (z. B. slug bei Sheets-Tools). Über die API geht das nur, wenn das Tool den Parameter ausdrücklich deklariert.
  • Geprüft werden required, type, enum, minLength/maxLength, pattern, minimum/maximum, maxItems und items.type. Verstöße → 400 mit Liste in details.
  • Das Tool bekommt zusätzlich args['_api_key'] mit id, name und gekürzter IP des Aufrufers. Dieser Wert wird serverseitig gesetzt und lässt sich nicht fälschen.

Je enger das Schema, desto sicherer das Tool. Ein pattern für IDs kostet eine Zeile und hält Unfug fern, bevor die Datenbank angefasst wird.

Standardmäßig nur lesend

Tools laufen über die API im Read-only-Kontext ($ctx['read_only'] === true), auch alle Tools, die sie intern aufrufen. Dann gilt:

  • executePreparedQuery mit mode: 'write' wird verweigert
  • executeQuery (freies SQL) ist über die API immer gesperrt
  • writeToGoogleSheet und clearFromGoogleSheet werden verweigert

Ein Tool, das schreiben soll, deklariert das ausdrücklich:

'external_api' => true,
'external_api_write' => true,   // bewusst schreibend — Begründung im Kommentar

Der Read-only-Kontext deckt DB-Gateway und Google Sheets ab. Andere Seiteneffekte (Mails, deklarative api-Calls mit POST) hält er nicht auf — dafür ist die Freigabe selbst da: Gib nur Tools frei, deren Wirkung du für Key-Inhaber verantworten kannst.

Statuscodes

Status Bedeutung Body
200 Tool wurde ausgeführt Rückgabe des Tools (kann success: false enthalten)
400 Parameter ungültig oder Tool mit Exception abgebrochen {"success":false,"error":"Ungueltige Parameter","details":[…]} bzw. neutrale Meldung
401 Key fehlt, falsches Format, unbekannt, widerrufen oder abgelaufen {"success":false,"error":"Ungueltiger oder fehlender API-Key"} + WWW-Authenticate: Bearer
403 Tool nicht verfügbar (siehe Freigabe) oder Workspace gesperrt {"success":false,"error":"Tool nicht verfuegbar"}
405 Falsche Methode (Tools: nur POST, Spec: nur GET)
429 Rate-Limit erreicht {"success":false,"error":"…","retry_after":17} + Retry-After
500 Unerwarteter Fehler {"success":false,"error":"Interner Fehler"}

Interne Fehlermeldungen gehen nie nach außen, sie stehen im Log.

Rate-Limit

Jeder Key hat ein Limit in Aufrufen pro Minute (Default 60, einstellbar 1–10 000). Gezählt wird in einem festen Minutenfenster (:00 bis :59), nicht gleitend. Jede Antwort mit gültigem Key trägt:

Header Inhalt
X-RateLimit-Limit Limit pro Minute
X-RateLimit-Remaining verbleibende Aufrufe im aktuellen Fenster
X-RateLimit-Reset Unix-Zeit, zu der das Fenster endet
Retry-After nur bei 429: Sekunden bis zum nächsten Fenster

Abgelehnte Aufrufe (429) zählen nicht mit. Bei vielen gleichzeitigen Requests kann das Limit kurzzeitig um deren Anzahl überschritten werden.

API-Aufrufe werden derzeit nicht auf das Kontingent deines Plans angerechnet.

OpenAPI-Spec

curl https://app.taskmonkey.de/api/v1/openapi.json -H 'Authorization: Bearer tm_live_…'

liefert eine OpenAPI 3.1-Spec mit genau den Tools, die dieser Key aufrufen darf — erzeugt aus den parameters-Schemas. Damit lassen sich Clients generieren, Postman/Insomnia befüllen oder die API in Tools wie n8n, Make oder einen GPT/Agenten einbinden. Ohne Key gibt es 401: die Tool-Liste ist nicht öffentlich.

Keys verwalten

Backend → Verwaltung → API-Keys (/manage/api-keys, Rolle Manager oder Admin):

  • Liste mit Status, Präfix, erlaubten Tools, Limit, zuletzt benutzt, Aufrufe der letzten 24 h und gesamt
  • Bearbeiten: Name, Tools, Limit, Ablaufdatum; darunter die letzten 50 Aufrufe mit Status und Dauer
  • Widerrufen wirkt sofort (nächster Aufruf → 401). Der Eintrag bleibt fürs Audit sichtbar.

Tools, die im Key stehen, aber nicht (mehr) external_api tragen, werden rot markiert — Aufrufe darauf enden mit 403.

Im Builder

Im Builder (/manage/builder → Tools → Tool öffnen) hat jedes Tool einen Tab API. Für ein Tool mit external_api zeigt er:

  • Endpoint (POST {Base-URL}/api/v1/tools/{tool}) und den Header Authorization: Bearer tm_live_… — nur als Platzhalter, ein echter Key wird nie angezeigt
  • Beispiel-Request aus args_fixture bzw. dem parameters-Schema (nur deklarierte Felder — alles andere verwirft die API ohnehin)
  • Snippets für curl, JavaScript, Python, PHP (Guzzle), PHP (curl) und eine Postman-Collection v2.1 (Key als Collection-Variable apiKey). Die Code-Snippets lesen den Key aus der Umgebungsvariable TASKMONKEY_API_KEY, damit er nicht im Code landet.
  • welche Keys das Tool erlauben (Name, Präfix, Limit, Status) mit Link auf API-Keys — ohne passenden Key gibt es einen Hinweis
  • Link und curl-Aufruf für die OpenAPI-Spec, Hinweis nur lesend bzw. schreibend (external_api_write)
  • Statuscodes, Rate-Limit-Header, Sicherheits-Checkliste, Beispiel-Response (echte letzte Antwort mit maskierten Feldern oder schematisch)

Ist das Tool (noch) nicht freigegeben, erklärt der Tab den Unterschied zur Public Tool API und bietet an, die Freigabe per Builder-Chat vorzunehmen (der zeigt die Änderung vor dem Schreiben). Ist external_api gesetzt, aber nicht exakt true — oder das Tool ist async —, warnt der Tab, dass die Freigabe nicht greift.

Ausprobieren funktioniert nur mit einem Key, den du dort einfügst. Er wird nicht gespeichert, nur mit diesem einen Request gesendet und beim Verlassen des Tabs verworfen. Der Aufruf läuft echt (inklusive Nutzungs-Log und Rate-Limit des Keys).

Sicherheit

  • Keys sind Passwörter und gehören ins Backend. Nie in Browser-JavaScript, Apps, öffentliche Repos oder Screenshots. Die API erzwingt das zum Teil selbst: /api/v1/* sendet keine CORS-Header (auch nicht beim Preflight), Browser verweigern Aufrufe von fremden Domains also. Wenn deine Website die Daten braucht, ruft dein eigener Server die API auf und reicht nur das Nötige an den Browser weiter. Für anonyme Aufrufe direkt aus dem Browser ist die Public Tool API gedacht (dort ist CORS offen).
  • Ein Key pro System. Dann lässt sich ein einzelnes System widerrufen, ohne die anderen zu stören, und die Nutzung ist pro System sichtbar.
  • Ablaufdatum setzen, wenn ein Key nur für ein Projekt gebraucht wird.
  • Nur Anzeige-Felder zurückgeben. Was das Tool zurückgibt, sieht der Aufrufer. Keine internen IDs, Tokens oder Metadaten durchreichen.
  • Schreibende Tools begründen. Neben external_api_write steht im Kommentar, warum das vertretbar ist.

Checkliste für jedes Tool mit external_api:

  • parameters so eng wie möglich (pattern, enum, maximum)
  • SQL steht fest im Tool, Werte nur über params (executePreparedQuery)
  • Rückgabe auf die nötigen Felder reduziert
  • external_api_write nur, wo wirklich geschrieben wird
  • Keine Mails, LLM-Calls oder teuren Seiteneffekte, die ein Key-Inhaber beliebig oft auslösen kann — oder bewusst mit niedrigem Limit

Logging

Jeder Aufruf mit gültigem Key landet im Nutzungs-Log (api_key_requests: Key, Tool, Status, Dauer, gekürzte IP) — sichtbar in der Key-Ansicht. Einträge werden nach 90 Tagen gelöscht (täglicher Cron prune_api_key_requests); die Gesamtzahl und „zuletzt benutzt" am Key bleiben erhalten. Der Tool-Lauf selbst erscheint zusätzlich wie gewohnt im Execution-Log mit Entry-Type api_key und einer Chat-ID apikey_<key-id>_<zeit>_<zufall> (tm monitor, tm watch).

Testen

Tool-Logik lokal wie jedes andere Tool:

bin/cake test_tool -t meinworkspace --tool getKunde --args='{"kundennummer":"K10001"}'

test_tool läuft ohne Read-only-Kontext und ohne Parameter-Filter. Den kompletten Weg prüfst du per curl gegen /api/v1/… mit einem Test-Key.

Technik

Was Wo
Endpoint src/Controller/Api/ExternalApiController.php
Key-Logik, Gate, Rate-Limit src/Service/ApiKey/ApiKeyService.php
Parameter-Prüfung src/Service/ApiKey/ToolArgsValidator.php
OpenAPI src/Service/ApiKey/OpenApiBuilder.php
Verwaltung src/Controller/Manage/ApiKeysController.php
Routen config/routes.php (/api/v1/tools/{tool}, /api/v1/openapi.json)
Tabellen api_keys, api_key_requests

Siehe auch: Webhooks — Ereignisse senden und Tools per eingehendem Webhook auslösen.

Zuletzt aktualisiert: 2026-09-25