Upload Font
Learn how to upload custom fonts to your workspace
Published ·Updated
/v1/brand-assets/fonts/addcurl -X POST "https://api.orshot.com/v1/brand-assets/fonts/add" \
-H "Authorization: Bearer <ORSHOT_API_KEY>"const res = await fetch("https://api.orshot.com/v1/brand-assets/fonts/add", {
method: "POST",
headers: {
Authorization: "Bearer <ORSHOT_API_KEY>",
},
});
const data = await res.json();import requests
response = requests.post(
"https://api.orshot.com/v1/brand-assets/fonts/add",
headers={"Authorization": "Bearer <ORSHOT_API_KEY>"},
)
data = response.json()$ch = curl_init("https://api.orshot.com/v1/brand-assets/fonts/add");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer <ORSHOT_API_KEY>",
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);require "net/http"
require "json"
uri = URI("https://api.orshot.com/v1/brand-assets/fonts/add")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer <ORSHOT_API_KEY>"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)Overview#
This endpoint allows you to upload custom fonts to your workspace. You can upload font files from a URL, as a base64-encoded string, or as a binary upload. Uploaded fonts become available for use in your templates.
https://api.orshot.com/v1/brand-assets/fonts/addRequest#
await fetch("https://api.orshot.com/v1/brand-assets/fonts/add", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer <ORSHOT_API_KEY>",
},
body: JSON.stringify({
file: "https://example.com/fonts/BrandFont-Bold.ttf",
name: "BrandFont-Bold.ttf", // optional
tags: ["heading", "brand"], // optional
}),
});const formData = new FormData();
formData.append("file", fontFile); // File object
formData.append("name", "BrandFont-Bold.ttf"); // optional
await fetch("https://api.orshot.com/v1/brand-assets/fonts/add", {
method: "POST",
headers: {
Authorization: "Bearer <ORSHOT_API_KEY>",
},
body: formData,
});{
"data": {
"font": {
"id": 457,
"created_at": "2025-09-11T10:15:30.123Z",
"font_family": "BrandFont-Bold",
"file_name": "BrandFont-Bold-Hn4Ks8Pq2Vx.ttf",
"file_url": "https://storage.orshot.com/cloud/w-50/renders/custom_fonts/BrandFont-Bold-Hn4Ks8Pq2Vx.ttf",
"file_size": 184320,
"file_type": "font/ttf",
"tags": ["heading", "brand"],
"workspace_id": 50,
"user_id": "abcdef01-2345-6789-abcd-ef0123456789"
},
"url": "https://storage.orshot.com/cloud/w-50/renders/custom_fonts/BrandFont-Bold-Hn4Ks8Pq2Vx.ttf"
}
}Request Parameters#
| Parameter | Type | Required | Description |
|---|---|---|---|
file | String | Yes | URL, base64-encoded string, or binary upload of the font file |
name | String | No | Custom name for the font. It becomes the font_family without its extension. Defaults to the uploaded file's name for multipart uploads. URL and base64 uploads get an auto-generated name, so pass name to choose the font family |
tags | String[] | No | Array of tags to associate with the font (e.g., ["heading", "brand"]) |
readFromFile | Boolean | No | true reads the family name, weight and style from the font file, so every weight of a family lands under one font_family (see below). Defaults to false |
Uploading a Family with Several Weights#
By default the family is named after the file, so BrandFont-Bold.ttf becomes the family BrandFont-Bold. To upload a whole family, send each weight with readFromFile: true:
for (const url of [
"https://example.com/fonts/BrandFont-Regular.ttf",
"https://example.com/fonts/BrandFont-Bold.ttf",
"https://example.com/fonts/BrandFont-BoldItalic.ttf",
]) {
await fetch("https://api.orshot.com/v1/brand-assets/fonts/add", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer <ORSHOT_API_KEY>",
},
body: JSON.stringify({ file: url, readFromFile: true }),
});
}All three files land under one family, BrandFont, as Regular (400), Bold (700) and Bold Italic. Each row returns its font_weight and font_style. Text set in BrandFont at weight 700 then uses the Bold file, the same as fonts uploaded from the dashboard.
- TTF and OTF files carry their family, weight and style inside the file, so the file name doesn't matter
- WOFF and WOFF2 files keep that data compressed, so the family and weight are read from the file name (
BrandFont-SemiBold.woff2). Upload TTF or OTF when you can - Pass
nameto set the family yourself. The weight and style still come from the file - If no family can be found, the request returns
400with the codeFONT_FAMILY_UNREADABLE
Supported Font Formats#
- TrueType (
.ttf) - OpenType (
.otf) - Web Open Font Format (
.woff) - Web Open Font Format 2 (
.woff2)
Notes#
- Maximum file size: 10MB
- Uploaded fonts are immediately available for use in templates
font_familyis thenamewithout its extension (or the family inside the file withreadFromFile: true). Use it exactly as returned in templates- Tags are trimmed and lowercased. Duplicates, empty values and items that are not strings are removed
Error responses#
Input problems return 400 with a plain-language error and, for the two cases agents hit most, a stable code:
FONT_NOT_A_FONT: the bytes carry no TTF, OTF, WOFF or WOFF2 signature. Usually a filename, a description, or placeholder text was sent instead of the file, or a URL served an HTML page.FONT_DATA_INCOMPLETE: the data starts as a real font file but ends early. The base64 was cut off in transit, the URL served a partial file, or the upload stopped before the end. Theerrornames the fix for how the file was sent.
Hosted chat clients such as ChatGPT and Claude.ai cannot send file bytes, so a base64 file from them is almost always cut off. Pass a publicly reachable font URL instead.
Query Parameters#
| Parameter | Type | Required | Description |
|---|---|---|---|
embedId | String | No | Embed instance ID. Pair with embedUserId to scope this call to one embed user |
embedUserId | String | No | The same userId your app passes to the embed URL. Requires embedId |
Per-User Libraries#
Pass embedId and embedUserId together to add the font to that embed user's private library instead of the shared workspace library. This is how one embed serves many brands or tenants, each with its own brand space.
Omit them and this endpoint behaves exactly as documented above. See Per-User Brand Assets for the full picture.
Rate Limits#
- 30 requests per minute per endpoint
Ready to automate?
Start rendering images, PDFs and videos from your templates in under 2 minutes. Free plan, no credit card.
Get your API key- Image, PDF and video generation via API
- Visual editor with AI and smart layouts
- Zapier, Make, MCP and 40+ integrations
- White-label embed for your own app
- 100 free credits a month, no credit card required