Skip to content
All guides

Build with Stratafold

Structured content, published snapshots, signed events and reviewable repository changes.

Delivery API

Create a Delivery key in Workspace settings → Developer tools. Each key selects its workspace; tenant and workspace headers cannot change that scope. Use a Preview key only on a trusted server.

curl 'https://api.stratafold.com/delivery/v1/content/blog_post?page=1&pageSize=20&order=publishedAt:desc' \
  -H 'Authorization: Bearer YOUR_DELIVERY_KEY'

All paths below begin with https://api.stratafold.com/delivery/v1 and require Bearer authorization.

MethodPathPurpose
GET/content-typesAvailable schemas and fields
GET/content/:typeApiIdPublished entries; page, pageSize, slug, order, locale, fallback, include
GET/content/:typeApiId/:idOrSlugOne entry; preview, locale, fallback, include
GET/pagesPublished page tree
GET/pages/by-path?path=/aboutPage at its website address
GET/menusMenus; locale and fallback
GET/menus/:apiIdOne menu
GET/redirects/resolve?from=/old-addressRedirect destination and status
GET/search?q=flowersPublished-content search
GET/alertsCurrently active site banners
GET/eventsEvent occurrences
GET/events/:id.icsCalendar download
GET/forms/:keyPublished form fields and captcha requirements
POST/forms/:key/uploadsMultipart file upload; response contains assetId and url
POST/forms/:key/submissionsValidated answers, honeypot and captcha token
POST/subscriptionsDouble-opt-in subscription request

Entry lists return items, page, pageSize, total, totalPages. Each entry contains id, contentType, title, slug, path, fields, seo, publishedAt, updatedAt, version. List page size is 1–100; the default is 20. Supported order values: publishedAt:desc, publishedAt:asc, title:asc, title:desc and updatedAt:desc.

Set preview=true with a Preview key to read drafts. Published responses read published fields and search metadata. Set include=1 for one level of references; locale=fr-FR chooses a language, and fallback=false prevents language fallback.

Errors have {error:{code,message,details?}}. Handle 401 for invalid keys, 403 for insufficient scope or disallowed browser origins, 404 for missing content, and 429 with Retry-After. Keep private preview keys out of client bundles; a public Delivery key may be used with the workspace browser-origin allowlist.

Typed TypeScript client

Download the dependency-free client and keep it in your server integration.

import { createStratafoldClient } from './stratafold-client';
type Content = { blog_post: { title: string; body: string } };
const cms = createStratafoldClient<Content>({ key: process.env.STRATAFOLD_DELIVERY_KEY! });
const posts = await cms.list('blog_post', { pageSize: 20 });
for (const post of posts.items) console.log(post.fields.title);

Forms and subscriptions

Read a form definition, render its fields and visibility rules, then submit answers keyed by each field’s apiId. File questions upload first and reference the returned assetId. When requireCaptcha is true, render Cloudflare Turnstile using captchaSiteKey and supply its token. Keep contactMethod empty for the honeypot.

{
  "answers": {
    "name": "Alex",
    "email": "alex@example.com",
    "message": "Please tell me more."
  },
  "contactMethod": "",
  "captchaToken": "TOKEN_IF_REQUIRED"
}

POST subscriptions with email, topics (public topic keys), optional name, frequency, locale and sourceUrl. A successful request sends a confirmation email; it does not imply a confirmed subscriber.

Verify webhook deliveries

Configure an HTTPS endpoint in developer tools. Save the signing secret shown at creation. Signatures use HMAC-SHA256 with the secret’s SHA-256 hexadecimal digest as the key, over the timestamp, a dot, and the exact raw request body. Reject timestamps outside five minutes, compare in constant time, and deduplicate delivery IDs in durable storage.

import { createHash, createHmac, timingSafeEqual } from 'node:crypto';
const timestamp = request.headers.get('x-stratafold-timestamp') ?? '';
const signature = request.headers.get('x-stratafold-signature') ?? '';
const rawBody = await request.text();
if (!/^\d+$/.test(timestamp) || Math.abs(Date.now()/1000 - Number(timestamp)) > 300)
  return new Response('Expired', { status: 400 });
const signingKey = createHash('sha256').update(process.env.STRATAFOLD_WEBHOOK_SECRET!).digest('hex');
const expected = createHmac('sha256', signingKey).update(timestamp + '.' + rawBody).digest();
if (!/^v1=[0-9a-f]{64}$/.test(signature)) return new Response('Invalid', { status: 400 });
const supplied = Buffer.from(signature.slice(3), 'hex');
if (!timingSafeEqual(expected, supplied)) return new Response('Invalid', { status: 400 });
const event = JSON.parse(rawBody);
// Atomically record event.deliveryId, then enqueue your work once.
return new Response(null, { status: 204 });

Headers: x-stratafold-event, x-stratafold-delivery, x-stratafold-timestamp and x-stratafold-signature. The JSON body contains event, data and deliveryId. Return a 2xx response after durably accepting work; failed attempts are retried.

GitHub publishing contract

Commit stratafold.publish.json at the repository root before connecting it. Stratafold manages data in the named directory, creates branches under stratafold/, and opens reviewable changes; it does not directly rewrite the production branch.

{
  "version": 1,
  "productionBranch": "main",
  "managedDirectory": "content/posts",
  "format": "json",
  "contentType": "blog_post",
  "liveUrlTemplate": "https://example.com/blog/{slug}",
  "liveMarkerAttribute": "data-stratafold-entry",
  "liveRevisionAttribute": "data-stratafold-revision",
  "managedImageDirectory": "public/stratafold-images"
}

Replace example.com with your production host and blog_post with the content type’s API ID. Content and image directories must be separate, non-overlapping safe paths. Supported formats are json and markdown; branch and directory values are validated when connecting.

Render each published document’s ownership markers on its live page:

<article
  data-stratafold-entry={document.stratafoldEntryId}
  data-stratafold-revision={document.stratafoldRevision}
>
  <h1>{document.title}</h1>
  {/* Render the document's typed blocks through your components. */}
</article>

Stratafold checks the live URL and both markers after merge before reporting the change as live. Keep the generated markers intact; review and deploy repository changes through your normal workflow.