maesn
Für Entwickler

FreshBooks anbinden: Zwei Kennungen, zwei Datumsfilter

FreshBooks ist vom Account-Konzept auf ein Business-Konzept gewechselt und hat beide behalten. Welche Kennung eine Ressource verlangt, entscheidet mehr als die URL: es entscheidet auch, ob Sie nach Änderungen seit einem Zeitpunkt fragen können oder nur seit einem Tag.

Lennart Svensson, CTO und Mitgründer bei Maesn
Lennart Svensson
CTO und Mitgründer · · Aktualisiert
Illustration zu FreshBooks anbinden: Jeden Datensatz zurückbekommen
Der Kontext

Zwei Kennungen, und der Grund steht im Changelog

Jeder FreshBooks-Aufruf braucht eine von zwei Kennungen, und welche davon ist keine Geschmacksfrage. Buchhaltungsressourcen erreichen Sie unter /accounting/account/{accountId}/, Zeiterfassung und Projekte dagegen unter /timetracking/business/{businessId}/.

FreshBooks erklärt den Grund, statt ihn Sie erschließen zu lassen: „Aus historischen Gründen ist FreshBooks vom Account-Konzept auf ein Business-Konzept gewechselt, viele Ressourcen verwenden aber weiterhin accountIds.“ Die Migration hat stattgefunden, die alte Adressierung ist geblieben und beide sind aktiv.

Beide Werte kommen aus einem einzigen Aufruf von /me, und zwar innerhalb von business_memberships. Jede Mitgliedschaft hat eine eigene ID und ein verschachteltes Business-Objekt, und dieses Business-Objekt trägt die Account-ID und die Business-ID, die Sie tatsächlich brauchen.

Diese Verschachtelung ist die erste Falle, und die Dokumentation weist darauf hin: „Verwechseln Sie die ID des Business nicht mit der ID des business_membership-Objekts selbst.“ Zwei der drei IDs in diesem Ausschnitt sind plausibel und eine ist richtig.

Ein Aufruf, beide Kennungen und die ID, die keine von beiden istHTTP
GET /auth/api/v1/users/me HTTP/1.1
Host: api.freshbooks.com
Authorization: Bearer {access-token}
 
# Die Form, auf die es in der Antwort ankommt:
# "business_memberships": [
# {
# "id": 111, <- die ID der MITGLIEDSCHAFT, nicht die gesuchte
# "role": "owner",
# "business": {
# "id": 240340, <- die businessId
# "account_id": "ABC123" <- die accountId
# }
# }
# ]
 
GET /accounting/account/ABC123/invoices/invoices
GET /timetracking/business/240340/time_entries

Die Form der Antwort und beide Pfadvarianten aus dem Identity Model von FreshBooks, geprüft am 13. August 2026. Das Lesen von /me erfordert user:profile:read, das neuen Anwendungen standardmäßig mitgegeben wird.

Nicht jeder Nutzer hat eine Account-ID

Die zwei Kennungen sind nicht gleich universell, und das ist der Fall, der den Produktivbetrieb erreicht, bevor jemand ihn eingeplant hat. FreshBooks: „Alle FreshBooks-Nutzer haben eine Identity und eine Business-Ressource und damit eine business_id. Die meisten Nutzer haben Accounts … aber nicht alle.“

Das genannte Beispiel ist ein Kunde, der eine Rechnung erhält, sie ansieht und speichert. Diese Person existiert mit einer Rolle im Business eines anderen und hat keinen eigenen Account. Die Anweisung ist eindeutig: „Sie müssen den Fall sauber behandeln, dass Sie zu einem Nutzer keinen Account finden.“

Falls diese Trennung weiterhin nach einer erfundenen Unterscheidung klingt, entscheidet FreshBooks sie aus einer anderen Richtung. Jedes ausgelieferte Webhook trägt beide Werte, und die Begründung ist das Modell selbst: „Weil einige FreshBooks-Ressourcen account verwenden und andere business (siehe Identity Model), werden sowohl account_id als auch business_id mitgeliefert.“ Ein Hersteller, der eine Kennung unter zwei Namen ausliefert, hätte keinen Grund, beide in jede Benachrichtigung zu legen.

Welche Objekte eine Anbindung erreicht, Feld für Feld, steht auf der Seite zur FreshBooks-Schnittstelle. Worum es im Rest dieses Texts geht, ist das, was die Trennung der Kennungen entscheidet, nachdem Sie sie aufgelöst haben, denn die Antwort beschränkt sich nicht auf das Routing.

Das Problem

Dieselbe Trennung entscheidet, wie fein Sie nach Zeit filtern können

Fragen Sie beide Hälften von FreshBooks, was sich seit einem Zeitpunkt geändert hat, bekommen Sie zwei verschiedene Antworten, weil sie zwei verschiedene Arten von Zeitpunkt annehmen. Die Buchhaltungsseite nimmt ein Datum. Die Projektseite nimmt einen Zeitstempel.

Auf Buchhaltungsressourcen tragen die Between-Filter einen Vorbehalt, in den Worten von FreshBooks: „Ein besonderer Hinweis zu Betweens auf Datums-Zeit-Spalten wie ‚updated‘: diese haben in der Regel nur tagesgenaue Auflösung und nehmen die Eingabeform YYYY-MM-DD.“

Auf der Projektseite ist dieselbe Idee ein vollständiger Zeitstempel. FreshBooks dokumentiert updated_since auf Zeiteinträgen als Wert, der „im ISO-8601-Format vorliegen muss“, und das eigene Beispiel trägt Sekunden.

Der praktische Unterschied ist die Größe der kleinsten Frage, die Sie stellen können. Ein Abgleich, der alle fünfzehn Minuten läuft, kann die Projektseite nach den letzten fünfzehn Minuten fragen. Auf der Buchhaltungsseite ist das schmalste verfügbare Fenster der heutige Tag, und der wird den ganzen Tag über größer.

Die schmalste Frage, die jede Hälfte beantwortetHTTP
# Buchhaltung: ein Datum, und die Einheit ist der Tag
GET /accounting/account/{accountId}/invoices/invoices
?search[date_min]=2026-08-13
&search[date_max]=2026-08-13
 
# Zeiterfassung: ein Zeitstempel, und die Einheit ist die Sekunde
GET /timetracking/business/{businessId}/time_entries
?updated_since=2026-08-13T15:00:53Z

Beide Filterformen und der Hinweis zur Auflösung aus der Referenz zu Suche, Paginierung und Includes, geprüft am 13. August 2026. Die Zeilenumbrüche dienen dem Lesen; die Parameter stehen auf einer Zeile.

Das ist der Teil der Naht, den keine Schicht entfernt. Die Kennungen aufzulösen ist echte Arbeit, und sie lässt sich einmal hinter einer Schnittstelle erledigen. Einen Zeitstempel zu erfinden, den die Buchhaltungsseite nie erfasst hat, ist eine andere Art von Anfrage, und die kann niemand bedienen.

Die Entwurfsentscheidung landet also so oder so bei Ihnen. Entweder Ihr Buchhaltungsdurchlauf greift einen ganzen Tag zurück und Sie nehmen in Kauf, Datensätze erneut zu lesen, die Sie schon haben, oder er greift weiter zurück und Sie nehmen mehr davon in Kauf.

Das Problem

Sortieren und Suchen wechseln an derselben Linie die Form

Die Naht taucht auf derselben Dokumentationsseite noch zweimal auf, und beide Male ist es ein Wechsel der Syntax und kein Wechsel der Fähigkeit. Sortiert wird für Buchhaltungs-Endpunkte anders geschrieben als für projektartige.

FreshBooks benennt die Trennung direkt: „Die Form der Sortierung unterscheidet sich leicht zwischen /accounting-Endpunkten und projektartigen Endpunkten.“ Die Buchhaltung nimmt ein Suffix, ?sort=field_name_asc oder ?sort=field_name_desc. Projektartige Endpunkte nehmen ein führendes Minus, ?sort=field_name gegen ?sort=-field_name.

Die Suche trennt sich genauso. Buchhaltungsressourcen nehmen Parameter in eckigen Klammern, die url-kodiert werden müssen, in der Form ?search[key]=value. Projektartige Ressourcen nehmen einfache Query-Parameter, etwa ?complete=true oder ?billed=true.

Nichts davon ist für sich genommen schwierig. Schwierig ist es im Kopf zu behalten, denn ein Helfer, der eine Sortierung oder einen Filter baut, ist genau die Art Code, die man einmal schreibt und überall wiederverwendet, und hier ist derselbe Helfer auf der halben Fläche falsch.

Ein Produkt, zwei RegelwerkeBeide Seiten dokumentiert
Pfad
Buchhaltung

account/{accountId}

Unter /accounting

Zeiterfassung

business/{businessId}

Unter /timetracking

Datumsfilter
Buchhaltung

search[date_min]=2026-08-13

Löst auf einen Tag auf

Zeiterfassung

updated_since=…T15:00:53Z

Löst auf eine Sekunde auf

Sortierung
Buchhaltung

?sort=due_date_asc

Ein Suffix setzt die Richtung

Zeiterfassung

?sort=-due_date

Ein Minus setzt die Richtung

Suche
Buchhaltung

?search[key]=value

In Klammern und kodiert

Zeiterfassung

?complete=true

Ein einfacher Parameter

Dieselbe Linie, viermal. Nur die erste der vier ist sichtbar, während Sie eine URL lesen, und die zweite ist die, die entscheidet, wonach ein Abgleich fragen kann. Jede Zelle stammt aus dem Identity Model von FreshBooks und aus seiner Such-Referenz.

Die Pfadformen aus dem Identity Model, die anderen drei Zeilen aus der Referenz zu Suche, Paginierung und Includes, beide geprüft am 13. August 2026.

So funktioniert es

Acht Ressourcen senden Events, und alle acht liegen auf einer Seite

FreshBooks sendet native Events. Acht Ressourcen sind über uns angebunden, und sie tragen drei Verben: created, updated und deleted.

Ein Abonnement ist schmal, und es gilt je Kunde. Unser Create-Aufruf nimmt eine resource und einen eventType je Abonnement, der Bestand, den Sie betreiben, ist also ein Raster aus Ressourcen und Verben, multipliziert mit jedem verbundenen Account.

Die eigene Event-Tabelle von FreshBooks ist breiter als die angebundene Menge. Sie führt fünfzehn Substantive, darunter project und time_entry, Events gibt es also auf beiden Seiten der Naht, auch wenn die acht angebundenen alle auf der Buchhaltungsseite liegen.

Sie hat außerdem ein viertes Verb. sendByEmail existiert auf Rechnungen und Angeboten, und dass eine Rechnung an einen Kunden gemailt wird, ist ein echter Moment in einer Forderung. In einem Vokabular aus created, updated und deleted hat es keine Entsprechung.

Die 15 Substantive, die FreshBooks veröffentlichtIn der Tabelle gezählt
  • bill
  • bill_vendor
  • category
  • client
  • credit_note
  • estimate
  • expense
  • invoice
  • item
  • payment
  • project
  • recurring
  • service
  • tax
  • time_entry

Sieben davon erreichen Sie über einen zugeordneten Ressourcennamen. Die übrigen senden Events bei FreshBooks, ohne heute einen Weg über die einheitliche Schnittstelle zu haben.

Vier Verben bei FreshBooks, drei im Umschlag
  • createCREATED
  • updateUPDATED
  • deleteDELETED
  • sendByEmailKeine Entsprechung
Fünfzehn Substantive in der Event-Tabelle von FreshBooks gegen die Ressourcen, die ein Abonnement über uns benennen kann, und vier Verben gegen drei. Die achte angebundene Ressource ist als ACCOUNT dokumentiert und entspricht keinem Substantiv in der veröffentlichten Tabelle von FreshBooks, sie ist hier deshalb nicht als zugeordnetes Paar gezeichnet.

Substantive und Verben aus der Webhook-Callback-Referenz von FreshBooks, die angebundenen Ressourcen und der Umschlag mit drei Verben aus unserer eigenen FreshBooks-Dokumentation, beide geprüft am 13. August 2026.

Die Zustellung hat drei Kanten, die vor dem ersten Kunden in den Code gehören, und FreshBooks dokumentiert alle drei. Eine Zusage zur Geschwindigkeit gibt es überhaupt nicht: „die Zustellung kann zwischen wenigen Sekunden und mehreren Minuten liegen.“

Die zweite Kante ist die, die Teams erwischt. Alles außer einem 2xx zählt als Fehlschlag, und FreshBooks benennt den Fall, an den man nicht denkt: „einschließlich 3xx-HTTP-Weiterleitungscodes“. Ein Endpunkt, der eine Weiterleitung auf seinen kanonischen Host beantwortet, ist die ganze Zeit still gescheitert.

Die dritte ist ein Timeout von zehn Sekunden, und das Gegenmittel steht im selben Absatz: „erwägen Sie, die Verarbeitung in der Anwendung zurückzustellen, bis eine Antwort auf das Webhook gesendet wurde.“ Wiederholte Fehlschläge verwerfen die Nachricht, und eine lange Serie davon kann das Webhook deaktivieren, bis Sie den Verifizierungs-Token erneut senden.

So funktioniert es

Die Signatur liegt über einer Zeichenkette, die Sie selbst wieder aufbauen

Jeder Callback trägt einen X-FreshBooks-Hmac-SHA256-Header, und ihn zu prüfen ist die Stelle, an der eine korrekte Implementierung trotzdem scheitert. Der Digest wird nicht über die Bytes gebildet, die Sie empfangen haben.

Die Nutzlast kommt form-urlencoded an, als Parameter in Query-Form. Die Signatur wird beschrieben als HMAC über „eine UTF-8-kodierte JSON-Zeichenkette der Parameter unter Verwendung des Verifier-Keys“. Sie kodieren die Parameter also erneut als JSON, und der genaue Wortlaut dieses JSON ist die Eingabe.

Zwei Details entscheiden, ob Ihr Digest passt. FreshBooks wandelt jeden Wert zuerst in eine Zeichenkette um, die numerischen IDs stehen also in Anführungszeichen. Und die Serialisierung trägt Leerzeichen, was die Dokumentation ausschreibt, weil sie es muss.

Das Leerzeichen ist Teil der Signatur

FreshBooks, wörtlich: „Die Python-Methode json.dumps liefert JSON in der Form {"key": "value", "key2": "value"}. Beachten Sie die Leerzeichen nach jedem ‚:‘ und ‚,‘. Wenn Ihr json stringify diese nicht enthält, ergibt sich eine andere Signatur.“

JSON.stringify gibt solche Leerzeichen nicht aus. Der Fehlschlag ist ein gültiges Event, ein korrektes Secret, korrekter Code und ein Digest, der nie passt, und nichts in der Antwort sagt Ihnen, welches der vier falsch ist.

Die signierte Zeichenkette wieder aufbauen, Leerzeichen eingeschlossenJavaScript
import { createHmac, timingSafeEqual } from "node:crypto";
 
// Der Body kommt form-urlencoded an, nicht als JSON.
const params = Object.fromEntries(new URLSearchParams(rawBody));
 
// Jeder Wert wird als Zeichenkette signiert, und die Trenner
// tragen die Leerzeichen, die json.dumps standardmäßig schreibt.
const signed = JSON.stringify(
Object.fromEntries(
Object.entries(params).map(([k, v]) => [k, String(v)])
)
).replace(/":/g, '": ').replace(/,"/g, ', "');
 
const digest = createHmac("sha256", verifier)
.update(signed, "utf8")
.digest("base64");
 
const sent = Buffer.from(header, "base64");
const ours = Buffer.from(digest, "base64");
const ok = sent.length === ours.length &&
timingSafeEqual(sent, ours);

Die Serialisierungsregel, die Umwandlung in Zeichenketten und der Header-Name aus der Webhook-Callback-Referenz von FreshBooks, geprüft am 13. August 2026. Die Reihenfolge der Schlüssel folgt den Parametern, wie sie ankommen, erhalten Sie sie also, statt zu sortieren.

Das Signatur-Secret wird nie als solches ausgegeben. Wenn Sie einen Callback registrieren, sendet FreshBooks einen Verifizierungscode an die URL, und Sie senden ihn zurück, um den Besitz zu bestätigen. Derselbe Code ist danach der Signaturschlüssel: „er wird als Secret verwendet, um die Signaturen zu berechnen, mit denen geprüft wird, ob Webhooks von FreshBooks stammen“.

Der Wert, den Sie für einen einmaligen Handshake-Token halten könnten, ist also das langlebige Secret für jedes folgende Event. Ihn zu speichern ist nicht optional, und er gehört dorthin, wo Ihre anderen Signaturschlüssel liegen, statt in eine Logzeile über das Onboarding. Wie Fehlschläge dieser Art aussehen, sobald sie vereinheitlicht sind, steht auf der Seite, auf der ein Fehlermodell beschrieben ist.

Die Eckdaten

Die Paginierung endet bei 100, ohne es zu sagen

FreshBooks deckelt eine Seite bei 100 Datensätzen und sagt Ihnen nicht, wenn es das getan hat. Die Dokumentation benutzt das Wort selbst: „per_page wird stillschweigend bei 100 gedeckelt“.

Das eigene Beispiel ist eindeutig. Eine Anfrage mit ?per_page=2000 ist kommentiert mit „gibt nur 100 Rechnungen zurück“. Es gibt keinen Fehler, keine Warnung und keinen Header, der sagt, dass die Anfrage geändert wurde.

Damit ist der Fehlschlag ein Korrektheitsproblem und kein Performance-Problem. Ein Abgleich, der nach allem fragt und liest, was zurückkommt, ist im Test vollständig, wo der Kunde vierzig Rechnungen hat, und im Produktivbetrieb still falsch, wo er viertausend hat.

Was FreshBooks mit einer Seitenanfrage macht, die es nicht erfüllt
Sie sendenWas passiert
per_page über 100Stillschweigend auf 100 gedeckelt
page=0Gibt Seite 1 zurück
page hinter der letztenGibt eine leere Liste zurück
Eine negative pageGibt einen Fehler zurück, der den Parameter benennt

Alle vier Verhaltensweisen aus dem Abschnitt zur Paginierung, geprüft am 13. August 2026. FreshBooks rät außerdem, das Limit selbst zu setzen, damit „Sie wissen, wie viele Ergebnisse Sie erwarten“. Lesen Sie pages und total aus dem Umschlag der Antwort und laufen Sie weiter, bis page gleich pages ist.

Das Zweite, was ein Sammelaufruf standardmäßig nicht liefert, sind die verknüpften Daten. In den Worten von FreshBooks „erfordern einige Beziehungen zusätzliche Aufrufe, damit wir sie vollständig füllen“, und eine Anfrage muss sie ausdrücklich benennen.

Eine Rechnung, die ohne ?include[]=lines gelesen wird, liefert den Kopf und keine Positionen zurück, und das ist die zweite Fassung derselben stillen Lücke: eine Antwort, die korrekt geformt und unvollständig ist. Mehrere Includes lassen sich in einem Aufruf kombinieren.

Zwei stille Lücken in einer AnfrageHTTP
# Gibt 100 zurück, nicht 2000, und sagt nichts dazu
GET /accounting/account/{accountId}/invoices/invoices
?per_page=2000
 
# Ohne include[] fehlen die Positionen einfach
GET /accounting/account/{accountId}/invoices/invoices
?include[]=lines&include[]=allowed_gateways
 
# Der Umschlag, der sagt, wie weit Sie wirklich gekommen sind
# "pages": { "page": 1, "pages": 14, "per_page": 100,
# "total": 1389 }

Beide Parameter und der Umschlag der Antwort aus der Referenz zu Suche, Paginierung und Includes, geprüft am 13. August 2026.

Die Eckdaten

Es lebt immer nur ein Refresh Token

FreshBooks fährt gewöhnliches OAuth 2.0 mit dem Authorization-Code-Flow, und eine Regel darin ist nicht gewöhnlich. Refresh Token rotieren, und je Nutzer und Anwendung existiert zu jedem Zeitpunkt nur einer davon.

Die Dokumentation ist zu allen drei Teilen präzise: „Refresh Token leben ewig, sind aber einmalig verwendbar, und je Nutzer und Anwendung kann immer nur ein Refresh Token aktiv sein … alle alten Refresh Token werden sofort ungültig.“

Die Asymmetrie darunter macht daraus ein Nebenläufigkeitsproblem statt eines Speicherproblems. Bearer Token verhalten sich anders: „Sie könnten zu einem gegebenen Zeitpunkt mehrere gültige Bearer Token haben.“

Zwei Worker, die denselben Kunden gleichzeitig aktualisieren, bekommen also beide erfolgreich einen Bearer Token, und nur einer von beiden hält danach einen brauchbaren Refresh Token. Der andere hält einen toten, und der Weg zurück ist, dass der Kunde Ihre Anwendung erneut von Hand autorisiert. Refreshes gehören hinter eine Sperre je Kunde.

Die Lebensdauer des Access Token nicht fest eintragen

FreshBooks veröffentlicht keine feste Lebensdauer für einen Bearer Token. Es bittet Sie stattdessen, den Wert auszulesen: „Bearer Token sind nicht langlebig (prüfen Sie den Ablauf im Token)“.

Eine Zahl, die in Ihrer Konfiguration steht, ist eine Zahl, die sich von der Wahrheit entfernen kann, ohne dass etwas laut scheitert, und die Anweisung lautet hier, sie bei jeder Ausgabe aus dem Token zu nehmen.

Scopes haben eine verwandte Eigenschaft, die umso mehr kostet, je später Sie ihr begegnen. Sie lassen sich nicht an Ort und Stelle erweitern: „Es ist nicht möglich, Scopes aus einem bestehenden Access Token zu entfernen. Der einzige Weg, zugestimmte Scopes zu reduzieren oder zu ergänzen, ist, den Token zu widerrufen und den App-Autorisierungsablauf erneut zu beginnen.“

Ein Scope, den Sie beim Onboarding nicht angefragt haben, ist ein Scope, den Sie bekommen, indem Sie einen bestehenden Kunden erneut durch die Zustimmung schicken. Die Scope-Grammatik lautet entity:object:action über 22 Objekte, mit zwei Aktionsklassen, wobei write das Anlegen, Bearbeiten, Archivieren und Löschen zusammen abdeckt.

Die Token-Rotation, die Asymmetrie beim Bearer Token und die Anweisung zum Ablauf aus der Authentifizierungsseite von FreshBooks; die Scope-Grammatik und die Zustimmungsregel aus seiner Scopes-Seite; beide geprüft am 13. August 2026. Gleichzeitige Änderungen erscheinen als 409 Conflict, in der Fehlerreferenz beschrieben als Ressource, die „durch eine andere Anfrage geändert wird“.

Wo unser Teil endet

Was auf Ihrer Seite bleibt

Der größte Teil der Naht lässt sich abfangen, und die Teile, die es nicht tun, teilen sich sauber auf. Vier Dinge hören auf, Ihre zu sein:

  • Das Routing der Kennungen. Beide Werte werden beim Verbinden aufgelöst und je Kunde gespeichert, keine Aufrufstelle muss also wissen, ob die gewünschte Ressource über Account oder über Business adressiert wird.
  • Die zwei Query-Grammatiken. Eine Sortierform und eine Filterform erreichen Ihren Code, statt eines Suffix auf der einen Hälfte der Fläche und eines Minuszeichens auf der anderen.
  • Paginierung und Includes. Der stille Deckel und die fehlenden Positionen sind dieselbe Fehlerklasse, und beide werden behandelt, bevor eine Antwort in das Common Data Model normalisiert wird.
  • Die Token-Rotation und ihr Wettlauf. Ein einmalig verwendbarer Refresh Token je Nutzer und Anwendung ist ein Sperrproblem, und es wird hier einmal gelöst statt in jeder Anbindung.

Drei Dinge bewegen sich nicht, und zwei davon sind Entscheidungen statt Arbeit:

  • Die Auflösung des Buchhaltungs-Deltas. Ein Tag ist das kleinste Fenster, das FreshBooks auf dieser Seite erfasst. Keine Schnittstelle kann aus einem Datum einen Änderungszeitstempel machen, wie weit jeder Durchlauf zurückgreift und wie viel erneutes Lesen Sie in Kauf nehmen, bleibt also Ihre Wahl.
  • Ihr Empfänger. Das Budget von zehn Sekunden, die Warteschlange dahinter und die Signaturprüfung gehören Ihnen, weil sie auf Ihrer Infrastruktur laufen. Antworten Sie zuerst und verarbeiten Sie danach, und denken Sie daran, dass eine Weiterleitung als Fehlschlag zählt.
  • Welche Berechtigungen Sie anfragen. Scopes lassen sich nicht erweitern, ohne einen Kunden erneut durch die Zustimmung zu schicken, die Liste, die Sie beim Onboarding anfragen, ist also eine Entscheidung über das Produkt, das Sie bauen wollen, und keine Einstellung.

Eine Anmerkung zu den Objekten selbst. Kunden und Lieferanten sind heute lesend und schreibend unterstützt, und der größte Teil des übrigen Katalogs ist als auf Anfrage markiert, was verfügbar auf Anfrage bedeutet statt nicht verfügbar. Wenn Eingangsrechnungen oder Gutschriften für Ihr Produkt zählen, fragen Sie den Lesezugriff zusammen mit dem Event an, denn das sind zwei getrennte Anfragen.

Die Objektzustände aus unseren eigenen Coverage-Daten, die festhalten, was heute freigeschaltet ist, und nicht, was FreshBooks technisch erreichen kann.

FAQ

Häufige Fragen

Warum braucht FreshBooks zwei verschiedene Kennungen?

FreshBooks ist vom Account-Konzept auf ein Business-Konzept gewechselt und hat beide behalten. Buchhaltungs-Endpunkte werden als /accounting/account/{accountId}/ adressiert, Zeiterfassung und Projekte verwenden /timetracking/business/{businessId}/. Beide Werte kommen aus einem Aufruf von /me, innerhalb des Arrays business_memberships.

Wie lese ich beide Kennungen aus, ohne sie zu verwechseln?

Rufen Sie /me auf und laufen Sie business_memberships durch. Jeder Eintrag hat eine eigene ID und ein verschachteltes Business-Objekt, und dieses Business-Objekt trägt sowohl die account_id als auch die ID, die Sie brauchen. FreshBooks warnt ausdrücklich davor, die ID der Mitgliedschaft mit der ID des Business zu verwechseln.

Wie weit zurück kann ich FreshBooks-Datensätze nach Datum filtern?

Auf Buchhaltungsressourcen haben die Between-Datumsfilter eine tagesgenaue Auflösung und nehmen die Form YYYY-MM-DD, das kleinste Fenster, das Sie anfragen können, ist also ein ganzer Tag. Ressourcen der Zeiterfassung nehmen updated_since als vollständigen ISO-8601-Zeitstempel, der auf die Sekunde auflöst.

Wie viele Ergebnisse liefert eine FreshBooks-Seite?

Höchstens 100. FreshBooks dokumentiert, dass per_page stillschweigend gedeckelt wird, eine Anfrage nach 2.000 Rechnungen liefert also 100 zurück, ohne Fehler und ohne Warnung. Lesen Sie die Felder pages und total aus dem Umschlag der Antwort und fragen Sie weiter an, bis page gleich pages ist.

Warum passt meine FreshBooks-Webhook-Signatur nie?

Die Signatur ist ein HMAC über eine JSON-Serialisierung der Parameter, und FreshBooks baut diese Zeichenkette so, wie json.dumps aus Python es tut, mit einem Leerzeichen nach jedem Doppelpunkt und jedem Komma. Ein standardmäßiges JSON.stringify lässt diese Leerzeichen weg und erzeugt einen anderen Digest. Alle Werte werden zuvor in Zeichenketten umgewandelt.

Was passiert, wenn zwei Prozesse gleichzeitig einen FreshBooks-Token aktualisieren?

Einer von beiden gewinnt. Je Nutzer und Anwendung kann nur ein Refresh Token aktiv sein, und die Ausgabe eines neuen Bearer Token macht jeden älteren Refresh Token sofort ungültig. Der Verlierer hält einen toten Token, und der Kunde muss die Anwendung erneut autorisieren, Refreshes gehören also hinter eine Sperre.

Einmal bauen, auf der Unified API.

FreshBooks teilt seine Query-Grammatik in zwei und löst ein Buchhaltungsdatum auf den Tag auf. Das nächste System zieht beide Linien woanders, oder es zieht sie gar nicht. Bauen Sie gegen eine Schnittstelle, und jeder dieser Unterschiede wird zu einem Feld, das Sie lesen, statt zu einem Zweig, den Sie pflegen.