Overview
Every export from the GoalGen Creator plugin produces a manifest.json conforming to StudioTemplateManifestV1. Creator Studio uses this file to reconstruct the template on the canvas.
{
"_schema": "StudioTemplateManifestV1",
"_pluginVersion": "1.1.0",
"name": "Birthday Announcement",
"frameWidth": 1080,
"frameHeight": 1080,
"layers": [
{
"id": "2:45",
"name": "Background",
"kind": "rect",
"x": 0,
"y": 0,
"width": 1080,
"height": 1080,
"rotation": 0,
"visible": true,
"opacity": 1,
"editableStyle": {
"fill": "#1a1a2e"
}
},
{
"id": "2:46",
"name": "profile-photo",
"kind": "image",
"x": 290,
"y": 120,
"width": 500,
"height": 500,
"rotation": 0,
"visible": true,
"opacity": 1,
"src": "profile-photo.png"
}
],
"slots": [
{
"slotId": "slot-0",
"targetLayerId": "2:46",
"role": "userImage"
}
]
}
Top-level fields
| Field | Type | Required | Description |
|---|
_schema | "StudioTemplateManifestV1" | ✅ | Schema identifier. Always this exact string. |
_pluginVersion | string | ✅ | Plugin version that produced this file (e.g. "1.1.0"). |
name | string | ✅ | Display name of the template, taken from the Figma frame name. |
frameWidth | number | ✅ | Frame width in pixels (Figma document units). |
frameHeight | number | ✅ | Frame height in pixels. |
layers | Layer[] | ✅ | Ordered array of all exported layers (back-to-front). |
slots | Slot[] | — | Dynamic slot assignments. Present when the user has marked at least one slot in the plugin. |
Layer object
Every item in layers shares a common set of geometry fields, plus kind-specific fields.
Common geometry fields
| Field | Type | Description |
|---|
id | string | Figma node ID (e.g. "2:45"). Unique within the manifest. |
name | string | Original Figma layer name. |
kind | "rect" | "path" | "image" | "text" | Layer classification (see below). |
x | number | Horizontal offset from the frame origin, in pixels. |
y | number | Vertical offset from the frame origin, in pixels. |
width | number | Bounding box width, in pixels. |
height | number | Bounding box height, in pixels. |
rotation | number | Clockwise rotation in degrees. |
visible | boolean | Whether the layer was visible at export time. |
opacity | number | Layer opacity, 0–1. |
kind: "rect" — Rectangle
A simple solid rectangle. No asset file. Style is encoded directly in the manifest.
{
"kind": "rect",
"editableStyle": {
"fill": "#ff6b6b",
"cornerRadius": 12
}
}
editableStyle field | Type | Description |
|---|
fill | string | CSS hex colour (e.g. "#ff6b6b"). |
cornerRadius | number | Border radius in pixels. |
kind: "path" — Vector path (manifest-encoded)
A vector shape exported as path data. No asset file.
{
"kind": "path",
"pathData": "M 0 0 L 100 0 L 50 100 Z",
"editableStyle": {
"fill": "#ffffff"
}
}
| Field | Type | Description |
|---|
pathData | string | SVG path d attribute value. |
editableStyle.fill | string | Fill colour. |
kind: "image" — Raster or vector asset
A layer exported as a PNG or SVG file.
{
"kind": "image",
"src": "hero-illustration.svg"
}
| Field | Type | Description |
|---|
src | string | Filename of the asset (relative to the ZIP root). After Studio import this is replaced with an absolute CDN URL. |
At import time, Creator Studio replaces all relative src values with the permanent CDN URLs of the uploaded assets.
kind: "text" — Text layer
A text layer. The text content is preserved but font rendering is left to the canvas engine.
{
"kind": "text",
"editableStyle": {
"content": "Happy Birthday!",
"fontFamily": "Inter",
"fontSize": 72,
"fontWeight": 700,
"color": "#ffffff",
"textAlign": "center"
}
}
editableStyle field | Type | Description |
|---|
content | string | The raw text content. |
fontFamily | string | Font family name as registered in Figma. |
fontSize | number | Font size in pixels. |
fontWeight | number | CSS font weight (100–900). |
color | string | CSS hex colour. |
textAlign | "left" | "center" | "right" | "justified" | Horizontal alignment. |
Slot object
Slots declare which layers are dynamic — i.e., replaceable by the end-user when they personalise a template.
{
"slotId": "slot-0",
"targetLayerId": "2:46",
"role": "userImage"
}
| Field | Type | Description |
|---|
slotId | string | Unique identifier within the manifest (auto-generated as slot-0, slot-1, …). |
targetLayerId | string | The id of the layer this slot controls. Must match a layer in layers[]. |
role | SlotRole | What kind of content the user provides. |
SlotRole values
| Value | Description | UI colour |
|---|
userImage | User’s photo or personal image | Purple #7b61ff |
userText | User-editable text (name, caption, etc.) | Orange #ff9f43 |
brandLogo | Brand or organisation logo | Green #00DB65 |
A single frame can have multiple slots. Each slot targets exactly one layer. One layer can only have one slot role at a time.
TypeScript types
The canonical types are defined in GoalGen Creator/code.ts:
type SlotRole = 'userImage' | 'userText' | 'brandLogo';
interface TemplateSlot {
slotId: string;
targetLayerId: string;
role: SlotRole;
}
interface LayerEntry {
id: string;
name: string;
kind: 'image' | 'path' | 'rect' | 'text';
x: number;
y: number;
width: number;
height: number;
rotation: number;
visible: boolean;
opacity: number;
src?: string;
pathData?: string;
editableStyle?: Record<string, unknown>;
}
interface StudioTemplateManifest {
_schema: 'StudioTemplateManifestV1';
_pluginVersion: string;
name: string;
frameWidth: number;
frameHeight: number;
layers: LayerEntry[];
slots?: TemplateSlot[];
}
The schema is versioned. If you need to make breaking changes, increment _schema to StudioTemplateManifestV2 and handle migration in Creator Studio’s import pipeline.