curl --request POST \
--url https://api.print.app/v2/connectors \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"title": "Print side → pages",
"mode": "pageIndex",
"source": {
"selector": "input[name=\"Print side\"]:checked"
},
"mappings": [
{
"from": "Front",
"to": "1"
},
{
"from": "Back",
"to": "2"
},
{
"from": "Front and back",
"to": "1,2"
}
]
}
'import requests
url = "https://api.print.app/v2/connectors"
payload = {
"title": "Print side → pages",
"mode": "pageIndex",
"source": { "selector": "input[name=\"Print side\"]:checked" },
"mappings": [
{
"from": "Front",
"to": "1"
},
{
"from": "Back",
"to": "2"
},
{
"from": "Front and back",
"to": "1,2"
}
]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
title: 'Print side → pages',
mode: 'pageIndex',
source: {selector: 'input[name="Print side"]:checked'},
mappings: [
{from: 'Front', to: '1'},
{from: 'Back', to: '2'},
{from: 'Front and back', to: '1,2'}
]
})
};
fetch('https://api.print.app/v2/connectors', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.print.app/v2/connectors",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'title' => 'Print side → pages',
'mode' => 'pageIndex',
'source' => [
'selector' => 'input[name="Print side"]:checked'
],
'mappings' => [
[
'from' => 'Front',
'to' => '1'
],
[
'from' => 'Back',
'to' => '2'
],
[
'from' => 'Front and back',
'to' => '1,2'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.print.app/v2/connectors"
payload := strings.NewReader("{\n \"title\": \"Print side → pages\",\n \"mode\": \"pageIndex\",\n \"source\": {\n \"selector\": \"input[name=\\\"Print side\\\"]:checked\"\n },\n \"mappings\": [\n {\n \"from\": \"Front\",\n \"to\": \"1\"\n },\n {\n \"from\": \"Back\",\n \"to\": \"2\"\n },\n {\n \"from\": \"Front and back\",\n \"to\": \"1,2\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.print.app/v2/connectors")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"title\": \"Print side → pages\",\n \"mode\": \"pageIndex\",\n \"source\": {\n \"selector\": \"input[name=\\\"Print side\\\"]:checked\"\n },\n \"mappings\": [\n {\n \"from\": \"Front\",\n \"to\": \"1\"\n },\n {\n \"from\": \"Back\",\n \"to\": \"2\"\n },\n {\n \"from\": \"Front and back\",\n \"to\": \"1,2\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.print.app/v2/connectors")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"title\": \"Print side → pages\",\n \"mode\": \"pageIndex\",\n \"source\": {\n \"selector\": \"input[name=\\\"Print side\\\"]:checked\"\n },\n \"mappings\": [\n {\n \"from\": \"Front\",\n \"to\": \"1\"\n },\n {\n \"from\": \"Back\",\n \"to\": \"2\"\n },\n {\n \"from\": \"Front and back\",\n \"to\": \"1,2\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"object": "connector",
"id": "01KADX3Q7M2B8C4D5E6F7G8H9J",
"title": "Print side → pages",
"mode": "pageIndex",
"source": {
"type": "selector",
"selector": "input[name=\"Print side\"]:checked"
},
"query": "input[name=\"Print side\"]:checked",
"mappings": [
{
"from": "Front",
"to": "1"
},
{
"from": "Back",
"to": "2"
},
{
"from": "Front and back",
"to": "1,2"
}
],
"twoWay": true,
"page": null,
"text": null,
"event": null,
"created": "2026-10-27T10:00:00Z",
"modified": "2026-10-27T10:00:00Z"
}{
"error": true,
"code": "invalid_request",
"message": "width is required.",
"param": "width",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "unauthorized",
"message": "Missing or invalid secret key. Send it as \"Authorization: Bearer sk_…\".",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "account_suspended",
"message": "This account is suspended. Settle the outstanding invoice in the Print.App dashboard.",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "unsupported_media_type",
"message": "Send the request body as JSON, with \"Content-Type: application/json\".",
"requestId": "FZn-uhymjoEEJow="
}{
"message": "Too Many Requests"
}{
"error": true,
"code": "internal_error",
"message": "Something went wrong on our side. Retry the request; if it keeps failing, contact support with the requestId.",
"requestId": "FZn-uhymjoEEJow="
}Create a connector
Creates a connector from a source on the product page (a selector, or a Print Options field by role or id), a mode and, for modes that need them, a mapping per choice. Each mode’s rules are checked: page numbers for pageIndex, your own designs for designSelector, hex colours for underlayColor. Try it first with POST /v2/connectors/test, then attach it with POST /v2/designs/{id}/connectors.
curl --request POST \
--url https://api.print.app/v2/connectors \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"title": "Print side → pages",
"mode": "pageIndex",
"source": {
"selector": "input[name=\"Print side\"]:checked"
},
"mappings": [
{
"from": "Front",
"to": "1"
},
{
"from": "Back",
"to": "2"
},
{
"from": "Front and back",
"to": "1,2"
}
]
}
'import requests
url = "https://api.print.app/v2/connectors"
payload = {
"title": "Print side → pages",
"mode": "pageIndex",
"source": { "selector": "input[name=\"Print side\"]:checked" },
"mappings": [
{
"from": "Front",
"to": "1"
},
{
"from": "Back",
"to": "2"
},
{
"from": "Front and back",
"to": "1,2"
}
]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
title: 'Print side → pages',
mode: 'pageIndex',
source: {selector: 'input[name="Print side"]:checked'},
mappings: [
{from: 'Front', to: '1'},
{from: 'Back', to: '2'},
{from: 'Front and back', to: '1,2'}
]
})
};
fetch('https://api.print.app/v2/connectors', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.print.app/v2/connectors",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'title' => 'Print side → pages',
'mode' => 'pageIndex',
'source' => [
'selector' => 'input[name="Print side"]:checked'
],
'mappings' => [
[
'from' => 'Front',
'to' => '1'
],
[
'from' => 'Back',
'to' => '2'
],
[
'from' => 'Front and back',
'to' => '1,2'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.print.app/v2/connectors"
payload := strings.NewReader("{\n \"title\": \"Print side → pages\",\n \"mode\": \"pageIndex\",\n \"source\": {\n \"selector\": \"input[name=\\\"Print side\\\"]:checked\"\n },\n \"mappings\": [\n {\n \"from\": \"Front\",\n \"to\": \"1\"\n },\n {\n \"from\": \"Back\",\n \"to\": \"2\"\n },\n {\n \"from\": \"Front and back\",\n \"to\": \"1,2\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.print.app/v2/connectors")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"title\": \"Print side → pages\",\n \"mode\": \"pageIndex\",\n \"source\": {\n \"selector\": \"input[name=\\\"Print side\\\"]:checked\"\n },\n \"mappings\": [\n {\n \"from\": \"Front\",\n \"to\": \"1\"\n },\n {\n \"from\": \"Back\",\n \"to\": \"2\"\n },\n {\n \"from\": \"Front and back\",\n \"to\": \"1,2\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.print.app/v2/connectors")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"title\": \"Print side → pages\",\n \"mode\": \"pageIndex\",\n \"source\": {\n \"selector\": \"input[name=\\\"Print side\\\"]:checked\"\n },\n \"mappings\": [\n {\n \"from\": \"Front\",\n \"to\": \"1\"\n },\n {\n \"from\": \"Back\",\n \"to\": \"2\"\n },\n {\n \"from\": \"Front and back\",\n \"to\": \"1,2\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"object": "connector",
"id": "01KADX3Q7M2B8C4D5E6F7G8H9J",
"title": "Print side → pages",
"mode": "pageIndex",
"source": {
"type": "selector",
"selector": "input[name=\"Print side\"]:checked"
},
"query": "input[name=\"Print side\"]:checked",
"mappings": [
{
"from": "Front",
"to": "1"
},
{
"from": "Back",
"to": "2"
},
{
"from": "Front and back",
"to": "1,2"
}
],
"twoWay": true,
"page": null,
"text": null,
"event": null,
"created": "2026-10-27T10:00:00Z",
"modified": "2026-10-27T10:00:00Z"
}{
"error": true,
"code": "invalid_request",
"message": "width is required.",
"param": "width",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "unauthorized",
"message": "Missing or invalid secret key. Send it as \"Authorization: Bearer sk_…\".",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "account_suspended",
"message": "This account is suspended. Settle the outstanding invoice in the Print.App dashboard.",
"requestId": "FZn-uhymjoEEJow="
}{
"error": true,
"code": "unsupported_media_type",
"message": "Send the request body as JSON, with \"Content-Type: application/json\".",
"requestId": "FZn-uhymjoEEJow="
}{
"message": "Too Many Requests"
}{
"error": true,
"code": "internal_error",
"message": "Something went wrong on our side. Retry the request; if it keeps failing, contact support with the requestId.",
"requestId": "FZn-uhymjoEEJow="
}Authorizations
A secret key from Print.App admin → Domains → Secret keys, sent as Authorization: Bearer sk_…. Server-side only.
Headers
Makes a retry safe: a second request with the same key and the same body gets the first response back (for 24 hours) instead of creating the resource again. Use a new key, such as a UUID, for every new request.
1 - 255Body
A new connector. Attach it to designs with POST /v2/designs/{id}/connectors.
The connector's name, shown in the dashboard.
1 - 200What the connector does:
pageIndex: Shows only the pages a choice maps to (to: page numbers such as1or1,2). The other pages are set aside: not shown, printed or saved.designSelector: Opens another design for each choice (to: a design id). Every design it can open must be assigned to the product and carry this connector too.pageClone: Repeats one page (page, default the first) as many times as the number chosen, when the editor opens.pageCount: Sends the number of pages to the field as the customer adds or removes pages (editor to product page).printedPageCount: Sends the number of pages (sides) that carry a print to the field, so the price can follow (editor to product page). The field takes a number.pageWidth: Sets the page width from a number field, in the design's unit (a Print Options size field carries its own). With twoWay, a resize in the editor is sent back.pageHeight: Sets the page height from a number field, in the design's unit (a Print Options size field carries its own). With twoWay, a resize in the editor is sent back.pageOrientation: Turns the pages (orpagealone) landscape or portrait, from the choice's wording or its mapping (to: landscape or portrait).textValue: Puts what the customer types into the text item titledtext. With twoWay, edits in the editor are sent back.underlayColor: Sets the product colour of an underlay design (a T-shirt, a mug):tois a hex colour, or leave the choice unmapped when it is the colour's name in the design.event: Fires an editor event (event.name, withevent.data) when the field changes. For integrations that listen to editor events.
pageIndex, designSelector, pageClone, pageCount, printedPageCount, pageWidth, pageHeight, pageOrientation, textValue, underlayColor, event Where on the product page the connector reads (and, for two-way modes, writes): a CSS selector for a control of the store's theme, or a Print Options field by role (preferred: one connector then works on every product) or by field id. GET /v2/product-page lists both for a page.
Show child attributes
Show child attributes
What each choice on the product page becomes in the editor. from is the choice as the page sends it: its value, or for dropdowns and Print Options choices also the text the customer sees (case and surrounding spaces ignored). Radio choices send their value only.
200Show child attributes
Show child attributes
For pageWidth, pageHeight, textValue and underlayColor: also send changes made in the editor back to the product page.
For pageClone, pageWidth, pageHeight and pageOrientation: the page (from 1) it acts on. Default: the first page (pageOrientation: every page).
1 <= x <= 500For textValue: the title of the text item it fills.
1 - 200For event: the editor event to fire.
Show child attributes
Show child attributes
Response
Created.
Links an option on a store's product page to the editor: the choice a customer makes shows the right pages, opens the right design, sets the size or the colour.
"connector"null for a connector tried in a test without saving it.
One of: pageIndex, designSelector, pageClone, pageCount, printedPageCount, pageWidth, pageHeight, pageOrientation, textValue, underlayColor, event.
Show child attributes
Show child attributes
The source as the editor stores it: the selector, or po:role/<role>, po:field/<id>.
Show child attributes
Show child attributes
Show child attributes
Show child attributes