DocumentationMarkdown editionagents.md

Images

How to put a photo on a page when all you have is a URL: the upload route, storage keys, kinds and size caps.

Image fields — avatar, theme.backgroundImage, a link's image — accept only storage keys, never URLs. A key is what POST /api/upload returns after it has fetched, re-encoded (AVIF, EXIF stripped) and stored the picture. This keeps third-party pixels off pages that promise not to track anyone.

Upload by URL

curl -X POST https://tapmy.link/api/upload \
  -H "Authorization: Bearer tapmy_sk_…" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/photo.jpg", "kind": "avatar" }'
{ "key": "avatar/66dc…/3f9c….avif", "bytes": 84120, "width": 1000, "height": 1000, "lqip": "data:image/webp;base64,…" }

Then save the key into the page:

curl -X PATCH https://tapmy.link/api/profiles/{id} -H "Authorization: Bearer …" \
  -H "Content-Type: application/json" -d '{ "avatar": "avatar/66dc…/3f9c….avif" }'

Kinds and caps

`kind`Used forMax source sizeStored at
avatarthe profile photo8 MB≤ 1000 px wide, ≈300 KB
carda link block's image8 MB≤ 1400 px wide, ≈350 KB
backgroundtheme.backgroundImage30 MB≤ 1600 px wide, ≈600 KB

Where the crop is anchored

Templates crop: the photo is round, the background is full-bleed, a card image is a strip. Each image field has a companion anchor — avatarFocus, theme.backgroundFocus, a link's imageFocus — as { "x": 0–100, "y": 0–100 } percent of the picture, the point that stays in view. Absent or null means the centre. A face in the top-right of a landscape photo wants something like { "x": 70, "y": 25 }.

curl -X PATCH https://tapmy.link/api/profiles/{id} -H "Authorization: Bearer …" \
  -H "Content-Type: application/json" -d '{ "avatar": "avatar/…avif", "avatarFocus": { "x": 70, "y": 25 } }'

At creation, page.avatarFocus applies to the photo fetched from avatarUrl. The MCP tool set_avatar_from_url takes a focus argument.

Multipart

The same route accepts multipart/form-data with fields file and kind — what the editor uses — if you do have the bytes.

Shortcut at creation

POST /api/agent/pages takes avatarUrl; the server does the fetch and the save for you. A failure there is reported in warnings and does not stop the page.

Errors

bad_url, blocked (private address, or the host refused us), unreachable, too_large, not_image, processing_failed. HEIC from an iPhone is accepted. Instagram CDN URLs expire within days — copy them at once, which is what avatarUrl does.