Schnittstelle
Verkäufe, Rückerstattungen und Auszahlungen laufen automatisch in die Buchhaltung – als Rechnung, gebucht, in derselben Nummernreihe wie alles andere.
Schnittstelle
In drei Schritten
1. Schlüssel holen. In fily: Einstellungen → Schnittstelle → Schlüssel ausstellen. Er wird einmal angezeigt – kopier ihn sofort. Sichtbar ist der Bereich für Inhaber und Administration.
2. Beim Verkauf einliefern. In deinem Stripe-Webhook, bei checkout.session.completed oder invoice.paid:
await fetch("https://fily-files.fily-files-worker.workers.dev/v1/verkaeufe", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.FILY_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
referenz: session.id, // Idempotenz
datum: new Date(session.created * 1000).toISOString().slice(0, 10),
kunde: {
name: session.customer_details.name,
land: session.customer_details.address?.country ?? "CH",
email: session.customer_details.email,
},
positionen: [{
text: "Pro-Abo",
netto: session.amount_subtotal, // Rappen, ohne MWST
konto: "3400",
}],
}),
});3. Bei der Auszahlung abrechnen. Bei payout.paid die Gebühr und den Bankeingang buchen – siehe *Die Auszahlung* weiter unten. Bei charge.refunded die Rückerstattung.
Mehr braucht es nicht. Alles Weitere auf dieser Seite erklärt die Feinheiten.
Vorher ausprobieren? GET /v1/test sagt, ob der Schlüssel gilt und welche Firma er öffnet; ?probe=1 an jedem Endpunkt rechnet den Rumpf durch, ohne zu buchen. Siehe *Erst probieren* gleich unten.
Erst probieren
Bevor der erste echte Verkauf durchläuft: zwei Wege, beide ohne dass etwas gebucht wird.
Gilt mein Schlüssel?
GET https://fily-files.fily-files-worker.workers.dev/v1/test
Authorization: Bearer fily_live_…{
"ok": true,
"firma": "apgraphics",
"waehrung": "CHF",
"mwst": "effective",
"ertragskontoDa": true,
"rechnungen": 42
}Steht dort der falsche Firmenname, hast du den falschen Schlüssel eingesetzt – besser jetzt als später, wenn die Rechnung in fremden Büchern steht. Ist ertragskontoDa false, fehlt das Konto 3400; dann scheitert der erste Verkauf, und du stehst mit einem Webhook da, der 400 zurückgibt.
GET oder POST, beides geht. Es wird nichts geschrieben – auch *zuletzt benutzt* nicht: eine Probe ist keine Benutzung.
Stimmt mein Rumpf?
Häng ?probe=1 an – an jeden der drei Endpunkte:
POST /v1/verkaeufe?probe=1
Authorization: Bearer fily_live_…Schick den Rumpf, den du auch im Ernstfall schicken würdest. Zurück kommt, was daraus geworden wäre:
{
"probe": true,
"gebucht": false,
"hinweis": "Alles geprueft, nichts gebucht. So saehe es im Ernstfall aus.",
"ergebnis": {
"nummer": "2026-0001", "netto": 10000, "mwst": 810,
"total": 10810, "behandlung": "inland", "neu": true
}
}Also: welche Nummer die Rechnung bekäme, wie viel MWST anfiele und wie fily die Steuer herleitet. Stimmt etwas nicht, kommt dieselbe Fehlermeldung wie im Ernstfall – „Der Verkauf hat keine Positionen mit Betrag", „Betrag in EUR eingeliefert, die Firma bucht in CHF".
Die Probe ist kein Nachbau. Sie ruft genau die Funktion, die auch bucht, und dreht danach alles zurück: Rechnung, Buchung, angelegter Kunde, gezogene Nummer. Die nächste echte Rechnung bekommt dieselbe Nummer, die die Probe genannt hat. Ein Nachbau wäre beim nächsten Feld veraltet und würde etwas anderes sagen als der Ernstfall – und genau darauf würde man sich verlassen.
Antwort ist 200, nicht 201: erschaffen wurde nichts.
Verkäufe einliefern
Für Systeme, die automatisch verkaufen – der Webhook hinter einer Bezahlseite, ein Abo-Dienst, ein eigener Shop. Ein Verkauf wird zur Rechnung und gleich verbucht, in derselben Nummernreihe wie von Hand geschriebene Rechnungen.
POST https://fily-files.fily-files-worker.workers.dev/v1/verkaeufe
Authorization: Bearer fily_live_…
Content-Type: application/jsonDen Schlüssel stellst du in fily aus: Einstellungen → Schnittstelle. Er wird einmal angezeigt. In fily liegt nur sein Abdruck; geht er verloren, stellst du einen neuen aus und ziehst den alten zurück.
Der Schlüssel sagt auch, um welche Firma es geht – deshalb steht keine in der Adresse.
Der Rumpf
{
"referenz": "stripe_ch_3Qx7…",
"datum": "2026-08-26",
"faellig": "2026-09-25",
"kunde": {
"name": "Berliner Software GmbH",
"land": "DE",
"email": "buchhaltung@kunde.de",
"uid": "DE811234567",
"adresse": { "street": "Torstrasse 1", "zip": "10119", "city": "Berlin", "country": "DE" }
},
"positionen": [
{ "text": "Pro-Abo 08/2026", "netto": 4900, "menge": 1, "satz": 8.1, "konto": "3400" }
]
}| Feld | Pflicht | Bedeutung |
|---|---|---|
referenz | – | Deine Kennung für diesen Verkauf. Nimm sie. Siehe unten. |
datum | – | Rechnungsdatum, sonst heute |
faellig | – | Zahlbar bis, sonst gleich datum |
kunde.name | ja | Gibt es ihn noch nicht, wird er angelegt |
kunde.land | – | Zwei Buchstaben, sonst CH. Entscheidet über die MWST. |
positionen[].netto | ja | In Rappen, ohne MWST. 4900 sind CHF 49.00 |
positionen[].satz | – | Nur im Inland, sonst 8.1 |
positionen[].konto | – | Ertragskonto, sonst 3400 |
mwst | – | Überschreibt die Herleitung: inland, ausland, befreit, ausgenommen |
Antwort:
{ "rechnung": "…", "nummer": "2026-0042", "netto": 4900,
"mwst": 0, "total": 4900, "behandlung": "ausland", "neu": true }Zweimal senden ist erlaubt
Webhooks werden wiederholt – gerade dann, wenn die Antwort verlorenging und dein System nicht weiss, ob es geklappt hat. Schick dieselbe referenz nochmal: fily gibt dieselbe Rechnung zurück, mit "neu": false, und bucht nicht ein zweites Mal.
Ohne referenz gibt es diesen Schutz nicht. Dann ist jeder Aufruf ein neuer Verkauf.
MWST
Das Land des Kunden entscheidet, und zwar so:
- `CH` → normal versteuert, mit dem Satz der Position.
- alles andere → *Leistung im Ausland*. Der Ort der Leistung liegt beim Empfänger (Art. 8 Abs. 1 MWSTG), also fällt keine Schweizer MWST an. Ein mitgeschickter
satzwird ignoriert.
Das ist eine Herleitung, keine Rechtsauskunft. Sie stimmt für digitale Dienstleistungen. Warenlieferungen, Grundstücke, Anlässe und Telekom folgen anderen Regeln – setz dann mwst ausdrücklich.
In der Abrechnung erscheint der Auslandumsatz in Ziffer 221 und wird dort von Ziffer 200 wieder abgezogen. Im Empfängerland kann trotzdem eine Steuerpflicht bestehen; das entscheidet nicht fily.
Fremdwährung
fily bucht in der Währung der Firma. Lieferst du etwas anderes ein, wird es abgewiesen statt geraten – eine Buchung in der falschen Währung sieht richtig aus und ist um den Kurs daneben.
Rechne im eigenen System um. Stripe liefert bei jeder Zahlung den Kurs und den Betrag in deiner Auszahlungswährung mit.
Rückerstattungen
POST /v1/rueckerstattungen{
"referenz": "re_1QxDeF…",
"verkauf": "cs_test_a1b2",
"datum": "2026-09-01",
"betrag": 3000
}verkauf ist die referenz des ursprünglichen Verkaufs. betrag ist brutto – was beim Kunden ankommt, also die Zahl, die Stripe meldet. Lässt du ihn weg, geht alles zurück.
Daraus wird eine Gutschrift: dieselben Konten wie beim Verkauf, nur andersherum.
Erlös soll 2 775
MWST soll 225
an Debitoren haben 3 000Storniert wird nichts. Die Rechnung bleibt stehen, die Gutschrift kommt daneben, und beide sind im Journal zu sehen – das ist der Unterschied zwischen einer Korrektur und einer Fälschung.
Teilbeträge
Die Steuer wird im Verhältnis geteilt: netto anteilig, Steuer als Rest. So ergeben beide zusammen immer genau den erstatteten Betrag, ohne dass durch zweimaliges Runden ein Rappen verlorengeht. Die Positionen gehen anteilig auf dieselben Ertragskonten zurück, auf denen sie entstanden sind.
Mehrere Teilerstattungen zur selben Rechnung sind erlaubt – zusammen dürfen sie die Rechnung nicht überschreiten:
Zu viel: die Rechnung lautet ueber 10810, erstattet waeren dann mehr.Die Auszahlung
Beim Verkauf entsteht eine Forderung – das Geld liegt bei Stripe, nicht auf der Bank. Erst die Auszahlung bringt es herüber, gekürzt um die Gebühr. Dafür der zweite Endpunkt:
POST /v1/auszahlungen
Authorization: Bearer fily_live_…{
"referenz": "po_1QxAbC…",
"datum": "2026-08-28",
"betrag": 29902,
"gebuehr": 800,
"verkaeufe": ["stripe_ch_3Qx7…", "stripe_de_3Qy8…"],
"konto": "1020"
}betrag ist, was auf dem Bankkonto ankommt. verkaeufe sind die referenz-Werte der Verkäufe, die diese Auszahlung deckt. Stecken Rückerstattungen darin, nenn sie in rueckerstattungen:
{
"referenz": "po_1QxAbC…",
"betrag": 7610,
"gebuehr": 200,
"verkaeufe": ["cs_test_a1b2"],
"rueckerstattungen": ["re_1QxDeF…"]
}Daraus wird eine Buchung:
Bank soll 29 902 was ankommt
Finanzaufwand soll 800 die Gebühr
an Debitoren haben 30 702 die Summe der RechnungenDer Erlös bleibt damit brutto stehen – er wurde beim Verkauf gebucht und wird hier nicht angefasst. Wer nur die Auszahlung bucht, weist zu wenig Umsatz aus und rechnet die MWST auf einer zu kleinen Grundlage ab.
Die Rechnungen gelten danach als bezahlt.
Über die Debitoren geht nur der Rest – die Gutschrift hat die Forderung schon gemindert, als sie erfasst wurde. Wer sie hier nochmals abzieht, bucht sie zweimal.
fily rechnet nach
Verkäufe − Rückerstattungen − Gebühr = Auszahlung, rappengenau. Geht es nicht auf, kommt eine 400 mit den Zahlen:
Geht nicht auf: 2 Rechnungen ergeben 30702, minus 800 Gebuehr
waeren 29902, eingeliefert wurde 30402.Das ist Absicht. Eine Auszahlung, die nicht aufgeht, deutet auf eine Rückerstattung oder einen Verkauf hin, der nie eingeliefert wurde – beides will man wissen und nicht wegrunden. Kennt fily einen der genannten Verkäufe nicht, steht er in der Meldung.
Erfass die Rückerstattung, bevor du die Auszahlung schickst, in der sie steckt – sonst kennt fily sie noch nicht.
Fehler
| HTTP | Bedeutung | Nochmal versuchen? |
|---|---|---|
| 400 | Der Rumpf stimmt nicht – die Meldung sagt, was fehlt | nein, erst korrigieren |
| 401 | Kein Schlüssel im Kopf | nein |
| 403 | Schlüssel unbekannt oder zurückgezogen | nein |
| 429 | Zu viele Anfragen (120 je Minute und Schlüssel) | ja, mit Abstand |
| 5xx | Bei uns ist etwas schiefgegangen | ja |
Der Schlüssel
Er schreibt Buchungen. Er gehört auf deinen Server – nicht in eine Webseite, nicht ins Repository, nicht in eine mobile App. Wer ihn hat, kann in deinem Namen Umsatz erfassen.
Zurückziehen kannst du ihn jederzeit unter Einstellungen → Schnittstelle. Bereits erfasste Verkäufe bleiben stehen.