सामग्री पर जाएँ
दस्तावेज़ीकरण

डिलीवरी webhook: अपनी साइट पर लेख प्राप्त करना

वह हस्ताक्षरित webhook जो AutoPSEO के लेख आपकी साइट तक पहुँचाता है: हेडर, हर इवेंट और उसका payload, दिए जाने वाले उत्तर, रीट्राई, प्रतीक्षारत कमांड और IndexNow।

प्रॉपर्टी → कंटेंट → लेख कहाँ प्रकाशित होते हैं22 मिनट में पढ़ें

AutoPSEO जो भी लेख प्रकाशित करता है, अपडेट करता है या हटाता है, उसे हस्ताक्षरित HTTPS POST के रूप में आपकी अपनी साइट पर भेजता है। आपकी साइट लेख को सहेजती है और अपने टेम्प्लेट से दिखाती है, इसलिए सामग्री आपके अपने सिस्टम में रहती है और आप कभी AutoPSEO छोड़ दें तब भी काम करती रहती है। यह पेज उस डेवलपर के लिए संदर्भ है जो रिसीवर बना रहा है: हस्ताक्षर, हर इवेंट और उसका payload, AutoPSEO जो उत्तर पढ़ता है, रीट्राई, प्रतीक्षारत कमांड और IndexNow।

webhook सेट अप करें

  1. प्रॉपर्टी का Content पेज खोलें और Where articles are published पर जाएँ।
  2. Webhook address on your site भरें। यह किसी सार्वजनिक होस्ट नाम पर https:// URL होना चाहिए: IP पते और localhost स्वीकार नहीं किए जाते। AutoPSEO रीडायरेक्ट का पालन नहीं करता, इसलिए अंतिम पता दें।
  3. Your site's address भरें, उदाहरण के लिए https://example.com। हर कंटेंट टाइप का पाथ इसी के नीचे जुड़ता है, इसलिए /answers पर रहने वाले टाइप का लेख https://example.com/answers/<slug> पर होता है।
  4. Save and send a test चुनें। AutoPSEO एक साइनिंग सीक्रेट बनाता है (whsec_ के बाद 48 हेक्स अक्षर) और आपके पते पर ping भेजता है। जो रिसीवर हस्ताक्षर जाँचता है, वह यह पहला ping विफल कर देगा, क्योंकि उसके पास अभी सीक्रेट नहीं है।
  5. Show या Copy से Signing secret देखें, उसे अपनी साइट पर सीक्रेट के रूप में सहेजें, फिर Send a test चुनें।
  6. What your site handles पढ़ें: हर कमांड के आगे Connected, Not connected, Declared, not tested या Not tested दिखता है।

नई प्रॉपर्टी में लेख शुरुआत में AutoPSEO के अपने पते पर होस्ट होते हैं (देखें Hosting), और होस्टिंग तथा webhook साथ-साथ चल सकते हैं। जब आपकी साइट पर लेख दिखने लगें, तो Stop hosting on AutoPSEO चुनें: हर लाइव लेख आपकी साइट के पते पर चला जाता है और article.published के रूप में आपके webhook को भेजा जाता है। इस बटन के लिए ज़रूरी है कि webhook ने अपने पिछले टेस्ट का उत्तर दिया हो और आपकी साइट का पता दिया गया हो। होस्टिंग बंद होने तक webhook हटाया नहीं जा सकता।

New secret साइनिंग सीक्रेट को तुरंत बदल देता है और पुराना सीक्रेट उसी क्षण सत्यापित होना बंद कर देता है। नया सीक्रेट बनाते ही उसे अपनी साइट पर लगा दें।

AutoPSEO किसी webhook के लिए डिलीवरी तभी कतार में रखता है जब उसके पिछले ping का उत्तर 2xx रहा हो। विफल ping (सहेजते समय, Send a test पर, या साइट स्कैन की शुरुआत में) webhook को त्रुटि की स्थिति में डाल देता है, और त्रुटि की स्थिति में प्रकाशित लेख उसके लिए कतार में नहीं रखे जाते। ping का उत्तर देते रहें, और आउटेज ठीक करने के बाद Send a test चुनें।

हेडर और हस्ताक्षर

हर अनुरोध JSON बॉडी वाला POST होता है और उसमें ये हेडर होते हैं:

हेडरमान
Content-Typeapplication/json
User-AgentAutoPSEO-Webhook/1
X-AutoPSEO-Eventइवेंट का नाम, जो बॉडी में event के रूप में भी होता है
X-AutoPSEO-Deliveryडिलीवरी आईडी, जो बॉडी में id के रूप में भी होती है। एक डिलीवरी के हर रीट्राई पर यह वही रहती है
X-AutoPSEO-Timestampइस प्रयास पर हस्ताक्षर के समय का Unix समय, सेकंड में
X-AutoPSEO-Signaturesha256= के बाद <timestamp>.<raw body> का हेक्स HMAC-SHA256

अनुरोध सत्यापित करने के लिए:

  1. टाइमस्टैम्प हेडर, एक पूर्ण विराम और अनुरोध की रॉ बॉडी लें, ठीक उसी रूप में जिसमें वह पहुँची है, JSON पार्स करने से पहले।
  2. उस स्ट्रिंग पर HMAC-SHA256 निकालें। कुंजी पूरा साइनिंग सीक्रेट है, जैसा ऐप में दिखता है, whsec_ उपसर्ग समेत, UTF-8 बाइट्स के रूप में।
  3. परिणाम को हेक्स में एन्कोड करें और X-AutoPSEO-Signature में sha256= के बाद वाले हिस्से से constant time में तुलना करें। दोनों अलग हों तो अनुरोध अस्वीकार कर दें।

हर प्रयास पर नए सिरे से, उसके अपने टाइमस्टैम्प के साथ हस्ताक्षर होता है, इसलिए आप ऐसे अनुरोध अस्वीकार कर सकते हैं जिनका टाइमस्टैम्प आपकी घड़ी से बहुत दूर हो (पाँच मिनट की सीमा आम है), और इससे रीट्राई नहीं टूटते। HMAC की परिभाषा RFC 2104 में है।

हर बॉडी में id, event, occurredAt (ISO 8601, UTC) और site: { propertyId, domain } होते हैं।

इवेंट

इवेंटप्रकारAutoPSEO इसे कब भेजता है
pingजाँचwebhook सहेजने पर, Send a test पर और हर साइट स्कैन की शुरुआत में
testजाँचसफल ping के तुरंत बाद, हर कमांड के लिए एक
article.publishedडिलीवरीकोई लेख पहली बार लाइव होता है; होस्टिंग बंद करने पर हर लाइव लेख
article.updatedडिलीवरीनया रिविज़न लाइव होता है (रिफ्रेश, ऑप्टिमाइज़ेशन, रोलबैक, हटाने के बाद दोबारा प्रकाशन, भाषाएँ जोड़ना) या कवर जोड़ा गया
article.unpublishedडिलीवरीकोई लेख हटाया जाता है, या किसी दूसरे पेज में मर्ज किया जाता है
path.addडिलीवरीटाइप्ड कंटेंट टाइप के किसी नए मान को मंज़ूरी मिली
path.removeडिलीवरीकिसी ने किसी मान का पाथ हटाने को कहा
path.checkकॉलAutoPSEO ऐसे पते पर पेज की योजना बनाने से पहले जहाँ आपकी साइट पर कोई पेज नहीं है, या कोई नया मान सुझाने से पहले
site.exportकॉलसाइट स्कैन के दौरान, रोज़ के टॉपिक सिंक में, और AutoPSEO किसी लाइव लेख में बदलाव करने से पहले
articles.adoptedकॉलस्कैन द्वारा आपकी साइट के एक्सपोर्ट किए लेख इम्पोर्ट कर लेने के बाद
site.handoverकॉलजब कोई कंटेंट टाइप लिखने का काम AutoPSEO को सौंपता है, या वापस लेता है

डिलीवरी आउटबॉक्स से होकर जाती हैं: वे बदलाव के साथ ही उसी डेटाबेस बैच में दर्ज होती हैं, तुरंत भेजी जाती हैं, रीट्राई होती हैं, और आपकी साइट के लिए प्रतीक्षा में रह सकती हैं। कॉल सीधे अनुरोध और उत्तर होते हैं: AutoPSEO उत्तर उसी समय पढ़ता है। पाथ कमांड कब भेजे जाते हैं, यह Typed pages and new paths में बताया गया है।

लेख का payload

article.published और article.updated में पूरा लेख होता है, सभी भाषाओं समेत:

{
  "id": "0f6d2c1e-7a51-4c1b-9d0e-5b8f3a2e9c41",
  "event": "article.published",
  "occurredAt": "2026-10-12T08:30:00.000Z",
  "site": { "propertyId": "prop_123", "domain": "example.com" },
  "article": {
    "id": "a4c09e2b-6f1d-4e57-8b3a-1d2e3f4a5b6c",
    "slug": "plan-a-content-calendar",
    "path": "/answers/plan-a-content-calendar",
    "type": { "key": "answers", "name": "Answers", "path": "/answers" },
    "lang": "en",
    "langs": ["en", "de"],
    "rev": 1,
    "url": "https://example.com/answers/plan-a-content-calendar",
    "datePublished": "2026-10-12",
    "dateModified": "2026-10-12",
    "contentHash": "9f2c4e1a7b3d5f6081a2c3d4",
    "writer": "platform",
    "category": "Planning",
    "copy": {
      "title": "How to plan a content calendar",
      "metaDescription": "A content calendar in five steps, with a template.",
      "h1": "How to plan a content calendar",
      "directAnswer": "List your topics, give each a date and an owner, then review the plan weekly.",
      "keyTakeaways": ["Plan a quarter at a time.", "Give every piece one owner."],
      "sections": [{ "heading": "Start from your topics", "bodyHtml": "<p>Begin with the searches you want to win.</p>" }],
      "faq": [{ "q": "How far ahead should I plan?", "a": "One quarter is enough for most teams." }],
      "sources": [{ "title": "Creating helpful content", "url": "https://developers.google.com/search/docs/fundamentals/creating-helpful-content" }]
    },
    "translations": { "de": { "title": "Einen Redaktionsplan erstellen", "h1": "Einen Redaktionsplan erstellen" } },
    "cover": {
      "url": "https://app.autopseo.com/media/covers/prop_123/<sha256>.jpg",
      "sha256": "<sha256>",
      "contentType": "image/jpeg",
      "width": 1536,
      "height": 1024
    }
  }
}

जर्मन प्रविष्टि यहाँ संक्षिप्त की गई है; हर अनुवाद की संरचना copy जैसी ही होती है।

फ़ील्डअर्थ
idलेख के लिए AutoPSEO की आईडी। यह कभी नहीं बदलती, इसलिए इसे कुंजी के रूप में इस्तेमाल करना सुरक्षित है
slugपते का अंतिम हिस्सा। टाइप्ड पेजों पर इसमें / से जुड़े खंड होते हैं, जैसे google/software-engineer
pathआपकी साइट के सापेक्ष पता: टाइप का पाथ और slug। पेज बनने के बाद यह स्थिर रहता है
type{ key, name, path }। key स्थिर है; path वह जगह है जहाँ उस टाइप के लेख रहते हैं (साइट रूट के लिए खाली)
lang, langsप्राथमिक भाषा और इस रिविज़न की सभी भाषाएँ
revरिविज़न संख्या
urlAutoPSEO जिस रूप में पूरा पता जानता है: होस्टिंग बंद होने पर आपकी साइट का, या वह url जो आपके रिसीवर ने पिछली बार उत्तर में दिया
datePublished, dateModifiedYYYY-MM-DD। प्रकाशन तिथि बाद के रिविज़न में भी स्थिर रहती है
contentHashरिविज़न का फ़िंगरप्रिंट
writerरिविज़न किसने लिखा: platform (AutoPSEO), mcp (आपका अपना AI), user, import (आपकी साइट का अपना पाठ) या aggregate (लिस्ट पेज)
copyप्राथमिक भाषा में लेख
translationsअन्य भाषाएँ, भाषा कोड के अनुसार कुंजीबद्ध
categoryइसके लिए आपकी साइट की श्रेणी, जब आपका फ़ॉर्मैट श्रेणियाँ रखता है
topic{ externalId, keyword }, जब लेख आपकी साइट के सौंपे गए किसी टॉपिक के लिए लिखा गया हो, ताकि आप उस टॉपिक को बंद कर सकें
partsटाइप्ड पेजों पर, हर हिस्से का मान: { "company": "google", "role": "software-engineer" }
kindलिस्ट पेज पर "list"। इसकी copy { title, h1, items: [{ from, text }] } होती है, जहाँ from उस पेज का पाथ है जिससे हर आइटम आता है, और parts उस मान का नाम बताता है जिसे यह इकट्ठा करता है
coverAutoPSEO द्वारा लेख के लिए बनाया गया कवर। इसे एक बार डाउनलोड करके अपनी प्रति रखें; पता कभी नहीं बदलता क्योंकि फ़ाइल का नाम इमेज का SHA-256 है
basedOnarticle.updated पर: आपके पेज का वह संस्करण जिससे यह रिविज़न लिखा गया (नीचे टकराव देखें)
redirectToमर्ज के बाद article.unpublished पर: पुराना पता अब कहाँ ले जाता है। इसका उत्तर 301 से दें और वहाँ कुछ नया न रखें

article.unpublished में वही article फ़ील्ड होते हैं, सिवाय copy, translations, category और cover के।

copy के अंदर

फ़ील्डअर्थ
title, metaDescription, h1पेज का शीर्षक, मेटा विवरण और हेडिंग
directAnswerसबसे पहले दिखाया जाने वाला संक्षिप्त उत्तर
keyTakeawaysस्ट्रिंग्स की सूची
sections[{ heading, bodyHtml }]
faq[{ q, a }]
sources[{ title, url, lastVerified? }]
questionवैकल्पिक: खोजने वाले का प्रश्न उसके अपने शब्दों में, जब आपका फ़ॉर्मैट इसे माँगता हो
categoryवैकल्पिक: आपकी साइट की श्रेणियों में से एक
listsवैकल्पिक: कुंजी के अनुसार सादे पाठ की सूचियाँ, उन कंटेंट टाइप के लिए जो इन्हें माँगते हैं (इंटरव्यू गाइड के नमूना प्रश्न)

AutoPSEO जो लेख लिखता है उनमें bodyHtml केवल p, ul, li, strong, em, a और code का उपयोग करता है, और लिंक में केवल उनका href रहता है। आपकी अपनी साइट से लिया गया पाठ आपका HTML बनाए रखता है। अगर आपके टेम्प्लेट को ज़रूरत हो, तो उसे अपने sanitiser से भी गुज़ारें।

आपका रिसीवर क्या उत्तर दे

कोई भी 2xx डिलीवरी पूरी कर देता है। AutoPSEO उत्तर की बॉडी से एक वैकल्पिक JSON ऑब्जेक्ट भी पढ़ता है; इसे 64 KB से कम रखें, क्योंकि इससे बड़ा उत्तर विफलता गिना जाता है।

फ़ील्डप्रभाव
urlवह https:// पता जहाँ लेख अब मौजूद है। AutoPSEO इसे लेख के URL के रूप में दर्ज करता है
versionआपके पेज का संस्करण, अधिकतम 128 अक्षरों की एक अपारदर्शी स्ट्रिंग। यह अगले अपडेट के साथ basedOn के रूप में लौटता है
urlsवे https:// पते जो इस डिलीवरी से बदले, IndexNow के लिए (हर भाषा, या वे पते जिन्हें आपने देना बंद किया)। [] का अर्थ है कि कुछ नहीं बदला
cover"wanted", जब आपके पास लेख के लिए कवर नहीं है और डिलीवरी में भी कोई कवर नहीं था। AutoPSEO एक बनाकर उसे article.updated में भेजता है

त्रुटि वाले उत्तरों के अलग-अलग अर्थ होते हैं:

आपका उत्तरAutoPSEO इसे कैसे पढ़ता हैक्या होता है
{ "error": "unknown_event" } के साथ 4xx, या 501आप यह कमांड अभी नहीं संभालतेडिलीवरी बिना रीट्राई के प्रतीक्षा में रहती है
{ "error": "conflict" } के साथ 409अपडेट ऐसे संस्करण से लिखा गया था जो अब आपके पेज पर नहीं हैडिलीवरी बिना रीट्राई के विफल हो जाती है, और अगले बदलाव से पहले लेख आपकी साइट से दोबारा पढ़ा जाता है
path.add या path.remove के उत्तर में, 408 या 429 के अलावा कोई 4xx, { "error": "<code>", "details": [] } के साथआपके कारण सहित अस्वीकृतिदर्ज की जाती है; देखें Typed pages and new paths
बाकी सब: 10 सेकंड में कोई उत्तर नहीं, नेटवर्क त्रुटि, रीडायरेक्ट, 408, 429, 5xx या कोई अन्य 4xxअस्थायी विफलताबैकऑफ़ के साथ रीट्राई होता है

रीट्राई और अप्रचलित डिलीवरी

बदलाव सहेजते ही डिलीवरी भेज दी जाती है, और पाँच मिनट का एक शेड्यूल जो भी देय हो उसे सबसे पुराने से शुरू करके भेजता है। अस्थायी विफलता के बाद AutoPSEO प्रयासों के बीच 1 मिनट, फिर 5 मिनट, 15 मिनट, 1 घंटा, 3 घंटे, 6 घंटे और 12 घंटे प्रतीक्षा करता है। आठवाँ विफल प्रयास डिलीवरी को विफल चिह्नित कर देता है, और Content पेज पर webhook के पास एक लाल बैज उसे गिनता है।

जब उसी लेख की कोई नई डिलीवरी मौजूद हो, तो अब भी लंबित पुरानी डिलीवरी को superseded चिह्नित कर दिया जाता है और कभी भेजा नहीं जाता। हर डिलीवरी में पूरा लेख होता है, इसलिए आपकी साइट को केवल सबसे नई की ज़रूरत है। एक ही मान से जुड़े पाथ कमांड भी इसी तरह काम करते हैं।

आपका रिसीवर एक ही डिलीवरी दो बार देख सकता है, जैसे जब उसने लेख सहेज लिया पर 10 सेकंड के टाइमआउट के बाद उत्तर दिया। X-AutoPSEO-Delivery पर डुप्लिकेट हटाएँ, या article.id पर सहेजकर हर लिखावट को idempotent बनाएँ। article.updated ऐसे लेख तक भी पहुँच सकता है जिसे आपने article.unpublished के बाद हटा दिया था, क्योंकि हटाए गए लेख को दोबारा प्रकाशित करने पर अपडेट भेजा जाता है: अपडेट को "बनाओ या बदल दो" मानें।

ऐसे कमांड जिन्हें आपकी साइट अभी नहीं संभालती

कोई साइट प्रोटोकॉल को एक-एक कमांड करके लागू कर सकती है। AutoPSEO हर कमांड की स्थिति रखता है और उसे तीन तरीकों से जानता है:

  • टेस्ट। हर सफल ping के बाद AutoPSEO हर कमांड के लिए एक test भेजता है। बॉडी { id, event: "test", occurredAt, site, test: { event: "<command>", ... } } होती है, जहाँ test में असली कमांड जैसा बना एक नमूना होता है, जो <type path>/autopseo-connection-test के बारे में है, ऐसा पता जो किसी साइट पर नहीं होता। इसे वैसे ही जाँचें जैसे असली कमांड को जाँचते, कुछ बदलें नहीं, और 2xx से उत्तर दें: Connected के लिए { "test": "<command>", "ok": true }, Not connected के लिए { "test": "<command>", "ok": false, "error": "unknown_event" }। ok: false और किसी अन्य त्रुटि कोड वाला उत्तर, जैसे ऐसे पते को हटाना जो आपके पास नहीं है, फिर भी connected गिना जाता है। नमूना हमेशा test के नीचे रहता है, शीर्ष स्तर पर कभी नहीं, इसलिए जो रिसीवर टेस्ट के बारे में कुछ नहीं जानता उसे उसमें कोई लेख नहीं मिलता।
  • असली डिलीवरी और कॉल। 2xx कमांड को connected चिह्नित करता है; unknown_event उसे not connected चिह्नित करता है।
  • ping का उत्तर। आपके उत्तर में autopseo.events में सूचीबद्ध कमांड Declared, not tested दिखते हैं, जब तक कोई टेस्ट या डिलीवरी और कुछ न बताए।

जो कमांड connected नहीं है, उसकी डिलीवरी प्रतीक्षा में रहती है। Content पेज बताता है कि क्या प्रतीक्षा में है, जैसे "Your site does not take articles down yet", और जो भी काम आपने हाथ से कर लिया हो उसके लिए Done on the site बटन होता है; तब AutoPSEO वह डिलीवरी कभी नहीं भेजता। जब कोई बाद का टेस्ट या डिलीवरी कमांड को connected पाती है, तो उस पर प्रतीक्षारत सब कुछ भेज दिया जाता है।

ping का उत्तर

ping का उत्तर 2xx से दें। जो साइट केवल लेख प्राप्त करती है, उसे इससे अधिक कुछ नहीं चाहिए। AutoPSEO को यह बताने के लिए कि आपकी साइट और क्या करती है, autopseo ऑब्जेक्ट शामिल करें:

{
  "ok": true,
  "autopseo": {
    "version": 1,
    "events": ["article.published", "article.updated", "article.unpublished"],
    "covers": true,
    "indexNow": { "key": "3f1c9a7e5b2d4f60", "keyLocation": "https://example.com/3f1c9a7e5b2d4f60.txt" },
    "export": ["articles", "topics", "profile"],
    "adopt": true,
    "handover": true,
    "types": ["answers"],
    "writer": "site"
  }
}
फ़ील्डअर्थ
version1। 1 या उससे अधिक का version न होने पर ऑब्जेक्ट अनदेखा कर दिया जाता है
eventsवे कमांड जिन्हें आप संभालते हैं, टेस्ट होने तक declared के रूप में दिखाए जाते हैं
coverstrue जब आपके पेज हर लेख के साथ कवर दिखाते हैं। तब AutoPSEO का लेखक लिखते समय कवर बनाता है, इसलिए पहली डिलीवरी में वह होता है, और आपके अपने AI से भी एक देने को कहा जाता है। देखें Covers
indexNow{ key, keyLocation? }: वह IndexNow कुंजी जो आप सर्व करते हैं (8 से 128 अक्षर, अंक और डैश) और, जब कुंजी फ़ाइल /<key>.txt पर नहीं है, उसका https:// स्थान
exportsite.export क्या सौंपता है: articles, topics, profile
adoptआप articles.adopted स्वीकार करते हैं, ताकि पूरा स्कैन आपके मौजूदा लेख अपने हाथ में ले सके
handoverआप site.handover स्वीकार करते हैं
typesवे कंटेंट टाइप कुंजियाँ जिन्हें आप स्वीकार करते हैं (अधिकतम 50)
writerआज आपके नए लेख कौन लिखता है: site या autopseo

आपके द्वारा सर्व किए जाने वाले पतों के लिए IndexNow

जब आपका ping उत्तर indexNow घोषित करता है, तो आपके स्वीकार की हुई डिलीवरी के बाद AutoPSEO api.indexnow.org पर IndexNow को ping करता है, और आपकी साइट खुद कुछ ping नहीं करती।

  • यह आपके उत्तर में सूचीबद्ध urls भेजता है। urls न हो तो आपके उत्तर का url भेजता है, वह भी न हो तो लेख का URL। urls: [] कुछ नहीं भेजता।
  • केवल आपके अपने होस्ट के पते शामिल होते हैं, जैसा IndexNow protocol माँगता है: keyLocation का होस्ट, नहीं तो आपकी साइट के पते का होस्ट।
  • हर डिलीवरी रन प्रति साइट एक ping भेजता है, अधिकतम 10,000 पतों के साथ।
  • 200 और 202 स्वीकार किए जाते हैं। 429, 5xx या कोई उत्तर न मिलने पर पते रख लिए जाते हैं और एक घंटे बाद फिर कोशिश होती है, हर बार दोगुना होकर अधिकतम एक दिन तक (Retry-After माँगे तो उससे अधिक), और बाद की डिलीवरी जो भी जोड़े उसके साथ। 403 (कुंजी अमान्य) या 422 (पते होस्ट पर नहीं) दर्ज करके छोड़ दिए जाते हैं।
  • Content पेज आखिरी ping दिखाता है: कब, कितने पते, HTTP स्टेटस और अगला प्रयास।
  • जब AutoPSEO लेख होस्ट भी करता है, तो होस्ट किए गए पेज AutoPSEO की अपनी कुंजी से ping किए जाते हैं, और आपकी कुंजी इस्तेमाल नहीं होती।

Indexing के अंतर्गत IndexNow टूल एक अलग, मैन्युअल सबमिटर है; देखें IndexNow।

टकराव: आपकी साइट का पाठ ही मूल है

आपके उत्तर का version (या आपके एक्सपोर्ट में किसी लेख का version) वह है जो AutoPSEO मानता है कि आपका पेज किस संस्करण पर है। हर article.updated इसे basedOn के रूप में लाता है। अगर तब से आपकी साइट पर पेज बदल गया है, जैसे किसी संपादक के सुधार के बाद, तो { "error": "conflict" } के साथ 409 से उत्तर दें और कुछ सहेजें नहीं। डिलीवरी बिना रीट्राई के विफल हो जाती है।

किसी लाइव लेख में कतार में लगा बदलाव लिखे जाने से पहले, अगर आपकी साइट site.export संभालती है, तो AutoPSEO उस लेख को site.export और paths से आपकी साइट से दोबारा पढ़ता है। अगर आपका संस्करण आगे बढ़ चुका है, तो आपका पाठ लेख का अगला रिविज़न बन जाता है (writer import), और उसके बाद का अपडेट आपका मौजूदा संस्करण basedOn के रूप में लाता है।

path.add और path.remove

ये दोनों डिलीवरी किसी टाइप्ड कंटेंट टाइप के एक मान से जुड़ी हैं, उदाहरण के लिए /interviews/{company}/{role} में कोई कंपनी। इनमें article की जगह path ऑब्जेक्ट होता है:

{
  "id": "7c1e9d2a-3b4f-4e8a-9c0d-1e2f3a4b5c6d",
  "event": "path.add",
  "occurredAt": "2026-10-12T08:30:00.000Z",
  "site": { "propertyId": "prop_123", "domain": "example.com" },
  "path": {
    "path": "/interviews/jane-street",
    "type": { "key": "interviews", "name": "Interviews", "path": "/interviews" },
    "part": "company",
    "value": {
      "value": "jane-street",
      "name": "Jane Street",
      "aliases": [],
      "url": "https://<its own site>/",
      "sameAs": ["https://<its careers page>/"],
      "description": "<what it is, in a few words>",
      "facts": [
        { "text": "<one sentence, as the source states it>", "source": "https://<the page that says it>", "lastVerified": "2026-10-01", "tier": "official" }
      ]
    },
    "takes": { "role": ["software-engineer"] }
  }
}
फ़ील्डअर्थ
pathआपकी साइट पर मान का अपना पता
typeकंटेंट टाइप, { key, name, path } के रूप में
partपते का वह हिस्सा जिसे यह मान भरता है
value{ value, name }, और path.add पर AutoPSEO के लेखक द्वारा जुटाए गए aliases, url, sameAs, description और facts। हर तथ्य में text, source, lastVerified और official या reported में से एक tier होता है
takespath.add पर: अन्य हिस्सों के वे मान जिन्हें लोग इसके साथ खोजते हैं
redirectTopath.remove पर: अब से मान के पते कहाँ ले जाएँ, 301 के साथ

कमांड स्वीकार करने पर 2xx से उत्तर दें; path.add पर आप वह url जोड़ सकते हैं जहाँ मान अब मौजूद है, और path.remove पर वे urls जिन्हें आपने देना बंद किया, जो IndexNow को जाते हैं। अस्वीकार करने के लिए { "error": "<code>", "details": ["<why>"] } के साथ 4xx से उत्तर दें। path.add पर invalid_value मान को आपके कारण के साथ लेखक के पास वापस भेज देता है (तीसरी अस्वीकृति अंतिम होती है) और कोई भी अन्य कोड उसे हमेशा के लिए अस्वीकार कर देता है। path.remove पर not_found को पूरा हुआ माना जाता है, और कोई भी अन्य कोड, जैसे 409 in_use, सब कुछ जैसा था वैसा छोड़ देता है। प्रतीक्षारत पाथ कमांड को Done on the site चिह्नित करना आपकी साइट की हाँ गिना जाता है।

कॉल: अनुरोध जिनका उत्तर AutoPSEO पढ़ता है

कॉल पर हस्ताक्षर डिलीवरी की तरह ही होते हैं। कॉल डिफ़ॉल्ट रूप से 60 सेकंड तक और री-रीड तथा पाथ चेक के लिए 30 सेकंड तक प्रतीक्षा करता है, और 24 MB तक के उत्तर पढ़ता है। 2xx के अलावा कोई भी उत्तर कॉल को विफल कर देता है।

site.export

अनुरोध में export: { what, cursor, limit } होता है, और टॉपिक के लिए since (ISO समय) या री-रीड के लिए paths भी। उत्तर { "items": [], "next": "<cursor>" } दें; AutoPSEO cursor को आपके next पर सेट करके फिर पूछता है, जब तक वह खाली या null न हो जाए।

  • what: "articles" पूरे साइट स्कैन में चलता है, प्रति पेज 10, जब आपने articles के साथ export और adopt: true घोषित किया हो। 400 उत्तर का अर्थ है "समर्थित नहीं"। paths: ["/answers/x"] और limit: 1 के साथ यह एक लेख को दोबारा पढ़ता है; उत्तर में केवल वही लेख दें।
  • what: "topics" हर स्कैन में और रोज़ के सिंक में चलता है, प्रति पेज 50; रोज़ का सिंक since भेजता है, यानी पिछली बार खींचने का समय।
  • what: "profile" हर स्कैन में चलता है। उत्तर { "profile": { types, languages, rules, categories, format } } दें, हर एक वैकल्पिक: types को [{ key, name, path }] के रूप में, और rules में brandTerms, synonyms, noise, onTopic और offTopic।

एक्सपोर्ट किए गए लेख को externalId, एक slug (किसी भी लिपि के अक्षर और अंक तथा हाइफ़न, अधिकतम 200 अक्षर, खंड / से जुड़े), उस slug पर समाप्त होने वाला path, और कम से कम title, h1 तथा एक सेक्शन वाली copy चाहिए। वैकल्पिक फ़ील्ड: lang (डिफ़ॉल्ट en), type (कंटेंट टाइप की कुंजी), url, keyword, category, datePublished, dateModified, version, translations, owner (AutoPSEO की लेख आईडी, अपनाए जाने के बाद) और maintenance: { kind: "refresh" or "optimize", reason }, जो वह कार्य कतार में लगाता है। हर एक्सपोर्ट किया लेख आपके पाठ को पहले रिविज़न के रूप में लेकर प्रबंधित लेख बन जाता है, और कुछ वापस नहीं भेजा जाता। दोबारा एक्सपोर्ट करना सुरक्षित है, और जिस लेख को AutoPSEO ने हटा दिया है वह एक्सपोर्ट के ज़रिए कभी वापस नहीं आता।

टॉपिक आइटम { keyword, status?, volume?, source?, externalId?, type?, reason? } होता है, जहाँ status queued, planned या rejected है।

articles.adopted

इम्पोर्ट के बाद AutoPSEO आपसे कहता है कि उसे नए लेख प्रबंधित करने दें: { "articles": [{ "externalId", "articleId", "slug", "path" }] }। उत्तर { "adopted": ["<externalId>"], "refused": [{ "externalId": "<id>", "reason": "<why>" }] } दें। जिस लेख की आप पुष्टि नहीं करते उसे AutoPSEO से फिर हटा दिया जाता है, जब तक AutoPSEO उसका कोई रिविज़न पहले ही न लिख चुका हो।

site.handover

{ "handover": { "types": ["answers"], "writer": "autopseo" } }, जो तब भेजा जाता है जब कोई प्रॉपर्टी के Your site पेज पर Let AutoPSEO write them या Hand writing back to the site चुनता है। उत्तर { "ok": true, "writer": "autopseo" } दें। AutoPSEO को सौंपने के बाद, आपकी साइट अपने जाँचे हुए टॉपिक खुद लिखने की बजाय AutoPSEO को दे देती है।

path.check

{ "paths": [{ "path": "/interviews/google/data-engineer" }, { "path": "/interviews/jane-street", "new": { "part": "company", "value": "jane-street", "name": "Jane Street" } }] }। हर पाथ के लिए एक परिणाम दें: { "results": [{ "path": "/interviews/google/data-engineer", "ok": true }, { "path": "/interviews/jane-street", "ok": false, "error": "not_allowed", "details": ["We only list companies with a careers page."] }] }। अस्वीकृति आपके कारण के साथ रखी जाती है और दोबारा नहीं पूछी जाती; जो पाथ आप छोड़ देते हैं वह बाद में फिर पूछा जाता है।

एक न्यूनतम रिसीवर

एक Cloudflare Worker जो Web Crypto API से हस्ताक्षर सत्यापित करता है, तीन लेख कमांड संभालता है, और बाकी को unknown_event कहकर अस्वीकार करता है। यह लेख ARTICLES के रूप में बाँधे गए KV नेमस्पेस में रखता है, और साइनिंग सीक्रेट AUTOPSEO_SECRET नाम के Worker सीक्रेट में।

const HANDLED = ["article.published", "article.updated", "article.unpublished"];
const MAX_SKEW_SECONDS = 300;
const SITE = "https://example.com";

const json = (data, status = 200) =>
  new Response(JSON.stringify(data), { status, headers: { "content-type": "application/json" } });

function hexToBytes(hex) {
  if (!/^[0-9a-f]{64}$/.test(hex)) return null;
  const out = new Uint8Array(32);
  for (let i = 0; i < 32; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
  return out;
}

async function verified(request, body, secret) {
  const timestamp = request.headers.get("X-AutoPSEO-Timestamp") || "";
  const signature = (request.headers.get("X-AutoPSEO-Signature") || "").replace(/^sha256=/, "");
  if (!/^\d+$/.test(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > MAX_SKEW_SECONDS) return false;
  const bytes = hexToBytes(signature);
  if (!bytes) return false;
  const enc = new TextEncoder();
  const key = await crypto.subtle.importKey("raw", enc.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["verify"]);
  // verify() compares in constant time.
  return crypto.subtle.verify("HMAC", key, bytes, enc.encode(`${timestamp}.${body}`));
}

export default {
  async fetch(request, env) {
    if (request.method !== "POST") return new Response("Method not allowed", { status: 405 });
    const body = await request.text();
    if (!(await verified(request, body, env.AUTOPSEO_SECRET))) return json({ error: "bad_signature" }, 401);
    let msg;
    try {
      msg = JSON.parse(body);
    } catch {
      return json({ error: "bad_json" }, 400);
    }

    if (msg.event === "ping") return json({ ok: true, autopseo: { version: 1, events: HANDLED } });

    if (msg.event === "test") {
      const command = msg.test && msg.test.event;
      // Check the sample as you would the real command, and store nothing.
      return HANDLED.includes(command)
        ? json({ test: command, ok: true })
        : json({ test: command, ok: false, error: "unknown_event" });
    }

    if (msg.event === "article.published" || msg.event === "article.updated") {
      const a = msg.article;
      const stored = JSON.parse((await env.ARTICLES.get(a.id)) || "null");
      if (a.basedOn && stored && stored.version && stored.version !== a.basedOn) {
        return json({ error: "conflict", details: ["The page changed on the site."] }, 409);
      }
      // Any change to the page, here or in your own editor, gets a new version.
      const version = crypto.randomUUID();
      await env.ARTICLES.put(a.id, JSON.stringify({ ...a, version }));
      return json({ url: SITE + a.path, version });
    }

    if (msg.event === "article.unpublished") {
      const a = msg.article;
      if (a.redirectTo) await env.ARTICLES.put(a.id, JSON.stringify({ path: a.path, redirectTo: a.redirectTo }));
      else await env.ARTICLES.delete(a.id);
      return json({ ok: true });
    }

    return json({ error: "unknown_event" }, 400);
  },
};

Node.js पर, उसी तरह createHmac और timingSafeEqual से सत्यापित करें, उस रॉ बॉडी पर जो आपके फ़्रेमवर्क को मिली:

import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyAutopseo(rawBody, headers, secret) {
  const timestamp = String(headers["x-autopseo-timestamp"] || "");
  const given = String(headers["x-autopseo-signature"] || "");
  if (!/^\d+$/.test(timestamp) || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const expected = "sha256=" + createHmac("sha256", secret).update(timestamp + ".").update(rawBody).digest("hex");
  return given.length === expected.length && timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}

आगे: webhook तक क्या पहुँचेगा, यह लेखन और समीक्षा तथा रखरखाव तय करते हैं; पूरा प्रवाह कंटेंट इंजन का अवलोकन में है।

अपना SEO कंटेंट ऑटोपायलट पर चलाएँ

Search Console और अपनी साइट कनेक्ट करें। AutoPSEO उसे स्कैन करता है और तुरंत पहले विषयों की योजना बना देता है। क्रेडिट कार्ड की ज़रूरत नहीं।