Universal webhook for SEO blog autopublishing
Webhook connects the four Blogent actions to your existing CMS. Return each action’s complete result from the endpoint after the native CMS operation finishes.
Blogent reads the existing article archive, creates planned articles and updates published articles in place through one authenticated JSON endpoint.
Actions and stable identities
- Shopify, HubSpot, Webflow and Framer use the connected native publisher. WordPress, OpenCart, Strapi and Lovable use the installed Blogent connector; v0 and Replit adapt it to the existing app. Wix, Make, n8n and custom endpoints implement the synchronous contract; Zapier, Albato and ApiX-Drive use completion callbacks.
inventory: sendcursor: nullandlimit; return{articles: [snapshot], next_cursor: string|null}. Repeat the returned cursor until null.read: sendtargetand/orcontent_id; return{article_snapshot: snapshot}. Explicit nativetargetis authoritative. Blogent’s logicalcontent_idmay differ from the native identity.- Each snapshot contains
identity, title, alias, date, article, target, revision, urlsand optional image/author metadata.articlemaps locales to full title, alias, HTML, preview and SEO fields. Unknown original dates may be null and unavailable URLs may be an empty map. create: send the article payload, a uniqueoperation_id, logicalcontent_id, empty target and null expected revision. Return{posted:true,target,revision,urls}after complete publication.update: send existing native target IDs and the latestexpected_revision. Preserve original IDs, URLs/aliases, publication date, author and assets. Compare the revision atomically before applying changes.
{
"inventory_request": {"contract_version":"2.0","action":"inventory","cursor":null,"limit":10},
"inventory_response": {"articles":[],"next_cursor":null},
"read_request": {"contract_version":"2.0","action":"read","content_id":"logical-content-id","target":{"en":"native-42"}},
"read_response": {"article_snapshot":{"identity":"native-42","title":"Article","alias":"article","date":null,"article":{"en":{"title":"Article","alias":"article","text":"<p>Body</p>","preview":"Summary","meta_title":"Article","meta_description":"Description"}},"target":{"en":"native-42"},"revision":"storage-revision","urls":{}}},
"update_identity_fields": {"contract_version":"2.0","action":"update","operation_id":"new-operation-uuid","content_id":"logical-content-id","target":{"en":"native-42"},"expected_revision":"storage-revision"},
"write_response": {"posted":true,"target":{"en":"native-42"},"revision":"new-storage-revision","urls":{}},
"callback_body": {"contract_version":"2.0","request_id":"received-request-uuid","action":"inventory","result":{"articles":[],"next_cursor":null}}
}
Completion callbacks for queued workflows
- Select Zapier, Albato or ApiX-Drive in Blogent. Each outgoing action includes an opaque
request_id, the exactcallback_urland a request-scopedcallback_token. Keep these fields through every workflow step. - Branch by
action. Inventory/read query the native CMS; create/update use the same durable transaction, operation ledger and revision checks as a synchronous connector. - After completing the action, POST JSON to the supplied callback URL with header
Authorization: Bearer <callback_token>. Body:{contract_version:"2.0",request_id,action,result}. The result has the same shape as that action’s synchronous response. - For a failed action, send
result:{posted:false,message:"reason",status:409}with the appropriate 4xx/5xx status value. A partial publication must never produceposted:true. - Callbacks are bound to the request, action and configuration. Identical repeats are accepted; changed results return 409, invalid tokens 401 and expired callbacks 410. Requests expire after one hour. Blogent may resend the identical request up to five times, at least two minutes apart. Deduplicate by request_id and operation_id.
- The connection test resumes while callbacks are pending: inventory → create → read → update of the same real article. A queued acknowledgment stays pending. Verification uses the authenticated integration, without crawling public pages.
HTML tags that require styling
h1, h2, h3, h4
p, ul, ol, li, a, strong, img
table, thead, tbody, tr, th, td
details, summary
For <a>, make sure the rel and target attributes are supported.
.tldr - for the first p (can be styled with larger font size)
.faq-section - wrapper for <details> in FAQ
What you need
- An HTTPS endpoint accepting POST JSON with
contract_version: "2.0"and an authenticated request header. - Implement all four actions:
inventory,read,createandupdate. Include existing articles using explicit native IDs and locale mappings. - Use durable storage, a persistent operation ledger and transactional locking or compare-and-swap. A process-local lock is insufficient.
- Copy temporary assets into permanent destination storage before confirming creation; keep existing assets on updates.
- Return final action results synchronously, or configure the authenticated completion callback for Zapier, Albato and ApiX-Drive. Queue acceptance never means publication.
JSON payload
{
"contract_version": "2.0",
"action": "create",
"operation_id": "8f8c0380-17c8-4e9f-9bc0-fdb5bef60d30",
"content_id": "8f8c0380-17c8-4e9f-9bc0-fdb5bef60d30",
"target": {},
"expected_revision": null,
"image": null,
"image_category": "desert-tours",
"alias": "string-for-url",
"date": "2026-09-13 12:00:00",
"rubric": "category-slug",
"params": "key:value|value;key2:value;",
"author": {
"id": 7,
"identifiers": {"lovable": "cms-author-42"},
"name": "Olena Kovalenko",
"gender": "female",
"age": 37,
"position": "Head of Customer Success",
"description": "Works with customer onboarding and retention strategy."
},
"article": {
"en": {
"title": "text",
"alias": "string-for-url-en",
"preview": "html",
"meta_title": "text",
"meta_description": "text",
"reading_time_minutes": 7,
"toc": [
{
"title": "Main section",
"id": "main-section",
"children": [
{
"title": "Nested point",
"id": "nested-point",
"children": []
}
]
}
],
"text": "html"
}
}
}
Fields
contract_versionis2.0;actionselects inventory, read, create or update.operation_ididentifies one intended mutation. Persist its full request hash and final receipt; an unchanged retry replays that receipt.content_idis Blogent’s logical identity.targetmaps each locale to the authoritative destination ID.expected_revisionis null for creation and the latest authenticated snapshot revision for update.alias, localized aliases anddateestablish identity on creation; updates retain the destination’s original values.articlemaps locale codes to title, alias, preview HTML, text HTML, meta_title, meta_description, reading_time_minutes and toc.- Optional
image,image_category,rubric,paramsand structuredauthoruse explicit destination mappings. Only the current platform’s author identifier is authoritative.
If you need product matching, pass attributes in params (format key=value1|value2;). Store the values in your CMS to filter the catalog or show recommendations.
In the dashboard webhook settings, add a custom header pair and verify it before parsing the body. Recommended: X-Blogent-Token with a random secret of at least 32 characters stored only in server-side secrets. Blogent includes the exact configured pair in every request.
Idempotency and delivery rules
- Validate every locale before writing. Commit all locale changes and the durable operation receipt atomically; staged assets alone are not a successful publication.
- Check a saved operation receipt before revision validation. Replay an identical request; reject changed reuse of operation_id with 409.
- Compare expected_revision under transactional locking or compare-and-swap. Native/manual edits must also change that revision. A stale revision returns 409 without overwriting content.
- Preserve native IDs, URLs, aliases, original publication date, author and assets when updating. Never create another article to simulate an update.
- Sanitize HTML, preserve supported link attributes and copy required creation images to durable storage using bounded HTTPS fetches.
- Synchronous custom receivers must finish within the sender’s 60-second request timeout. Queued platform submission waits up to 25 seconds, then awaits an authenticated callback.
HTTP responses and retries
200 {posted:true,target,revision,urls}confirms complete create/update publication with every locale’s native ID and the new revision.- Inventory/read return their documented result objects. A generic
success:true, empty body or 202 response is not a final action result. - 400/422 rejects invalid data; 401/403 rejects authorization; 409 rejects changed operation reuse or stale revisions. Return a concise error message without secrets.
- 429/5xx means a temporary failure. Retry the same operation and body; do not allocate another article identity for an ambiguous delivery.
- For Zapier, Albato and ApiX-Drive, the callback carries the final action result. Workflow history and manual visual checks do not substitute for this callback.
Receiver acceptance checklist
- Reject invalid authorization and malformed actions before mutation.
- Inventory preexisting native articles, read full snapshots and exhaust pagination without inventing identities or dates.
- Create and then update one real test article, retaining its native IDs, aliases, original date, author and assets.
- Replay an identical operation after process restart; reject changed request reuse and stale/native-editor revisions.
- Verify concurrent writers and failure of any locale/image cannot produce a successful receipt.
- Test all queued actions and callbacks, including invalid tokens, changed duplicates and expiry. No public-page crawl is required.
Shortcodes
Blogent can automatically insert a shortcode into the article body so marketing or interactive blocks appear directly inside the content.
- In the SEO Blog dashboard, you specify one shortcode for the blog, for example
[contact-form]or[products_slider category="chairs"]. - During article generation, Blogent places this shortcode in a natural position in the middle of the content without breaking the article structure.
- Your website or CMS must process that shortcode and replace it with the final block: a form, CTA, promo code, product slider, banner, and so on.
- If the shortcode requires parameters, pass them in the format expected by your website or plugin.
- Marketing call to action:
[cta] - Discount promo code:
[promo_code] - Contact form:
[contact-form] - Product slider:
[products_slider] - Featured product selection:
[featured_products] - Banner or info widget:
[info_banner]
Shortcode names and parameters depend on your website. If shortcode support is not implemented yet, your developer needs to add the handler or install the relevant plugin.
Safe PHP receiver skeleton
The skeleton deliberately fails until a native CMS adapter is connected. Implement all four actions, durable operation receipts and atomic revision checks inside handleBlogentV2(). For queued automation, send the same final result through its authenticated callback.
declare(strict_types=1);
function respond(int $status, array $body): never
{
http_response_code($status);
header('Content-Type: application/json; charset=utf-8');
echo json_encode($body, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
exit;
}
/** Implement against the existing CMS, with authenticated inventory/read and
* transactional create/update. Persist operation_id + canonical request hash +
* complete result in the same commit as all locales. Replay before checking the
* revision; compare expected_revision under a row lock/CAS. Preserve native IDs,
* URLs, aliases, original dates, authors and assets on update. Throw on partial
* failure. Return each action's final result, never a queue acknowledgment.
*/
function handleBlogentV2(array $command): array
{
throw new LogicException('Connect the native CMS adapter before deployment.');
}
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') respond(405, ['posted' => false, 'message' => 'POST required']);
$secret = getenv('BLOGENT_WEBHOOK_TOKEN');
$token = $_SERVER['HTTP_X_BLOGENT_TOKEN'] ?? '';
if (!is_string($secret) || strlen($secret) < 32 || !is_string($token) || !hash_equals($secret, $token)) {
respond(401, ['posted' => false, 'message' => 'Unauthorized']);
}
if (!str_starts_with(strtolower($_SERVER['CONTENT_TYPE'] ?? ''), 'application/json')) {
respond(415, ['posted' => false, 'message' => 'JSON required']);
}
$raw = file_get_contents('php://input');
if (!is_string($raw) || strlen($raw) > 4 * 1024 * 1024) respond(413, ['posted' => false, 'message' => 'Payload too large']);
try {
$command = json_decode($raw, true, flags: JSON_THROW_ON_ERROR);
if (!is_array($command) || ($command['contract_version'] ?? null) !== '2.0'
|| !in_array($command['action'] ?? null, ['inventory', 'read', 'create', 'update'], true)) {
throw new InvalidArgumentException('Unsupported Blogent action');
}
// The adapter must validate action-specific fields and result completeness.
$result = handleBlogentV2($command);
respond(200, $result);
} catch (InvalidArgumentException | JsonException $error) {
respond(422, ['posted' => false, 'message' => $error->getMessage()]);
} catch (Throwable $error) {
respond(500, ['posted' => false, 'message' => 'Integration action failed']);
}
Webhook tester
Run inventory, create, read and update on one real test article. Queued integrations wait for their completion callbacks.
What you get after connecting Webhook
- Read the existing archive and full native snapshots before planning new articles.
- Update existing native articles while preserving IDs, URLs, original dates, authors and assets.
- Replay identical operations safely and reject stale revisions or changed operation reuse.
Requirements and compatibility
- Actions
- Implement inventory, read, create and update against the existing CMS and explicit locale mappings.
- Concurrency
- A transactional adapter with durable operation_id receipts and atomic expected_revision checks; individual no-code upserts are insufficient.
- Completion
- Return the final JSON result synchronously. Writes require posted:true, complete target IDs and revision.
Frequently asked questions about the Webhook integration
Does accepting the webhook confirm publication?
No. Blogent waits for the completed action result. A generic acknowledgment, an execution-history entry or a partial locale result does not finish the operation.
What does the connection test do?
Inventory, create, authenticated read and update of the same real article. It resumes while callbacks are pending and does not crawl the public page.
How are retries and edits protected?
The destination stores each operation ID, full request hash and receipt durably. Identical retries replay it; changed reuse and stale revisions return 409. Native editor changes must also advance the revision.