Findings from the kickoff "verify before scaffolding" research. Trust these over training data; re-verify only if reality contradicts them.
- Payload 3.85.2 — all
payload+@payloadcms/*packages pinned exactly (no^/~), versions in lockstep. - Supported Next.js:
>=15.2.9 <15.3 || >=15.3.9 <15.4 || >=15.4.11 <15.5 || >=16.2.6 <17. Use Next 16.2.x (16.2.10 current LTS). 15.5–16.1.x hard-blocked. - pnpm is Payload's recommended package manager.
sharpmust be added manually for upload image features. React-19 peer-dep warnings from admin deps are benign. After version changes: remove lockfile + node_modules, reinstall,pnpm dedupe.
type: private: original undeliverable unsigned, but derived versions are public by default → Strict Transformations must be ON.- With strict on, a named transformation flagged
allowed_for_strict: truedelivers on the fly, unsigned:image/private/t_preview/<public_id>. This is the Preview path. f_autocannot live inside a named transformation. Chain it:t_preview/f_auto, and allowlist that combined variant (or eager-generate).q_autois fine inside the named transformation.- Fallback (
type: authenticated) not needed for images; note it disallows all on-the-fly derivations (eager-only + always signed). Superseded for video: video Originals ship astype: authenticatedwith eager-generated derived assets and signed URLs throughout — see §5 ofsrc/lib/cloudinary/README.md. - Original download:
cloudinary.v2.utils.private_download_url(publicId, format, { expires_at, attachment: true })— purpose-built, time-limited, API-endpoint URL (not CDN-cached). This isbuildDownloadUrl's mechanism. Default type isprivate;expires_atis UNIX seconds (default 1h). - Upload AI: tags via
categorization: 'google_tagging'(orimagga_tagging/aws_rek_tagging) +auto_tagging: <0–1 threshold>→ land in responsetagsarray. Caption viadetection: 'captioning'→ returned as acaptionvalue in upload response (exact nested JSON path unconfirmed — inspect a live response defensively).
CldImagerequires a Client Component boundary in this version (usesuseStateinternally) — the "may render in a Server Component" guidance did not hold;PreviewImagecarries'use client'.strictTransformationsrestricts the URL to named transformations only and drops the defaultf_auto/q_autoand responsive width params. PlainnamedTransformations: ['preview']delivered a 636KB PNG (no format negotiation).- Without
strictTransformations, CldImage injectsc_limit,w_*andf_auto/q_autoas separate chained components → not on the strict allowlist → 401. - The fix:
namedTransformations: ['preview/f_auto'](slash inside the value) emits exactly the allowlisted chaint_preview/f_auto→ 200, WebP, 113KB. This is a string-concatenation artifact, not a documented feature — re-verify on next-cloudinary upgrades. - CldImage always appends
/v1/and an?_a=analytics param; both are harmless under strict mode, but the library logs a dev-console warning ("props [version] not supported with Strict Transformations") per image. Cosmetic. - Requires
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME(delivery URLs only — API secret stays server-side).
sharpremoved frompayload.config.ts+ deps: upload collections still accept files; Payload just skips local image processing (resizing, crop UI, dimension extraction).- Photo
width/heightare writable via the Local APIupdateand are now populated from Cloudinary's upload response insyncCloudinaryOriginal.
stripenpm v22.3.0, pins API version2026-06-24.dahlia. Do not passapiVersion— rely on SDK pin.- Classic hosted Checkout unchanged:
mode: 'payment',line_items[].price_data{currency, unit_amount, product_data{name, images(max 8)}},metadata,success_urlwith{CHECKOUT_SESSION_ID},cancel_url. Don't setui_mode(defaults to hosted). - Webhook in App Router:
await req.text()is already raw — no bodyParser config.stripe.webhooks.constructEvent(body, sig, secret). Setexport const runtime = 'nodejs'explicitly. Return 200 fast. checkout.sessions.retrieve:payment_status ∈ {paid, unpaid, no_payment_required}; require=== 'paid'.metadataset at creation is returned.