Concepts
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 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_<publicId>_<secret>. 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 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; they cannot write anything.
$ 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:
{
"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:
- If the entry has a target revision in the scoped change set, return that revision.
- Otherwise, return the entry's currently published revision.
- 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:
$ 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:
$ 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:
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:
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 (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/<token>. 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
$ 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). Listing previews returns metadata only; raw tokens are never shown again.
