Fundamentals

Published-content delivery

Understand release pointers, immutable manifests, media URLs, caching, and missing-content behavior.

PeopleTide Content · HTTP integration guides

Discover once. Follow the pinned URLs.

GET /delivery/v1/{organisation}/{site}/current.json returns releaseId, hash, and url. Fetch that manifest URL, locate an entry by type and slug, then fetch entry.url. Returned URLs are relative to the Content origin.

JSON
{
  "releaseId": "RELEASE_UUID",
  "hash": "MANIFEST_SHA256",
  "url": "/delivery/v1/ORG_UUID/SITE_UUID/manifests/MANIFEST_SHA256.json"
}

Select a document from the manifest.

A manifest contains entries with id, revision, type, slug, object, and url. Use entries from this manifest throughout a render. Do not depend on array ordering or construct a document URL from a slug.

Cache the right layer.

The current pointer uses public, max-age=0, s-maxage=30. Immutable manifests, objects, and media use public, max-age=31536000, immutable. An integration may memoise discovery for up to 30 seconds. Framework caches and static builds can add their own delay; configure them explicitly.

Follow published media references.

Published media fields include a delivery URL after release resolution. Resolve it against the Content origin, retain alt text in your model, and validate its origin and delivery prefix. Use that URL instead of an authoring upload URL.

Separate missing content from an outage.

A site without an active release returns 404. A missing slug in a valid manifest should render your site’s 404. Network failures, invalid JSON, and delivery failures should show an unavailable state or fail a static build. Unpublishing updates discovery within its cache window; previously public immutable URLs remain available under the retention policy.

Use a credential-free loader.

This is the shared helper used by the JavaScript framework guides.

JavaScript
// delivery.js — no SDK or API key required. Node 22+ or a modern browser.
export async function getArticle({ origin, organization, site, slug }) {
  const base = new URL(origin);
  if (base.protocol !== 'https:' && !['localhost', '127.0.0.1'].includes(base.hostname)) {
    throw new Error('Use an HTTPS Content origin');
  }
  if (![organization, site].every(id => /^[a-f0-9-]{36}$/.test(id))) {
    throw new Error('Set your organization and site UUIDs');
  }
  const prefix = '/delivery/v1/' + organization + '/' + site + '/';
  async function read(path) {
    const url = new URL(path, base);
    if (url.origin !== base.origin || !url.pathname.startsWith(prefix) || url.search || url.hash) {
      throw new Error('Unexpected delivery URL');
    }
    const response = await fetch(url, {
      credentials: 'omit', cache: 'no-store', redirect: 'error',
      signal: AbortSignal.timeout(8000),
    });
    if (!response.ok) throw new Error('Content delivery returned ' + response.status);
    return response.json();
  }
  const current = await read(prefix + 'current.json');
  const manifest = await read(current.url);
  const entry = manifest.entries.find(item => item.type === 'article' && item.slug === slug);
  if (!entry) return null;
  const document = await read(entry.url);
  return { releaseId: current.releaseId, document };
}