Webhook-Endpoint — Angebot importieren
Der Webhook-Endpoint legt ein einzelnes Angebot als Entwurf in deiner Flino-Organisation an. Er ist idempotent über die Kombination external_source + external_id, sodass derselbe Aufruf mehrfach abgesendet kein Duplikat erzeugt.
Endpoint
Abschnitt betitelt „Endpoint“POST https://<euer-projekt>.supabase.co/functions/v1/zapier-offer-importDen <euer-projekt>-Teil findest du in Einstellungen → Integrationen → API-Tokens in der Sektion „Zapier Webhook URL”.
Diese Supabase-Functions-URL ist die kanonische und dauerhafte Endpoint-Adresse für den Webhook. Eine eigene API-Subdomain (z. B. api.flino.io) ist aktuell nicht geplant — verwende die hier gezeigte URL als stabile Adresse.
Authentifizierung
Abschnitt betitelt „Authentifizierung“Header:
x-flino-token: <dein-api-token>Content-Type: application/jsonDen <dein-api-token> erstellst du wie unter API-Tokens beschrieben. Pro Token ist der Scope fest auf offer_import:write gesetzt.
Methode
Abschnitt betitelt „Methode“Nur POST wird akzeptiert. Andere Methoden geben 405 Method not allowed zurück.
Request Body — Felder
Abschnitt betitelt „Request Body — Felder“Der Body muss ein JSON-Objekt sein. Listen oder Strings werden mit 400 Request body must be a JSON object abgewiesen.
Pflichtfelder
Abschnitt betitelt „Pflichtfelder“| Feld | Typ | Beschreibung |
|---|---|---|
title | string, nicht leer | Titel des Angebots, z. B. „Website-Redesign Mustermann GmbH” |
external_id | string, nicht leer | Eindeutige ID aus deinem System — Schlüssel für Idempotenz |
external_source | string, nicht leer | Quell-System, z. B. zoho, pipedrive, zapier, myform — wird intern in Kleinbuchstaben gespeichert |
Optionale Felder
Abschnitt betitelt „Optionale Felder“| Feld | Typ | Beschreibung / Default |
|---|---|---|
amount | number | null | Angebotsbetrag in Hauptwährungs-Einheit (z. B. 1500.00) |
currency | string | EUR (Default), USD oder CHF. Andere Werte werden mit 400 abgewiesen. |
contact_name | string | null | Voller Name. Flino splittet automatisch in Vor- und Nachname. |
contact_email | string | null | Muss ein valides E-Mail-Format haben. Wird in Kleinbuchstaben gespeichert. |
company_name | string | null | Firmenname; Flino legt eine neue Firma an oder verwendet eine bestehende |
offer_number | string | null | Eigene Angebotsnummer |
valid_until | string | null | ISO-Datum, z. B. 2026-12-31. Ungültige Datumswerte werden ignoriert (Wert bleibt null). |
Konsumiert / abgeleitet
Abschnitt betitelt „Konsumiert / abgeleitet“- Anrede und Sprache werden im Phase-1-Webhook nicht übermittelt — der Kontakt bekommt sie manuell oder beim ersten Versand-Modal nachgepflegt.
- Adresse wird im Phase-1-Webhook nicht übermittelt.
Idempotenz
Abschnitt betitelt „Idempotenz“Flino prüft pro Aufruf, ob in deiner Organisation bereits ein Angebot mit derselben Kombination external_source + external_id existiert.
- Existiert es: kein neues Angebot, Antwort
200mitcreated: falseund der bestehendenoffer_id. - Existiert es nicht: neues Angebot wird angelegt, Antwort
200mitcreated: true.
Das ist nützlich, wenn dein Quell-System Retries macht — du läufst nicht in Duplikate.
Erfolgs-Response
Abschnitt betitelt „Erfolgs-Response“Status 200 OK:
{ "success": true, "created": true, "offer_id": "8d3f7e09-1e0e-44e5-b6c3-49e3a7c2a131", "status": "draft"}Bei Idempotenz-Treffer:
{ "success": true, "created": false, "offer_id": "8d3f7e09-1e0e-44e5-b6c3-49e3a7c2a131", "status": "draft"}Vollständiges Beispiel
Abschnitt betitelt „Vollständiges Beispiel“Request
Abschnitt betitelt „Request“POST /functions/v1/zapier-offer-import HTTP/1.1Host: <euer-projekt>.supabase.coContent-Type: application/jsonx-flino-token: 9a3f...c1e2
{ "title": "Website-Redesign Mustermann GmbH", "amount": 4900, "currency": "EUR", "contact_name": "Anna Mustermann", "contact_email": "anna@mustermann.de", "company_name": "Mustermann GmbH", "offer_number": "ANG-2026-0042", "valid_until": "2026-06-30", "external_id": "deal_19238", "external_source": "zoho"}Response
Abschnitt betitelt „Response“{ "success": true, "created": true, "offer_id": "8d3f7e09-1e0e-44e5-b6c3-49e3a7c2a131", "status": "draft"}Fehlerfälle
Abschnitt betitelt „Fehlerfälle“| HTTP-Status | Body error | Bedeutung |
|---|---|---|
400 | Invalid JSON body | Body ist kein gültiges JSON |
400 | Request body must be a JSON object | Body ist Liste oder String |
400 | Field 'title' is required and must be a non-empty string | title fehlt oder leer |
400 | Field 'external_id' is required and must be a non-empty string | external_id fehlt oder leer |
400 | Field 'external_source' is required and must be a non-empty string | external_source fehlt oder leer |
400 | Field 'currency' must be one of: EUR, USD, CHF | unzulässige Währung |
400 | Field 'amount' must be a number | amount als String oder Boolean |
400 | Field 'contact_email' is not a valid email address | Mail-Format ungültig |
401 | Missing x-flino-token header | Header fehlt |
401 | Invalid or unknown token | Token nicht gefunden |
401 | Token has been revoked | Token wurde widerrufen |
403 | Token lacks required scope: offer_import:write | Scope passt nicht |
405 | Method not allowed | nur POST erlaubt |
500 | Organisation owner not found | Org-Konfigurationsproblem — Support kontaktieren |
500 | Failed to create offer: ... | Datenbank-Problem |
Alle Fehler-Antworten haben das Schema:
{ "success": false, "error": "<Klartext-Beschreibung>"}Was Flino aus deinem Aufruf macht
Abschnitt betitelt „Was Flino aus deinem Aufruf macht“- Token validieren — SHA-256-Lookup, Scope-Check, Revoke-Check.
- Idempotenz prüfen — gibt’s das Angebot schon? Wenn ja, dasselbe zurückgeben.
- Firma anlegen oder wiederverwenden — wenn
company_namegesetzt ist, sucht Flino zuerst eine vorhandene Firma mit demselben Namen in deiner Org. Findet sie keine, wird eine neue angelegt. - Kontakt anlegen oder wiederverwenden — über
contact_email(wenn gesetzt) als Schlüssel; sonst über Name. Flino splittetcontact_nameautomatisch in Vor- und Nachname. - Angebot als
draftanlegen — mit Versandstatusnot_sent, Quellezapier, deinenexternal_idundexternal_sourcefür die nächste Idempotenz-Prüfung.
Was passiert nach dem Import
Abschnitt betitelt „Was passiert nach dem Import“- Das Angebot landet im Posteingang und in der Pipeline mit Status Neu.
- Du öffnest es manuell, ergänzt fehlende Daten (Anrede, Mail-Adresse, Beschreibung) und versendest es wie gewohnt aus Flino.
- Es startet keine Sequenz, es wird keine Mail verschickt — der Webhook ist reines Anlegen.
Rate-Limits
Abschnitt betitelt „Rate-Limits“Aktuell keine Flino-spezifischen Rate-Limits. Wir behalten uns vor, bei Missbrauch limitierend einzugreifen.
Idempotenz für Wiederholungen wird per external_id + Inhalts-Hash sichergestellt — wiederholte Aufrufe mit identischen Daten erzeugen kein doppeltes Angebot.
Wenn du Massen-Importe planst (mehrere hundert Angebote auf einmal), staffel die Aufrufe und sprich kurz mit dem Support, damit deine Org nicht aus Versehen als Anomalie auffällt.