This reference covers the three API endpoints for creating renders and editable designs: POST /api/v1/create, POST /api/v1/studio/create, and POST /api/v1/studio/create-and-render. By the end you will know which endpoint to use for each workflow and how to build the field-value map that drives dynamic content.
Authentication
All /api/v1/* routes require an API key sent as a Bearer token in the Authorization header:
Authorization: Bearer YOUR_API_KEY
Generate keys in Settings → API Keys. Keys are scoped to your organization; each key carries full org-level access. Studio routes additionally require the Studio feature to be enabled for your organization.
Choosing the right endpoint
| Goal | Endpoint |
|---|---|
| Render a finished image without saving an editable design | POST /api/v1/create |
| Create an editable design in Studio (no immediate render) | POST /api/v1/studio/create |
| Create an editable design and render it in one call | POST /api/v1/studio/create-and-render |
| Fill team photo slots one player at a time | POST /api/v1/studio/create/team |
POST /api/v1/create — render without a Studio design
Submit a render job against a template. The server enqueues the render and returns immediately with a job ID you can poll. For short renders you can block and receive the result in a single request using ?sync=true.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
template_id |
string | Yes | ID of the template to render. Retrieve IDs from GET /api/v1/templates. |
fields |
object | No | Dynamic content keyed by api_field_name. See Field-value map below. |
player_id |
string | No | Jersey number, student ID, or any short identifier. Stored on the job and forwarded in webhook/export output. On /api/v1/studio/create/team it is used to route the photo to the correct slot; on this endpoint it is metadata only. |
webhook_url |
string (HTTPS URL) | No | Receive a POST notification when the render completes. |
send_to |
string | No | Dispatch the finished render to a destination. See Send-To below. |
send_to_qty |
integer (1–99) | No | Number of copies to deliver (Dropbox only; default 1). |
send_to_overwrite |
boolean | No | When true (default), overwrite an existing file at the destination. When false, Dropbox auto-renames the upload instead. |
main_event_code |
string | No | GFcrew Pro only. Stored on the job and forwarded to outbound webhooks and the GFcrew destination. |
destination_event_code |
string | No | GFcrew Pro only. Paired with main_event_code; required when send_to is integration:gfcrew. |
Async mode (default)
Without ?sync=true, the endpoint returns 202 Accepted immediately:
POST /api/v1/create
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY
{
"template_id": "tpl_abc123",
"fields": {
"player_name": "Jordan Williams",
"jersey_number": "12",
"player_photo": "https://example.com/photos/jordan.jpg"
}
}
HTTP/1.1 202 Accepted
{
"job_id": "rj_xyz789",
"status": "pending",
"poll_url": "/api/v1/create/rj_xyz789"
}
Poll GET /api/v1/create/{job_id} until status is "complete", then read output_url for the finished image. Status values are: pending, processing, complete, failed.
Sync mode
Add ?sync=true to block up to 25 seconds. If the render finishes within that window, you get a 200 OK with the result inline:
POST /api/v1/create?sync=true
HTTP/1.1 200 OK
{
"job_id": "rj_xyz789",
"status": "complete",
"template_id": "tpl_abc123",
"output_url": "https://cdn.templified.io/renders/rj_xyz789.jpg",
"render_ms": 4200,
"created_at": "2026-06-12T14:00:00Z",
"completed_at": "2026-06-12T14:00:04Z"
}
If the render is still in progress at 25 seconds, the server falls back to 202 with a poll_url. Sync mode is suitable for single renders in real-time event workflows. For batches of more than 1–2 photos, use async polling instead.
Field-value map
The fields object maps each dynamic layer's API field name to its value for this render. API field names are set in the template editor and returned by GET /api/v1/templates/{id}/fields. Supplying an unknown key returns a 400 error that lists valid names — typos never silently render with template defaults.
| Layer type | Value |
|---|---|
| Text (dynamic) | String — the text content to render |
| Photo | HTTPS URL of the image file (JPEG, PNG, WebP, GIF supported) |
| QR code | String — the URL or text to encode |
Discover a template's fields
GET /api/v1/templates/tpl_abc123/fields
{
"template_id": "tpl_abc123",
"template_name": "5x7 Individual Plaque",
"pages": 1,
"fields": [
{ "name": "Player Name", "api_field_name": "player_name", "type": "text", "is_dynamic": true, "page": 1 },
{ "name": "Jersey Number", "api_field_name": "jersey_number", "type": "text", "is_dynamic": true, "page": 1 },
{ "name": "Player Photo", "api_field_name": "player_photo", "type": "dynamic_graphic", "is_dynamic": true, "page": 1 }
]
}
Two-page templates
Two-page templates render both pages and return a single PDF. The fields response lists layers from both pages with a page annotation (1 or 2). Supply values for both pages in the same fields object — the renderer handles the stitching automatically. The pages field in the template list and fields response is 2 for these templates.
POST /api/v1/studio/create — create an editable design
Creates a Studio design from a template with optional field pre-population. The design is saved and editable in Studio — team members can open it, adjust photos or text, and send it later. No render is triggered.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
template_id |
string | Yes | Source template ID. |
name |
string | Yes | Design name as it will appear in Studio. Must be unique within the target folder. |
folder_id |
string | No | Existing Studio folder ID. Mutually exclusive with folder_path. |
folder_path |
string | No | Slash-separated path (e.g. "Clients/Acme/Q1"). Missing folders are created automatically. Mutually exclusive with folder_id. If neither is provided, the design lands in "Unsorted". |
fields |
object | No | Dynamic field values keyed by api_field_name. Photo values must be HTTPS URLs; the server downloads and stores the image. |
overwrite |
boolean | No | When true, replace a design with the same name in the same folder in place, preserving its ID. Default false. |
player_id |
string | No | Stored on the design; surfaced in exports and webhook output. |
main_event_code |
string | No | GFcrew Pro only. |
destination_event_code |
string | No | GFcrew Pro only. |
A successful call returns 201 Created with the new design's ID and folder:
HTTP/1.1 201 Created
{
"instance": {
"id": "inst_def456",
"name": "Jordan Williams",
"studioFolderId": "sf_ghi789",
"sourceTemplateId": "tpl_abc123",
"layerCount": 8,
"groupCount": 2,
"canvasWidth": 1500,
"canvasHeight": 2100,
"playerId": "12"
}
}
POST /api/v1/studio/create-and-render — create and render in one call
Combines design creation with an immediate render. Useful when you want both an editable record in Studio and a finished image in one API call. Accepts all the same fields as /studio/create, plus the render-specific options below.
Additional fields
| Field | Type | Description |
|---|---|---|
webhook_url |
string (HTTPS URL) | Notify this URL when the render completes. |
send_to |
string | Dispatch the finished render. Supports integration:dropbox, integration:webhook, integration:gfcrew, and preset:<id> (presets can include GFITpro Auto Print destinations). |
send_to_qty |
integer (1–99) | Number of copies (default 1). |
send_to_overwrite |
boolean | Whether to overwrite at the destination (default true). Propagates to every destination in a preset. |
Without ?sync=true, returns 202 Accepted with the design object and a job ID to poll:
HTTP/1.1 202 Accepted
{
"instance": { "id": "inst_def456", ... },
"job_id": "rj_xyz789",
"status": "queued",
"poll_url": "/api/v1/create/rj_xyz789"
}
With ?sync=true, the call blocks up to 25 seconds and returns 200 OK with output_url in the body when the render finishes on time.
POST /api/v1/studio/create/team — fill team photo slots one at a time
For team composites — templates with sequential photo slots named player_photo_1, player_photo_2, and so on — this endpoint adds one player's photo per call. The server auto-creates the design on the first call, then fills the next open slot on each subsequent call using the same name and folder.
How slots work
Slots are detected by a trailing numeric suffix on the api_field_name (e.g. player_photo_1, player_photo_2). The server fills the lowest-numbered empty slot. When you pass a player_id, the server checks whether a slot for that player is already filled: if so, it replaces that slot's photo in place rather than filling the next empty one. This lets you correct a roster without resubmitting the whole team.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
template_id |
string | Yes | Source template ID. |
name |
string | Yes | Design name. Used to locate an existing design on subsequent calls. |
image_url |
string (HTTPS URL) | Yes | The player's photo. |
player_id |
string | No | Jersey number or unique identifier for this player. Used for in-place slot replacement when re-submitting a player. |
fields |
object | No | Additional dynamic field values (e.g. player name or jersey number text layers). |
face_bbox |
object | No | Face bounding box in pixel coordinates (left, top, right, bottom). When provided, face placement uses these coordinates instead of running detection. |
content_bbox |
object | No | Non-transparent content bounding box, paired with face_bbox for precise face placement. |
folder_id / folder_path
|
string | No | Target folder; same semantics as /studio/create. |
main_event_code |
string | No | GFcrew Pro only. Applied on the first call (design creation); ignored on subsequent slot-fill calls. |
destination_event_code |
string | No | GFcrew Pro only. Same behavior as main_event_code. |
A successful call returns 200 OK:
{
"instance_id": "inst_def456",
"slot_filled": "player_photo_3",
"slots_remaining": 2,
"all_slots_full": false
}
When all_slots_full is true, every slot on the template is filled. At that point you can render the design using POST /api/v1/studio/create-and-render with overwrite: true and the same name, or send it from Studio.
Send-To on API renders
Both POST /api/v1/create and POST /api/v1/studio/create-and-render accept a send_to field to dispatch the finished render automatically. The format is integration:<type> or preset:<id>.
| Value | Where it sends | Notes |
|---|---|---|
integration:dropbox |
Your connected Dropbox | On /v1/create, uploads to /Templified/<templateName>/<timestamp>.jpg. On /studio/create-and-render, uploads to /Templified/<designName>.jpg. Use a preset for a custom path. |
integration:webhook |
Your configured outbound webhook | Posts the standard studio.export JSON body. |
integration:gfcrew |
GFcrew Pro | Requires both main_event_code and destination_event_code. On /v1/create, each call creates a fresh GFcrew upload. On /studio/create-and-render, uses the design's ID for replace semantics. |
preset:<id> |
Every destination in the saved preset | Only available on /studio/create-and-render. Can include GFITpro Auto Print destinations. Retrieve preset IDs from Settings → Send-To Presets or the GET /api/v1/send-targets endpoint. |
When send_to is set, the poll response includes a send_to_status field (pending, delivering, delivered, or failed) once the render itself completes. A send_to_error field is included when the status is failed.
Direct GFcrew send
To push a render directly to GFcrew without keeping a Studio design, use POST /api/v1/create with send_to: "integration:gfcrew" and both event codes. Each call to this endpoint is treated by GFcrew as a new upload — if you need GFcrew's replace semantics (overwriting a prior upload for the same athlete), use POST /api/v1/studio/create-and-render instead, which ties the GFcrew send to the design's persistent ID.
Error codes
| Status | Meaning |
|---|---|
400 |
Validation error — response body includes the invalid field and, for unknown fields keys, the list of valid api_field_name values. |
402 |
Billing limit reached (trial render cap, or insufficient balance). |
403 |
Instance billing block (trial design cap, or subscription suspended). |
404 |
Template or folder not found. |
409 |
Duplicate design name in folder (use overwrite: true to replace), or Send-To integration not connected. |
422 |
External image URL could not be downloaded or is not a valid image. |
Comments
0 comments
Article is closed for comments.