Start a preview
Exchange a one-time preview code on the server without exposing long-lived keys.
Your frontend owns routing and rendering. Its server exchanges Jetrepo’s one-time code and keeps the resulting read-only credential away from public JavaScript and shared caches.
Exchange the code
Send the bootstrap code to the Delivery API within 60 seconds. Do not add an authentication header.
curl https://delivery.example.com/preview/sessions/exchange \
-H 'content-type: application/json' \
--data '{"bootstrapCode":"jp_boot_REPLACE_ME"}'
{
"accessToken": "jp_prev_REPLACE_ME",
"session": {
"id": "SESSION_ID",
"backendId": "BACKEND_ID",
"targetId": "TARGET_ID",
"targetOrigin": "https://preview.example.com",
"perspective": "DRAFT",
"entityId": "ENTRY_ID",
"typeId": "TYPE_ID",
"expiresAt": "2026-07-15T12:30:00.000Z"
}
}
The bootstrap code is stored hashed, expires after 60 seconds, and works once. The returned Bearer expires after 30 minutes.
Fetch Preview content
Keep the Bearer on your server and use it for Delivery GraphQL or REST:
curl https://delivery.example.com/graphql \
-H 'content-type: application/json' \
-H 'authorization: Bearer jp_prev_REPLACE_ME' \
--data '{"query":"{ article(id: \"ENTRY_ID\") { _id title } }"}'
Exchange and Preview responses are private and non-cacheable. Clear your application session when Jetrepo reports expiry, revocation, a disabled target, or invalid context.
Access
The receiving frontend needs no CMS permission. The human creating the session needs previewTarget.read, previewSession.create, and content.read over every applicable Data Type; Release preview also needs release.read. Jetrepo rechecks these permissions during exchange and use.
Exchange errors
| Condition | Status |
|---|---|
| Malformed request | 400 |
| Invalid, replayed, expired, or revoked code | 401 |
| Disabled target or invalid context | 403 |
The endpoint does not install browser CORS middleware. Your application owns secure session storage and route mapping.