TEASEDocs
ProductsLinkLanding-page builder

Upload a custom HTML design

Fully replace the block builder with your own markup while keeping buttons and attribution working.

Newer surface, staged rollout

The custom-design library, sanitizer, and slot validator described below are implemented and wired end to end — upload, activation, publish, and live serving all work today. The module that lays out its on-disk paths still labels the asset-serving piece a later rollout stage in its own comments, so treat this as a newer, less battle-tested corner of the builder than buttons/profile/gate, and verify a design in preview before publishing it to real traffic.

Instead of the block builder (buttons/profile/gate/etc.), a domain can run your own HTML document as its landing page. TEASE injects one small script to wire up the money buttons and attribution — everything else in your markup, CSS, and animation is served as you wrote it.

Create a design and upload it

Designs are a library, separate from any one domain — create one, upload a document into it, then point a domain at it.

# 1. Create the design
curl -X POST "https://app.tease.link/api/admin/landing-designs" \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "My hand-built page" }'
# → { "id": 42, "status": "draft", ... }

# 2. Upload a document — a single .html file, or a .zip with index.html + assets
curl -X POST "https://app.tease.link/api/admin/landing-designs/42/upload" \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -F "file=@page.zip"

A .zip may carry index.html plus CSS/image/font/video assets (.css .png .jpg .jpeg .webp .svg .gif .woff .woff2 .mp4 .webm), up to 50 files and 5 MB uncompressed total; a bare HTML document is capped at 512 KB. Not sure where to start? GET /api/admin/landing-designs/starter.zip downloads a starter kit (index.html with every slot, angel.css, a README.md, and a PROMPT.md written for feeding to an AI page builder).

The two required slots

Your document must carry two data-angel attributes for the upload to be publishable:

SlotPurpose
[data-angel="name"]Where the creator's name is injected
[data-angel="buttons"] (a container) or [data-angel="button"] (a row template the runtime clones)Where the money buttons render — this is the actual payment path

Missing either one still stores the draft (so you can fix it in the editor) but the upload response comes back ok: false with the specific slot errors, and such a draft cannot be activated on a domain until it's fixed.

What gets stripped on upload

Every document is sanitized before it's stored — <script> tags, on* event-handler attributes, javascript:/vbscript: URIs, <iframe>/<object>/<embed>, <base>, and any navigation pointing straight at a money destination (only the runtime-wired button slot is allowed to reach OnlyFans/your smart-link host) are all removed. Inline <style>, animations, gradients, @font-face, <canvas>, and data: image URIs are left untouched — visual richness survives, executable code and attribution-bypassing links don't.

Activate it on a domain

curl -X POST "https://app.tease.link/api/admin/bg/config?domain=links.creator.com" \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "design": "custom", "custom_design_id": 42 }'

This re-validates the design's slots and rejects activation (422) if they're missing — a broken design can never go live even if the upload response was already flagged. Once active, publishing the domain (as usual) promotes the design's staged assets to its public, served copies.

Confirm it worked

GET /api/admin/landing-designs/42 echoes back html_draft, warnings, and whether it has_published. Use GET /api/admin/landing-designs/42/preview to render the draft through the same runtime the live page uses, before touching any domain. After publishing, load the domain directly and check the money buttons actually navigate through your tracked link, not a bare outbound URL.

Deleting a design in use

Deleting a design is refused (409) while any draft or published domain still references it — the error lists which domains, so you know what to switch first.

What's next

On this page