MemoriesWeave API
Provides programmatic access to MemoriesWeave — a platform for creating photo memory collections with custom HTML layouts. The agent designs HTML layouts and pushes them to memories. The API provides photos, conversations, workspace context, and people data.
How to call the API
ALWAYS use curl via the Bash tool. Do not use WebFetch, fetch(), or any other HTTP client — they fail due to redirects and auth header stripping.
API="https://grandiose-loris-729.eu-west-1.convex.site/api/v1"
KEY="<user-provided-api-key>"
curl -s "$API/ENDPOINT" -H "Authorization: Bearer $KEY"
The user provides their API key (format: mw_sk_<32hex>) when requesting this skill.
Workflow: Creating or editing a memory
Follow these steps in order. Do not skip steps.
Step 1: Lock the memory FIRST
The VERY FIRST API call must be locking the memory. Do this before gathering context, before reading HTML, before anything else. This shows a loading animation on the user's screen immediately.
curl -s -X POST "$API/memories/{memoryId}/lock" -H "Authorization: Bearer $KEY"
If creating a new memory (no memory ID yet), lock it immediately after creation.
Step 2: Gather context
# Get workspace list
curl -s "$API/workspaces" -H "Authorization: Bearer $KEY"
# Get workspace context (who the people are, relationship info, instructions)
curl -s "$API/workspaces/{wsId}/context" -H "Authorization: Bearer $KEY"
# Get registered people (names, roles, descriptions)
curl -s "$API/workspaces/{wsId}/persons" -H "Authorization: Bearer $KEY"
Step 3: Save a "before" snapshot
curl -s -X POST "$API/memories/{memoryId}/snapshots" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"label": "Before edit — description of planned change"}'
Step 4: Select photos
Use tag filtering as a starting point, then verify with conversation context.
IMPORTANT: dateFrom and dateTo must be Unix timestamps in milliseconds, NOT date strings.
Example: July 1, 2025 = 1751328000000, not 2025-07-01. Calculate with: new Date("2025-07-01").getTime()
# Filter by tag (e.g. couple, selfie, portrait, traveling)
curl -s "$API/workspaces/{wsId}/photos?tag=couple&limit=50" -H "Authorization: Bearer $KEY"
# Filter by date range (Unix ms timestamps)
curl -s "$API/workspaces/{wsId}/photos?dateFrom=1751328000000&dateTo=1754006400000&limit=50" -H "Authorization: Bearer $KEY"
# If user provides a photo ID, fetch it directly
curl -s "$API/photos/{photoId}" -H "Authorization: Bearer $KEY"
# Get conversation context around a photo (what was happening when it was shared)
curl -s "$API/workspaces/{wsId}/conversations/by-photo/{photoId}" -H "Authorization: Bearer $KEY"
Tags are AI-generated and may be inaccurate. Use them as a first filter, then verify with conversation context. Do not rely on tags alone.
Pagination: If a search doesn't return enough results, check meta.hasMore in the response. If true, use the cursor value to fetch the next batch:
# First request
curl -s "$API/workspaces/{wsId}/photos?tag=couple&limit=50" -H "Authorization: Bearer $KEY"
# Response includes: "meta": { "cursor": "abc123", "hasMore": true }
# Get next batch using cursor
curl -s "$API/workspaces/{wsId}/photos?tag=couple&limit=50&cursor=abc123" -H "Authorization: Bearer $KEY"
Keep paginating until you find what you need or hasMore is false.
Step 5: Choose the correct format
curl -s "$API/digital-formats" -H "Authorization: Bearer $KEY"
Common formats: Phone Wallpaper (1170x2532 iPhone 13, 1320x2868 iPhone 16 Pro Max), Desktop (3840x2160), Social Post (1080x1080).
Step 5b: VERIFY every photo before using it
Before including ANY photo in your design, you MUST verify it exists and get its actual data by calling:
curl -s "$API/photos/{photoId}" -H "Authorization: Bearer $KEY"
NEVER fabricate, guess, or construct photo URLs. Every photo URL must come from an actual API response (urls.original, urls.medium, or urls.thumbnail). If a photo search returns no results for a month, report that to the user — do not invent URLs.
Verification checklist for every photo:
- You received the photo ID from the API (not made up)
- You called
GET /photos/{photoId}and got a valid response - The
dateTakenfield matches the month you're placing it on - The
tagsor photo content matches what you expect (couple/selfie/andrea etc.) - The
urls.originalURL is what you're putting in the HTML
If the API returns empty results for a time period, try different search strategies:
- Search by
tag=couple,tag=selfie,tag=andreawithout date range - Then filter results by
dateTakenin your code - Try broader date ranges
- If truly no photos exist for a month, tell the user
Step 6: Design HTML and push
Design self-contained HTML with inline styles. For multi-page memories, wrap each page in <div data-mw-page="N">.
curl -s -X PATCH "$API/memories/{memoryId}" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"customHtml": "<div data-mw-page=\"1\" style=\"width:1170px;height:2532px;...\">...</div>"}'
When resizing, include digitalFormat in the same PATCH:
curl -s -X PATCH "$API/memories/{memoryId}" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"customHtml": "...", "digitalFormat": {"category": "phone_wallpaper", "widthPx": 1170, "heightPx": 2532, "outputFormat": "png"}}'
Step 7: Verify ALL images load, then screenshot
Before screenshotting, verify that every image URL in your HTML actually loads. Extract all image URLs from your HTML and spot-check at least 3 by fetching them:
curl -sI "https://pub-....r2.dev/photos/.../photo.jpg" | head -1
# Should return: HTTP/2 200 (not 404 or 403)
If any return 404, you used a fabricated or wrong URL — go back to Step 5b and fix it.
Then take screenshots to verify the design:
curl -s "https://www.memoriesweave.com/api/screenshot?memoryId={memoryId}&page=1" \
-H "Authorization: Bearer $KEY"
Optional query params: widthPx and heightPx to override dimensions (useful for physical products where dimensions come from the product specs, not digitalFormat). Example: &widthPx=3772&heightPx=5250 for wall calendars.
Returns { "data": { "screenshotUrl": "https://..." } }. Download and view the screenshot. Check that:
- All photos are visible (no broken image icons)
- Photos show the correct people/content
- No duplicate-looking photos on the same page
- Text is readable and correctly positioned
- Captions match the actual photo they appear under — you cannot determine which photo is in which position from HTML order alone. You MUST screenshot the page and visually confirm that each caption describes the photo it appears next to. Tags can be misleading (e.g. a "couple selfie on a bridge" might be tagged "coffee date" if they were near a cafe).
When editing captions: ALWAYS screenshot the page BEFORE changing any caption text. Look at the screenshot to understand which photo is in position 1, 2, and 3. Then match your caption to what you actually see in the screenshot.
Step 8: Save an "after" snapshot
curl -s -X POST "$API/memories/{memoryId}/snapshots" \
-H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" \
-d '{"label": "After edit — description of what changed"}'
Step 9: Unlock the memory
This removes the loading animation from the user's screen.
curl -s -X POST "$API/memories/{memoryId}/unlock" -H "Authorization: Bearer $KEY"
Always unlock when done, even if an error occurred.
Creating physical product memories
To create a memory for a physical product (canvas print, poster, calendar, photo book, etc.):
- Get the product list:
GET /products - Create the memory with
mode: "physical"andproductId:curl -s -X POST "$API/workspaces/{wsId}/memories" \ -H "Authorization: Bearer $KEY" \ -H "Content-Type: application/json" \ -d '{"title": "My Canvas Print", "mode": "physical", "productId": "<product_id_from_products_list>"}' - The API automatically creates a pod design session with the correct product dimensions and print specs.
- Push HTML with
PATCH /memories/{id}using the product's pixel dimensions (e.g., 9000x7200 for canvas print, 3772x5250 for wall calendar, 3075x2475 for hardcover photo book). - The memory can then be ordered through the website's print flow.
Product response fields
Each product from GET /products includes:
provider—"printful","blurb", or"gelato"(determines print fulfillment)bleedMm— bleed margin in mm (5 for Printful, 3.175 for Blurb)pageCountMin/pageCountMax— valid page range for variable-page products (null for fixed)fileFormat—"png"for Printful products,"pdf"for Blurb photo booksvariants[]— available size options with per-variant dimensions and pricing
Product-specific rules
Wall calendars MUST always have exactly 14 pages: cover + 12 months (Jan-Dec) + back page. Each month page needs a correct calendar grid with proper day-of-week starts. Dimensions: 3772x5250px. Never use duplicate photos across pages. Every photo on relationship months must show the people (not food, scenery, or screenshots).
Hardcover photo books (Blurb) have 20-240 pages. Dimensions: 3075x2475px (10×8" Standard Landscape with 3.175mm/0.125" bleed at 300 DPI). All pages are interior content pages. Users download the generated PDF and upload to Blurb via "PDF to Book" (blurb.co.uk/pdf-to-book), where they design the cover separately. When generating print files, the system automatically:
- Assembles all pages into a sequential multi-page PDF (interior pages only)
- Pads to even page count if needed (Blurb requirement)
- Also provides individual PNG files + ZIP for proofing
Available sizes: 7×7" (18×18cm), 8×10" (20×25cm), 10×8" (25×20cm, default), 12×12" (30×30cm), 13×11" (33×28cm).
Paper: Premium Lustre 148gsm, ImageWrap hardcover, 300 DPI. Pricing in GBP (£28.00 base for 10×8", £0.34/extra page). Never use duplicate photos across pages. Each page should feature unique content and photos from different events.
Phone wallpapers support up to 10 pages. Each page is a standalone wallpaper at the device's resolution.
Bulk export endpoints
For large projects (e.g., 240-page photo books), use the export endpoints to get ALL data in one call.
CRITICAL — BANDWIDTH WARNING: Export endpoints read the ENTIRE workspace catalog from the database. Each call costs significant database bandwidth (~5-7 MB for photos, ~1 MB for conversations). You MUST:
- Call each export endpoint AT MOST ONCE per session — never re-fetch what you already have
- Cache the results in memory for the duration of your work — reference the cached data for all subsequent operations
- Use
dateFrom/dateTofilters when you only need a subset (e.g., a single month) to reduce bandwidth - Never call exports in a loop or retry — if the call succeeds, you have everything
Photo export — GET /workspaces/:wsId/photos/export?dateFrom=TS&dateTo=TS
- Returns ALL photos (no pagination cap, no 100-item limit)
- Each photo includes a
persons[]array:[{id, name, confidence}]— who is in the photo - Use
personsto prioritize photos (e.g., couple together > person alone > other) - Response:
{ data: [...], meta: { total: N } } - Call once, cache the result, reference from cache for all page designs
Conversation export — GET /workspaces/:wsId/conversations/export?dateFrom=TS&dateTo=TS
- Returns ALL conversation chunks (no pagination cap)
- Each chunk includes
text(full inline message content) ANDtextUrl(fallback URL) - Response:
{ data: [...], meta: { total: N } } - Call once, cache the result, reference from cache for all page designs
Per-page HTML push — PATCH /memories/:id/pages/:pageNum with {"html": "..."}
- Each page is stored as its own ~5KB document in the database (no 1MB field limit)
- Push one page at a time instead of the entire HTML in one PATCH
- Automatically wraps in
<div data-mw-page="N">if not present - Creates or replaces the page (upsert by memoryId + pageNumber)
- Returns
{ pageNumber, totalPages }so you can track progress - Pages can be pushed in any order (not necessarily sequential)
GET /memories/:id/htmlautomatically concatenates all pages in order- Screenshots and print file generation load individual pages efficiently
Batch page push — PATCH /memories/:id/pages/batch with {"pages": [{"pageNumber": N, "html": "..."}, ...]}
- Push up to 50 pages at once (reduces 233 API calls → ~5 calls)
- Same upsert behavior as single-page push
- Returns
{ pagesUpserted, totalPages }
Delete all pages — DELETE /memories/:id/pages
- Removes all HTML pages for a memory (clean slate for redesigns)
- Returns
{ deletedCount }
Set page count — PATCH /memories/:id with {"pageCount": N}
- Sets the expected page count on the memory document
- UI and print pipeline use this to know how many pages to expect without scanning
Photo selection rules
- Always check conversation context around candidate photos before selecting them.
- If user provides a photo ID, use it directly via
GET /photos/{photoId}. - Prefer tags:
couple,selfie,portrait,andrea,suliman,romantic,joyful,traveling. - Avoid tags:
food,screenshot,meme,document,sticker,illustration(unless requested). - Prefer portrait orientation (height > width) for phone wallpapers.
- Use
urls.originalfor high-resolution output,urls.mediumfor web display. - No duplicate or visually similar photos on the same page or across pages. Check the
fileNamefield — it contains a timestamp like00013190-PHOTO-2025-09-27-19-46-12.jpg. If two photos have timestamps within 5 minutes of each other, they are from the same event/session and look nearly identical (e.g. two photobooth strips from the same booth, two selfies at the same spot). When placing multiple photos on the same page, ensure each photo is from a different date or different event (timestamps at least 1 hour apart). - Cover and back pages must use unique photos not used on any month page.
- Each month's photos must be from that actual month. Check the
dateTakentimestamp. Do not use April photos on the August page. - Use the earliest occurrence of a photo. Photos in WhatsApp are sometimes resent months later. The
dateTakenfield reflects when the photo was originally taken, NOT when it was shared in the chat. When filtering by date range, a photo taken in July may appear in a December conversation chunk because it was resent. Always usedateTakento determine which month a photo belongs to — ifdateTakensays July, it goes on the July page, never December, even if it was found in a December conversation search.
Photo URLs
Each photo has three variants:
urls.thumbnail— 300px wide WebPurls.medium— 800px wide WebPurls.original— Full resolution
Slideshow / Digital Frame Video Export
Render multi-page digital memories as MP4 videos with Ken Burns effects and transitions. Only available for digital memories (not physical products). The rendered video includes AI quality review via Gemini.
Workflow
- Design a multi-page memory first (at least 2 pages with
data-mw-page) - Configure the slideshow:
curl -s -X POST "$API/memories/{memoryId}/slideshow/render" \ -H "Authorization: Bearer $KEY" \ -H "Content-Type: application/json" \ -d '{ "resolution": "1920x1080", "reviewModel": "google/gemini-3-flash-preview", "slides": [ {"page": 1, "durationSeconds": 6, "kenBurns": {"direction": "zoom_in_center", "intensity": 0.3}, "transition": {"type": "crossfade", "durationSeconds": 1.5}}, {"page": 2, "durationSeconds": 5, "kenBurns": {"direction": "pan_left", "intensity": 0.5}, "transition": {"type": "fade_black", "durationSeconds": 2.0}}, {"page": 3, "durationSeconds": 7, "kenBurns": {"direction": "zoom_out_center", "intensity": 0.3}} ] }' - Poll for completion:
curl -s "$API/memories/{memoryId}/slideshow" -H "Authorization: Bearer $KEY" - When status is "completed", download the video from the
videoUrlfield - Read the
reviewFeedbackfor quality assessment - If issues are identified, adjust the config and re-render
Ken Burns Directions
| Direction | Description | Best for |
|---|---|---|
| zoom_in_center | Slow zoom into center | Portraits, close-ups |
| zoom_out_center | Start zoomed, pull out | Group shots, landscapes |
| pan_left | Pan from right to left | Wide scenes, landscapes |
| pan_right | Pan from left to right | Wide scenes |
| pan_up | Pan from bottom to top | Tall subjects |
| pan_down | Pan from top to bottom | Tall subjects |
| none | No motion (static) | Text-heavy slides |
Transition Types
| Type | FFmpeg Effect | Feel |
|---|---|---|
| crossfade | Dissolve | Classic, elegant |
| fade_black | Fade to black | Chapter break |
| fade_white | Fade to white | Dreamy |
| slide_left | Slide left | Timeline progression |
| slide_right | Slide right | Reverse timeline |
| circle_open | Circle reveal | Nostalgic, vintage |
| dissolve | Pixel dissolve | Soft, organic |
| wipe_right | Wipe right | Clean, modern |
Resolutions
| Resolution | Label | Best for |
|---|---|---|
| 1920x1080 | Full HD | Digital frames, Chromecast, most TVs |
| 3840x2160 | 4K UHD | Samsung Frame TV, 4K displays |
Pricing
- Rendering: Free (compute only, no AI cost)
- AI Review: Gemini Flash ~$0.04/review, Gemini Pro ~$0.10/review (credits deducted with 2x markup)
Endpoint reference
Account
| Method | Endpoint | Description |
|---|---|---|
| GET | /me | User info and plan |
| GET | /me/credits | Credit balance |
| GET | /me/usage | Usage summary |
Workspaces
| Method | Endpoint | Description |
|---|---|---|
| GET | /workspaces | List workspaces |
| GET | /workspaces/:id | Workspace details |
| GET | /workspaces/:id/context | AI context (description, relationship, instructions) |
| GET | /workspaces/:id/persons | Registered people |
Photos
| Method | Endpoint | Description |
|---|---|---|
| GET | /workspaces/:wsId/photos | List photos. Params: cursor, limit, tag, dateFrom, dateTo |
| GET | /workspaces/:wsId/photos/export | Bulk export ALL photo metadata (no pagination cap). Includes persons[] array. Params: dateFrom, dateTo |
| GET | /photos/:id | Single photo details and URLs |
| PATCH | /photos/:id | Update caption or tags |
Memories
| Method | Endpoint | Description |
|---|---|---|
| GET | /workspaces/:wsId/memories | List memories |
| GET | /memories/:id | Memory details |
| POST | /workspaces/:wsId/memories | Create memory. Body: title, description, optional mode ("digital"|"physical"), optional productId (required if physical), optional digitalFormat |
| PATCH | /memories/:id | Update title, description, customHtml, or digitalFormat |
| DELETE | /memories/:id | Delete memory |
| GET | /memories/:id/html | Get rendered HTML |
| PATCH | /memories/:id/pages/:pageNum | Push HTML for a single page. Body: {"html": "<div>..."}. Returns {pageNumber, totalPages} |
| PATCH | /memories/:id/pages/batch | Batch push up to 50 pages. Body: {"pages": [{pageNumber, html}, ...]}. Returns {pagesUpserted, totalPages} |
| DELETE | /memories/:id/pages | Delete all HTML pages for a memory. Returns {deletedCount} |
| POST | /memories/:id/lock | Show AI working overlay on frontend |
| POST | /memories/:id/unlock | Remove AI working overlay |
| GET | /memories/:id/snapshots | List version history |
| POST | /memories/:id/snapshots | Create snapshot. Body: {"label": "..."} |
| POST | /memories/:id/snapshots/:snapId/restore | Restore a previous version |
Conversations
| Method | Endpoint | Description |
|---|---|---|
| GET | /workspaces/:wsId/conversations | List chat chunks |
| GET | /workspaces/:wsId/conversations/export | Bulk export ALL conversation chunks (no pagination cap). Includes textUrl for each chunk. Params: dateFrom, dateTo |
| GET | /workspaces/:wsId/conversations/search?start=TS&end=TS | Search by date range |
| GET | /workspaces/:wsId/conversations/by-photo/:photoId | Messages around a photo |
Formats and products
| Method | Endpoint | Description |
|---|---|---|
| GET | /digital-formats | All format presets with dimensions |
| GET | /products | Print product catalog |
Slideshow (digital memories only)
| Method | Endpoint | Description |
|---|---|---|
| POST | /memories/:id/slideshow/render | Trigger slideshow video render with Ken Burns + transitions |
| GET | /memories/:id/slideshow | Get slideshow render status + video download URL |
| GET | /slideshow/presets | List available Ken Burns, transition, and resolution presets |
Screenshot (uses website domain, not API domain)
| Method | URL | Description |
|---|---|---|
| GET | https://www.memoriesweave.com/api/screenshot?memoryId=X&page=N | Render page to JPEG |
Pricing
- Free: All GET endpoints, CRUD operations, pushing HTML, snapshots, lock/unlock.
- Credits: AI operations only (design chat, content generation, photo captioning via Trigger.dev pipeline). The agent designing HTML does not consume credits.
Rate limits
| Plan | CRUD ops/min | AI ops/min |
|---|---|---|
| Free | 60 | 2 |
| Starter | 120 | 5 |
| Plus | 300 | 10 |
| Pro | 600 | 20 |
Conversation Summaries
Generate personalized summaries from WhatsApp conversations at any granularity — by day, by month, by year, or for a custom date range. The agent reads conversations, understands what happened, and writes warm, factual summaries suitable for scrapbooks, photo books, and memory collections.
Summary types
The user can request any of these:
| Type | User says | What it produces |
|---|---|---|
| Day summaries | "Create day summaries from April to January" | One summary per day — what happened that specific day |
| Month summaries | "Summarise each month from April to January" | One summary per month — the highlights, milestones, and themes of that month |
| Year summary | "Give me a year summary of our conversations" | One overall summary — the full story arc across the entire period |
| Custom range | "Summarise what happened between June 10 and June 20" | One summary covering that specific date range |
Day summaries produce a title (2-3 words), titleLine2 (1-3 words), and summary (1-3 sentences).
Month summaries produce a title, titleLine2, and summary (3-5 sentences covering key events, milestones, and the mood of that month).
Year summaries produce a title, titleLine2, and summary (5-10 sentences telling the full story arc).
Custom range produces a title, titleLine2, and summary (length proportional to the range — 1-3 sentences for a few days, more for longer ranges).
All summaries follow the same quality rules: third person, factual, specific, warm and nostalgic.
Workflow: Generating summaries
When the user asks for summaries (e.g., "create day summaries from April to January" or "summarise each month"), follow these steps:
Step 1: Export conversations to local .txt files
# Get all conversations with inline text
curl -s "$API/workspaces/{wsId}/conversations/export?dateFrom=TS&dateTo=TS" \
-H "Authorization: Bearer $KEY" > convos_export.json
Then group by date into monthly .txt files for easy reading:
// Group conversation text by date, save as monthly .txt files
// Each date section starts with: ========== YYYY-MM-DD ==========
// Contains the FULL conversation text for that day (no truncation)
Save as convos_YYYY-MM.txt (e.g., convos_2025-04.txt). Include ALL message text — never truncate.
Step 2: Generate summaries by reading conversations
Read the actual conversation text and write summaries. Each summary has:
title: First line of a decorative title (2-3 words, e.g., "Sweet Mornings")titleLine2: Second line (1-3 words, e.g., "& Missing You")summary: Description of what ACTUALLY happened. Must be:- Written in third person (use actual names, not "I" or "you")
- Specific — reference real places, events, quotes from the conversation
- Factual — only include what is explicitly stated in messages
- Warm and nostalgic — like a scrapbook caption looking back
For day summaries: Process one day at a time. Use parallel agents (one per 2 months) for speed. Each agent reads the .txt files and saves to a separate JSON file (e.g., summaries_apr_may.json). Summary length: 1-3 sentences.
For month summaries: Read ALL conversations for that month. Identify the key events, milestones, recurring themes, and emotional arc. Summary length: 3-5 sentences. Title should capture the month's main theme (e.g., "The Month We" / "Fell in Love" or "Exams &" / "Adventures").
For year summaries: Read conversations across the full period (or use the day/month summaries as input). Tell the complete story arc — how it started, key milestones, how the relationship evolved. Summary length: 5-10 sentences. Title should capture the overall journey (e.g., "Our Story" / "300 Days").
For custom range summaries: Read conversations in the specified range. Length proportional to the range — a few days gets 1-3 sentences, a few weeks gets 3-5.
Step 3: Verify summaries (CRITICAL — run at least 2 passes)
Launch verification agents that re-read each day's conversation and check every claim in the summary:
- Is every fact directly supported by a message?
- Is every event on the correct date? (Messages after midnight = next day)
- Is anything fabricated, assumed, or embellished?
- Is anything from an adjacent day bleeding in?
- Who said what — correct attribution?
- "Planned" vs "actually happened" accurately distinguished?
Common AI errors to watch for (from experience with 94 corrections across 5 passes):
- Date bleeding (~30%): Messages after midnight attributed to wrong day
- Fabricated details (~20%): AI inventing names, places, events not in text
- Planned vs happened (~15%): Saying they did something they only discussed
- Wrong attribution (~10%): Mixing up who said what
- Adjacent day bleed (~10%): Details from wrong day
- Embellishment (~10%): Adding unsupported adjectives/details
- Wrong specifics (~5%): Incorrect amounts, times, place names
Run multiple verification passes until a pass comes back with 0-2 fixes. Minimum 2 passes recommended, 3-5 for print-quality output.
Step 4: Merge and save
Merge all per-month files into a single day_summaries.json. Generate a review HTML for the user to browse.
Step 5: Create review HTML
Build a browsable HTML file showing all summaries so the user can review before applying:
<!-- Each day shown as a card with date, title, and summary -->
<div class="day">
<div class="day-date">2025-04-12</div>
<div class="day-title">The First Date</div>
<div class="day-summary">Their very first date! Both nervous, both excited...</div>
</div>
Using day summaries in photo books
When integrating summaries into photo book pages, render them as Polaroid Story Cards — a Polaroid-shaped card with:
- Watercolor wash background (layered CSS radial gradients)
- "this day was about" header in small Playfair Display caps
- Big decorative title in Dancing Script (line 1 in espresso, line 2 in rose)
- Gold divider (line + ✦ + line)
- Summary text in Caveat cursive, centered
- Handwritten caption at Polaroid bottom
For single-day pages: caption says "a day to remember" For multi-day pages: each day gets its own card with "our 3rd May" / "our 4th May" captions
Error codes
| Code | Status | Meaning |
|---|---|---|
bad_request | 400 | Invalid parameters |
unauthorized | 401 | Missing or invalid API key |
not_found | 404 | Resource not found |
rate_limited | 429 | Too many requests |
internal_error | 500 | Server error |