Aloita integraatio
- Pyydä ylläpitäjää luomaan sovelluksesi kohdassa Ylläpito → API ja integraatiot. Jokaisella sovelluksella on erillinen ympäristö, sisältöalueet, kanavat, kielet, kirjoittajat, vanhenemisaika ja pyyntökiintiö.
- Säilytä tunniste palvelimellasi. Tuotannossa käytetään ft_live_-tunnisteita ja hiekkalaatikossa ft_test_-tunnisteita. Lähetä jokaisen pyynnön mukana Authorization: Bearer <credential>.
- Lue /capabilities ja /content/taxonomies saadaksesi sallitut kirjoittaja- ja aiheiden tunnisteet. Valmistele asiakirjasi OpenAPI-määrityksen skeeman avulla.
- Luo luonnos, lähetä se tarkistettavaksi ja kuuntele julkaisutapahtumia. Automaatiosi voi valmistella hyödyllistä sisältöä, mutta toimittaja vastaa edelleen julkaisusta.
Perus-URL: https://www.formtruth.com/integrations/v1
Sisältö ja julkaiseminen
Jokainen ulkoinen luonnos vaatii henkilökunnan hyväksynnän sen nykyisestä sisällöstä ennen julkaisua. Ilmoitettu ihmisen laatima menetelmä tai automaattinen julkaisuasetus ei myönnä toimituksellista hyväksyntää.
Julkaisut, ostajan oppaat ja usein kysytyt kysymykset käyttävät samaa asiakirjamuotoa ja versiohistoriaa. Käytä reittiä /content/posts, /content/guides tai /content/faqs vastaavan käyttöoikeuden kanssa. Sovellukset voivat käyttää vain omistamaansa sisältöä; yksityisen luonnoksen lukeminen poikkeaa julkaistun verkkosivuston lukemisesta.
Asiakirjoissa on lähdekieli, oikea kirjoittaja, aiheet, lähteet sekä lokalisoidut otsikot, URL-tunnukset, yhteenvedot ja rakenteiset tekstiosiot. Tekoälyn avustamissa tai automaattisissa artikkeleissa on ilmoitettava lukijan tavoite ja alkuperäinen panos. Käännösmuokkaukset eivät saa muuttaa tuotetietoja, lukuja, kuvatunnisteita, linkkejä tai rakenteellisia mittoja.
Jokainen kirjoitus tarvitsee Idempotency-Key-avaimen. Päivitykset, tarkistus, julkaisu ja poisto vaativat myös If-Match-kentän lainatulla ETag-arvolla. Tarkistetun sisällön muokkaaminen mitätöi kyseisen hyväksynnän. Lähteen muuttaminen tekee asianomaisista käännöksistä vanhentuneita, kunnes ne on yhdenmukaistettu ja tarkistettu uudelleen.
Ladatut kuvat pysyvät hyväksymättöminä, kunnes toimittaja tarkistaa oikeudet ja laadun. Julkaistut versiot ovat muuttumattomia tilannekuvia. Julkaisun peruminen poistaa sen julkisesta sisällöstä, syötteistä ja hakunäkyvyydestä.
API-viite
Alla olevat polut ovat suhteessa perus-URL-osoitteeseen. Samat artikkelireitit koskevat oppaita ja usein kysyttyjä kysymyksiä, kun sisältöperhe vaihdetaan. Lataa OpenAPI täydellisiä pyyntöskeemoja, vaadittuja käyttöoikeuksia ja vastauksia varten.
| Menetelmä | Polku | Tarkoitus |
|---|
GET | /capabilities | Tehokkaat käyttöoikeutesi ja rajoituksesi |
GET / POST | /content/posts | Luettele artikkelisi / luo luonnos |
GET / PATCH | /content/posts/{id} | Lue luonnos ja ETag / tallenna versio |
PUT | /content/posts/by-external-id/{externalId} | Luo tai päivitä luonnos pysyvän ulkoisen tunnisteen mukaan |
POST | /content/posts/{id}/submit-review | Lähetä toimitukselliseen tarkistukseen |
POST | /content/posts/{id}/publications | Julkaise hyväksytyt kielet tai ajasta julkaisu |
DELETE | /content/posts/{id}/publications/{releaseId} | Peru julkaisu ja ilmoita syy |
GET | /content/taxonomies | Aktiiviset aiheet ja sallitut kirjoittajat |
POST | /content/imports | Lisää jonoon enintään 50 luonnosta ja tarkastele kohteiden tuloksia |
POST | /assets/uploads | Lataa kuva oikeus- ja laatutarkistusta varten |
POST | /assets/{id}/complete | Lue tallennetun latauksen tila |
GET | /jobs/{id} | Lue tuontisi tulokset |
GET / POST | /webhooks/subscriptions | Listaa tai rekisteröi HTTPS-tapahtumavastaanottimesi |
POST | /webhooks/subscriptions/{id} | Päivitä, vaihda avain tai poista toimitus käytöstä |
Tuoteluettelo, tilaukset ja varasto on varattu myöhemmille API-moduuleille. Ne eivät ole vielä käytettävissä tämän v1-yhdyskäytävän kautta.
Virheet ja uudelleenyritykset
Lähetä pakkaamaton UTF-8 JSON. Pyynnön rungon rajat ovat 2 MiB tavallisille kirjoituksille, 8 MiB tuonneille ja 18 MiB koodatuille kuvanlatauksille; rungon on saavuttava kymmenen sekunnin kuluessa. 413 tarkoittaa, että runko on liian suuri, 415 tarkoittaa tukematonta muotoa ja 408 tarkoittaa, että lataus aikakatkaistiin.
Virhevastaukset sisältävät kentät error.code, error.message, soveltuvin osin kenttätiedot, retryable ja request_id. Säilytä X-Request-Id-tunniste tukea varten. 401 tarkoittaa tunnisteongelmaa; 403 tarkoittaa käyttöoikeusongelmaa; 412 tarkoittaa muuttunutta versiota; 422 tarkoittaa virheellistä tai valmistelematonta sisältöä; 428 tarkoittaa puuttuvaa ennakkoehtoa.
Noudata 429-tilanteessa Retry-After-otsaketta. Yritä tilapäisen 503-virheen tai verkkokatkoksen jälkeen uudelleen rajatulla odotusajalla ja SAMALLA avaimella ja syötteellä. Eri hyötykuorma tarvitsee uuden avaimen. Selvitä 409-tilanteessa edellisen toiminnon tila sen sijaan, että toistaisit sen tarkistamatta. Tuonnit palauttavat kohdekohtaiset tulokset osoitteessa /jobs/{id}.
Allekirjoitetut webhookit
Käytä julkista HTTPS-vastaanotinta portissa 443. Tapahtumiin kuuluvat content.review_required, content.published, content.withdrawn, content.publication_failed ja import.completed. Vahvista raakapyyntöteksti ennen sen jäsentämistä.
HMAC-SHA256(secret, timestamp + "." + event_id + "." + raw_body)
Lue X-Formtruth-Timestamp-, X-Formtruth-Event-Id- ja X-Formtruth-Signature-otsakkeet. Hylkää aikaleimat, jotka ovat yli viiden minuutin päässä. Allekirjoitusotsake voi sisältää useita pilkuin eroteltuja v1=-arvoja avainten 24 tunnin vaihtojakson aikana; hyväksy mikä tahansa säilyttämälläsi avaimella kelvolliseksi todettu allekirjoitus.
Tallenna tapahtumatunniste atomisesti päällekkäisyyksien estämiseksi, lisää työ jonoon ja palauta 2xx nopeasti. Toimitus voidaan yrittää uudelleen, joten päällekkäinen tapahtuma ei saa julkaista tai veloittaa kahdesti. Säilytä allekirjoitussalaisuus erillään API-tunnisteesta.
Koodiesimerkit
Valmistele document.json KnowledgeDocument-skeeman ja oikeiden sallittujen taksonomiatunnisteiden avulla. Aseta FORMTRUTH_API_TOKEN turvallisesti palvelimellesi. Nämä esimerkit luovat luonnoksen; julkaiseminen edellyttää edelleen tarkistusprosessia.
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.