انٹیگریشن شروع کریں
- کسی منتظم سے کہیں کہ وہ Admin → API اور انٹیگریشنز میں آپ کی ایپلیکیشن بنائے۔ ہر ایپلیکیشن کا الگ ماحول، مواد کے دائرہ ہائے کار، چینلز، زبانیں، مصنفین، میعاد اور درخواستوں کا کوٹا ہوتا ہے۔
- اسناد اپنے سرور پر محفوظ کریں۔ پروڈکشن میں ft_live_ اسناد استعمال ہوتی ہیں؛ سینڈ باکس میں ft_test_ استعمال کریں۔ ہر درخواست کے ساتھ Authorization: Bearer <credential> بھیجیں۔
- مجاز مصنف اور موضوع کی شناختیں حاصل کرنے کے لیے /capabilities اور /content/taxonomies پڑھیں۔ اپنی دستاویز تیار کرنے کے لیے OpenAPI میں موجود اسکیما استعمال کریں۔
- مسودہ بنائیں، اسے جائزے کے لیے جمع کریں اور اشاعت کے ایونٹس سنیں۔ آپ کی خودکاری مفید مواد تیار کر سکتی ہے، جبکہ اجرا کی ذمہ داری ایڈیٹر کے پاس رہتی ہے۔
بنیادی URL: https://www.formtruth.com/integrations/v1
مواد اور اشاعت
ہر بیرونی ڈرافٹ کو ریلیز سے پہلے اس کے موجودہ مواد کی عملے کی منظوری درکار ہوتی ہے۔ اعلان کردہ انسانی تحریری طریقہ یا خودکار اشاعت کی ترتیب ادارتی منظوری فراہم نہیں کرتی۔
پوسٹس، خریداروں کی رہنمائیاں اور اکثر پوچھے گئے سوالات ایک ہی دستاویزی فارمیٹ اور ورژن کی تاریخ استعمال کرتے ہیں۔ متعلقہ اجازت کے ساتھ /content/posts، /content/guides یا /content/faqs استعمال کریں۔ ایپلیکیشنز صرف اسی مواد تک رسائی حاصل کر سکتی ہیں جس کی وہ مالک ہوں؛ نجی مسودہ پڑھنا شائع شدہ ویب سائٹ پڑھنے سے مختلف ہے۔
دستاویزات میں ماخذ زبان، حقیقی مصنف، موضوعات، ماخذ، اور مقامی عنوانات، سلگز، خلاصے اور ساخت یافتہ باڈی بلاکس شامل ہوتے ہیں۔ AI کی معاونت سے تیار کردہ یا خودکار مضامین میں اپنے قاری کا مقصد اور اصل حصہ واضح کرنا ضروری ہے۔ ترجمے کی ترامیم مصنوعات کے حقائق، اعداد، تصویری شناختوں، لنکس یا ساختی پیمائشوں کو تبدیل نہیں کر سکتیں۔
ہر تحریر کو Idempotency-Key درکار ہے۔ تازہ کاری، جائزہ، اشاعت اور واپسی کے لیے quoted ETag کے ساتھ If-Match بھی درکار ہے۔ جائزہ شدہ مواد میں ترمیم متاثرہ منظوری کو کالعدم کر دیتی ہے۔ ماخذ تبدیل کرنے سے متاثرہ ترجمے اس وقت تک پرانے ہو جاتے ہیں جب تک کہ دوبارہ سیدھ میں لا کر جائزہ نہ لیا جائے۔
اپ لوڈ کی گئی تصاویر اس وقت تک غیر منظور شدہ رہتی ہیں جب تک ایڈیٹر حقوق اور معیار کی جانچ نہ کر لے۔ شائع شدہ ریلیزز ناقابلِ تغیر اسنیپ شاٹس ہوتی ہیں۔ واپس لینے سے ریلیز عوامی مواد، فیڈز اور تلاش کے نتائج سے ہٹا دی جاتی ہے۔
API حوالہ
ذیل کے راستے بنیادی URL کے نسبتاً ہیں۔ مواد کے خاندان کو تبدیل کر کے یہی مضمون کے راستے گائیڈز اور اکثر پوچھے گئے سوالات پر بھی لاگو ہوتے ہیں۔ مکمل درخواست اسکیما، درکار دائرہ ہائے کار اور جوابات کے لیے OpenAPI ڈاؤن لوڈ کریں۔
| طریقہ | راستہ | مقصد |
|---|
GET | /capabilities | آپ کی مؤثر اجازتیں اور حدود |
GET / POST | /content/posts | اپنے مضامین کی فہرست بنائیں / مسودہ تیار کریں |
GET / PATCH | /content/posts/{id} | مسودہ اور ETag پڑھیں / ترمیم محفوظ کریں |
PUT | /content/posts/by-external-id/{externalId} | اپنے مستحکم بیرونی ID کے ذریعے ڈرافٹ بنائیں یا اپ ڈیٹ کریں |
POST | /content/posts/{id}/submit-review | ادارتی جائزے کے لیے جمع کریں |
POST | /content/posts/{id}/publications | منظور شدہ زبانیں شائع کریں یا اشاعت کا وقت مقرر کریں |
DELETE | /content/posts/{id}/publications/{releaseId} | وجہ کے ساتھ اشاعت واپس لیں |
GET | /content/taxonomies | فعال موضوعات اور مجاز مصنفین |
POST | /content/imports | 50 تک مسودے قطار میں لگائیں اور آئٹم کے نتائج دیکھیں |
POST | /assets/uploads | حقوق اور معیار کے جائزے کے لیے تصویر اپ لوڈ کریں |
POST | /assets/{id}/complete | محفوظ شدہ اپ لوڈ کی حالت پڑھیں |
GET | /jobs/{id} | اپنے درآمدی نتائج پڑھیں |
GET / POST | /webhooks/subscriptions | اپنے HTTPS ایونٹ ریسیورز کی فہرست بنائیں یا رجسٹر کریں |
POST | /webhooks/subscriptions/{id} | کلید کو اپ ڈیٹ یا تبدیل کریں، یا ترسیل غیر فعال کریں |
کیٹلاگ، آرڈرز اور انوینٹری بعد کے API ماڈیولز کے لیے محفوظ ہیں۔ یہ ابھی اس v1 گیٹ وے کے ذریعے دستیاب نہیں ہیں۔
خرابیاں اور دوبارہ کوششیں
غیر کمپریسڈ UTF-8 JSON بھیجیں۔ درخواست کی باڈی کی حدیں عام تحریروں کے لیے 2 MiB، درآمدات کے لیے 8 MiB اور انکوڈ شدہ تصویر اپ لوڈز کے لیے 18 MiB ہیں؛ باڈی دس سیکنڈ کے اندر پہنچنی چاہیے۔ 413 کا مطلب ہے کہ باڈی بہت بڑی ہے، 415 کا مطلب غیر تعاون یافتہ فارمیٹ ہے اور 408 کا مطلب اپ لوڈ ٹائم آؤٹ ہو گیا۔
خرابیاں error.code، error.message، ضرورت پڑنے پر فیلڈ کی تفصیلات، retryable اور request_id واپس کرتی ہیں۔ معاونت کے لیے X-Request-Id محفوظ رکھیں۔ 401 کا مطلب اسناد کا مسئلہ ہے؛ 403 کا مطلب اجازت کا مسئلہ ہے؛ 412 کا مطلب تبدیل شدہ ورژن ہے؛ 422 کا مطلب غیر درست یا تیار نہ کیا گیا مواد ہے؛ 428 کا مطلب گم شدہ پیشگی شرط ہے۔
429 کے لیے Retry-After کی پابندی کریں۔ عارضی 503 یا نیٹ ورک میں رکاوٹ کی صورت میں محدود بیک آف کے ساتھ اسی کلید اور ان پٹ سے دوبارہ کوشش کریں۔ مختلف پے لوڈ کے لیے نئی کلید درکار ہے۔ 409 کی صورت میں پچھلی کارروائی کی تطبیق کریں، اسے بلا سوچے سمجھے دوبارہ نہ دہرائیں۔ درآمدات فی آئٹم نتائج /jobs/{id} کے ذریعے واپس کرتی ہیں۔
دستخط شدہ ویب ہُکس
443 پورٹ پر عوامی HTTPS وصول کنندہ استعمال کریں۔ ایونٹس میں content.review_required، content.published، content.withdrawn، content.publication_failed اور import.completed شامل ہیں۔ خام درخواست کے باڈی کو پارس کرنے سے پہلے اس کی تصدیق کریں۔
HMAC-SHA256(secret, timestamp + "." + event_id + "." + raw_body)
X-Formtruth-Timestamp، X-Formtruth-Event-Id اور X-Formtruth-Signature پڑھیں۔ پانچ منٹ سے باہر کے ٹائم اسٹیمپس مسترد کریں۔ کلید کی تبدیلی کے 24 گھنٹے کے دورانیے میں دستخطی ہیڈر میں کوما سے جدا کردہ متعدد v1= اقدار شامل ہو سکتی ہیں؛ اپنی محفوظ رکھی ہوئی کلید کے لیے کسی بھی درست دستخط کو قبول کریں۔
ایونٹ کی شناخت کو نقل سے بچاؤ کے لیے جوہری طور پر محفوظ کریں، کام قطار میں شامل کریں اور فوراً 2xx واپس کریں۔ ترسیل دوبارہ کی جا سکتی ہے، اس لیے نقلی ایونٹ کو دوبارہ شائع یا دوبارہ چارج نہیں کرنا چاہیے۔ دستخطی راز کو API اسناد سے الگ رکھیں۔
کوڈ کی مثالیں
KnowledgeDocument اسکیما اور حقیقی مجاز ٹیکسونومی شناختوں کا استعمال کرتے ہوئے document.json تیار کریں۔ اپنے سرور پر FORMTRUTH_API_TOKEN کو محفوظ طریقے سے سیٹ کریں۔ یہ مثالیں ایک مسودہ بناتی ہیں؛ اشاعت کے لیے پھر بھی جائزے کا عمل درکار ہے۔
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.