Start a preview
Return a single-use agent-created review URL without exposing a Delivery key or Preview Bearer.
Your frontend owns routing and rendering. A delegated agent creates a Candidate-bound Preview handoff, claims its URL once, and returns that URL to the reviewer. The reviewer needs no Jetrepo login: possession of the short-lived link is the review capability.
Agent workflow
Use the existing four Management tools; Preview adds no fifth tool.
- Inspect
DELIVERYand select an enabled Verified Preview Target. The descriptor includescandidate_preview.create@1, the ten-minute handoff lifetime, and thecontrol_operation:CLAIM_SECRETstep. - Propose a Change Set containing
candidate_preview.create@1, bound to the stable Backend, Environment, exact Candidate ID and digest, target, and optional paired entity/type route context. Supply both route IDs or neither; Jetrepo rejects incomplete context before execution. - Execute that handoff Candidate and wait for the Operation to reach
SUCCEEDED. - Call
control_operationwithaction: "CLAIM_SECRET"for that Operation. - Return the claimed URL to the reviewer. The claim is bound to the same principal and credential, disappears after the first claim, and is never retained in Candidate, Operation, item outcome, or audit data.
The Operation result contains only the Preview session ID, expiry, immutable Candidate provenance, and secretClaimAvailable: true.
{
"stableBackendId": "BACKEND_ID",
"environmentId": "ENVIRONMENT_ID",
"candidateId": "CANDIDATE_ID",
"candidateDigest": "64_CHARACTER_SHA256",
"entityId": "OPTIONAL_PAIRED_ENTITY_ID",
"typeId": "OPTIONAL_PAIRED_TYPE_ID",
"targetId": "VERIFIED_TARGET_ID"
}
The claimed value is a link on the Jetrepo Management origin:
https://api.example.com/preview/handoffs/jp_boot_REDACTED
Opening the link does not consume it. Jetrepo presents an explicit confirmation so chat and email link scanners cannot spend the capability. Confirmation server-posts the one-time bootstrap code to the verified frontend route. The frontend exchanges it, installs an opaque browser session, and removes the Jetrepo handoff URL from the active page.
Publish the frontend protocol
Serve GET /.well-known/jetrepo-preview from the deployed frontend origin. @jetrepo/preview/protocol provides the strict descriptor and response helpers.
{
"protocolVersion": 1,
"origin": "https://preview.example.com",
"bootstrap": {
"paths": [
"/preview/article/{entityId}",
"/preview/landing_page/{entityId}"
],
"method": "POST",
"contentType": "application/x-www-form-urlencoded",
"formField": "jetrepo_preview_code"
},
"readiness": {
"sessionFormField": "jetrepo_preview_session",
"messageType": "jetrepo-preview-status"
},
"capabilities": { "readOnly": true }
}
A delegated agent with previewTarget.update can register the target through preview_target.create@1. New and imported targets remain Unverified. After deployment, run preview_target.verify@1; Jetrepo checks public DNS, bounded redirects, response size and type, canonical origin, protocol version, and bootstrap-path agreement before marking the target Verified.
Changing the origin or URL template resets verification and revokes existing sessions. Failed verification stores a safe diagnostic code without retaining fetched content or network addresses.
Exchange the code
The frontend server receives jetrepo_preview_code as a form field and exchanges it immediately. 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": "PHYSICAL_BACKEND_ID",
"environmentId": "ENVIRONMENT_ID",
"candidateId": "CANDIDATE_ID",
"candidateDigest": "64_CHARACTER_SHA256",
"manifestHash": "64_CHARACTER_SHA256",
"targetId": "TARGET_ID",
"targetOrigin": "https://preview.example.com",
"perspective": "CANDIDATE",
"expiresAt": "2026-08-21T12:30:00.000Z"
}
}
The handoff/bootstrap code is stored hashed, expires after ten minutes, and exchanges once. The returned Bearer expires after 30 minutes.
Fetch Candidate content
Keep the Bearer on your server and use it for Delivery REST:
curl https://delivery.example.com/rest/article/ENTRY_ID \
-H 'authorization: Bearer jp_prev_REPLACE_ME'
Exchange and Preview responses are private and non-cacheable. Do not place them in a shared CDN cache. Clear the application session when Jetrepo reports expiry, revocation, target failure, Candidate drift, or an invalid Environment generation.
Share sessions across replicas
Use an opaque, HttpOnly, Secure, partitioned browser cookie. Store the associated Preview session in shared infrastructure so any frontend replica can serve the next request. @jetrepo/preview/redis provides a strict Redis-compatible store adapter with absolute expiry and malformed-state cleanup.
The process-local memory store is development-only and refuses production use. Do not fall back to it during a shared-store outage; fail closed instead.
Access and trust
Creating the handoff requires operation.execute and previewSession.create; claiming its URL requires operation.read and the exact principal plus credential that created the Operation. Jetrepo also rechecks operation.read, previewTarget.read, previewSession.create, and scoped content.read under the delegated authority before creating the session.
The receiving frontend needs no Management permission and the reviewer needs no Jetrepo account. The capability grants only read access to the exact immutable Candidate on the verified target. It cannot approve, execute, mutate, or publish.
Exchange and read errors
| Condition | Status |
|---|---|
| Malformed handoff URL | 404 |
| Expired, revoked, exchanged, or unavailable URL | 410 |
| Malformed exchange request | 400 |
| Invalid, replayed, expired, or revoked code | 401 |
| Disabled, failed, or unverified target | 403 |
| Invalid exchange context | 403 |
| Invalidated Candidate on the first content read | 403 |
| Candidate base drift on the first content read | 409 |
Every subsequent REST read rechecks the exact Candidate digest, manifest materialization, base manifest, target, and authorization before returning content.