# Previews > Read-only preview tokens that render a change set consistently on your site or a Myna-hosted fallback. A preview token is a random, read-only credential that lets anyone holding it see the exact content a [change set](/concepts/change-sets) will publish — before it publishes. Tokens are project-bound, revocable, and expire on their own, so a preview link pasted into a pull request or chat thread carries bounded risk. ## Token model - Tokens look like `myna_prv__`. The raw value is returned exactly once at creation; Myna stores only a SHA-256 hash. - A token is scoped to exactly one [change set](/concepts/change-sets) or one entry — never a whole project's drafts. - Default expiry is 24 hours; `expiresInSeconds` (60 to 604800) overrides it, capped server-side at the 7-day maximum. - Tokens grant read access to scoped draft content and to draft [assets](/concepts/assets); they cannot write anything. ```bash $ myna preview create chs_01... # POST /v1/projects/:project/previews (scope: preview:write) # Body: exactly one of {"changeSetId": "..."} or {"entryId": "..."}, # plus optional expiresInSeconds. ``` The response contains the customer URL, the Myna-hosted fallback, and the expiry: ```json { "data": { "id": "prv_01...", "changeSetId": "chs_01...", "entryId": null, "url": "https://example.com/api/myna-preview?token=myna_prv_...&path=%2Fblog%2Flaunch", "fallbackUrl": "https://app.myna.sh/previews/myna_prv_...", "expiresAt": "2026-07-24T09:41:00.000Z", "revokedAt": null } } ``` ## Resolution rules Given a valid token, Myna resolves each entry in the token's scope deterministically: 1. If the entry has a target revision in the scoped change set, return that revision. 2. Otherwise, return the entry's currently published revision. 3. Never reveal unrelated drafts — draft edits outside the token's scope are invisible. A change-set-scoped token resolves every item's staged `targetRevisionId`, so a multi-entry change previews consistently as one unit. An entry-scoped token resolves that entry's current draft revision. The resolution endpoint is: ```bash $ curl "https://api.myna.sh/v1/previews/resolve?token=myna_prv_..." # Or: Authorization: Bearer myna_prv_... # Optional ?entry=ent_01... narrows the response to one entry in scope. ``` Each resolved entry includes its collection key, slug, fields, and `meta.revision` plus `meta.scoped` (whether the change set supplied the revision or it fell back to the published one). ## Overlay: previewing the site, not the diff The resolve endpoint returns the contents of a change set. To render a page, you usually want the opposite view: the collection as it would look once that change set is published. Pass the token to an ordinary content read and Myna composes it server-side: ```bash $ curl "https://api.myna.sh/v1/projects/my-site/collections/posts/entries?preview=myna_prv_..." ``` - Updated entries replace their published version. - Created entries appear. - Unpublished and deleted entries disappear. - Everything the change set does not touch stays exactly as published. `order`, `filter`, `limit`, and `cursor` apply to that composition, so a previewed list sorts and paginates exactly as the published one does. A token only applies to the project it was minted for. Preview responses carry `cache-control: no-store` and `x-robots-tag: noindex, nofollow`, and no ETag, so unpublished content is neither cached nor indexed. The SDK uses this mode by default: ```ts const myna = createMyna({ project: "my-site", previewToken }); await myna.entries.list("posts", { order: "-publishedAt" }); // the composed collection // The change set's own snapshot, for building a diff or review UI: const scoped = createMyna({ project: "my-site", previewToken, previewMode: "scoped" }); ``` `previewMode: "scoped"` returns only the entries the change set touches. It suits a diff or review interface; it is the wrong choice for a site preview, because a list rendered from it shows the change set rather than the site. ## Customer preview URLs Each project can define a customer preview URL template, for example: ```text https://example.com/api/myna-preview?token={token}&path={path} ``` On preview creation, `{token}` is replaced with the raw token and `{path}` with a representative path built from the collection's [path template](/concepts/collections) (such as `/blog/{slug}` filled with the entry's slug). Your preview route then exchanges the token for draft content via the resolve endpoint and renders it with your own components. ## Myna-hosted fallback When no template is configured — or as an always-available second link — `fallbackUrl` points at `https://app.myna.sh/previews/`. The hosted preview renders structured fields, sanitized Markdown, references, and assets. It is deliberately not a replica of your site. Preview responses are excluded from indexes and caches: the resolve endpoint sends `x-robots-tag: noindex, nofollow` and `cache-control: no-store`, and the hosted preview follows the same policy. ## Revocation and expiry ```bash $ myna preview list $ myna preview revoke prv_01... # DELETE /v1/projects/:project/previews/:preview (scope: preview:write) ``` Revocation is immediate. Expired or revoked tokens fail resolution with `NOT_FOUND` — indistinguishable from a token that never existed (see [error codes](/management-api/errors)). Listing previews returns metadata only; raw tokens are never shown again.