Zum Inhalt springen

Fileverity-API-Dokumentation

Beispielcode

Vollständige Node.js- und Python-Programme für die Fileverity-API: ein Bild prüfen, ein großes PDF prüfen, von KI verfassten Text erkennen und Webhook-Signaturen prüfen.

Als Markdown ansehenAktualisiert am 2026-11-04

Diese Seite enthält vier vollständige Programme für die Fileverity-API, jeweils in Node.js ab Version 18 und in Python ab Version 3.9: ein Bild prüfen und auf das Ergebnis warten, ein großes PDF prüfen, von KI verfassten Text erkennen und Webhooks mit Signaturprüfung empfangen. Kopieren Sie eines, setzen Sie FILEVERITY_API_KEY und führen Sie es aus.

Die Programme rufen die HTTPS-API direkt auf, mit dem in Node.js integrierten fetch und dem Paket requests in Python, sodass Sie nur einen HTTP-Client brauchen. Die Aufrufe, Felder und Statuswerte entsprechen denen, die in Dateien, Analysen, Textprüfungen und Webhooks beschrieben sind.

ProgrammWas es tutAufrufe
Ein Bild prüfenLädt ein Bild hoch, wartet auf das Ergebnis und gibt die Zusammenfassung und die Berichts-URL aus.POST /files, POST /analyses, GET /analyses/{id}
Ein großes PDF prüfenLädt ein Dokument über eine vorsignierte URL hoch, fragt per Polling ab, bis die Prüfung abgeschlossen ist, und speichert den PDF-Bericht.POST /files, GET /files/{id}, POST /analyses, GET /analyses/{id}, GET /analyses/{id}/report.pdf
Von KI verfassten Text erkennenSendet eine Textdatei und gibt die Einstufung, den Score und jede von KI verfasste Passage aus.POST /text/detections
Webhooks empfangenPrüft die Signatur jeder Zustellung, ignoriert Duplikate und verarbeitet die Ereignisse.Ihr Endpunkt, aufgerufen mit analysis.completed, analysis.failed und balance.low

Vor dem Ausführen

  • Erstellen Sie in der Konsole unter API-Schlüssel einen Schlüssel im Modus Test. Ein Testschlüssel liefert feste Beispielergebnisse und wird nie berechnet, sodass jedes Programm kostenlos läuft. Um echte Dateien zu prüfen, erstellen Sie einen Schlüssel im Modus Live. Die Programme bleiben unverändert.
  • Node.js: Speichern Sie jedes Programm als .mjs-Datei, damit es import und await auf oberster Ebene nutzen kann, und führen Sie es mit node aus. Es muss nichts installiert werden.
  • Python: Führen Sie pip install requests aus. Der Webhook-Empfänger benötigt zusätzlich pip install flask.
export FILEVERITY_API_KEY=fv_test_...

Wie die Programme mit Schlüsseln, Fehlern und Wiederholungen umgehen

Die drei Programme, die die API aufrufen, verwenden dieselben Funktionen request und api. Übernehmen Sie sie unverändert in Ihren eigenen Code.

AspektWas die Programme tun
SchlüsselWird aus FILEVERITY_API_KEY gelesen und nie in den Code geschrieben. Rufen Sie die API von Ihrem Server aus auf, nicht aus Browser-Code.
IdempotenzJede POST-Anfrage sendet einen Idempotency-Key, eine neue UUID pro Anfrage, und verwendet ihn bei jeder Wiederholung erneut, sodass eine Wiederholung eine Prüfung nie zweimal ausführt oder berechnet. Damit auch ein erneuter Lauf des gesamten Skripts sicher ist, leiten Sie den Schlüssel stattdessen aus Ihrer eigenen Datensatz-ID ab. Siehe Idempotenz.
FehlerEine Antwort ohne 2xx-Status enthält error.type, error.code, error.message, error.param und error.request_id. Die Programme brechen mit code: message (request request_id) ab. Verzweigen Sie anhand von code und geben Sie dem Support die Anfrage-ID an. Siehe Fehler.
WiederholungenEin Netzwerkfehler, ein 5xx-Status (internal_error, analysis_failed, service_unavailable), rate_limited (429) und idempotency_in_progress (409) werden mit bis zu 5 Versuchen wiederholt. Gewartet wird die Zeit aus dem Header retry-after, wenn die Antwort einen enthält, sonst 2, 4, 8 und 16 Sekunden. Jeder andere 4xx-Status beendet das Programm sofort. pending_files_limit ist ebenfalls ein 429, hat aber kein retry-after und wird deshalb nicht wiederholt.
Rate-LimitMeldet eine Antwort x-ratelimit-remaining-requests: 0, wartet das Programm x-ratelimit-reset-requests Sekunden bis zum nächsten Aufruf, damit kein Fehler 429 auftritt. Siehe Rate-Limits und Header.
TimeoutsDie Python-Programme setzen pro Anfrage ein Timeout von 90 Sekunden, länger als die 60 Sekunden, die Prefer: wait eine Anfrage offenhalten kann.

Ein Bild prüfen und auf das Ergebnis warten

Das Programm lädt das Bild als Multipart hoch, erstellt mit Prefer: wait=60 eine Analyse, sodass die Anfrage offen bleibt, bis das Ergebnis vorliegt, und gibt die Zusammenfassung und die Berichts-URL aus. Endet das Warten vorher, antwortet die API mit 202 und status: processing, und das Programm fragt GET /analyses/{id} alle 3 Sekunden ab. Siehe Warten, Polling oder Webhooks.

Speichern Sie es als check-image.mjs oder check_image.py und führen Sie node check-image.mjs photo.jpg oder python check_image.py photo.jpg aus. Multipart nimmt Bilder bis 4 MB an. Für ein größeres Bild bis 10 MB verwenden Sie den Upload über eine vorsignierte URL aus dem nächsten Programm und geben Sie den Content-Type des Bildes an, zum Beispiel image/jpeg.

import { randomUUID } from "node:crypto";import { readFile } from "node:fs/promises";import { basename } from "node:path"; const base = "https://api.fileverity.com/v1";const key = process.env.FILEVERITY_API_KEY;const path = process.argv[2];if (!key || !path) throw new Error("Usage: FILEVERITY_API_KEY=... node check-image.mjs photo.jpg"); const retryable = ["rate_limited", "idempotency_in_progress"];const sleep = (seconds) => new Promise((resolve) => setTimeout(resolve, seconds * 1000)); async function request(url, init) {  for (let attempt = 1; ; attempt++) {    let response = null;    let problem;    try {      response = await fetch(url, init);      if (response.ok) {        const remaining = response.headers.get("x-ratelimit-remaining-requests");        const reset = Number(response.headers.get("x-ratelimit-reset-requests"));        if (remaining === "0") await sleep(reset);        return response;      }      problem = (await response.json().catch(() => ({}))).error ?? {};    } catch (error) {      problem = { code: "network_error", message: error.message };    }    const status = response?.status ?? 0;    const retry = status === 0 || status >= 500 || retryable.includes(problem.code);    if (!retry || attempt === 5) {      const { code = status, message = "Request failed", request_id: id = "unknown" } = problem;      throw new Error(`${code}: ${message} (request ${id})`);    }    await sleep(Number(response?.headers.get("retry-after")) || 2 ** attempt);  }} async function api(method, route, { headers, body } = {}) {  const auth = { Authorization: `Bearer ${key}` };  const response = await request(base + route, { method, headers: { ...auth, ...headers }, body });  return response.json();} const form = new FormData();form.append("file", new Blob([await readFile(path)]), basename(path));const file = await api("POST", "/files", {  headers: { "Idempotency-Key": randomUUID() },  body: form,}); let analysis = await api("POST", "/analyses", {  headers: {    "Content-Type": "application/json",    "Idempotency-Key": randomUUID(),    Prefer: "wait=60",  },  body: JSON.stringify({ model: "fvimg-2", file: file.id }),});while (analysis.status === "processing") {  await sleep(3);  analysis = await api("GET", `/analyses/${analysis.id}`);} if (analysis.status !== "succeeded") {  throw new Error(`The check ${analysis.status}: ${analysis.error?.message}`);}const { level, action, assessment, ai, edit } = analysis.summary;console.log(`${analysis.id} ${analysis.status}`);console.log(`level ${level}, action ${action}, assessment ${assessment}`);console.log(`AI generation: ${ai}, editing: ${edit}`);console.log(`Report: ${analysis.report_url}`);
an_8Hk2pQ4vN9sL1xR7cW3mY6zB succeededlevel L2, action accept, assessment authentic_untouchedAI generation: very_low, editing: noneReport: https://fileverity.com/verify/FV-7K2M-9QXD-4T8R

summary ist die stabile, maschinenlesbare Einstufung einer Bildprüfung: level, action, assessment, ai und edit, beschrieben unter Zusammenfassung bei Bildern. report_url ist die öffentliche Verifizierungsseite des Berichts. Mit einem Testschlüssel ist das Ergebnis ein festes Beispiel, und hinter seiner Berichts-URL gibt es keine Seite.

Ein großes PDF prüfen

Dokumente bis 50 MB werden in drei Schritten hochgeladen: Fordern Sie mit POST /files und einem JSON-Anfragetext eine Upload-URL an, senden Sie die Bytes mit PUT an diese URL und warten Sie, bis die Datei den Status uploaded zeigt. Danach erstellt das Programm die Analyse und fragt alle 5 Sekunden ab, weil Dokumente in eine Warteschlange kommen können und eine Prüfung eine Weile dauern kann. Es gibt den Fortschritt, die Zusammenfassung und die Berichts-URL aus und speichert den PDF-Bericht neben dem Skript.

Speichern Sie es als check-pdf.mjs oder check_pdf.py und führen Sie node check-pdf.mjs contract.pdf oder python check_pdf.py contract.pdf aus. Für einen anderen Dokumenttyp ändern Sie content_type auf einen der akzeptierten Content-Types. Für ein verschlüsseltes Dokument fügen Sie der Analyseanfrage options: { password } hinzu.

import { randomUUID } from "node:crypto";import { readFile, writeFile } from "node:fs/promises";import { basename } from "node:path"; const base = "https://api.fileverity.com/v1";const key = process.env.FILEVERITY_API_KEY;const path = process.argv[2];if (!key || !path) throw new Error("Usage: FILEVERITY_API_KEY=... node check-pdf.mjs contract.pdf"); const retryable = ["rate_limited", "idempotency_in_progress"];const sleep = (seconds) => new Promise((resolve) => setTimeout(resolve, seconds * 1000)); async function request(url, init) {  for (let attempt = 1; ; attempt++) {    let response = null;    let problem;    try {      response = await fetch(url, init);      if (response.ok) {        const remaining = response.headers.get("x-ratelimit-remaining-requests");        const reset = Number(response.headers.get("x-ratelimit-reset-requests"));        if (remaining === "0") await sleep(reset);        return response;      }      problem = (await response.json().catch(() => ({}))).error ?? {};    } catch (error) {      problem = { code: "network_error", message: error.message };    }    const status = response?.status ?? 0;    const retry = status === 0 || status >= 500 || retryable.includes(problem.code);    if (!retry || attempt === 5) {      const { code = status, message = "Request failed", request_id: id = "unknown" } = problem;      throw new Error(`${code}: ${message} (request ${id})`);    }    await sleep(Number(response?.headers.get("retry-after")) || 2 ** attempt);  }} async function api(method, route, { headers, body } = {}) {  const auth = { Authorization: `Bearer ${key}` };  const response = await request(base + route, { method, headers: { ...auth, ...headers }, body });  return response.json();} const bytes = await readFile(path);const file = await api("POST", "/files", {  headers: { "Content-Type": "application/json", "Idempotency-Key": randomUUID() },  body: JSON.stringify({    filename: basename(path),    content_type: "application/pdf",    bytes: bytes.length,  }),}); if (file.status === "pending") {  const { url, method, headers } = file.upload;  await request(url, { method, headers, body: bytes });  let ready = false;  for (let attempt = 0; attempt < 15 && !ready; attempt++) {    ready = (await api("GET", `/files/${file.id}`)).status === "uploaded";    if (!ready) await sleep(2);  }  if (!ready) throw new Error("The upload was not confirmed.");}console.log(`${file.id} uploaded`); let analysis = await api("POST", "/analyses", {  headers: { "Content-Type": "application/json", "Idempotency-Key": randomUUID() },  body: JSON.stringify({ model: "fvdoc-3", file: file.id }),});const deadline = Date.now() + 15 * 60 * 1000;while (analysis.status === "processing") {  if (Date.now() > deadline) {    throw new Error(`Still processing. Fetch it later with GET /analyses/${analysis.id}`);  }  console.log(`${analysis.id} processing: ${analysis.progress?.stage ?? "starting"}`);  await sleep(5);  analysis = await api("GET", `/analyses/${analysis.id}`);} if (analysis.status !== "succeeded") {  throw new Error(`The check ${analysis.status}: ${analysis.error?.message}`);}const { level, action, reason, pages, images, text, findings } = analysis.summary;console.log(`${analysis.id} ${analysis.status}`);console.log(`level ${level}, action ${action}, reason ${reason ?? "none"}`);console.log(`${pages} pages, ${images} photos examined, text: ${text ?? "not evaluated"}`);console.log(`findings: ${findings.join(", ") || "none"}`);console.log(`Report: ${analysis.report_url}`); if (analysis.livemode) {  const pdf = await request(`${base}/analyses/${analysis.id}/report.pdf`, {    headers: { Authorization: `Bearer ${key}` },  });  const name = `Fileverity-${analysis.report_id}.pdf`;  await writeFile(name, Buffer.from(await pdf.arrayBuffer()));  console.log(`Saved ${name}`);}
file_Pw3mY6zB8Hk2pQ4vN9sL1xR7 uploadedan_Zc4vB7nP1sK8tL3wR6yQm2Xa processing: queuedan_Zc4vB7nP1sK8tL3wR6yQm2Xa processing: analyzingan_Zc4vB7nP1sK8tL3wR6yQm2Xa processing: reportingan_Zc4vB7nP1sK8tL3wR6yQm2Xa succeededlevel L2, action accept, reason clean12 pages, 3 photos examined, text: humanfindings: noneReport: https://fileverity.com/verify/FV-3PXH-6WQ2-8NDCSaved Fileverity-FV-3PXH-6WQ2-8NDC.pdf
  • Das PUT geht direkt an den Speicher. Es braucht keinen Header Authorization, und die Header in upload.headers werden genau so gesendet, wie sie zurückgegeben wurden. Siehe Upload über vorsignierte URL.
  • Mit einem Testschlüssel kommt die Datei bereits als uploaded zurück, und es gibt nichts zu senden, daher überspringt das Programm den Upload. Die Analyse liefert sofort ein festes Beispiel, und es gibt kein Beispiel-PDF, daher speichert das Programm den Bericht nur bei einem Live-Schlüssel.
  • Laden Sie das PDF herunter, sobald die Prüfung abgeschlossen ist. Die Seitenbilder und Textauszüge eines Dokuments werden 3 Stunden lang aufbewahrt, und ein später heruntergeladenes PDF enthält sie nicht mehr. Siehe PDF-Bericht.
  • Eine Prüfung, die mit failed oder canceled endet, wurde nicht berechnet. Das Programm bricht mit der Fehlermeldung der Analyse ab.

Von KI verfassten Text erkennen

Die Textprüfung braucht nur einen einzigen synchronen Aufruf. Das Programm liest eine Textdatei, sendet sie an POST /text/detections und gibt die Einstufung, den Score, den Anteil des von KI verfassten Textes und jede von KI verfasste Passage mit ihren Offsets aus. Die Erkennung von Texten, die von KI verfasst wurden, unterstützt Englisch, Französisch, Deutsch, Spanisch und Chinesisch (en, fr, de, es, zh). Die Sprache wird automatisch erkannt; übergeben Sie einen Code als zweites Argument, um sie festzulegen. Der Text muss mindestens 100 Wörter und darf höchstens 100.000 Zeichen umfassen.

Speichern Sie es als detect-text.mjs oder detect_text.py und führen Sie node detect-text.mjs essay.txt oder python detect_text.py essay.txt fr aus. Der Schnellstart enthält einen Befehl, der eine Beispieldatei essay.txt erstellt. Mit einem Testschlüssel ist die Antwort ein festes Ergebnis für einen von Menschen verfassten Text, sodass das Programm keine Passage ausgibt.

import { randomUUID } from "node:crypto";import { readFile } from "node:fs/promises"; const base = "https://api.fileverity.com/v1";const key = process.env.FILEVERITY_API_KEY;const [, , path, language] = process.argv;if (!key || !path) {  throw new Error("Usage: FILEVERITY_API_KEY=... node detect-text.mjs essay.txt [en|fr|de|es|zh]");} const retryable = ["rate_limited", "idempotency_in_progress"];const sleep = (seconds) => new Promise((resolve) => setTimeout(resolve, seconds * 1000)); async function request(url, init) {  for (let attempt = 1; ; attempt++) {    let response = null;    let problem;    try {      response = await fetch(url, init);      if (response.ok) {        const remaining = response.headers.get("x-ratelimit-remaining-requests");        const reset = Number(response.headers.get("x-ratelimit-reset-requests"));        if (remaining === "0") await sleep(reset);        return response;      }      problem = (await response.json().catch(() => ({}))).error ?? {};    } catch (error) {      problem = { code: "network_error", message: error.message };    }    const status = response?.status ?? 0;    const retry = status === 0 || status >= 500 || retryable.includes(problem.code);    if (!retry || attempt === 5) {      const { code = status, message = "Request failed", request_id: id = "unknown" } = problem;      throw new Error(`${code}: ${message} (request ${id})`);    }    await sleep(Number(response?.headers.get("retry-after")) || 2 ** attempt);  }} async function api(method, route, { headers, body } = {}) {  const auth = { Authorization: `Bearer ${key}` };  const response = await request(base + route, { method, headers: { ...auth, ...headers }, body });  return response.json();} const text = await readFile(path, "utf8");const detection = await api("POST", "/text/detections", {  headers: { "Content-Type": "application/json", "Idempotency-Key": randomUUID() },  body: JSON.stringify({ text, language }),}); const { verdict, score, ai_fraction: share, usage, segments } = detection;console.log(`verdict: ${verdict} (score ${score.toFixed(2)})`);console.log(`AI-written share: ${Math.round(share * 100)}% of ${usage.characters} characters`);console.log(`language: ${detection.language}, billed units: ${usage.billed_units}`);const points = Array.from(text);for (const { start, end, score: passage } of segments) {  const excerpt = points.slice(start, Math.min(end, start + 80)).join("");  console.log(`${start}-${end} score ${passage.toFixed(2)}: ${excerpt}`);}
verdict: mixed (score 0.97)AI-written share: 50% of 889 characterslanguage: en, billed units: 1267-495 score 0.97: In today's fast-paced logistics landscape, it is essential for businesses to pri590-809 score 0.93: Moving forward, it is crucial to implement robust quality assurance measures and

start und end zählen Unicode-Codepoints, nicht Bytes und nicht UTF-16-Einheiten. Python-Strings werden bereits nach Codepoints indiziert; das Node.js-Programm zerlegt den Text zuerst mit Array.from. Siehe Segment-Offsets.

Die Textprüfung liefert eine statistische Schätzung, keinen Nachweis der Urheberschaft. Nutzen Sie sie als einen Anhaltspunkt für eine Prüfung durch Menschen und stützen Sie Entscheidungen, die eine Person betreffen, nicht allein darauf.

Webhooks empfangen und Signaturen prüfen

Ein Webhook teilt Ihrem Server mit, wann eine Prüfung abgeschlossen ist, sodass Sie nicht abfragen müssen. Dieser Empfänger ist ein minimaler HTTP-Server: das Modul http von Node.js und Flask für Python. Er prüft die Signatur jeder Zustellung, ignoriert eine Zustellung, die er bereits verarbeitet hat, und gibt das Ereignis aus. Er folgt der Seite Webhooks.

  1. Machen Sie Port 3000 unter einer öffentlichen HTTPS-Adresse erreichbar, zum Beispiel mit einem Tunnel zu Ihrem Rechner. Endpunkt-URLs müssen HTTPS am Standardport 443 verwenden und auf eine öffentliche Adresse zeigen.
  2. Öffnen Sie in der Konsole Webhooks, fügen Sie einen Endpunkt mit der URL https://your-host/webhooks/fileverity hinzu und wählen Sie seine Ereignisse. Kopieren Sie das Signing-Secret, das mit whsec_ beginnt. Es wird nur einmal angezeigt.
  3. Speichern Sie das Programm als webhook-receiver.mjs oder webhook_receiver.py und starten Sie es mit dem Secret: FILEVERITY_WEBHOOK_SECRET=whsec_... node webhook-receiver.mjs oder FILEVERITY_WEBHOOK_SECRET=whsec_... python webhook_receiver.py.
  4. Wählen Sie beim Endpunkt Test senden. Testschlüssel senden nie Ereignisse; Test senden stellt stattdessen ein signiertes Beispielereignis zu.
import { createHmac, timingSafeEqual } from "node:crypto";import { createServer } from "node:http"; const secret = process.env.FILEVERITY_WEBHOOK_SECRET;if (!secret) throw new Error("Set FILEVERITY_WEBHOOK_SECRET to the endpoint's signing secret.");const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");const port = Number(process.env.PORT) || 3000;const seen = new Set(); function verify(headers, body) {  const id = headers["webhook-id"];  const timestamp = headers["webhook-timestamp"];  const signatures = headers["webhook-signature"];  if (!id || !timestamp || !signatures) return false;  if (!(Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300)) return false;  const expected = createHmac("sha256", key).update(`${id}.${timestamp}.`).update(body).digest();  return signatures.split(" ").some((entry) => {    const [version, value = ""] = entry.split(",");    const given = Buffer.from(value, "base64");    return version === "v1" && given.length === expected.length && timingSafeEqual(given, expected);  });} function handleEvent(event) {  const { type, data } = event;  if (data.test) {    console.log(`Test event ${event.id}`);  } else if (type === "analysis.completed") {    const { level, action } = data.summary;    console.log(`${data.id} succeeded: level ${level}, action ${action}, ${data.report_url}`);  } else if (type === "analysis.failed") {    console.log(`${data.id} ${data.status}: ${data.error.code}`);  } else if (type === "balance.low") {    const money = (cents) => `US$${(cents / 100).toFixed(2)}`;    console.log(`Balance ${money(data.balance)} is below your ${money(data.threshold)} alert`);  }} async function receive(req, res) {  if (req.method !== "POST" || req.url !== "/webhooks/fileverity") return res.writeHead(404).end();  const chunks = [];  let size = 0;  for await (const chunk of req) {    size += chunk.length;    if (size > 1_000_000) return res.writeHead(413).end();    chunks.push(chunk);  }  const body = Buffer.concat(chunks);  if (!verify(req.headers, body)) return res.writeHead(400).end();  const event = JSON.parse(body.toString("utf8"));  try {    if (!seen.has(event.id)) {      handleEvent(event);      seen.add(event.id);    }  } catch (error) {    console.error(`Could not handle ${event.id}`, error);    return res.writeHead(500).end();  }  res.writeHead(200).end();} createServer((req, res) => receive(req, res).catch(() => res.destroy())).listen(port, () => console.log(`Listening on port ${port}`));
Listening on port 3000Test event evt_Qm4vB7nP1sK8tL3wR6yZ2cXaan_8Hk2pQ4vN9sL1xR7cW3mY6zB succeeded: level L2, action accept, https://fileverity.com/verify/FV-7K2M-9QXD-4T8R
  • Unveränderter Anfragetext. Die Signatur umfasst genau die gesendeten Bytes. Die Programme lesen den Anfragetext als Bytes, berechnen einen HMAC-SHA256 über webhook-id, einen Punkt, webhook-timestamp, einen Punkt und diese Bytes, mit dem Base64-dekodierten Secret als Schlüssel, und parsen das JSON erst, nachdem die Signatur als gültig bestätigt wurde.
  • Vergleich in konstanter Zeit. timingSafeEqual in Node.js und hmac.compare_digest in Python vergleichen das Ergebnis mit jeder v1-Signatur im Header webhook-signature.
  • Zeitstempel-Toleranz. Ein webhook-timestamp, der mehr als fünf Minuten von der Systemzeit abweicht oder keine Zahl ist, wird abgelehnt.
  • Schnelle Antworten. Eine gültige Zustellung erhält 200, sobald das Ereignis verarbeitet ist. Bei einer ungültigen Signatur lautet die Antwort 400. Schlägt die Verarbeitung fehl, antwortet das Programm mit 500, und Fileverity wiederholt die Zustellung später mit derselben Ereignis-ID. Siehe Zustellung und Wiederholungen.
  • Duplikate. Die Zustellung erfolgt mindestens einmal, und eine Wiederholung behält die Ereignis-ID. Die Programme merken sich die IDs, die sie verarbeitet haben, und antworten mit 200, ohne ein Ereignis zweimal zu verarbeiten. Ersetzen Sie im Produktivbetrieb das Set im Arbeitsspeicher durch einen Unique Constraint auf die Ereignis-ID in Ihrer Datenbank und stellen Sie die Arbeit in eine Warteschlange, damit die Antwort schnell bleibt.
  • Ereignisdaten. analysis.completed und analysis.failed enthalten die Analyse ohne result; rufen Sie GET /analyses/{id} auf, wenn Sie den vollständigen Bericht brauchen. balance und threshold in balance.low sind in US-Cent angegeben.

Wie es weitergeht