Skip to documentation
Guides

Website automation

Blog automation

Connect a site through a signed integration when it owns its CMS, or use ReputePilot as its managed content store.

Two modes, one rule

ModeForSource of truthWhere you manage content
PushSites with their own CMS/databaseThe site's databaseBoth: ReputePilot studio and the site's own admin
HostedSites with no CMS (static, plain HTML)ReputePilotReputePilot studio only
Exactly one source of truth per site, chosen at connection time. No site ever has two writable copies, so there is no synchronization to break.

Push mode — the signed webhook

The site implements a single endpoint (for example /api/v1/content/public/reputepilot/blogs) and verifies three headers on every request. ReputePilot signs with a per-site shared secret; only holders of the secret can write.

Request signature (v2)

signature = lowercase_hex( HMAC_SHA256( secret,
  "v2\n{unix_ts}\n{nonce}\n{METHOD}\n{path_and_query}\n{raw_body}" ) )

headers:
  x-rp-timestamp:  unix seconds        (max clock skew: 5 minutes)
  x-rp-nonce:      UUID, single-use    (replays rejected forever)
  x-rp-signature:  hex HMAC as above   (constant-time compare)
  • Binding the method and path means a captured signature can never be replayed against another verb or resource.
  • Nonces are single-use; retried deliveries of a completed request return the recorded result idempotently.
  • Unsigned or invalid requests are rejected 401; an unconfigured receiver fails closed with 503.

Operations

VerbPurposeNotes
POST /blogsDeliver a blog (publish or draft)Duplicate slug or near-duplicate content → 409
GET /blogsInventory of ReputePilot-authored postsAll statuses, drafts included; returns version tokens and coverUrl
GET /blogs/{id}Full detail including markdown
PUT /blogs/{id}Update fields, replace the cover, publish, or take down to draftRequires the current version token; stale writes → 409 so neither side silently overwrites the other
DELETE /blogs/{id}Archive (soft delete)Recoverable from the site admin; hard deletion stays site-only

Scope: the webhook may only manage content it originated. Site-authored posts return 403 on any remote mutation.

Delivery payload

POST /blogs carries the complete article and its cover image. Every field is camelCase JSON; the body is signed byte-for-byte.

{
  "title":     "How to Create a Contract Intake Form That Reduces Review Delays",
  "subtitle":  "A practical guide to collecting the right details before review.",
  "slug":      "how-to-create-contract-intake-form-reduce-review-delays",
  "excerpt":   "2-3 plain-language sentences",
  "tags":      ["contract intake", "legal operations"],
  "category":  "Operations",
  "seo":       { "metaTitle": "...", "metaDescription": "...",
                 "canonicalPath": "/blogs/<slug>", "keywords": ["..."] },
  "markdown":  "## First section ...",
  "mode":      "publish",              // or "draft"
  "cover": {
    "url":         "https://api.reputepilot.app/api/blog-covers/cov_<id>.jpg",
    "contentType": "image/jpeg",
    "width":       1200,
    "height":      800,
    "alt":         "<title>",
    "data":        "<base64 of the same JPEG>"
  }
}
  • The cover is generated per article and branded with your site name: a 1200×800 editorial JPEG under 900 KB, suitable for og:image,twitter:image, and structured-data image.
  • Store data directly (no outbound request needed) or sideload fromurl, which is permanent and public. Both carry identical bytes.
  • Report the stored cover back as coverUrl on inventory and detail rows (an explicit null when a post has none). ReputePilot uses that signal to attach a cover to any post that shipped without one, via PUT with acover object — so a receiver that adds cover support later is backfilled automatically.
  • A cover you cannot store should never fail the article: accept the post, leavecoverUrl null, and the backfill will retry.

The writing engine

  • Checks freshness from the site's public blog list — stateless across restarts.
  • If the newest post is older than the cadence (default 24 h), it generates an article, then validates: minimum length, SEO metadata bounds, duplicate-title rejection, and no leading H1 (the page renders its own title).
  • Renders the cover image, then delivers signed; a 409 from the site is treated as a skip, never a retry loop.
  • Each cycle also attaches a cover to at most one earlier post the site reports as cover-less, with a bounded number of attempts per post.

Hosted mode — ReputePilot owns the store

Create a site in the studio and get three consumption surfaces immediately. All content management — editor with live preview, explicit publish/draft per post, AI draft generation, automatic cadence — happens in ReputePilot.

SurfaceURL shapeUse
Content APIGET /public/blog/{site_key}/posts[/{slug}]SSR/SSG sites fetch JSON at render or build time
Feeds/public/blog/{site_key}/rss.xml · sitemap.xmlSyndication and search engines; both answer 404 until the site's public blog URL is set
Embed/public/blog/embed.jsPlain HTML sites: one script tag renders the whole blog
<div id="rp-blog"></div>
<script src="https://api.reputepilot.app/public/blog/embed.js"
        data-site="YOUR_SITE_KEY" defer></script>
  • Only published posts are ever served publicly; the site key is an unguessable capability identifier granting read-only access.
  • Markdown is rendered server-side with raw HTML escaped — XSS-safe by construction.
  • Generated posts include a cover object (url, alt,width, height) branded with the site name; the RSS feed carries it as an enclosure and the embed renders it above each post.
  • Optional rebuild webhook: static sites get pinged after content changes so they redeploy automatically.

Search visibility for hosted blogs

Hosted posts are indexed under your domain, never under api.reputepilot.app. The API host's robots.txt allows crawlers to fetch only /public/blog/ and /api/blog-covers/; every other API path is disallowed, and public JSON is data for your renderer, not a page. Everything below switches on once the site's public blog URL (an https URL on your website, no query string) is set in the studio under Site settings → Search visibility.

SignalWhat ReputePilot emitsWhat your site does
CanonicalcanonicalUrl = {publicBaseUrl}/{slug} on every public post; null until configuredRender each post at exactly that address from the Content API and set <link rel="canonical"> to it
Sitemap/public/blog/{site_key}/sitemap.xml listing the blog root and each published canonical with lastmodAdd Sitemap: https://api.reputepilot.app/public/blog/{site_key}/sitemap.xml to your robots.txt, then submit the same URL in Search Console for your verified domain
Article datasite.authorName, site.publisherName, site.publisherLogoUrl (https only) plus seo, cover, publishedAt, updatedAtEmit Article JSON-LD from those fields on the rendered page
Slug changesOld slugs stay in previousSlugs; the Content API answers an old slug with a 301 to the current oneMirror the redirect (301) on your own routes so rankings follow the post
  • The sitemap lives on the API host but lists URLs on yours. Search engines accept thatcross-host arrangement only when the sitemap URL appears in your robots.txt — the studio provides the exact line to copy.
  • The drop-in embed renders in the browser with hash routing (#/slug), so it does not create indexable pages. Use the Content API for any page you want in search; keep the embed for sites where search traffic is not the goal.
  • Public responses are cacheable for 120 s (300 s at shared caches); a static site should rebuild on the optional webhook rather than polling.
  • Push-mode posts follow the receiving site's own SEO: seo.canonicalPath (for example /blogs/{slug}) is resolved against that site's host and indexed there.

Plan limits

StarterProAuthorityAgency
Hosted blog sites1310
Blog posts / month830120

Quotas are enforced centrally before any AI generation runs; exceeding a limit returns plan_limit.