gotchas.md
447 lines · small-business/skills/social-content-engine/reference/gotchas.md at 2574e29
Most marks come from matching words in the file, so treat them as pointers.
Gotcha: Rebuilding the calendar instead of extending it
Why it matters: The calendar is the durable artifact — it is the reason this is a content operation and not a campaign generator. When each run starts from a blank calendar, the owner re-approves dates they already approved, and posts that were scheduled last month quietly get duplicated or dropped.
Raw lines 7 to 13✗ Bad
Owner: "Give me another month of posts."
→ Build a fresh 30-day calendar from scratch.
→ Two rows collide with posts already scheduled in HubSpot.Raw lines 14 to 21✓ Good
Open the existing calendar first, then report:
Published since last run: 6 posts (Jun 2 – Jun 24)
Still scheduled: 4 posts (through Jul 6)
Gap starting: Jul 7
"I'll extend from Jul 7. Same cadence, three a week?"Raw lines 22 to 34
Gotcha: Writing a caption before reading the voice profile
Why it matters: Voice fidelity is the stated adoption barrier for this skill. Owners do not abandon it because the graphics are wrong; they abandon it because the caption sounds like a brand account and not like them. The shared profile exists so a correction made once holds across every skill.
Raw lines 35 to 41✗ Bad
Draft 12 captions in the owner's general industry register, present them, and rewrite all 12 after the owner says "this doesn't sound like me."
Raw lines 42 to 46✓ Good
Read ../../../shared/voice-profile.md first. Anchor on the never-dos before each caption, not just the first. If no profile exists, build one from their own posts and confirm it before drafting at volume.
Raw lines 47 to 54
Gotcha: Restating a Shopify price from memory
Why it matters: A wrong price in a public post cannot be quietly fixed after a hundred people have seen it. Prices change between sessions, and the calendar is long-lived, so a number carried over from an earlier run is routinely stale.
Raw lines 55 to 61✓ Good
Read variants[].price fresh for every post that names a number. If the connector did not return one, say the price is unavailable and ask. Only claim "was X, now Y" when compare_at_price exists and is genuinely higher.
Raw lines 66 to 73
Gotcha: Generating ANY Canva design for an email row
Why it matters: Canva is out of scope for the email path entirely. Canva email-template autofill produces placeholder graphics (stock landscapes, blank rectangles) in service-grid tiles, and variation thumbnails fail to render in chat. Emails are text-only from this skill, period. Generating a Canva email design — even one — breaks that rule.
Raw lines 74 to 82✗ Bad
Calendar has 6 social rows + 2 email rows.
→ Step 6 generates 6 social designs + 2 email designs.
→ Email designs ship with placeholder service tiles.Or any variation: "I'll generate the email template just to give you a visual reference," "I found an email template that might work, want to preview it?", "Here's a Canva design for the Jun 5 email…" — all forbidden.
Raw lines 83 to 95✓ Good
Step 2: Calendar built with Path column.
- 6 rows tagged Canva (social)
- 2 rows tagged Text-only (no Canva)
Step 6: Generates exactly 6 Canva designs. Email rows are not passed
to any Canva endpoint.
Step 8: Drafts 6 social captions + 2 full emails (subject + preheader +
body) as plain text.
Step 9: Stages 6 social posts. Surfaces 2 emails inline for the owner
to copy into their email tool.If the owner explicitly asks for a Canva email design ("can you make me a Canva version of the Jun 5 email?"), redirect: "This skill keeps emails as text-only because the Canva email path produces placeholder graphics. If you want a Canva-designed email, build it directly in Canva and I can help you write the copy here."
Raw lines 96 to 117
Gotcha: Generating without a budget plan
Why it matters: Canva caps API usage at 100 requests/minute per token, and each design costs ~5 calls (autofill + export + polling). A 10-row campaign with 4 candidates per row generates 40 designs and ~200 API calls — and the math gets worse with parallelism. Without a pre-flight budget, the owner hits quota mid-campaign with no clear recovery path and four already-generated posts sitting unused.
Raw lines 118 to 126✗ Bad
Calendar approved → immediately fire 4 candidates × 8 rows in parallel
→ ~160 API calls in 90 seconds → RATE_LIMIT_EXCEEDED on post 5 → loop
on retry → owner blocked.Raw lines 127 to 134✓ Good
Step 4: "8 Canva rows × 3 candidates = 24 designs, ~120 API calls.
Proceed at default 3 candidates per row?"
Step 6: row 1's 3 candidates fire in parallel → 30s pause → row 2 →
30s pause → row 3 → … finishes at ~12 designs/minute, well under the
quota.
If quota does hit anyway: stop, surface progress, ask the owner
whether to drop to single candidates, pause an hour, or move to
captions with what's done.Raw lines 135 to 151
Gotcha: Skipping Checkpoint 1 (calendar approval) before generating assets
Why it matters: Generating 10+ Canva designs takes API calls, time, and user attention. If the calendar has the wrong dates, wrong channels, or the owner changes their mind about a theme, all that work is discarded. The calendar checkpoint is the cheapest place to catch misalignment.
Raw lines 152 to 158✗ Bad
Receive the brief → immediately start calling POST /autofills for all 12 posts → present designs before the owner has agreed on the schedule.
Owner: "Oh wait, I didn't want any posts the week of the 15th, I'm traveling."
Raw lines 159 to 165✓ Good
Present the calendar table first. Wait for explicit "looks good" before opening a single Canva API call.
Raw lines 166 to 172
Gotcha: Template has more image slots than the brief provides photos for
Why it matters: Multi-image social templates (Instagram carousels, product grids) can have 3+ image slots. When the brief gives you 1 photo and the template demands 3, the unfilled slots silently render Canva's stock landscape graphic — generic clouds and green hills where product photos should be. The design looks "complete" until the owner opens it.
(Note: email rows skip Canva entirely in this skill — see SKILL.md Step 2. This gotcha applies only to social templates with multiple image slots.)
Raw lines 173 to 183✗ Bad
Build manifest with one row per design ("Carousel — needs Hero_Image"). Upload the one photo. Generate. Carousel ships with slide 1 populated and slides 2-3 both showing identical generic landscapes.
Raw lines 184 to 189✓ Good
Build manifest slot-by-slot. Surface the gap explicitly:
The "3-product carousel" template has 3 image slots. The brief gave me
1 photo. How should I fill the other 2?
1. Reuse the same photo across all 3 slots
2. I'll send you 2 more photos
3. Pick a single-image templateWait for the owner's choice before generating. Don't let placeholder graphics ship.
Raw lines 190 to 207
Gotcha: Trusting status: success without exporting and visually verifying
Why it matters: A successful autofill job means Canva accepted the request and produced a design — not that the design contains the right images. If an asset_id was empty, stale, or wrong-typed, the job still returns status: success and the design renders with default placeholder graphics. The only way to know is to look at the rendered output.
✗ Bad
Job status: success ✓
result.design.id: present ✓
→ Present to ownerResult: owner opens the carousel and sees stock landscape graphics in 3 of 4 designs.
Raw lines 216 to 226✓ Good
After the job succeeds, export each design to a permanent PNG via POST /exports (or export-design MCP). Look at each PNG and confirm it contains real assets — not Canva's default landscape, not gray rectangles, not lorem-ipsum text. Only present to the owner after visual verification passes.
Raw lines 227 to 236
Gotcha: One design in a batch fails to render
Why it matters: When 1 of 4 designs in a batch fails (job error, expired preview URL, placeholder rendered) and you present the partial carousel anyway, the owner sees three real designs and one "Show Image" gray rectangle. They lose trust in the rest of the carousel even though it's fine.
Raw lines 237 to 244✗ Bad
Week 1 designs are ready:
[Option A — real preview]
[Option B — real preview]
[Option C — real preview]
[Option D — Show Image placeholder]
Which one should I use?Raw lines 245 to 255✓ Good
When you detect the broken design during Step 6 verification, regenerate just that one design with the correct asset_id. Re-export, re-verify. Only present once all four designs in the batch have permanent, verified previews. If the second attempt also fails, ask the owner whether to skip, swap template, or try a different photo.
Raw lines 256 to 265
Gotcha: Generating assets without confirming all autofill fields have values
Why it matters: Canva autofill silently renders empty placeholders when a field value is missing or the referenced asset_id doesn't exist. The result is a professional-looking template with blank white rectangles where the product photo and headline should be.
✗ Bad
Build calendar → immediately fire all POST /autofills in parallel → half the designs come back with empty image placeholders because product photos weren't uploaded first.
✓ Good
Build calendar → inventory every autofill field slot-by-slot → upload missing product images → confirm all field values with owner → then generate designs in parallel, with every field pre-populated.
Raw lines 279 to 286
Gotcha: Passing invalid values to image autofill fields
Why it matters: When an autofill image field receives an empty string, a file path, a URL, or a stale/invalid asset_id, Canva does not error — it silently renders the template's default placeholder graphic (e.g., a generic landscape with clouds and hills). The design looks "finished" but the hero image is stock art, not the owner's product photo.
✗ Bad
Upload product photo → don't wait for the upload job to complete → pass the job_id (not the asset.id) into the autofill data → design renders with a placeholder landscape instead of the product photo.
✓ Good
Upload product photo via POST /v1/asset-uploads → poll GET /v1/asset-uploads/{job_id} until status == "success" → extract asset.id from the response → pass that exact ID into the autofill image field. Verify every image field has a confirmed asset.id before calling POST /autofills.
Raw lines 301 to 310
Gotcha: Embedding Canva CDN thumbnail URLs in markdown
Why it matters: Canva's autofill response returns short-lived, authenticated CDN URLs from design.canva.ai. These expire within minutes. If Claude embeds them as markdown images (), they render as broken "Show Image" placeholders by the time the message is shown.
✗ Bad
Generate 8 designs → manually embed each CDN thumbnail URL in the response as  → all images show as broken gray placeholders because the URLs expired before render.
✓ Good
Either let the native Cowork carousel render the tool result automatically, or export each design via POST /exports and embed the permanent export URL. As a final fallback, link to the design's permanent edit URL (https://www.canva.com/d/{design_id}). Never re-embed design.canva.ai URLs.
Raw lines 324 to 333
Gotcha: Listing designs one-by-one as individual image blocks
Why it matters: When Claude lists each design as a separate block ("Hero 1 / [image] / Hero 2 / [image]"), the images render as broken gray "Show Image" placeholders. Presenting all designs together in a single grouped batch triggers Cowork's side-by-side carousel, which renders correctly.
Raw lines 334 to 341✗ Bad
Hero 1 — Multi-property portfolio
[Show Image]
View in Canva: https://canva.com/d/xxx
Hero 2 — Coastal market case study
[Show Image]
View in Canva: https://canva.com/d/yyyRaw lines 342 to 353✓ Good
Present all designs in one grouped response so they render as a side-by-side carousel. Below the carousel, add a single prompt:
Here are 4 hero designs for Week 1. Scroll through the carousel
above and let me know which one to keep.Raw lines 354 to 365
Gotcha: Generating designs sequentially instead of in parallel
Why it matters: Generating designs one at a time means the owner waits through N round trips instead of one. Fire all generation requests within a batch at once and present selection after all jobs complete.
Raw lines 366 to 371✗ Bad
Generate design 1 → wait → show it → generate design 2 → wait → show it → … × 8 designs. Owner watches a slow drip for 10 minutes.
Raw lines 372 to 376✓ Good
Fire all designs in the current batch at once → poll all jobs concurrently → once all complete, export each → verify visually → then present.
Raw lines 377 to 383
Gotcha: Auto-selecting a brand template for Pro/Teams users
Why it matters: Pro/Teams users have no brand-template API access. If Claude guesses a template by name and generates with the wrong one, the owner gets designs in the wrong colors/fonts and has to redo the work in Canva manually.
Raw lines 384 to 390✗ Bad
Found template "Square Post - Blue". Using this for all feed posts.…followed by 8 designs in blue when the brand is green.
Raw lines 391 to 397✓ Good
I found 3 templates in your Canva library that could work for feed posts:
1. Square Post - Blue (last edited March 2026)
2. Summer Promo - Green (last edited April 2026)
3. Product Feature Card (last edited Jan 2026)
Which one should I use?Raw lines 398 to 410
Gotcha: Caption voice drift across a long calendar
Why it matters: When drafting 12–20 captions in one pass, the tone tends to drift — early captions follow the brief's voice markers, later ones slip into generic marketing copy. Owners notice immediately.
Raw lines 411 to 416✗ Bad
Post 1: "Our handmade candles are the perfect summer gift 🌿" (matches brief: "warm, conversational") Post 10: "Elevate your ambiance with our artisanal fragrance collection." (corporate drift)
Raw lines 417 to 421✓ Good
Before drafting, anchor on 2–3 voice markers from the brief (e.g., "casual, friendly, uses light humor"). Re-read the first draft caption before writing each new one. If the owner flags a voice mismatch on any caption, re-read that caption and flag it as the recalibration point for the remaining drafts.
Raw lines 422 to 430
Gotcha: Staging HubSpot posts without verifying scheduledAt is in the future
Why it matters: HubSpot rejects posts with scheduledAt in the past with a validation error. If the calendar was built on an earlier date and the owner is only now staging, some dates may have passed.
✗ Bad
Build calendar on April 1st for June posts → owner approves May 15th → try to stage without checking → post for "April 30th 10:00 AM" returns 400.
Raw lines 437 to 441✓ Good
Before calling POST /social/posts, compare each scheduledAt against the current UTC time. If any post date has passed, surface it: "The post scheduled for April 30th has already passed. Should I skip it, or reschedule it to next week?"