Blogent

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: send cursor: null and limit; return {articles: [snapshot], next_cursor: string|null}. Repeat the returned cursor until null.
  • read: send target and/or content_id; return {article_snapshot: snapshot}. Explicit native target is authoritative. Blogent’s logical content_id may differ from the native identity.
  • Each snapshot contains identity, title, alias, date, article, target, revision, urls and optional image/author metadata. article maps 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 unique operation_id, logical content_id, empty target and null expected revision. Return {posted:true,target,revision,urls} after complete publication.
  • update: send existing native target IDs and the latest expected_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 exact callback_url and a request-scoped callback_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 produce posted: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, create and update. 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_version is 2.0; action selects inventory, read, create or update.
  • operation_id identifies one intended mutation. Persist its full request hash and final receipt; an unchanged retry replays that receipt.
  • content_id is Blogent’s logical identity. target maps each locale to the authoritative destination ID.
  • expected_revision is null for creation and the latest authenticated snapshot revision for update.
  • alias, localized aliases and date establish identity on creation; updates retain the destination’s original values.
  • article maps locale codes to title, alias, preview HTML, text HTML, meta_title, meta_description, reading_time_minutes and toc.
  • Optional image, image_category, rubric, params and structured author use explicit destination mappings. Only the current platform’s author identifier is authoritative.
Product params

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.

Authorization header

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

  1. Reject invalid authorization and malformed actions before mutation.
  2. Inventory preexisting native articles, read full snapshots and exhaust pagination without inventing identities or dates.
  3. Create and then update one real test article, retaining its native IDs, aliases, original date, author and assets.
  4. Replay an identical operation after process restart; reject changed request reuse and stale/native-editor revisions.
  5. Verify concurrent writers and failure of any locale/image cannot produce a successful receipt.
  6. 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.

How it works
  • 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.
Usage examples
  • 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.

Reference only. The test generates fresh IDs once and reuses them while polling.

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.

Put your SEO blog on autopilot

Create a blog, connect Webhook, and Blogent will plan, write, link, and publish articles automatically.

Start now