Documentation

Publishing API Reference

Payload contracts and expected response semantics for PrismSEO publishing workflows.

Publishing API Reference

This is the exact contract PrismSEO uses when publishing to a custom webhook destination. If you're integrating a Next.js app, a headless CMS, or an internal pipeline, this page is the source of truth. For a step-by-step walkthrough, see the Webhook Integration guide.

Request

PrismSEO sends a single HTTPS POST to your configured URL.

POST <your-webhook-url> HTTP/1.1
Content-Type: application/json
Authorization: Bearer <your-webhook-token>
User-Agent: PrismSEO-Webhook/1.0
  • Method — always POST.
  • AuthAuthorization: Bearer <token>, where the token is the value you set on the connection. Validate it on every request.
  • Timeout — PrismSEO aborts after 15 seconds. Respond before then.
  • Transport — HTTPS only. Private, localhost, and non-HTTPS URLs are rejected.

Event envelope

{
  "event": "article.published",
  "integration_name": "My Production Site",
  "article": { }
}
  • event — one of:

- article.published — published live (mode = publish)

- article.drafted — published as a draft (mode = draft)

- test — a manual test event from the dashboard

  • integration_name — the connection's label.
  • article — the article object below.

Article object

  • id *(string)* — stable UUID. Use as the idempotency key.
  • title *(string)* — display title / H1.
  • slug *(string)* — URL-safe, de-duplicated slug.
  • content_html *(string)* — full article body as HTML.
  • excerpt *(string)* — short summary (falls back to meta description).
  • meta_title *(string)* — SEO title tag.
  • meta_description *(string)* — meta description.
  • published_at *(string)* — ISO 8601 timestamp of the event.
  • prism_css *(string, optional)* — CSS to style content_html. Inject once per page if you don't already style article HTML.

Response semantics

Your endpoint's HTTP status decides how PrismSEO records the publish:

  • 2xx — accepted. Recorded as published in Publish History.
  • 4xx — validation or auth problem on the payload/config. Recorded as failed; not retried (fix the request).
  • 5xx — your destination failed transiently. Recorded as failed; safe to retry because the payload was valid.

Response bodies are logged for debugging, so return a short JSON message on errors.

Reference receivers

Next.js (App Router)

// app/api/prismseo-webhook/route.ts
import { NextRequest, NextResponse } from 'next/server'

export async function POST(req: NextRequest) {
  if (req.headers.get('authorization') !== `Bearer ${process.env.PRISMSEO_WEBHOOK_TOKEN}`) {
    return NextResponse.json({ error: 'unauthorized' }, { status: 401 })
  }
  const { event, article } = await req.json()
  if (!article?.id || !article?.content_html) {
    return NextResponse.json({ error: 'invalid payload' }, { status: 400 })
  }
  await savePost(article, event === 'article.published' ? 'published' : 'draft')
  return NextResponse.json({ ok: true })
}

Node / Express

import express from 'express'
const app = express()
app.use(express.json({ limit: '2mb' }))

app.post('/prismseo-webhook', async (req, res) => {
  if (req.get('authorization') !== `Bearer ${process.env.PRISMSEO_WEBHOOK_TOKEN}`) {
    return res.status(401).json({ error: 'unauthorized' })
  }
  const { event, article } = req.body
  if (!article?.id || !article?.content_html) {
    return res.status(400).json({ error: 'invalid payload' })
  }
  await savePost(article, event === 'article.published' ? 'published' : 'draft')
  res.json({ ok: true })
})

Reliability recommendations

  • Treat article.id as the idempotency key — upsert, never insert blindly.
  • Persist the payload before any processing, so a downstream failure never loses the article.
  • Respond fast (under 15s); run deploys, image handling, and re-indexing after you reply.
  • Log the response body on non-2xx so repeated failures are debuggable from Publish History.

Next step

Apply this setup, then run your first publish cycle from your dashboard.