Skip to content

API Reference

The whiz.pub REST API is available at https://api.whiz.pub/v1. All requests and responses use JSON.

Interactive API documentation (ReDoc) is available at api.whiz.pub/v1.

Authentication

Most endpoints require a Bearer token in the Authorization header:

Authorization: Bearer YOUR_API_KEY

You receive an API key when you sign up (via the web dashboard, CLI, or API). You can also find it on your settings page at /app/settings.

Auth Levels

LevelDescriptionEndpoints
PublicNo authentication requiredPOST /v1/signup, POST /v1/auth/login, POST /v1/auth/verify-otp, GET /health
VerifiedRequires API key with verified emailAll other endpoints

New accounts receive a one-time code via email. Verify the code to complete signup and receive your API key. Returning users can log in by requesting a new code via POST /v1/auth/login.

Endpoints

MethodPathAuth LevelDescription
POST/v1/signupPublicCreate an account and blog
POST/v1/auth/loginPublicRequest a login code via email
POST/v1/auth/verify-otpPublicVerify email code and get API key
POST/v1/postsVerifiedCreate or update a post (upsert by slug)
GET/v1/postsVerifiedList posts (?limit=, ?offset=, ?tag=, ?status=)
GET/v1/posts/:slugVerifiedGet a specific post
PUT/v1/posts/:slugVerifiedUpdate a post (supports slug rename)
DELETE/v1/posts/:slugVerifiedDelete a post
GET/v1/dashboardVerifiedPer-tenant dashboard statistics
POST/v1/domainsVerifiedAdd a custom domain
DELETE/v1/domainsVerifiedRemove custom domain
POST/v1/domains/:domain/verifyVerifiedVerify domain DNS records
GET/v1/themeVerifiedGet theme and appearance settings
PUT/v1/themeVerifiedUpdate theme, fonts, custom CSS
PUT/v1/headlessVerifiedEnable or disable headless mode
GET/v1/tagsVerifiedList all tags with post counts
POST/v1/tagsVerifiedCreate a tag (with optional description)
PUT/v1/tags/:tagVerifiedRename a tag and/or update description
DELETE/v1/tags/:tagVerifiedDelete a tag from all posts
GET/v1/settingsVerifiedGet all blog settings
PUT/v1/settings/seoVerifiedUpdate display name, description
PUT/v1/settings/title-patternVerifiedUpdate title pattern
PUT/v1/settings/ai-accessVerifiedToggle AI access (llms.txt)
PUT/v1/settings/blogVerifiedUpdate blog display settings (posts per page)
PUT/v1/settings/landing-pageVerifiedToggle custom landing page
POST/v1/apikey/regenerateVerifiedRegenerate API key
DELETE/v1/apikeyVerifiedRevoke API key
POST/v1/cache/clearVerifiedClear all cached blog pages
DELETE/v1/accountVerifiedDelete account and all data
POST/v1/imagesVerifiedUpload an image (stored at original size)
POST/v1/images/ogVerifiedUpload an OG/featured image (resized to 1200x630)
POST/v1/faviconVerifiedUpload a custom favicon (PNG/ICO/SVG, max 512 KB)
POST/v1/favicon/resetVerifiedReset favicon to auto-generated
POST/v1/importVerifiedImport posts (multipart upload)
GET/v1/exportVerifiedExport posts (?format=whiz|jekyll|hugo|ghost|wordpress|bearblog|markdown)
POST/v1/previewVerifiedPreview markdown as HTML
GET/v1/saved-themesVerifiedList saved themes
POST/v1/saved-themesVerifiedSave a theme configuration
DELETE/v1/saved-themes/:idVerifiedDelete a saved theme
POST/v1/saved-themes/:id/applyVerifiedApply a saved theme to live blog
GET/v1/webhooksVerifiedList outbound webhooks
POST/v1/webhooksVerifiedCreate an outbound webhook
GET/v1/webhooks/:idVerifiedGet webhook details
PUT/v1/webhooks/:idVerifiedUpdate webhook configuration
DELETE/v1/webhooks/:idVerifiedDelete a webhook
POST/v1/webhooks/:id/regenerate-secretVerifiedRegenerate webhook signing secret
GET/v1/webhooks/:id/deliveriesVerifiedList recent webhook deliveries
POST/v1/webhooks/:id/testVerifiedSend a test ping event
GET/healthPublicHealth check

Pages

Posts support a post_type field: "post" (default) or "page". Pages are standalone content (e.g., About, Contact) that don't appear in the blog index or RSS feed.

  • Create a page: POST /v1/posts with "post_type": "page" in the body
  • List pages: GET /v1/posts?type=page
  • List posts only: GET /v1/posts?type=post (default)

Pages render at /{slug} on the public blog just like posts, but are excluded from the index, RSS feed, and tag listings.

Rate Limiting

The signup endpoint is rate-limited to 5 requests per minute per IP address. Other endpoints are not currently rate-limited but may be in the future.

Error Format

Errors return an appropriate HTTP status code with a JSON body:

json
{
  "error": "email already registered"
}

Common status codes:

CodeMeaning
400Invalid request body or parameters
401Missing or invalid API key
403Email not verified (for verified-only endpoints)
404Resource not found
429Rate limit exceeded
500Internal server error

Examples

Create a post

bash
curl -X POST https://api.whiz.pub/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "My First Post",
    "slug": "my-first-post",
    "content": "Hello, world! This is **markdown**.",
    "tags": ["intro", "blogging"],
    "status": "published",
    "summary": "A short excerpt for the index and RSS feed.",
    "featured_image": "https://example.com/social.jpg",
    "pinned": false,
    "date": "2025-01-15T10:30:00Z",
    "metadata": {"series": "Getting Started", "episode": 1}
  }'

The post endpoint performs an upsert — if a post with the given slug already exists, it is updated.

Optional post fields:

  • summary: custom excerpt. If omitted, whiz generates one from the content.
  • featured_image: HTTPS image URL for post-level social previews.
  • pinned: when true, the post appears before regular posts on the public index.
  • date: RFC3339 timestamp to override the publication date (e.g. 2025-01-15T10:30:00Z). Use to backdate or forward-date posts. Controls sorting order, display date, and RSS/sitemap timestamps. Defaults to the current time when omitted.
  • metadata: arbitrary JSON object (max 16 KB) stored with the post. Returned in all API responses, MCP tool outputs, and CLI. Also rendered in the post's JSON-LD structured data on the public blog. Useful for custom fields, integrations, CMS extensions, or agent-specific data.

List posts

bash
curl https://api.whiz.pub/v1/posts?limit=10&offset=0 \
  -H "Authorization: Bearer YOUR_API_KEY"

Query parameters:

ParameterTypeDescription
limitintMax results per page (default 20)
offsetintPagination offset (default 0)
tagstringFilter by tag name
statusstringFilter by status: draft or published
typestringPost type: post (default) or page

The response is a wrapped object with the posts array and total count:

json
{
  "posts": [...],
  "total": 43
}

All listing endpoints now return a total count alongside the results, enabling clients to implement pagination.

Get a single post

bash
curl https://api.whiz.pub/v1/posts/my-first-post \
  -H "Authorization: Bearer YOUR_API_KEY"

Public blogs also expose reader-facing discovery endpoints:

  • /rss.xml: RSS feed for published posts.
  • /tag/{tag}: HTML index of published posts with a matching tag.

Delete a post

bash
curl -X DELETE https://api.whiz.pub/v1/posts/my-first-post \
  -H "Authorization: Bearer YOUR_API_KEY"

Upload an image

Upload an image for use in blog posts (stored at original size):

bash
curl -X POST https://api.whiz.pub/v1/images \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "image=@photo.jpg"

Returns {"url": "https://..."}. Use the URL in markdown: ![alt](url).

Max file size: 3 MB. Accepted types: JPEG, PNG, GIF, WebP. SVG is not supported.

Upload an image for use as an OG image or featured image. The server resizes it to 1200x630 and stores it as JPEG:

bash
curl -X POST https://api.whiz.pub/v1/images/og \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "image=@hero.png"

Returns {"url": "https://..."}. Use the URL as featured_image on a post or as the site-wide og_image in SEO settings.

Max input size: 10 MB. Accepted types: JPEG, PNG, GIF, WebP.

Auto-generated OG images

When a post is published without a featured_image, whiz automatically generates a 1200x630 OG image using the blog's theme colors and the post title. Similarly, a site-level OG image is generated on signup using the blog name and description. Custom images uploaded via the dashboard or API always take priority.

Sign up

bash
curl -X POST https://api.whiz.pub/v1/signup \
  -H "Content-Type: application/json" \
  -d '{
    "email": "you@example.com",
    "subdomain": "yourname"
  }'

A one-time verification code is sent to your email. Verify it to complete signup and receive your API key.

Log in

Request a one-time login code for an existing account:

bash
curl -X POST https://api.whiz.pub/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "you@example.com"
  }'

Verify OTP

Submit the code from your email to authenticate and receive your API key:

bash
curl -X POST https://api.whiz.pub/v1/auth/verify-otp \
  -H "Content-Type: application/json" \
  -d '{
    "email": "you@example.com",
    "code": "123456"
  }'

The response includes your API key. Use it in subsequent requests.

Saved Themes

List all saved themes:

bash
curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://api.whiz.pub/v1/saved-themes

Save the current theme configuration:

bash
curl -X POST https://api.whiz.pub/v1/saved-themes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Dark Theme",
    "theme_preset": "ocean",
    "body_font": "inter"
  }'

Apply a saved theme to the live blog:

bash
curl -X POST https://api.whiz.pub/v1/saved-themes/THEME_ID/apply \
  -H "Authorization: Bearer YOUR_API_KEY"

Delete a saved theme:

bash
curl -X DELETE https://api.whiz.pub/v1/saved-themes/THEME_ID \
  -H "Authorization: Bearer YOUR_API_KEY"

Enable headless mode

bash
curl -X PUT https://api.whiz.pub/v1/headless \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true}'

When headless mode is enabled, the blog's public pages (subdomain, sitemap, robots.txt, etc.) return 404. The API, CLI, and MCP continue to work normally. Your subdomain remains reserved. Headless mode cannot be enabled while a custom domain is configured — remove the domain first.

Dashboard

bash
curl https://api.whiz.pub/v1/dashboard \
  -H "Authorization: Bearer YOUR_API_KEY"

Returns per-tenant dashboard statistics:

json
{
  "total_posts": 43,
  "published_posts": 38,
  "draft_posts": 5,
  "total_pages": 3,
  "total_tags": 12
}

Update blog display settings

bash
curl -X PUT https://api.whiz.pub/v1/settings/blog \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"posts_per_page": 15}'
FieldTypeDescription
posts_per_pageintNumber of posts per page on the blog index (5-100, default 10)

Controls pagination on the public blog index and tag pages.

Webhooks

whiz supports outbound webhooks that notify external URLs when content changes. Each webhook has a signing secret for HMAC-SHA256 signature verification.

Supported events: post.created, post.updated, post.deleted, page.created, page.updated, page.deleted

Limits: 3 webhooks per tenant (10 for donors). Last 5 deliveries are stored per webhook.

Create a webhook

bash
curl -X POST https://api.whiz.pub/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhook",
    "events": ["post.created", "post.updated", "post.deleted"],
    "description": "My custom UI"
  }'

The response includes the signing secret (shown only once). Save it to verify webhook signatures.

List webhooks

bash
curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://api.whiz.pub/v1/webhooks

Delete a webhook

bash
curl -X DELETE https://api.whiz.pub/v1/webhooks/WEBHOOK_ID \
  -H "Authorization: Bearer YOUR_API_KEY"

View delivery history

bash
curl -H "Authorization: Bearer YOUR_API_KEY" \
  https://api.whiz.pub/v1/webhooks/WEBHOOK_ID/deliveries

Send a test event

bash
curl -X POST https://api.whiz.pub/v1/webhooks/WEBHOOK_ID/test \
  -H "Authorization: Bearer YOUR_API_KEY"

Webhook payload format

Each delivery sends a POST request with the following JSON body:

json
{
  "event": "post.created",
  "timestamp": "2026-05-15T12:00:00Z",
  "data": {
    "slug": "my-post",
    "title": "My Post",
    "status": "published",
    "post_type": "post",
    "tags": ["go", "web"]
  }
}

Verifying signatures

Each request includes these headers:

HeaderDescription
X-Whiz-EventEvent type (e.g. post.created)
X-Whiz-SignatureHMAC-SHA256 signature: sha256=<hex>
X-Whiz-TimestampUnix timestamp used in signature
X-Whiz-DeliveryUnique delivery ID

To verify: compute HMAC-SHA256(secret, timestamp + "." + raw_body) and compare with the signature header.

Instant, agent-first blogging.