# Use your own images and fonts Upload a file once, then point at it with the `reference` string the upload response returns. There are two kinds, and the upload's `category` decides which one you get: | Reference | Goes in | Resolved as | | --- | --- | --- | | `asset:` | an `image_url` override, or a template image layer's `src` | the image bytes, inlined server-side | | `font:` | a `fontFamily` override, or a template text layer's `style.fontFamily` | an injected `@font-face` block | The renderer resolves both from your account server-side, so you never need a public URL. For images you can also pass a base64 `data:image/*` URI directly. References are private to the account that uploaded them. A reference to another account's upload, or to one you have since deleted, is not an error: the render completes with an `asset_unresolved` warning, the image layer is left blank, and the text layer falls back to its default font. The **App → Assets** page lists which templates use each upload, so check it before deleting. > **Tip: Prefer a UI?** > > Manage your uploads and copy references under **App → Assets**. ## Upload, list, and reference **Upload** ```bash # 1. Upload a file (multipart). 'name' is optional. curl -X POST https://yourdomain.com/api/uploads \ -H 'Authorization: Bearer YOUR_API_KEY' \ -F 'file=@screenshot.png' \ -F 'name=hero shot' # → 201 { "id": "...", "name": "hero shot", "reference": "asset:...", ... } ``` **List** ```bash # 2. List your uploads to find their ids / references later curl https://yourdomain.com/api/uploads \ -H 'Authorization: Bearer YOUR_API_KEY' # → { "uploads": [{ "id": "...", "name": "hero shot", "reference": "asset:..." }], "total": 1 } ``` **Reference** ```json # 3. Reference it when generating (works in image_url overrides and layer src) { "templateId": "your-template-id", "layers": { "photo": { "image_url": "asset:PASTE_UPLOAD_ID" } } } ``` ## Custom fonts (Pro) Upload a font file and reference it in a text layer's `fontFamily` with its **`font:`** reference. The same value works in three places: a per-render `layers` override on `/api/generate` and the batch endpoint, the `generate_render` and `update_layer` MCP tools, and the template JSON itself. The editor's font picker writes the same marker when you choose an uploaded font. One static face per upload, so upload bold or italic variants as separate files and reference each by its own id. There is no in-place replacement: to swap a font, upload the new file and update the templates that use the old id. **Upload** ```bash # Font uploads require the Pro plan. Max 5MB, OTF/TTF/WOFF/WOFF2 only # (validated by content, not filename). curl -X POST https://yourdomain.com/api/uploads \ -H 'Authorization: Bearer YOUR_API_KEY' \ -F 'file=@brand-font.woff2' \ -F 'category=font' \ -F 'name=Brand Sans' # → 201 { "id": "...", "name": "Brand Sans", "category": "font", "reference": "font:...", ... } ``` **Reference** ```json # Per-render override: fontFamily sits directly on the override object # (overrides are flat, there is no nested "style" key) { "templateId": "your-template-id", "layers": { "title": { "fontFamily": "font:PASTE_UPLOAD_ID" } } } ``` **Template JSON** ```json # Or bake it into the template itself: a text layer's style.fontFamily { "type": "text", "name": "title", "content": "Hello", "style": { "fontFamily": "font:PASTE_UPLOAD_ID", "fontSize": 64, "color": "#111111" } } ``` > **Warning: Non-Pro accounts** > > Custom fonts are a Pro plan feature. Uploading a font file on a non-Pro account is rejected with a **403** at upload time. If a template's `font:<id>` reference is rendered by an account that has since downgraded from Pro, the render still completes, the custom face is skipped and the layer falls back to its default font, with a `custom_fonts_blocked` warning in the response's `warnings` array. The public, unauthenticated `/api/render` URL endpoint never resolves custom fonts, whatever the owner's plan, so templates meant for Render URLs should use the built-in font list.