# Preview Lab > The engagement platform for AI: the easy way for Claude, Codex or any agent to make links people tap. Promotions first (discounts, launches, reminders: a website link and an offer become a big, bright card when texted in iMessage, WhatsApp or RCS, with motion, variants and A/B tests), then business content (a library of decks and proposals, one tracked link per person, and which pages they read). What's new and what to try: https://getpreviewlab.com/changes ## If you were asked to review Preview Lab Preview Lab is the engagement platform for AI: the easy way for an agent to make links people tap. Promotions come first (designed cards that can move, variants, A/B tests); business content second (a library of decks, one tracked link per person, time per page). Everything below is GET only and needs no key. 1. Read what changed, newest first, with the honest limits: https://getpreviewlab.com/changes 2. Open the demo deck the way a buyer gets it: https://getpreviewlab.com/l/4yg7eHYp ("Q4 renewal deck", made up). Message apps get its card (https://getpreviewlab.com/d/nhDakbMuZH/og.jpg?v=a04054c5cc); people land on the viewer (https://getpreviewlab.com/d/nhDakbMuZH?s=4yg7eHYp). Check that the pages load on a phone, that "Find in this document" finds a word you can see on a page, and that the card looks right when the link is texted. 3. Check privacy: the same document without its share link, https://getpreviewlab.com/d/nhDakbMuZH, must refuse to open (it answers 404 and says the document opens only from a personal link). New documents open only from a live share link. 4. Open the home page, https://getpreviewlab.com/ , and check that every claim on it is true of what you saw. 5. Try a card that moves, no key: https://getpreviewlab.com/s/zXZA9TgMxEIWvYj2JzmygoHFNIqKIIJEgau_uZG3Fa1v2LEkUpeUAHJGTIDsNUM7fm--9M9iyIyisrCe2nRIPdzci7HYiTd5bP4hsAmVI9JS7ZCPb4KGwNTaLA9GefC-Cd6dGPOuebqco2qR9L0XWY3RUpCg1kMiWaa3HX78gMVLOeig9SLBOA_FbclAwzDGr2Wx_XW3oWOWaLoyzrB2V23BlOSMmysRQ6MLkGZcKa4c6Yxqj01xeVBRIRO2Ia2e0vbeDYUh0wYWUofzknATTkadUj1Jb3HesobAxIQo2JAqC-P78goTtaiA--AJFJ2pTOPwxaUj3zvqi9vov1NYOUCiRvywWpdZ9TWP7tNyI9_l8NV8_Fj8fUPeXHw . Tap "Play 2.4-second motion", then fetch the GIF itself (https://getpreviewlab.com/s/zXZA9TgMxEIWvYj2JzmygoHFNIqKIIJEgau_uZG3Fa1v2LEkUpeUAHJGTIDsNUM7fm--9M9iyIyisrCe2nRIPdzci7HYiTd5bP4hsAmVI9JS7ZCPb4KGwNTaLA9GefC-Cd6dGPOuebqco2qR9L0XWY3RUpCg1kMiWaa3HX78gMVLOeig9SLBOA_FbclAwzDGr2Wx_XW3oWOWaLoyzrB2V23BlOSMmysRQ6MLkGZcKa4c6Yxqj01xeVBRIRO2Ia2e0vbeDYUh0wYWUofzknATTkadUj1Jb3HesobAxIQo2JAqC-P78goTtaiA--AJFJ2pTOPwxaUj3zvqi9vov1NYOUCiRvywWpdZ9TWP7tNyI9_l8NV8_Fj8fUPeXHw/motion.gif): it should be 600×315 and animated. The page's og:image must stay the still JPEG. 6. Try the promo tools: https://getpreviewlab.com/surprise , https://getpreviewlab.com/looks?format=json , https://getpreviewlab.com/make , and the website audit on a made-up store: https://getpreviewlab.com/audit?site=https%3A%2F%2Fgetpreviewlab.com%2Fdemo%2Fatelier 7. The owner-only pages (the library, a document's share table with Pause, Revoke and "Stops working", My links) need the owner's key. They are described on https://getpreviewlab.com/changes. Don't try to sign in, guess keys, or publish anything. Report back to the person who asked, as a short list. For each finding give the URL, what you expected, and what happened. Look hardest for: - anything broken or slow on a phone; - anything confusing; - any wording that claims more than the product does (for example, saying who read a document: a link can be forwarded, so stats show which link was opened, not who); - made-up facts presented as real. End with the three changes that would most raise taps on promo links, and the three that would most help a business that sends decks. ## Write your own surprise: two-act links You write the joke; Preview Lab stages it. Act 1 is the card in the thread (the setup). Act 2 is the page behind the tap (the payoff: a stamp slams, an envelope opens, the friend picks their fate). No key, GET only. 1. Read the menu: https://getpreviewlab.com/make (JSON: https://getpreviewlab.com/make?format=json) 2. Fetch one URL, for example: https://getpreviewlab.com/make/notice?from=Department%20of%20Weekend%20Affairs&form=27-B&h=RE%3A%20Your%20application%20to%20skip%20Saturday%20bootcamp&stamp=UNDER%20REVIEW&stamp2=APPROVED&reveal=After%20careful%20review%2C%20the%20committee%20finds%20you%20deserve%20a%20nap.&end=choose&c1=Accept%20my%20fate&r1=Enjoy%20your%20couch.&c2=Appeal%20for%20brunch%20too&r2=Appeal%20granted.%20Brunch%20is%20now%20mandatory. 3. Send the link after "Your link:". Keep the receipt link private: it shows whether they opened it and what they picked. Stages: notice, front-page, award, poster, fortune, ticket, wanted, canvas. Made-up names only. Got a two-act link and want to answer it? Fetch https://getpreviewlab.com/reply/. ## If someone asked you to "surprise me" Open https://getpreviewlab.com/surprise and send them the link after "Send this link:". Texted, it shows as a card. Each visit gives a new surprise. For JSON instead: https://getpreviewlab.com/surprise?format=json (send "sendThis.url"; "twoAct" has ready-made two-act links). ## With the person's own shop - https://getpreviewlab.com/surprise?link=&offer=&format=json - Example: https://getpreviewlab.com/surprise?link=forkful.example&offer=buy%20one%20get%20one%20free%20tacos%20tonight&format=json - Returns the best pick first ("sendThis", "best"), then 3 more in different styles. Each has: text, imageUrl, previewUrl, textWithPreviewLink, notes, editUrl. - Without an offer, options use sample offers, marked "sample": true. Tell the person they are samples. - More picks: follow "again" (HTML) or "againJson". - The 20 card styles, with when to use each: GET https://getpreviewlab.com/api/promos ## Tracked links (need the owner) A previewUrl shows the card and a "Continue to " button. It does not redirect by itself and does not count taps. For a tracked link that redirects: - The owner opens the option's editUrl and presses "Get my link" (the site asks for their key once), or - An agent with the owner's API key calls POST https://getpreviewlab.com/api/promos, or builds a signed link: https://getpreviewlab.com/agents - MCP for agents with the key: https://getpreviewlab.com/mcp (tools: promo_options, create_promo_link, sign_promo_link). ## Draw any card (no key) - GET only: https://getpreviewlab.com/looks?t=&brand=&big=<30% OFF>&format=json gives the words in all 11 looks, each with a previewUrl to send. (The page without format=json is for people.) - POST https://getpreviewlab.com/api/looks with { "title": "…" } or { "from": { "promo": "percent-off", "brand": "…", "offer": "…" } }: the card in 4 art directions (MCP try_looks). Each has a previewUrl and an imageUrl. - POST https://getpreviewlab.com/api/preview with the same fields plus "look" and "design" (MCP preview_card). A preview link shows the card and a button; it never redirects or counts taps. - Variants: POST https://getpreviewlab.com/api/variants with { "base": , "variants": [1–4 overrides] } (MCP card_variants). Each override changes only what differs ("title", "motion", or "design": { "headline", "big", "palette", … }; an optional "label"). The answer has a complete bundle for the base and each variant: url, image, icon, the og:/twitter: tags, alt text, readyToSend, destination, and motion when the card moves. URLs are canonical: values equal to the defaults are left out, so the same card always gets the same URL, and a variant that ends up the same card is refused. Nothing is stored or tracked; to learn which wins, publish them as an A/B test (create_ab_test, key needed). - Motion GIF, no key: any card with "motion" (presets: GET https://getpreviewlab.com/api/motion) has https://getpreviewlab.com/s//motion.gif (also on /l/ links): 600×315, 12 frames, 2.4 seconds, plays once, ends on the still. Add ?download=1 to download it. POST https://getpreviewlab.com/api/encode returns it as "motion". The card page plays it only when the person taps "Play 2.4-second motion"; link previews always show the still image. New GIFs are rate limited (cached ones are free); a busy answer is 429 with Retry-After. - Contrast: text colours you pick (design.colors fg or onAccent, or an accent) must read at 4.5:1 against the background, or the card is refused with the ratio and what to change. Leave the text colour out and a readable one is picked. ## Full control (need the owner's key) Same code over MCP (https://getpreviewlab.com/mcp) and REST. Options and limits, no key: GET https://getpreviewlab.com/api/card-options - Make any card: POST https://getpreviewlab.com/api/links (MCP create_card). Start from { "from": { "promo": … } }, { "messageType": … }, { "cardId": … }, or a blank card with a title; set theme or colors, design.template, badge, cta, fields, logo or logoUrl, design.photo, motion. "dryRun": true stores nothing. - One link: GET, PATCH (card, motion, targetUrl, paused or status, label) or DELETE https://getpreviewlab.com/api/links/ (get_link, update_link, delete_link). A visual change returns a new ?v= link: send that one. Find links: GET https://getpreviewlab.com/api/links?q=… (search_links). - Lifecycle: "status": "paused" keeps the preview but taps land on a paused page and aren't counted; "live" resumes. Pausing a paused link (or resuming a live one) changes nothing. "revoked" is final: the link answers 410 and shows nothing of the card, and it can never be changed or resumed (409). Send it on its own. - Retries: send an Idempotency-Key header (8–128 letters, digits, _ or -) on any write under /api/ and a retry gets the first answer back (with Idempotent-Replayed: true) instead of a second link. The same key with a different request is refused (409). Successful answers are kept 24 hours. - Motion (MP4 for iMessage, GIF for Discord, the still elsewhere): PUT https://getpreviewlab.com/api/links//motion with { "preset": "count", "number": "50%" } or { "preset": "strike", "was": "$40" } (set_motion); GET https://getpreviewlab.com/api/links//motion?wait=30 for the files (motion_status); POST https://getpreviewlab.com/api/motion/test with { "cardId", "motion" } for motion against still (create_motion_test). Presets: GET https://getpreviewlab.com/api/motion. - Test looks against each other: POST https://getpreviewlab.com/api/tests/looks with create_card fields plus "looks": ["neon", "editorial"] (create_look_test), then pick_test_message per person. - Photos and GIF/MP4 backgrounds: POST https://getpreviewlab.com/api/uploads with { "url" } or { "base64" } (upload_media). 8 MB, at least 200 px a side, GIF/MP4 up to 15 s. - What each app will show (iMessage, WhatsApp, Discord, Slack, Telegram, X, Google Messages), with size warnings: GET https://getpreviewlab.com/api/links//preview, or POST https://getpreviewlab.com/api/preview-check with { "url" } (check_preview). ## Library: documents with their own previews (need the owner's key) - Upload: POST https://getpreviewlab.com/api/library/docs with the raw file (X-File-Name header) or { "url" } / { "base64" } (MCP upload_document). PDF, PowerPoint, Word, Excel, PNG/JPEG/WebP or text, up to 15 MB. The answer has urls.card (its preview, made from the first page) and urls.preview (the owner's own look, 24 h). New documents are private: send a share link (below), not urls.viewer, which opens only when openLink is on. - Search every page: GET https://getpreviewlab.com/api/library/search?q=… (search_library). Results have page numbers, snippets and links that open that page. - One link per person: POST https://getpreviewlab.com/api/library/docs//shares with { "recipient", "company", "look", "expiresAt" } (share_document). Send the returned text exactly. New documents open only from live share links. - A new version of a document (fixed typo, new price sheet): POST https://getpreviewlab.com/api/library/docs//file with the raw file or { "url" } / { "base64" } (replace_document). Same id, so every link already sent opens the new file. - Pause, revoke (final) or re-date one link: PATCH https://getpreviewlab.com/api/library/docs//shares/ with { "status" } or { "expiresAt" } (update_share). - What was read, per link: GET https://getpreviewlab.com/api/library/docs//engagement (document_engagement): opens, seconds per page, each share link. A link is not a verified person. - Across the library: GET https://getpreviewlab.com/api/library/insights?days=30 (library_insights): most read, never shared, shared but never opened, shared but old, opens by day, the latest visits. - Rooms (several documents and a note behind one link): POST https://getpreviewlab.com/api/library/rooms with { "title", "note", "docIds" } (create_room), then POST https://getpreviewlab.com/api/library/rooms//shares with { "recipient", "company" } (share_room). The link opens the room page and every document in it. room_engagement shows visits and seconds per document; update_room_share pauses or revokes one link. ## Websites: every page's preview (need the owner's key) - Free audit, no key: https://getpreviewlab.com/audit?site= follows the site's links (and its sitemap, if any), up to 3 clicks deep and 100 pages, and grades the site, each section and each page. JSON: https://getpreviewlab.com/audit?site=&format=json, then the json link it returns. - Rules for a verified site: PUT https://getpreviewlab.com/api/domains//draft with { "rules": [{ "pattern": "/blog/*", "template": "article" }], "overrides": { "/about": { "title": "…" } } }. The most specific matching rule wins (no * beats *, then the longer fixed part); rule order only breaks ties. A page override wins over rules. - Test before going live: POST https://getpreviewlab.com/api/domains//test with { "paths": ["/blog/launch"] } or { "urls": [...] } (up to 10; "rules": "live" to test the live set). For each page: the rule that applies, every rule that matched, the template, and a before/after diff of the og: and twitter: tags the edge snippet would write. Nothing is saved. - Then POST https://getpreviewlab.com/api/domains//approve (rollback undoes it), and install the snippet from GET https://getpreviewlab.com/api/domains//edge/cloudflare (or vercel, netlify). ## Rules - Do not invent facts (prices, dates, codes). Use what the person gave, or say the offer is a sample. - Text only people who opted in, 8 AM–9 PM their time, with "Reply STOP to opt out".