Eine Integration starten
- Bitten Sie eine Administratorin oder einen Administrator, Ihre Anwendung unter Admin → API & Integrationen zu erstellen. Jede Anwendung verfügt über eine separate Umgebung, Inhaltsbereiche, Kanäle, Sprachen, Autorinnen und Autoren, eine Ablaufzeit und ein Anfragelimit.
- Speichern Sie die Zugangsdaten auf Ihrem Server. In der Produktion werden ft_live_-Zugangsdaten verwendet, in der Sandbox ft_test_. Senden Sie bei jeder Anfrage Authorization: Bearer <credential>.
- Lesen Sie /capabilities und /content/taxonomies, um die zulässigen IDs für Autorinnen, Autoren und Themen abzurufen. Verwenden Sie das Schema in OpenAPI, um Ihr Dokument vorzubereiten.
- Erstellen Sie einen Entwurf, reichen Sie ihn zur Prüfung ein und empfangen Sie Veröffentlichungsereignisse. Ihre Automatisierung kann nützliche Inhalte vorbereiten, während eine Redakteurin oder ein Redakteur für die Veröffentlichung verantwortlich bleibt.
Basis-URL: https://www.formtruth.com/integrations/v1
Inhalte & Veröffentlichung
Für jeden externen Entwurf ist vor der Veröffentlichung die Genehmigung des aktuellen Inhalts durch das Redaktionsteam erforderlich. Eine deklarierte Methode zur Erstellung durch Menschen oder eine Einstellung zur automatisierten Veröffentlichung gilt nicht als redaktionelle Genehmigung.
Beiträge, Kaufratgeber und FAQs verwenden dasselbe Dokumentformat und dieselbe Versionshistorie. Verwenden Sie /content/posts, /content/guides oder /content/faqs mit der entsprechenden Berechtigung. Anwendungen können nur auf Inhalte zugreifen, deren Eigentümer sie sind; das Lesen eines privaten Entwurfs unterscheidet sich vom Lesen der veröffentlichten Website.
Dokumente enthalten eine Ausgangssprache, eine tatsächliche Autorin oder einen tatsächlichen Autor, Themen, Quellen sowie lokalisierte Titel, Slugs, Zusammenfassungen und strukturierte Inhaltsblöcke. KI-gestützte oder automatisierte Artikel müssen das Leserziel und den eigenen ursprünglichen Beitrag ausweisen. Übersetzungsänderungen dürfen Produktfakten, Zahlen, Bild-IDs, Links oder strukturelle Abmessungen nicht verändern.
Jeder Schreibvorgang benötigt einen Idempotency-Key. Aktualisierungen, Prüfung, Veröffentlichung und Rücknahme benötigen außerdem If-Match mit dem in Anführungszeichen gesetzten ETag. Durch die Bearbeitung geprüfter Inhalte wird die betroffene Genehmigung ungültig. Durch eine Änderung der Quelle werden betroffene Übersetzungen als veraltet markiert, bis sie erneut abgeglichen und geprüft wurden.
Hochgeladene Bilder bleiben ungeprüft, bis eine Redakteurin oder ein Redakteur Rechte und Qualität kontrolliert hat. Veröffentlichte Versionen sind unveränderliche Momentaufnahmen. Durch das Zurückziehen wird die Veröffentlichung aus öffentlichen Inhalten, Feeds und der Suchauffindbarkeit entfernt.
API-Referenz
Die folgenden Pfade sind relativ zur Basis-URL. Für Ratgeber und FAQs gelten dieselben Artikelrouten, wenn Sie die Inhaltsfamilie ändern. Laden Sie OpenAPI für vollständige Anfrage-Schemata, erforderliche Berechtigungsbereiche und Antworten herunter.
| Methode | Pfad | Zweck |
|---|
GET | /capabilities | Ihre effektiven Berechtigungen und Limits |
GET / POST | /content/posts | Ihre Artikel auflisten / einen Entwurf erstellen |
GET / PATCH | /content/posts/{id} | Den Entwurf und den ETag lesen / eine Überarbeitung speichern |
PUT | /content/posts/by-external-id/{externalId} | Einen Entwurf anhand seiner stabilen externen ID erstellen oder aktualisieren |
POST | /content/posts/{id}/submit-review | Zur redaktionellen Prüfung einreichen |
POST | /content/posts/{id}/publications | Genehmigte Sprachen veröffentlichen oder eine Veröffentlichung planen |
DELETE | /content/posts/{id}/publications/{releaseId} | Eine Veröffentlichung mit Begründung zurückziehen |
GET | /content/taxonomies | Aktive Themen und zugelassene Autorinnen und Autoren |
POST | /content/imports | Bis zu 50 Entwürfe in die Warteschlange stellen und die Ergebnisse der einzelnen Elemente prüfen |
POST | /assets/uploads | Ein Bild zur Prüfung von Rechten und Qualität hochladen |
POST | /assets/{id}/complete | Den gespeicherten Upload-Status lesen |
GET | /jobs/{id} | Ihre Importergebnisse lesen |
GET / POST | /webhooks/subscriptions | HTTPS-Empfänger für Ereignisse auflisten oder registrieren |
POST | /webhooks/subscriptions/{id} | Den Schlüssel aktualisieren, rotieren oder die Zustellung deaktivieren |
Katalog, Bestellungen und Lagerbestand sind für spätere API-Module vorgesehen. Sie sind über dieses v1-Gateway derzeit nicht verfügbar.
Fehler & Wiederholungsversuche
Sende unkomprimiertes UTF-8-JSON. Die Grenzen für den Anfragetext betragen 2 MiB für gewöhnliche Schreibvorgänge, 8 MiB für Importe und 18 MiB für codierte Bild-Uploads; der Text muss innerhalb von zehn Sekunden eintreffen. Ein 413 bedeutet, dass der Text zu groß ist, 415 steht für ein nicht unterstütztes Format und 408 dafür, dass ein Upload das Zeitlimit überschritten hat.
Fehler enthalten error.code, error.message, gegebenenfalls Felddetails, retryable und request_id. Bewahren Sie X-Request-Id für den Support auf. Ein 401 bedeutet ein Problem mit den Zugangsdaten; 403 bedeutet fehlende Berechtigung; 412 bedeutet eine geänderte Version; 422 bedeutet ungültige oder noch nicht bereite Inhalte; 428 bedeutet eine fehlende Voraussetzung.
Beachten Sie bei 429 den Wert von Retry-After. Bei einem vorübergehenden 503 oder einer Netzwerkunterbrechung wiederholen Sie die Anfrage mit begrenztem Backoff und DEMSELBEN Schlüssel und derselben Eingabe. Eine andere Nutzlast benötigt einen neuen Schlüssel. Bei 409 gleichen Sie den vorherigen Vorgang ab, statt ihn blind zu wiederholen. Importe liefern Ergebnisse für einzelne Elemente über /jobs/{id}.
Signierte Webhooks
Verwenden Sie einen öffentlichen HTTPS-Empfänger an Port 443. Zu den Ereignissen gehören content.review_required, content.published, content.withdrawn, content.publication_failed und import.completed. Überprüfen Sie den unveränderten Anfrage-Body, bevor Sie ihn parsen.
HMAC-SHA256(secret, timestamp + "." + event_id + "." + raw_body)
Lesen Sie X-Formtruth-Timestamp, X-Formtruth-Event-Id und X-Formtruth-Signature. Lehnen Sie Zeitstempel außerhalb eines Zeitfensters von fünf Minuten ab. Der Signatur-Header kann während des 24-stündigen Zeitfensters für die Schlüsselrotation mehrere durch Kommas getrennte Werte im Format v1= enthalten. Akzeptieren Sie jede gültige Signatur für einen von Ihnen aufbewahrten Schlüssel.
Speichern Sie die Ereignis-ID atomar zur Duplikaterkennung, stellen Sie die Verarbeitung in die Warteschlange und geben Sie umgehend 2xx zurück. Die Zustellung kann wiederholt werden, daher darf ein doppeltes Ereignis weder zweimal veröffentlichen noch zweimal Gebühren auslösen. Bewahren Sie das Signiergeheimnis getrennt von den API-Zugangsdaten auf.
Codebeispiele
Bereiten Sie document.json anhand des Schemas KnowledgeDocument und echter zulässiger Taxonomie-IDs vor. Setzen Sie FORMTRUTH_API_TOKEN sicher auf Ihrem Server. Diese Beispiele erstellen einen Entwurf; für die Veröffentlichung ist weiterhin der Prüfungsablauf erforderlich.
import { readFile } from "node:fs/promises"
import { randomUUID } from "node:crypto"
const base = process.env.FORMTRUTH_URL || "https://www.formtruth.com"
const token = process.env.FORMTRUTH_API_TOKEN
if (!token) throw new Error("Set FORMTRUTH_API_TOKEN in a secure environment")
const document = JSON.parse(await readFile("document.json", "utf8"))
// Persist the key before calling. Reuse it only for an identical retry.
const key = process.env.FORMTRUTH_REQUEST_KEY || randomUUID()
console.log("Request key:", key)
const response = await fetch(base + "/integrations/v1/content/posts", {
method: "POST",
headers: { Authorization: "Bearer " + token, "Content-Type": "application/json", "Idempotency-Key": key },
body: JSON.stringify({ channel: "b2b", external_id: document.locales[document.source_locale].slug, document }),
})
const result = await response.json()
if (!response.ok) throw new Error(JSON.stringify(result.error))
console.log("Draft:", result.entry.id, "ETag:", response.headers.get("etag"))
// Creating a draft does not publish it. Submit for editorial review separately.
import json, os, uuid, urllib.request, urllib.error
base = os.environ.get("FORMTRUTH_URL", "https://www.formtruth.com")
token = os.environ["FORMTRUTH_API_TOKEN"]
with open("document.json", encoding="utf-8") as source:
document = json.load(source)
# Persist this key before sending; reuse for an identical retry.
key = os.environ.get("FORMTRUTH_REQUEST_KEY", str(uuid.uuid4()))
print("Request key:", key)
payload = {"channel": "b2b", "external_id": document["locales"][document["source_locale"]]["slug"], "document": document}
request = urllib.request.Request(base + "/integrations/v1/content/posts",
data=json.dumps(payload, ensure_ascii=False).encode("utf-8"), method="POST",
headers={"Authorization": "Bearer " + token, "Content-Type": "application/json", "Idempotency-Key": key})
try:
with urllib.request.urlopen(request, timeout=45) as response:
result = json.load(response)
print("Draft:", result["entry"]["id"], "ETag:", response.headers.get("ETag"))
except urllib.error.HTTPError as error:
raise RuntimeError(error.read().decode("utf-8")) from error
# Creating a draft does not publish it. Submit for editorial review separately.
import { createHmac, timingSafeEqual } from "node:crypto"
export function verifyWebhook(rawBody, headers, secret, now = Date.now()) {
const timestamp = headers.get("x-formtruth-timestamp") || ""
const eventId = headers.get("x-formtruth-event-id") || ""
const signatures = (headers.get("x-formtruth-signature") || "").split(",")
if (!/^[0-9]{10}$/.test(timestamp) || !eventId || Math.abs(now - Number(timestamp) * 1000) > 300000) return false
const expected = createHmac("sha256", secret).update(timestamp + "." + eventId + ".").update(rawBody).digest()
return signatures.some(value => {
const part = value.trim()
if (!/^v1=[a-f0-9]{64}$/.test(part)) return false
const actual = Buffer.from(part.slice(3), "hex")
return actual.length === expected.length && timingSafeEqual(actual, expected)
})
}
// Verify the raw body BEFORE JSON parsing. Atomically record eventId in durable
// storage to deduplicate retries; enqueue accepted work, then return HTTP 2xx.
// During rotation both v1 signatures are sent for 24 hours.
import hashlib, hmac, re, time
def verify_webhook(raw_body, headers, secret, now=None):
timestamp = headers.get("x-formtruth-timestamp", "")
event_id = headers.get("x-formtruth-event-id", "")
signatures = headers.get("x-formtruth-signature", "").split(",")
current = time.time() if now is None else now
if not re.fullmatch(r"[0-9]{10}", timestamp) or not event_id or abs(current - int(timestamp)) > 300:
return False
signed = (timestamp + "." + event_id + ".").encode("utf-8") + raw_body
expected = hmac.new(secret.encode("utf-8"), signed, hashlib.sha256).hexdigest()
return any(re.fullmatch(r"v1=[a-f0-9]{64}", part.strip()) and hmac.compare_digest(part.strip()[3:], expected) for part in signatures)
# Normalize header names to lowercase. Verify raw bytes before parsing JSON.
# Atomically persist event_id for deduplication, enqueue work, then return 2xx.