Contentful Delivery API / Management API / Preview API Setup
Contentful provides three separate HTTP APIs with different access tokens and base URLs. Confusion between them is a typical cause of draft content showing in production or changes not persisting.
Three APIs and their purpose
Content Delivery API (CDA) — read-only published content. Base URL: https://cdn.contentful.com. Token: delivery access token (read-only, can be committed to environment variables).
Content Preview API (CPA) — read-only, but includes drafts (unpublished entries). Base URL: https://preview.contentful.com. Token: preview access token. Used in Next.js Draft Mode / preview mode.
Content Management API (CMA) — full CRUD. Base URL: https://api.contentful.com. Token: personal access token or OAuth. Never used on frontend.
Client setup
import { createClient } from 'contentful';
// For production
const deliveryClient = createClient({
space: process.env.CONTENTFUL_SPACE_ID!,
accessToken: process.env.CONTENTFUL_DELIVERY_TOKEN!,
});
// For preview (Next.js Draft Mode)
const previewClient = createClient({
space: process.env.CONTENTFUL_SPACE_ID!,
accessToken: process.env.CONTENTFUL_PREVIEW_TOKEN!,
host: 'preview.contentful.com',
});
// Choose client by flag
export const getClient = (preview = false) =>
preview ? previewClient : deliveryClient;
Next.js Draft Mode + Preview API
// app/api/draft/route.ts
import { draftMode } from 'next/headers';
import { redirect } from 'next/navigation';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const secret = searchParams.get('secret');
const slug = searchParams.get('slug');
if (secret !== process.env.CONTENTFUL_PREVIEW_SECRET) {
return new Response('Invalid token', { status: 401 });
}
draftMode().enable();
redirect(`/blog/${slug}`);
}
// In page component
import { draftMode } from 'next/headers';
export default async function BlogPost({ params }) {
const { isEnabled } = draftMode();
const client = getClient(isEnabled);
const entry = await client.getEntries({
content_type: 'blogPost',
'fields.slug': params.slug,
});
}
CMA: content management programmatically
import { createClient } from 'contentful-management';
const cmaClient = createClient({
accessToken: process.env.CONTENTFUL_MANAGEMENT_TOKEN!,
});
const space = await cmaClient.getSpace(process.env.CONTENTFUL_SPACE_ID!);
const env = await space.getEnvironment('master');
// Creating an entry
const entry = await env.createEntry('blogPost', {
fields: {
title: { 'en-US': 'New Post' },
slug: { 'en-US': 'new-post' },
},
});
// Publishing
await entry.publish();
Environment variables for all three APIs are configured in .env.local and in CI/CD pipeline separately for preview and production environments.







