Začať integráciu
- Požiadajte administrátora, aby vytvoril vašu aplikáciu v časti Admin → API a integrácie. Každá aplikácia má samostatné prostredie, rozsahy obsahu, kanály, jazyky, autorov, platnosť a kvótu požiadaviek.
- Uchovávajte prihlasovací údaj na svojom serveri. Produkčné prostredie používa prihlasovacie údaje ft_live_, sandbox používa ft_test_. Pri každej požiadavke odošlite Authorization: Bearer <credential>.
- Prečítajte si /capabilities a /content/taxonomies a získajte povolené ID autorov a tém. Na prípravu dokumentu použite schému v OpenAPI.
- Vytvorte koncept, odošlite ho na kontrolu a sledujte udalosti publikovania. Vaša automatizácia môže pripraviť užitočný obsah, zatiaľ čo za vydanie zostáva zodpovedný editor.
Základná URL: https://www.formtruth.com/integrations/v1
Obsah a publikovanie
Každý externý koncept vyžaduje schválenie aktuálneho obsahu personálom pred zverejnením. Deklarovaná metóda ľudského autorstva alebo nastavenie automatizovanej publikácie neudeľuje redakčné schválenie.
Príspevky, nákupné príručky a často kladené otázky používajú jeden formát dokumentu a históriu verzií. Použite /content/posts, /content/guides alebo /content/faqs s príslušným oprávnením. Aplikácie môžu pristupovať iba k obsahu, ktorý vlastnia; čítanie súkromného konceptu sa líši od čítania publikovaného webu.
Dokumenty obsahujú zdrojový jazyk, skutočného autora, témy, zdroje a lokalizované názvy, identifikátory slug, súhrny a štruktúrované bloky tela. Články vytvorené s pomocou AI alebo automatizovane musia uvádzať cieľového čitateľa a pôvodný prínos. Úpravy prekladu nesmú meniť fakty o produktoch, čísla, ID obrázkov, odkazy ani štrukturálne rozmery.
Každý zápis potrebuje Idempotency-Key. Aktualizácie, kontrola, publikácia a stiahnutie tiež potrebujú If-Match s citovaným ETag. Úprava skontrolovaného obsahu zneplatňuje dotknuté schválenie. Zmena zdroja robí dotknuté preklady neaktuálnymi, kým nie sú zosúladené a znova skontrolované.
Nahrané obrázky zostávajú neschválené, kým editor neskontroluje práva a kvalitu. Publikované vydania sú nemenné snímky. Stiahnutie odstráni vydanie z verejného obsahu, informačných kanálov a vyhľadávania.
Referenčná dokumentácia API
Cesty uvedené nižšie sú relatívne k základnej URL. Pre príručky a často kladené otázky platia rovnaké trasy článkov, pričom sa zmení rodina obsahu. Stiahnite si OpenAPI, kde nájdete úplné schémy požiadaviek, požadované rozsahy a odpovede.
| Metóda | Cesta | Účel |
|---|
GET | /capabilities | Vaše platné oprávnenia a limity |
GET / POST | /content/posts | Zobraziť vaše články / vytvoriť koncept |
GET / PATCH | /content/posts/{id} | Prečítať koncept a ETag / uložiť revíziu |
PUT | /content/posts/by-external-id/{externalId} | Vytvoriť alebo aktualizovať koncept podľa vášho stabilného externého ID |
POST | /content/posts/{id}/submit-review | Odoslať na redakčnú kontrolu |
POST | /content/posts/{id}/publications | Publikovať schválené jazyky alebo naplánovať vydanie |
DELETE | /content/posts/{id}/publications/{releaseId} | Stiahnuť vydanie s uvedením dôvodu |
GET | /content/taxonomies | Aktívne témy a povolení autori |
POST | /content/imports | Zaradiť do frontu až 50 konceptov a skontrolovať výsledky položiek |
POST | /assets/uploads | Nahrať obrázok na kontrolu práv a kvality |
POST | /assets/{id}/complete | Prečítať trvalý stav nahrávania |
GET | /jobs/{id} | Prečítať výsledky importu |
GET / POST | /webhooks/subscriptions | Zoznam alebo registrácia vašich HTTPS prijímačov udalostí |
POST | /webhooks/subscriptions/{id} | Aktualizovať, vymeniť kľúč alebo vypnúť doručovanie |
Katalóg, objednávky a skladové zásoby sú vyhradené pre neskoršie moduly API. Prostredníctvom tejto brány v1 zatiaľ nie sú dostupné.
Chyby a opakovanie požiadaviek
Odosielajte nekomprimovaný UTF-8 JSON. Limity tela požiadavky sú 2 MiB pre bežné zápisy, 8 MiB pre importy a 18 MiB pre kódované nahrávanie obrázkov; telo musí prísť do desiatich sekúnd. 413 znamená, že telo je príliš veľké, 415 znamená nepodporovaný formát a 408 znamená, že nahrávanie vypršalo.
Chyby vracajú error.code, error.message, podrobnosti o poliach, ak sa uplatňujú, retryable a request_id. Pre potreby podpory si ponechajte X-Request-Id. 401 znamená problém s prihlasovacím údajom; 403 znamená nedostatočné oprávnenie; 412 znamená zmenenú verziu; 422 znamená neplatný alebo nepripravený obsah; 428 znamená chýbajúcu podmienku.
Pri 429 rešpektujte Retry-After. Pri dočasnom 503 alebo prerušení siete opakujte požiadavku s ohraničeným oneskorením a ROVNAKÝM kľúčom a vstupom. Odlišné údaje vyžadujú nový kľúč. Pri 409 zosúlaďte predchádzajúcu operáciu namiesto bezhlavého opakovania. Importy vracajú výsledky jednotlivých položiek prostredníctvom /jobs/{id}.
Podpísané webhooky
Použite verejný prijímač HTTPS na porte 443. Udalosti zahŕňajú content.review_required, content.published, content.withdrawn, content.publication_failed a import.completed. Pred spracovaním overte nespracované telo požiadavky.
HMAC-SHA256(secret, timestamp + "." + event_id + "." + raw_body)
Prečítajte X-Formtruth-Timestamp, X-Formtruth-Event-Id a X-Formtruth-Signature. Odmietnite časové pečiatky staršie než päť minút. Hlavička podpisu môže počas 24-hodinového okna výmeny kľúča obsahovať viac hodnôt v1= oddelených čiarkami; akceptujte každý platný podpis pre kľúč, ktorý si ponechávate.
Atómovo uložte ID udalosti na účely deduplikácie, zaraďte prácu do frontu a bezodkladne vráťte 2xx. Doručenie sa môže opakovať, preto duplicitná udalosť nesmie publikovať ani účtovať dvakrát. Podpisové tajomstvo uchovávajte oddelene od prihlasovacieho údaja API.
Príklady kódu
Pripravte document.json pomocou schémy KnowledgeDocument a skutočných povolených ID taxonómií. Na svojom serveri bezpečne nastavte FORMTRUTH_API_TOKEN. Tieto príklady vytvoria koncept; publikovanie stále vyžaduje pracovný postup kontroly.
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.