Docs
Model Context Protocol
43 tools

MCP server: let AI agents build your store

Connect your own AI (Claude Code, Claude Desktop, Codex, Cursor or any MCP client) to Printgram. It can create communities and pages, moderate members, upload artwork, check the placement on a rendered shirt and publish products (Printgram staff verify each design before it goes on sale), or keep designs just for you in your wardrobe, with exactly your permissions.

Printgram does not generate images. Your AI makes the artwork with its own image tool (or fetches an image by URL), uploads it, positions it (zoom, x / y, rotation, front / back, shirt colour) and publishes it to a community page.

Server endpoint
Streamable HTTP
stateless JSON
Sign in with Printgram
or API token
POST https://printgram.in/api/mcp
On this page
Start here · no token

Connect your AI

Add Printgram to Claude, ChatGPT or Codex and sign in with your Printgram account, then just ask: a design for your wardrobe, a page for your store, a new product. Your AI saves drafts and asks you before publishing anything.

URLhttps://printgram.in/api/mcp

Claude (claude.ai)

  1. 1
    Open Settings → Connectors and click Add custom connector.
  2. 2
    Name it Printgram, paste https://printgram.in/api/mcp and click Add.
  3. 3
    Click Connect. Printgram opens: sign in, choose what Claude may use (My stores and my wardrobe or Only My Wardrobe) and click Allow.
    Printgram asks: Allow Claude to use your Printgram account? with the choices My stores and my wardrobe or Only My Wardrobe, and Allow / Cancel buttons.
  4. 4
    In a chat, open the tools menu and switch Printgram on. Then just ask.

ChatGPT

  1. 1
    Open Settings → Apps & Connectors → Advanced settings and turn on Developer mode (ChatGPT Plus, Pro, Business, Enterprise or Edu).
  2. 2
    Click Create: name Printgram, MCP server URL https://printgram.in/api/mcp, authentication OAuth. Tick that you trust it and click Create.
  3. 3
    Printgram opens: sign in, choose what ChatGPT may use (My stores and my wardrobe or Only My Wardrobe) and click Allow.
    Printgram asks: Allow ChatGPT to use your Printgram account? with the choices My stores and my wardrobe or Only My Wardrobe, and Allow / Cancel buttons.
  4. 4
    In a new chat, click + → Developer mode and pick Printgram. Then just ask.

No token needed. The app acts as you and can never place orders, change your profile or create tokens. See or disconnect your apps any time in Settings → Developer.

Pictures your AI makes

  • ChatGPT uploads them to Printgram by itself.
  • Claude uploads them by itself once you allow Printgram: Settings → Capabilities → Code execution and file creation, turn on network egress and add printgram.in under Additional allowed domains (on Team and Enterprise plans an owner sets this). Until then it gives you a one-time upload link: open it, drop the image (download it from the chat first) and tell Claude it is uploaded.
  • Codex uploads files from your computer itself (it may ask you to allow the upload).
A one-time Printgram upload page: drop the image your AI app made, or click to browse.

Try it

  • “Use Printgram: who am I?”
  • “Make an original pirate-style compass design and add it to my wardrobe on a black tee.”
  • “In my community, create a page Summer Drop and add a draft T-shirt with a retro sunset design. Show me before publishing.”
Overview

How it works

  1. 1Connect & set up
    whoami · create_community

    Check who the token acts as, then pick a community you own (list_my_communities) or create one.

  2. 2Add a page
    create_page

    Optional collection or drop, saved as a draft. Once published it is public, members-only or staff-only.

  3. 3Generate the artwork
    your AI

    Your AI makes it with its own image tool, or finds an image URL. Transparent PNG, 2000 px or more.

  4. 4Upload it
    upload_image_* · curl

    Upload from a URL, as base64, or with curl to /api/v1/uploads. You get back a hosted url.

  5. 5Preview & iterate
    preview_mockup

    The AI gets a rendered shirt image and adjusts x / y / scale / rotation until it looks right.

  6. 6Review & publish
    create_product · publish_drafts

    Saved as a draft at /p/<slug> with server-rendered mockups and fixed pricing. Your AI opens it in your browser and publishes only after you say yes; it goes on sale once Printgram staff verify it.

Server overview
ItemDetails
EndpointPOST https://printgram.in/api/mcp
TransportMCP Streamable HTTP in stateless JSON mode. Every POST stands alone: there is no session id and no SSE stream, and GET / DELETE return 405.
AuthAuthorization: Bearer <token> on every request: a personal access token, or an access token from Sign in with Printgram (OAuth 2.1 with PKCE and dynamic client registration, which Claude and ChatGPT use in the browser). Cookies are never read on this endpoint.
IdentityA new server is created for each request and bound to the token's user. Tools call the same services as the website, and those services re-check roles and bans in the database.
LimitsJSON body up to 20.25 MB (a 15 MB image in base64 plus the envelope). maxDuration is 60 s, which only hosts that honour it (e.g. Vercel) enforce; a self-hosted next start does not cap requests. Image downloads time out after 20 s and image processing after 20 s.

Everything your AI creates starts as a draft. Nothing goes live until you have seen it and said yes, and a design goes on sale only once Printgram staff have verified it.

DraftPublished
Page (create_page)Only the owner, admins and moderators (signed in) see it and the designs on it; everyone else gets "not found".Visible according to its visibility: public, members or private.
Product (create_product)Only the owner, the community staff and its creator see it at /p/<slug>; nobody can buy it.Waits for Printgram staff to verify it (pending_review), then on sale for everyone who can see it. A product on a draft page stays hidden until the page is published.
  1. create_page and create_product return a review block: the reviewUrl, who can see the draft, how to show it, and the exact publish_drafts call.
  2. The agent shows you the drafts: it opens each reviewUrl in your browser (open on macOS, start on Windows, xdg-open on Linux) or gives you the links, shares the mockups, and asks: publish, change something, or keep it as a draft?
  3. Changes go through update_product_design, update_product or update_page; drafts stay drafts.
  4. Only after you say yes, publish_drafts { community, page?, products?, user_confirmed: true } publishes the page and its products in one call. The page goes live at once; the designs go on sale once Printgram staff verify them (below). update_page and update_product refuse status: "published".
What the agent gets back
// create_product result (excerpt): the draft and how to show it
{
  "product": {
    "slug": "neon-ronin-tee",
    "status": "draft",
    "url": "https://printgram.in/p/neon-ronin-tee"
  },
  "review": {
    "status": "draft",
    "reviewUrl": "https://printgram.in/p/neon-ronin-tee",
    "visibleTo": "Only @you and this community's staff, signed in on the website. Everyone else gets \"not found\" until it is published.",
    "showTheUser": "Before publishing, show the user: open https://printgram.in/p/neon-ronin-tee in their browser (macOS: open \"<url>\"; Windows: start \"\" \"<url>\"; Linux: xdg-open \"<url>\") or give them the link, and share the preview_mockup image. Then ask whether to publish it, change something, or keep it as a draft.",
    "toChangeIt": "update_product_design (artwork, placement, colours) or update_product (title, description, page). It stays a draft.",
    "publishAfterApproval": {
      "tool": "publish_drafts",
      "arguments": {
        "community": "neon-ronin-club",
        "page": "launch-drop",
        "products": [
          "neon-ronin-tee"
        ],
        "user_confirmed": true
      }
    }
  }
}

You can also publish on the website: a draft page shows its staff a Publish page + N designs button, a draft product a Publish button, the page settings have a Status field, and “Save as Draft” sits next to “Launch Page Drop” when you create a page. Pass status: "published" when creating only if you want to skip your own review (Printgram staff still verify the design).

Design reviews: Printgram staff verify every design

Printgram staff check every community design before it goes on sale. Designs that use other people's logos, brands, characters or artwork without a licence are not verified. Publishing a design staff have not verified yet (publish_drafts, create_product with status: "published", or a Publish button) sends it to review instead of putting it on sale:

StatusWho sees itWhat happens next
draftThe owner, the community staff and its creator.You publish it: it waits for review, or goes straight on sale if staff already verified this artwork.
pending_reviewThe same people; nobody can buy it.Staff verify it in the staff app and it goes on sale by itself (published), or mark it not verified (rejected).
publishedEveryone its community, page and visibility allow.New artwork sends it back to pending_review until staff verify it again; placement, colour, garment, title and page changes do not. Staff can also mark it not verified later.
rejectedThe owner, the community staff and its creator.Its reviewNote says why. Fix it and publish it again (publish_drafts or the Publish button): it goes back to review.
  • publish_drafts returns the designs it sent to review in submittedForReview and the ones already waiting in alreadyWaitingForReview. Pages are not reviewed.
  • Product results (create_product, get_product, list_products, …) carry status and verified, and for a rejected design the reviewNote (to the people who can edit it). On the website, designs under review are marked In review or Not verified on product cards, and the product page says why a design was not verified.
  • Platform admins' own designs count as verified and go on sale at once.
  • My Wardrobe designs are personal and never reviewed.
Just for you

My Wardrobe

My Wardrobe (/wardrobe, signed in) holds designs just for you: only you can see and order them, they are never listed, never reviewed and never sold to anyone else, and they need no community. Keep any artwork there for your own shirts, up to 100 designs; the studio's Buy for Myself button saves there too. Paste the wardrobe prompt into your AI, or ask a connected AI for a design just for you.

My Wardrobe tools
ToolWhat it does
list_wardrobeThe designs already in your wardrobe, with their artwork and placement.
add_to_wardrobeSaves a design (the same artwork and placement input as create_product) and returns its url (/wardrobe/<id>), where you pick a colour and size and order it.
update_wardrobe_itemChanges the title, description, colours, garment, artwork or placement. Preview changes with preview_mockup and the design's id as product.
remove_from_wardrobeRemoves a design. Orders you already placed are printed as usual.

Other accounts get NOT_FOUND for your wardrobe designs (Printgram staff can open them to print your orders). They never appear in community listings, list_products or get_product: your AI manages them with the wardrobe tools.

Claude, ChatGPT and Codex do not need this: they sign in (see Connect your AI). An API token is for things that can't sign in through a browser: scripts and automation, older MCP clients and the upload API (POST /api/v1/uploads).

Sign-in not working in your app, or connecting a script?

Each POST is independent, so you can call tools/list or tools/call without an initialize first. Responses are plain JSON. Set PRINTGRAM_TOKEN in your shell first.

initialize: server info and instructions
curl -sS -X POST "https://printgram.in/api/mcp" \
  -H "Authorization: Bearer $PRINTGRAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'
tools/list: every tool with its JSON Schema
curl -sS -X POST "https://printgram.in/api/mcp" \
  -H "Authorization: Bearer $PRINTGRAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  | jq '.result.tools[].name'
tools/call: results are JSON in content[0].text
curl -sS -X POST "https://printgram.in/api/mcp" \
  -H "Authorization: Bearer $PRINTGRAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"whoami","arguments":{}}}' \
  | jq -r '.result.content[0].text | fromjson'
preview_mockup: save the rendered PNG
# replace https://example.com/design.png with the url returned by an upload
curl -sS -X POST "https://printgram.in/api/mcp" \
  -H "Authorization: Bearer $PRINTGRAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"preview_mockup","arguments":{"artwork_url":"https://example.com/design.png","preset":"front-center","scale":0.9,"shirt_color":"#111111"}}}' \
  | jq -r '.result.content[] | select(.type=="image") | .data' | base64 --decode > preview.png
Result shapes
// success: the tool's JSON payload is a string in content[0].text
{ "jsonrpc": "2.0", "id": 3, "result": { "content": [ { "type": "text", "text": "{ \"user\": { … }, \"siteUrl\": \"…\" }" } ] } }

// tool failure (HTTP 200): permission, validation, not found …
{ "jsonrpc": "2.0", "id": 5, "result": { "isError": true, "content": [ { "type": "text",
  "text": "{ \"success\": false, \"code\": \"FORBIDDEN\", \"error\": \"Only the community owner can add products to this community.\" }" } ] } }

// missing / invalid token: HTTP 401 + WWW-Authenticate: Bearer realm="printgram"
{ "jsonrpc": "2.0", "error": { "code": -32001, "message": "Invalid, expired or revoked API token." }, "id": null }

Every upload is decoded and re-encoded with sharp. Metadata is removed, EXIF orientation is applied, only the first frame of an animation is kept, so the stored file is never your original bytes. SVG is not accepted: export vector artwork as a PNG first. The MCP upload tools return { success, url, width, height, mimeType, bytes, next } (plus a note in storage-less demo mode); POST /api/v1/uploads returns the first six fields. Use the url as artwork_url (or avatar_url / banner_url / cover_image_url).

purposeStored asLongest sideUsed for
artworkPNGup to 4096 pxshirt designs (default)
avatarWebPup to 1024 pxcommunity logo
bannerWebPup to 2560 pxcommunity banner
coverWebPup to 2560 pxpage cover

Limits: 15 MB, at most 8000 px per side and 50 megapixels. Formats: PNG, JPEG, WebP, GIF (AVIF and TIFF are decoded too). SVG is refused with UNSUPPORTED_MEDIA. Images are never upscaled.

Uploads are limited to 120 per user per hour (one per image the server fetches from an external URL or decodes and stores), shared by the MCP upload tools, POST /api/v1/uploads, the website's own image uploads (POST /api/upload: studio artwork, avatars, banners, covers) and the image work of other tools. Each server instance keeps its own window.

  • Charged only after validation. A request is charged once it has validated (auth, body, purpose, base64 and URL syntax), right before the image is fetched, decoded and stored; invalid requests cost nothing, and a caller already out of quota is refused before the body is read. An image that turns out not to be an image after it was fetched or decoded still counts.
  • Other tools count too. Image URLs and data URLs passed to other tools (avatar_url, banner_url, cover_image_url, front.artwork_url, …) cost one when they are re-hosted, and artwork on an external URL costs one every time it is downloaded to render a mockup (preview_mockup, product mockups). URLs returned by an upload tool, and data URLs in previews, are free: upload once, then preview the returned url.
  • Renders have their own, higher quota of 300 per hour (preview_mockup and product mockups), so iterating on a preview of uploaded artwork never eats the upload quota.

Each user can have at most 2 image calls (uploads, previews, product renders) running at once; extra parallel calls fail fast with RATE_LIMITED. Send one JSON-RPC message per request: batches are rejected.

upload_image_from_url

Imports a public image, such as your image generator's output URL. Those URLs often expire, so import them right away. The fetch is SSRF-safe:

  • https only. http:// works only when the server sets ALLOW_HTTP_IMAGE_FETCH=true (development). URLs with credentials are refused, and the port must be the default or 80, 443, 8080, 8443.
  • localhost, *.local, *.internal and private or internal addresses are refused: RFC 1918, loopback, link-local (including cloud metadata 169.254.169.254), CGNAT, multicast and reserved ranges, IPv6 ULA and link-local, and IPv4-mapped, NAT64 and 6to4 forms. Every DNS answer is checked when the connection is made, and the peer address is checked again.
  • At most 3 redirects, each re-validated; 20 s timeout; the download is capped at 15 MB while streaming.
  • A small data:image/…;base64,… URL (up to 2,048 characters, the url limit) is decoded instead of fetched; use upload_image_base64 for anything larger.

upload_image_base64

data is raw base64 (standard or URL-safe) or a data:image/…;base64,… URL, up to 15 MB decoded (about 21 million characters), for base64 the agent already holds as data (a tool output it can pass on unchanged). A model must not retype an image it generated in a chat as base64: long strings get corrupted, so use an upload link. It is decoded as base64, so an https:// URL is rejected with VALIDATION: use upload_image_from_url for URLs. Base64 makes tool calls large, so prefer a URL or curl for big files.

upload_image_file (ChatGPT)

The tool asks ChatGPT for an image from the conversation through openai/fileParams, the way ChatGPT's own Google Drive app receives files: ChatGPT passes { download_url, file_id } and Printgram downloads the image itself. If no download link arrives, the tool says so and the AI uses an upload link instead.

For Claude or ChatGPT in the browser, which have neither a public URL for the image they made nor an API token. create_upload_link returns pageUrl (/upload/<token>: you drop the image there, no sign-in) and uploadUrl (/api/upload-links/<token>: a program, such as the AI's code sandbox, sends the image as multipart file or raw bytes). The token is the only credential: it works once, expires after an hour and can only add one image to your uploads; a failed upload leaves it usable. You can have 10 waiting links. The page shrinks images over 4 MB in your browser first. check_upload_link then answers waiting, uploaded (with the image url) or expired.

POST /api/v1/uploads (curl)

Accepts a bearer token or a browser session. get_upload_instructions gives your AI the multipart, raw-body and JSON-URL commands with the site URL filled in (plus the endpoint, auth header, limits and response shape); the other commands below are extra examples.

Multipart: field "file" + optional "purpose"
curl -sS -X POST "https://printgram.in/api/v1/uploads" -H "Authorization: Bearer $PRINTGRAM_TOKEN" \
  -F "file=@./design.png" -F "purpose=artwork"
Raw bytes: purpose / filename in the query string
curl -sS -X POST "https://printgram.in/api/v1/uploads?purpose=artwork&filename=design.png" -H "Authorization: Bearer $PRINTGRAM_TOKEN" \
  -H "Content-Type: image/png" --data-binary @./design.png
JSON with a URL (same SSRF rules)
curl -sS -X POST "https://printgram.in/api/v1/uploads" -H "Authorization: Bearer $PRINTGRAM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/design.png","purpose":"artwork"}'
JSON with base64: exactly one of "url" or "data"
printf '{"data":"%s","filename":"design.png"}' "$(base64 < design.png | tr -d '\n')" > body.json
curl -sS -X POST "https://printgram.in/api/v1/uploads" -H "Authorization: Bearer $PRINTGRAM_TOKEN" \
  -H "Content-Type: application/json" --data-binary @body.json
Fetch a generated image, then upload it
# download what your image generator produced, then upload the bytes
curl -sSL -o design.png "https://<your-image-generator>/output.png"
curl -sS -X POST "https://printgram.in/api/v1/uploads" -H "Authorization: Bearer $PRINTGRAM_TOKEN" -F "file=@./design.png"
Response
{ "success": true, "url": "https://…/public-product-previews/<userId>/artwork/<uuid>.png",
  "width": 2048, "height": 2048, "mimeType": "image/png", "bytes": 734211 }

Errors are { success: false, error, code }: 400 VALIDATION, 401 without a valid token, 413 PAYLOAD_TOO_LARGE, 415 UNSUPPORTED_MEDIA, or 429 over the upload limit (with a Retry-After header and code: "RATE_LIMITED").

Image fields on other tools (avatar_url, banner_url, cover_image_url, front.artwork_url, …) also accept a public https image or a data URL. When Supabase Storage is configured they are fetched with the same rules and re-hosted, so expiring generator links cannot break your products. In storage-less demo mode, https links are stored as they are (avatars and banners on hosts the site cannot render are converted to inline data URLs), so upload expiring links first.
Storage-less demo mode: when Supabase is not configured, uploads return inline data URLs: data:image/png;base64,… for artwork and data:image/webp;base64,… for avatars, banners and covers, with a note. Pass them to other tools exactly as they are. Other results show them as (inline … data URL …) to keep responses small.

Placement is described on an 800 × 800 px mockup. The web studio, the server-side renderer and the MCP tools all use the same numbers, so a design placed by your AI renders exactly like one placed by hand.

FieldRangeMeaning
x-260 … 260Artwork centre: centerX = 400 + x/100 × 160. Negative moves left, 0 is centred, and 10 units are 16 px.
y-260 … 260centerY = base + y/100 × 200, where base is 410 on the front and 380 on the back. Negative moves up towards the collar and positive moves down towards the hem.
scale0.3 … 1.5Longest side = 240 px × scale (72 px to 360 px), with the aspect ratio kept. 0.3-0.45 small logo, 0.6-0.9 standard chest/back print, 1.1-1.5 oversized.
rotation-180 … 180Degrees clockwise around the artwork centre. The studio offers ±45°, and previews warn beyond ±45°.
Front of the 800 by 800 pixel mockup with the print area, the area the artwork centre can reach and two presetsprint centre (400, 410)centre rangeprint area(0, 0)(800, 800)+x →+y ↓
torso (printable) where the centre can go (x -260…260, y -260…260) front-center (x 0, y -40, scale 0.85) front-left-chest (x 45, y -70, scale 0.4)
  • Front and back are independent layers, and a product needs artwork on at least one side. Every product gets both views in Black, White, Blue and Red. A side without artwork is shown as a blank shirt, and the primary colour's front view is the thumbnail.
  • Defaults: front { x: 0, y: -40, scale: 0.85, rotation: 0 }, back { x: 0, y: -15, scale: 0.9, rotation: 0 }. When a side with artwork gets no placement at all, the result's warnings says the defaults were used.
  • Print area: x 250-550, y 220-600 on the front and 190-570 on the back. Only the part of the artwork inside it is printed; the rest is cut off. preview_mockup warns when part of it is cut off and suggests a placement that prints all of it (fitsPrintAreaAt).
  • A side can be sent nested (front / back, or design.front / design.back in previews) or at the top level: artwork_url, preset, x, y, scale, rotation apply to side (default: the top-level preset's side, else front). In create_product and add_to_wardrobe, a nested side needs its own artwork_url. In edits and previews, null removes a side's artwork.
  • Precedence: explicit x / y / scale / rotation always override a preset, wherever each is given (the preset fills only the numbers you omit); a top-level value wins over the same field in the nested layer (the result notes it); anything given nowhere keeps the defaults or the saved design.
  • Unknown or misplaced argument names (e.g. position, or shirt_color inside front) are rejected with MCP error -32602 … Unrecognized key(s) naming the key, never silently ignored.

Placement presets

PresetSidexyscalerot.LabelCentre (px)Longest side
front-centerfront0-400.850Centre chest(400, 330)204 px
front-left-chestfront45-700.40Left chest logo, on the wearer's left(472, 270)96 px
front-fullfront051.10Full front print(400, 420)264 px
back-upperback0-470.750Upper back, below the collar(400, 286)180 px
back-fullback001.10Full back print(400, 380)264 px
back-lowerback0420.80Lower back(400, 464)192 px

Shirt colours

Only these colours are printed, so only they are accepted (as hex or name; default #111111). A product offers its design colour plus any extra_colors; buyers switch between them on the product page. On dark shirts (#111111, #2563EB, #DC2626) use light or high-contrast artwork; on light shirts avoid white artwork.

  • Black#111111 · dark
  • White#FFFFFF
  • Blue#2563EB · dark
  • Red#DC2626 · dark

Garments & fixed pricing

Prices are fixed by the platform per garment type, in Indian rupees (INR) inclusive of all taxes; you cannot set a custom price. The designer payout is what the creator earns per item sold.

garment_typeGarmentFabricRetail (INR)Designer payout
tshirtHeavyweight T-Shirt100% Ring-spun 240 GSM Combed Cotton₹400₹30
sweatshirtCrewneck Sweatshirt350 GSM Fleece Interior, Ribbed Cuffs₹1,199₹350
hoodiePremium Pullover Hoodie400 GSM Ultra-Heavy French Terry, Double Hood₹1,499₹450

Sizes: S, M, L, XL, 2XL, 3XL (default S, M, L, XL, 2XL, 3XL). create_product takes shirt_color plus up to 3 extra_colors swatches.

Artwork recommendations

  • PNG with a transparent background works best (JPEG/WebP/GIF are accepted, GIFs use the first frame). SVG is not accepted: export vector artwork as a PNG first.
  • At least 2000px on the longest side for print quality (4000px is ideal). Artwork is stored at up to 4096px. preview_mockup warns below 1500 px.
  • Max file size 15MB; 8000px per side, ~50 megapixels total.
  • Trim transparent padding around the artwork so scale behaves predictably.
  • Avoid thin hairlines and tiny text below ~0.5 scale — they will not print well.
  • Generate the image with your own image tool, then upload it: upload_image_from_url for a public URL, create_upload_link for an image made in a chat (never retype it as base64), or the curl upload.
  • Only upload artwork you own or have rights to. No third-party logos, trademarks or copyrighted characters.

preview_mockup renders the shirt the same way the store will, and nothing is saved. It returns an image block (PNG, 800 px by default, size 256-1024) and a text block whose text is a JSON string: the effective layer (x / y / scale / rotation, the applied preset and placementSource), the placement in 800 px coordinates, the bounds after rotation, the source artwork size, warnings (default placement used, top-level values that replaced nested ones, low resolution, sleeves, collar, hem, rotation, blank side) and a nextStep with the exact create_product arguments.

Give it either input shape, or both:

  • flat, the way agents naturally write it: { artwork_url, side?, preset?, x, y, scale, rotation, shirt_color, garment_type }. The fields apply to side; if it is omitted, the preset's side is used, else the front when a top-level artwork or number is given, else the side that has artwork;
  • nested: { design: { shirt_color, garment_type, front: { artwork_url, x, y, scale, rotation, preset? }, back? } }. Pass product to start from a saved design (a product, or one of your wardrobe designs).
Explicit x / y / scale / rotation always override a preset, wherever each is given; a top-level value wins over the same field in design.<side> (and warnings notes it). Unknown or misplaced argument names are rejected with an error that names them, so a typo can never silently fall back to the defaults. Artwork on an external URL is downloaded for each preview and also costs one upload; preview the url an upload returned instead.
  1. Call get_design_guidelines once.
  2. Call preview_mockup with one of the shapes above.
  3. Look at the image, adjust y / scale / x / rotation (or the preset or colour) and preview again until warnings is empty.
  4. Call create_product with the same values. Use status: "draft" to review it on the site first. For a design just for you, call add_to_wardrobe instead.
preview_mockup arguments (flat)
{
  "artwork_url": "https://example.com/design.png",
  "side": "front",
  "x": 0, "y": -10, "scale": 0.9, "rotation": 0,
  "shirt_color": "#111111"
}
preview_mockup arguments (nested design, same result)
{
  "design": {
    "shirt_color": "#111111",
    "garment_type": "tshirt",
    "front": { "artwork_url": "https://example.com/design.png", "x": 0, "y": -10, "scale": 0.9, "rotation": 0 }
  },
  "side": "front"
}
preview_mockup result
// result.content
[
  { "type": "image", "mimeType": "image/png", "data": "<base64 PNG>" },
  { "type": "text", "text": "{\n  \"side\": \"front\",\n  \"size\": 800,\n  \"shirtColor\": \"#111111\", …" }
]

// content[1].text is a JSON STRING. Parse it first (jq: .text | fromjson). Parsed:
{
  "side": "front",
  "size": 800,
  "shirtColor": "#111111",
  "garmentType": "tshirt",
  "layer": {
    "artworkUrl": "https://example.com/design.png",
    "x": 0,
    "y": -10,
    "scale": 0.9,
    "rotation": 0,
    "preset": null,
    "placementSource": "explicit x, y, scale, rotation"
  },
  "placement": {
    "centerX": 400,
    "centerY": 390,
    "width": 216,
    "height": 216,
    "rotation": 0
  },
  "bounds": {
    "left": 292,
    "top": 282,
    "right": 508,
    "bottom": 498
  },
  "sourceArtwork": {
    "width": 2048,
    "height": 2048,
    "format": "png"
  },
  "warnings": [],
  "nextStep": "When it looks right, call create_product with front: { artwork_url: <same URL>, x: 0, y: -10, scale: 0.9, rotation: 0 } and shirt_color \"#111111\" (or the same values at the top level: artwork_url, side: \"front\", x, y, scale, rotation)."
}
create_product arguments (the front preset fills y / rotation; the explicit scale overrides it)
{
  "community": "neon-ronin-club",
  "page": "launch-drop",
  "title": "Neon Ronin Tee",
  "garment_type": "tshirt",
  "shirt_color": "#111111",
  "extra_colors": ["#FFFFFF","#2563EB"],
  "front": { "artwork_url": "https://example.com/design.png", "preset": "front-center", "scale": 0.9 },
  "back":  { "artwork_url": "https://example.com/back.png", "x": 0, "y": -20, "scale": 0.75, "rotation": 0 },
  "status": "draft"
}
43 tools · read from the live server

Tool reference

This reference is generated from the server's own tools/list response, so names, parameters, bounds and defaults always match what your AI receives. "Who can call it" is enforced by the services; platform admins also pass community staff checks. The blue notes describe behaviour the tool descriptions don't spell out.

Account (2)

Confirm the connection and see where you can act.

whoamiWho am I

read-only

Returns the Printgram account this API token acts as (id, username, display name, platform role) and the site URL. Every other tool runs with exactly these permissions. Call it first to confirm the connection works.

Who can call it: Any valid token

Parameters

No parameters. Call it with {}.

list_my_communitiesList my communities

read-only

Lists the communities you are an ACTIVE member of, with your role in each (owner > admin > moderator > member), member and product counts and the storefront URL. Only communities where your role is "owner" allow create_product; owner/admin can manage pages, settings and invite links; moderator+ can moderate members and review join requests.

Who can call it: Any valid token

Parameters

No parameters. Call it with {}.

Communities (3)

Create storefronts and change their settings.

create_communityCreate community

writes

Creates a new community storefront owned by you (you become its owner — the only role that can add products). Pick the slug carefully: the storefront lives at /c/<slug>. Images: pass URLs returned by upload_image_from_url / upload_image_base64 (or any public https image URL — it is re-hosted). Next step: create_page, then add designs with create_product.

Who can call it: Any valid token (you become the owner)

  • Community names are unique, ignoring case and extra spaces ("Night Owls" and "night owls" count as the same name): a name another community already uses fails with CONFLICT.
  • Slugs follow the website rule exactly: lowercase letters, digits and hyphens, 1 or 3-48 characters, no leading or trailing hyphen, not UUID-shaped. Reserved community slugs are refused with VALIDATION and the website's message: official, new, admin, api, settings, create, create-community, create-product, explore, communities, search, login, logout, signup, auth, profile, account, help, support, about, terms, privacy, mcp, printgram, www. The default slug comes from the name, so pass an explicit slug when the name maps to a reserved or too-short one.
  • view_access: "link" means anyone with the storefront URL can view it, but the community, its pages and its products are never listed (search, explore, the communities hub, the sitemap, featured pages), like visibility: "unlisted". members restricts viewing to active members; a public members-only community is still listed (locked).
Parameters
  • namestring
    required
    2 to 60 chars

    Display name, 2-60 characters.

  • slugstringoptional
    1 to 48 charspattern ^[a-z0-9](?:[a-z0-9-]{1,46}[a-z0-9])?$

    URL handle: lowercase letters, digits and hyphens, 1 or 3-48 characters, no leading/trailing hyphen (reserved words such as "admin" are refused). Defaults to a slug of the name.

  • taglinestringoptional
    max 120 chars

    One-line pitch shown under the name (max 120 chars).

  • descriptionstringoptional
    max 2,000 chars

    About text (max 2000 chars).

  • visibilitystringoptionaldefault "public"
    publicunlistedprivate

    public = listed in explore/search (unless view_access is "link"), anyone can view and join; unlisted = not listed, anyone with the URL can view/join; private = hidden everywhere, content for members only, joinable only via invite link, direct invitation or an approved join request.

  • view_accessstringoptionaldefault "everyone"
    everyonelinkmembers

    Who can browse the storefront of a public/unlisted community: everyone = anyone (a public community is listed in explore/search); link = anyone who has the storefront URL, but the community and its pages and products are never listed anywhere; members = active members only. Private communities are always members-only.

  • purchase_accessstringoptionaldefault "everyone"
    everyonelink_usersmembers

    Who can buy: everyone who can view, link_users (members or people with a storefront link), or members only.

  • avatar_urlstringoptional
    https:// URL or data:image URL (image ≤ 15 MB)

    Square logo image (https URL or data URL).

  • banner_urlstringoptional
    https:// URL or data:image URL (image ≤ 15 MB)

    Wide banner image, ideally 3:1 or wider.

get_communityGet community

read-only

Details of one community as YOU see it: settings, counts, your role, what you are allowed to do there (permissions), and the pages visible to you (members-only pages are flagged locked if you are not a member; private staff-only pages are hidden from non-staff). Fails for private communities you are not a member of.

Who can call it: Anyone who can view the community (private: active members)

  • A private or members-only community you are not a member of returns FORBIDDEN (BANNED if you are banned), not NOT_FOUND.
Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

update_communityUpdate community settings

writes
idempotent

Updates community settings. Owner or admin: name, tagline, description, avatar_url, banner_url, accent colour. OWNER ONLY: visibility, view_access, purchase_access and slug (changing the slug changes every storefront URL). Making a community private hides it from all listings and turns its public pages into members-only pages. Only pass the fields you want to change.

Who can call it: Owner or admin; visibility, access and slug: owner only

  • Community names are unique, ignoring case and extra spaces ("Night Owls" and "night owls" count as the same name): a name another community already uses fails with CONFLICT.
  • Slugs follow the website rule exactly: lowercase letters, digits and hyphens, 1 or 3-48 characters, no leading or trailing hyphen, not UUID-shaped. Reserved community slugs are refused with VALIDATION and the website's message: official, new, admin, api, settings, create, create-community, create-product, explore, communities, search, login, logout, signup, auth, profile, account, help, support, about, terms, privacy, mcp, printgram, www. The default slug comes from the name, so pass an explicit slug when the name maps to a reserved or too-short one.
  • view_access: "link" means anyone with the storefront URL can view it, but the community, its pages and its products are never listed (search, explore, the communities hub, the sitemap, featured pages), like visibility: "unlisted". members restricts viewing to active members; a public members-only community is still listed (locked).
Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • namestringoptional
    2 to 60 chars
  • taglinestring | nulloptional
    max 120 chars

    null clears it.

  • descriptionstring | nulloptional
    max 2,000 chars

    null clears it.

  • avatar_urlstring | nulloptional
    https:// URL or data:image URL (image ≤ 15 MB)

    New logo (https or data URL); null removes it.

  • banner_urlstring | nulloptional
    https:// URL or data:image URL (image ≤ 15 MB)

    New banner (https or data URL); null removes it.

  • accent_colorstringoptional
    pattern ^#[0-9a-fA-F]{6}$

    Theme accent colour, e.g. #8B5CF6.

  • visibilitystringoptional
    publicunlistedprivate

    public = listed in explore/search (unless view_access is "link"), anyone can view and join; unlisted = not listed, anyone with the URL can view/join; private = hidden everywhere, content for members only, joinable only via invite link, direct invitation or an approved join request.

  • view_accessstringoptional
    everyonelinkmembers

    Who can browse the storefront of a public/unlisted community: everyone = anyone (a public community is listed in explore/search); link = anyone who has the storefront URL, but the community and its pages and products are never listed anywhere; members = active members only. Private communities are always members-only.

  • purchase_accessstringoptional
    everyonelink_usersmembers

    Who can buy: everyone who can view, link_users (members or people with a storefront link), or members only.

  • slugstringoptional
    1 to 48 charspattern ^[a-z0-9](?:[a-z0-9-]{1,46}[a-z0-9])?$

    New URL handle (owner only), same rules as create_community.

Members & moderation (5)

Removals and bans (moderator and above) and role changes (owner and admins).

list_membersList community members

read-only

Lists members of a community you can view: username, role (owner/admin/moderator/member), status and join date. Banned members are only included for moderators and above (set include_banned). Paginate with offset/limit.

Who can call it: Anyone who can view the community; banned rows: moderator+

Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • include_bannedbooleanoptionaldefault false

    Also return banned members (moderator+ only).

  • rolestringoptional
    owneradminmoderatormember

    Only return members with this role.

  • limitintegeroptionaldefault 100
    1 to 500
  • offsetintegeroptionaldefault 0
    0 to 100,000

set_member_roleChange member role

writes
idempotent

Changes an active member's role to admin, moderator or member. You can only manage members ranked below you and only grant roles below your own; only the owner can promote to admin. The owner role cannot be assigned or changed.

Who can call it: Owner or admin, on members ranked below you; promote to admin: owner (or platform admin) only

  • Moderators cannot change roles at all (FORBIDDEN, "Only the community owner or an admin can change member roles."), not even to the role a member already has; they remove, ban and unban members instead.
Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • userstring
    required
    1 to 64 chars

    Target user: username (e.g. "alice" or "@alice") or user id.

  • rolestring
    required
    adminmoderatormember

    The new role.

remove_memberRemove member

writes
destructive
idempotent

Removes a member from the community (they may rejoin later if the community allows it — use ban_member to keep them out). Moderator+ only, and only for members ranked below you. The owner can never be removed.

Who can call it: Moderator+ on lower ranks (never the owner)

Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • userstring
    required
    1 to 64 chars

    Target user: username (e.g. "alice" or "@alice") or user id.

ban_memberBan member

writes
destructive
idempotent

Bans a user from the community: they lose access to members-only content, cannot purchase, and cannot rejoin by any route (join, invite link, invitation or request) until unbanned. Works on non-members too (pre-emptive ban). Moderator+ only, for users ranked below you; the owner can never be banned.

Who can call it: Moderator+ on lower ranks (never the owner)

Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • userstring
    required
    1 to 64 chars

    Target user: username (e.g. "alice" or "@alice") or user id.

  • reasonstringoptional
    max 500 chars

    Optional reason, visible to community staff.

unban_memberUnban member

writes
idempotent

Lifts a ban: restores a former member (as an active member with the "member" role); a pre-emptive ban of a non-member is simply lifted. Moderator+ only, and you must rank at least as high as whoever issued the ban.

Who can call it: Moderator+

Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • userstring
    required
    1 to 64 chars

    Target user: username (e.g. "alice" or "@alice") or user id.

Invites & join requests (6)

Invite links, direct invitations and join-request review, the ways into a private community.

list_join_requestsList join requests

read-only

Lists requests from users who asked to join a (private) community, with their profile and message. Moderator+ only. Approve or reject them with review_join_request.

Who can call it: Moderator+

Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • statusstringoptionaldefault "pending"
    pendingapprovedrejected

review_join_requestApprove or reject a join request

writes
idempotent

Approves (the user becomes an active member) or rejects a pending join request. Moderator+ only. Approval fails if the user has been banned meanwhile.

Who can call it: Moderator+

Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • request_idstring
    required
    1 to 64 chars

    Request id from list_join_requests.

  • decisionstring
    required
    approvereject

invite_userInvite a user

writes

Sends a direct invitation to a specific user (by username). They see it on their profile and can accept (joining immediately, even for private communities) or decline. Moderator+ only; fails if they are already a member, banned, or already have a pending invitation.

Who can call it: Moderator+

  • Inviting a banned user fails with CONFLICT ("Unban them before inviting them"), not BANNED.
Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • usernamestring
    required
    1 to 64 chars

    Username of the person to invite (with or without @).

  • messagestringoptional
    max 500 chars

    Optional personal note (max 500 chars).

Pages (4)

Collections / drops inside a community, each with its own visibility.

create_pageCreate community page

writes

Creates a page (a collection / drop) inside a community — owner or admin only. It starts as a DRAFT that only the community staff can see: show the user its reviewUrl, and publish it with publish_drafts once they approve. Products can then be placed on it with create_product(page=...). Visibility (who sees it once published): public, members (members-only) or private (staff-only). In a private community only members/private are allowed. The page lives at /c/<community>/<page-slug>.

Who can call it: Owner or admin

  • Saved as a draft by default (status: "draft"): only the owner and the community staff can see it, signed in; everyone else gets "not found". The result has a review block: the reviewUrl to show the user, who can see the draft, and the exact publish_drafts call to make once the user approves. Pass status: "published" only when the user explicitly asked to publish without reviewing.
  • Products on a draft page stay hidden until the page is published, even published ones.
  • Page slugs follow the website rule exactly: lowercase letters, digits and hyphens, 1-80 characters, not UUID-shaped. Reserved page slugs are refused with VALIDATION and the website's message: builder, join, members, new-page, settings, invites, invitations, requests, products, pages, edit, admin, api. The default slug comes from the title (a page titled "Members" fails), so pass an explicit slug such as members-drop.
Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • titlestring
    required
    1 to 80 chars

    Page title, e.g. "Summer Drop 2026".

  • slugstringoptional
    1 to 80 charspattern ^[a-z0-9](?:[a-z0-9-]{0,78}[a-z0-9])?$

    URL slug: lowercase letters, digits and hyphens, 1-80 characters (reserved words such as "members" are refused). Defaults to a slug of the title.

  • descriptionstringoptional
    max 1,000 chars
  • cover_image_urlstringoptional
    https:// URL or data:image URL (image ≤ 15 MB)

    Optional cover image (https or data URL, re-hosted).

  • visibilitystringoptional
    publicmembersprivate

    Defaults to public (members for private communities).

  • statusstringoptionaldefault "draft"
    draftpublished

    Defaults to draft (the user reviews it first). Use "published" only when the user explicitly asked to publish without reviewing.

update_pageUpdate community page

writes
idempotent

Edits a page (owner or admin only): title, slug, description, cover image, visibility or display order. Only pass fields you want to change; a draft stays a draft. Visibility is validated against the community (no public pages in private communities). status "draft" takes a published page back to staff-only; publishing is done with publish_drafts after the user approved the draft.

Who can call it: Owner or admin

  • status: "draft" takes a published page back to staff-only. status: "published" is refused with VALIDATION: publish with publish_drafts, after the user approved the draft.
  • Page slugs follow the website rule exactly: lowercase letters, digits and hyphens, 1-80 characters, not UUID-shaped. Reserved page slugs are refused with VALIDATION and the website's message: builder, join, members, new-page, settings, invites, invitations, requests, products, pages, edit, admin, api. The default slug comes from the title (a page titled "Members" fails), so pass an explicit slug such as members-drop.
Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • pagestring
    required
    1 to 120 chars

    Page slug (as in /c/<community>/<page-slug>) or page id.

  • titlestringoptional
    1 to 80 chars
  • slugstringoptional
    1 to 80 charspattern ^[a-z0-9](?:[a-z0-9-]{0,78}[a-z0-9])?$

    New URL slug, same rules as create_page.

  • descriptionstring | nulloptional
    max 1,000 chars

    null clears it.

  • cover_image_urlstring | nulloptional
    https:// URL or data:image URL (image ≤ 15 MB)

    New cover (https or data URL); null removes it.

  • visibilitystringoptional
    publicmembersprivate

    public = anyone who can view the community; members = active members only (listed with a lock for others); private = staff only (owner/admin/moderator), hidden from everyone else. A private community cannot have public pages.

  • statusstringoptional
    draftpublished

    "draft" unpublishes the page (staff-only again). To publish, use publish_drafts.

  • display_orderintegeroptional
    0 to 1,000

    Lower numbers are listed first.

delete_pageDelete community page

writes
destructive
idempotent

Deletes a page (owner or admin only). Products on it are NOT deleted — they move to the storefront root (no page) without becoming more visible: products of a members-only page become members-only (visibility "private"), and published products of a staff-only page are unpublished (status "draft"). Use update_product(page=..., visibility=..., status=...) afterwards to re-home or re-publish them.

Who can call it: Owner or admin

Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • pagestring
    required
    1 to 120 chars

    Page slug (as in /c/<community>/<page-slug>) or page id.

list_pagesList community pages

read-only

Lists the pages of a community that are visible to you, with visibility, status (draft / published) and URL. Members-only pages are flagged locked when you are not a member; staff-only and draft pages are only listed for staff. Also returns which visibilities new pages may use and whether you can manage pages.

Who can call it: Anyone who can view the community

Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

Posts (3)

The community feed: the owner's posts, which members like, comment on and reply to on the website.

create_postCreate community post

writes

Publishes a post to a community's feed — community OWNER only. A post is text with an optional image; members like it, comment and reply on the website. Posts appear newest first on the Posts tab of /c/<community>.

Who can call it: Community OWNER only

  • At most 20 posts per user per hour; beyond that the call fails with RATE_LIMITED. Likes, comments and replies happen on the website only: API tokens cannot be used for them (403 FORBIDDEN).
Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • bodystring
    required
    1 to 5,000 chars

    The post text. Line breaks are kept; more than one blank line in a row is collapsed.

  • image_urlstringoptional
    https:// URL or data:image URL (image ≤ 15 MB)

    Optional image (https or data URL, re-hosted).

list_postsList community posts

read-only

Lists a community's posts, newest first, with like and comment counts and a link to each post. Also says whether you can post there (only the owner can).

Who can call it: Anyone who can view the community

Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • limitintegeroptionaldefault 20
    1 to 50
  • offsetintegeroptionaldefault 0
    0 to 100,000

delete_postDelete community post

writes
destructive
idempotent

Deletes a post together with its comments, replies and likes — the community owner only (platform moderators too). This cannot be undone.

Who can call it: Community owner (or a platform moderator)

Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • poststring
    required
    1 to 64 chars

    Post id, as returned by create_post or list_posts.

upload_image_from_urlUpload image from URL

writes
fetches URLs

Imports an image from a public https URL (e.g. the output URL of your image generator — these often expire, so import right away). The server downloads it (max 15MB, private/internal addresses are refused), verifies it is a real image, strips metadata, converts it (PNG for artwork) and stores it. Returns a permanent url plus width/height.

Who can call it: Any valid token (120 uploads / hour)

  • Returns { success, url, width, height, mimeType, bytes, next }, plus a note in storage-less demo mode. The quota (120 per user per hour) is shared with POST /api/v1/uploads, the website uploads, re-hosted images and external artwork URLs fetched for previews and product mockups. A call is charged only when the image is actually fetched or decoded: invalid base64, a refused URL or a caller already out of quota costs nothing; an image rejected after that (e.g. not an image) still counts.
  • url may also be a data:image/…;base64,… URL of up to 2,048 characters (use upload_image_base64 for anything larger). http:// URLs are fetched only when the server sets ALLOW_HTTP_IMAGE_FETCH=true.
Parameters
  • urlstring
    required
    1 to 2,048 chars

    Public https:// URL of the image.

  • purposestringoptionaldefault "artwork"
    artworkcoveravatarbanner

    artwork = shirt designs (kept as PNG up to 4096px); avatar = community logo; banner / cover = wide header or page cover images.

upload_image_fileUpload an image from this chat (ChatGPT)

writes
fetches URLs

ChatGPT: uploads an image from this conversation (one you generated or the user attached) straight to Printgram. Pass the image as file; ChatGPT supplies its download link. Returns a permanent url to use as artwork_url. If no download link arrives, use create_upload_link instead. Other apps: upload_image_from_url for a public URL, create_upload_link for an image made in a chat.

Who can call it: Any valid token (120 uploads / hour)

  • Returns { success, url, width, height, mimeType, bytes, next }, plus a note in storage-less demo mode. The quota (120 per user per hour) is shared with POST /api/v1/uploads, the website uploads, re-hosted images and external artwork URLs fetched for previews and product mockups. A call is charged only when the image is actually fetched or decoded: invalid base64, a refused URL or a caller already out of quota costs nothing; an image rejected after that (e.g. not an image) still counts.
  • For ChatGPT: the tool lists file in _meta["openai/fileParams"] (Apps SDK), so ChatGPT fills file with { download_url, file_id, mime_type?, file_name? } for an image in the conversation and the server downloads download_url like upload_image_from_url. A file without an https (or data:image/) link answers VALIDATION and points to create_upload_link.
Parameters
  • fileobject
    required

    The image from this conversation (one you generated or the user attached). ChatGPT fills in the fields.

    • file.download_urlstring
      required
      1 to 4,096 chars

      Filled in by ChatGPT: a temporary download link for the file.

    • file.file_idstring
      required
      1 to 512 chars

      Filled in by ChatGPT.

    • file.mime_typestringoptional
      max 200 chars
    • file.file_namestringoptional
      max 500 chars
  • purposestringoptionaldefault "artwork"
    artworkcoveravatarbanner

    artwork = shirt designs (kept as PNG up to 4096px); avatar = community logo; banner / cover = wide header or page cover images.

upload_image_base64Upload image (base64)

writes

Uploads an image you have as bytes: raw base64 or a data:image/...;base64,... URL (max 15MB decoded). Only for base64 you already hold as data (e.g. a tool output you can pass on unchanged): do NOT type out an image you generated in a chat, since long base64 retyped by a model gets corrupted; use create_upload_link for those. Prefer upload_image_from_url when the image is online. The image is verified, converted and stored; returns its url, width and height.

Who can call it: Any valid token (120 uploads / hour)

  • Returns { success, url, width, height, mimeType, bytes, next }, plus a note in storage-less demo mode. The quota (120 per user per hour) is shared with POST /api/v1/uploads, the website uploads, re-hosted images and external artwork URLs fetched for previews and product mockups. A call is charged only when the image is actually fetched or decoded: invalid base64, a refused URL or a caller already out of quota costs nothing; an image rejected after that (e.g. not an image) still counts.
  • data is raw base64 (standard or URL-safe) or a data:image/…;base64,… URL, up to 15 MB decoded (about 20 million characters). An https:// URL is rejected with VALIDATION ("Image data is not valid base64."): use upload_image_from_url for URLs.
Parameters
  • datastring
    required
    raw base64 (standard or URL-safe) or a data:image/…;base64,… URL; ≤ 15 MB decoded

    Base64 image bytes or a data:image/png;base64,... URL.

  • filenamestringoptional
    max 200 chars

    Original file name (informational).

  • purposestringoptionaldefault "artwork"
    artworkcoveravatarbanner

    artwork = shirt designs (kept as PNG up to 4096px); avatar = community logo; banner / cover = wide header or page cover images.

get_upload_instructionsHow to upload files directly

read-only

Returns ready-to-run curl commands for uploading a local image file straight to POST /api/v1/uploads with a personal API token (best for large files when you run in a terminal with the token). Connections made with "Sign in with Printgram" (Claude, ChatGPT in the browser) have no such token: use create_upload_link instead. The response JSON contains the url to use as artwork_url.

Who can call it: Any valid token

  • Returns endpoint, method, auth, limits, three commands in examples (multipartFile, rawBody, fromUrl) with your site URL filled in, the response shape and next.
Parameters
  • purposestringoptionaldefault "artwork"
    artworkcoveravatarbanner

    artwork = shirt designs (kept as PNG up to 4096px); avatar = community logo; banner / cover = wide header or page cover images.

Design & preview (2)

Placement rules and a rendered mockup your agent can look at.

get_design_guidelinesDesign guidelines

read-only

Everything needed to design a product: placement coordinate system (how x, y, scale, rotation map onto the 800x800 mockup), limits, named placement presets, recommended shirt colours, garments with their fixed prices, artwork requirements (transparent PNG, >= 2000px, max 15MB) and the recommended end-to-end workflow. Read this before preview_mockup / create_product.

Who can call it: Any valid token

Parameters

No parameters. Call it with {}.

preview_mockupPreview mockup

read-only
fetches URLs

Renders the shirt mockup exactly as the store will show it and returns it as an IMAGE so you can look at it, plus the effective placement (x / y / scale / rotation and where they came from), the artwork centre, size and bounds, and warnings (low resolution, spilling onto sleeves, default placement used, etc.). Nothing is saved — iterate freely, then pass the same values to create_product. Give the artwork and placement either at the top level (artwork_url, side, preset, x, y, scale, rotation, shirt_color, garment_type — applied to side, default front) or in design (same layer shape as create_product); top-level values win over the same field in design, and explicit x / y / scale / rotation always win over a preset. Unknown argument names are rejected. Pass product to start from an existing product's saved design (for update_product_design). Each call counts against a render quota of 300 per hour; artwork on an external URL (not uploaded yet) is downloaded and also counts as one upload (120 per hour) — upload it once and preview the returned url.

Who can call it: Any valid token; product=… needs edit rights on it

  • A side can be given nested (front / back, or design.front / design.back in preview_mockup) or at the top level: artwork_url, preset, x, y, scale, rotation apply to side (default: the top-level preset's side, else front). Explicit x / y / scale / rotation always override a preset, wherever each is given (the preset fills only the numbers you omit); a top-level value wins over the same field in the nested layer, and the result notes the override. shirt_color / garment_type at the top level likewise win over design.shirt_color / design.garment_type.
  • Unknown or misplaced argument names (e.g. position, or shirt_color inside front) are rejected with MCP error -32602: Input validation error: … Unrecognized key(s) in object: … naming the key; nothing is silently dropped.
  • The JSON summary in content[1].text is a string: parse it before reading warnings or nextStep. layer echoes the effective x / y / scale / rotation, the applied preset and placementSource (e.g. defaults, preset "front-center" + explicit x); warnings says when artwork got the default placement because no placement was given.
  • Each call uses one render (300 per hour). Artwork on an external URL (neither returned by an upload nor a data URL) is downloaded for the preview and also counts as one upload (120 per hour): upload it once and preview the returned url.
Parameters
  • designobjectoptional

    Design to render: shirt colour, garment and a layer per side (partial values are merged onto the defaults). The top-level shortcuts (artwork_url, preset, x, y, scale, rotation, shirt_color, garment_type) do the same for one side.

    • design.shirt_colorstringoptional
      #111111#FFFFFF#2563EB#DC2626

      Garment colour (only these are printed): Black (#111111), White (#FFFFFF), Blue (#2563EB), Red (#DC2626). Hex or name. See get_design_guidelines.

    • design.garment_typestringoptional
      tshirthoodiesweatshirt

      Garment (fixed platform pricing: tshirt ₹400, sweatshirt ₹1,199, hoodie ₹1,499).

    • design.frontobjectoptional
      • design.front.artwork_urlstring | nulloptional
        https:// URL or data:image URL (image ≤ 15 MB)

        New artwork URL; null removes the artwork from this side; omit to keep it.

      • design.front.presetstringoptional
        front-centerfront-left-chestfront-fullback-upperback-fullback-lower

        Named placement: front-center (Centre chest), front-left-chest (Left chest logo, on the wearer's left), front-full (Full front print), back-upper (Upper back, below the collar), back-full (Full back print), back-lower (Lower back). Explicit x/y/scale/rotation override the preset values.

      • design.front.xnumberoptional
        -260 to 260

        Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

      • design.front.ynumberoptional
        -260 to 260

        Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

      • design.front.scalenumberoptional
        0.3 to 1.5

        Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

      • design.front.rotationnumberoptional
        -180 to 180

        Clockwise rotation in degrees (the web studio offers -45..45).

    • design.backobjectoptional
      • design.back.artwork_urlstring | nulloptional
        https:// URL or data:image URL (image ≤ 15 MB)

        New artwork URL; null removes the artwork from this side; omit to keep it.

      • design.back.presetstringoptional
        front-centerfront-left-chestfront-fullback-upperback-fullback-lower

        Named placement: front-center (Centre chest), front-left-chest (Left chest logo, on the wearer's left), front-full (Full front print), back-upper (Upper back, below the collar), back-full (Full back print), back-lower (Lower back). Explicit x/y/scale/rotation override the preset values.

      • design.back.xnumberoptional
        -260 to 260

        Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

      • design.back.ynumberoptional
        -260 to 260

        Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

      • design.back.scalenumberoptional
        0.3 to 1.5

        Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

      • design.back.rotationnumberoptional
        -180 to 180

        Clockwise rotation in degrees (the web studio offers -45..45).

  • artwork_urlstringoptional
    https:// URL or data:image URL (image ≤ 15 MB)

    Shortcut: artwork for side (URL from an upload tool, or any https image URL).

  • sidestringoptional
    frontback

    Which side to render; the top-level shortcuts apply to it. Default: the top-level preset's side; else front when a top-level artwork_url / x / y / scale / rotation is given; else the side that has artwork (front first).

  • presetstringoptional
    front-centerfront-left-chestfront-fullback-upperback-fullback-lower

    Shortcut: placement preset for side (it fills only the x / y / scale / rotation you do not give).

  • xnumberoptional
    -260 to 260

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

  • ynumberoptional
    -260 to 260

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

  • scalenumberoptional
    0.3 to 1.5

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

  • rotationnumberoptional
    -180 to 180

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Clockwise rotation in degrees (the web studio offers -45..45).

  • shirt_colorstringoptional
    #111111#FFFFFF#2563EB#DC2626

    Shortcut for design.shirt_color (wins over it). Garment colour (only these are printed): Black (#111111), White (#FFFFFF), Blue (#2563EB), Red (#DC2626). Hex or name. See get_design_guidelines.

  • garment_typestringoptional
    tshirthoodiesweatshirt

    Shortcut for design.garment_type (wins over it).

  • productstringoptional
    1 to 120 chars

    Existing product (or wardrobe design) id or slug whose saved design is the starting point (you must be able to edit it).

  • sizeintegeroptionaldefault 800
    256 to 1,024

    Output size in pixels (square). Placement numbers are always reported in 800px coordinates.

list_genresList genres

read-only

Lists the product genres (categories) with id and slug. Pass one as genre to create_product (defaults to the first genre).

Who can call it: Any valid token

Parameters

No parameters. Call it with {}.

create_productCreate product (a design, saved as a draft)

writes
fetches URLs

Adds a shirt design as a product in a community. ONLY the community OWNER can do this. It is saved as a DRAFT that only the owner and community staff can see: show the user its reviewUrl and the mockups, and publish it with publish_drafts once they approve (it goes on sale once Printgram staff verify it). For a design just for the user themselves, use add_to_wardrobe instead. Provide artwork for the front and/or back (artwork_url from an upload tool or any public https image — it is re-hosted) with placement (x, y, scale, rotation and/or a preset) — use preview_mockup first to get it right. Give each side as a front / back object, or one side at the top level (artwork_url, side, preset, x, y, scale, rotation; side defaults to front); top-level values win over the same field in the nested layer, and explicit numbers always win over a preset. Unknown argument names are rejected. Artwork is optional per side but at least one of front/back is required. Every product receives both front and back previews (an unprinted side is blank) in Black, White, Blue, Red; prices are fixed per garment. page places it on a community page (it inherits that page's visibility, e.g. members-only, and stays hidden while that page is a draft). Returns the product URL, a review block (how to show the draft and the exact publish_drafts call), the placement that was saved and warnings (e.g. default placement used).

Who can call it: Community OWNER only

  • Saved as a draft by default (status: "draft"): only the owner and the community staff can see it, signed in; everyone else gets "not found". The result has a review block: the reviewUrl to show the user, who can see the draft, and the exact publish_drafts call to make once the user approves. Pass status: "published" only when the user explicitly asked to publish without reviewing.
  • Printgram staff verify every design before it goes on sale. Publishing an unverified design sets status: "pending_review": only the owner and the community staff see it until staff verify it, then it goes on sale by itself. A design staff did not verify gets status: "rejected" and a reviewNote (shown to editors) saying why. Platform admins' own designs count as verified.
  • visibility defaults to public.
  • A side can be given nested (front / back, or design.front / design.back in preview_mockup) or at the top level: artwork_url, preset, x, y, scale, rotation apply to side (default: the top-level preset's side, else front). Explicit x / y / scale / rotation always override a preset, wherever each is given (the preset fills only the numbers you omit); a top-level value wins over the same field in the nested layer, and the result notes the override.
  • Unknown or misplaced argument names (e.g. position, or shirt_color inside front) are rejected with MCP error -32602: Input validation error: … Unrecognized key(s) in object: … naming the key; nothing is silently dropped.
  • Artwork is optional independently on front and back, but at least one side needs artwork. Every product gets a front and back preview in Black, White, Blue and Red; an unprinted side is shown as a blank shirt.
  • The result echoes the saved placement per side (x / y / scale / rotation, preset, source) and lists warnings, e.g. when a side with artwork got the default placement because no placement was given. Placement for a side without artwork is refused with VALIDATION.
Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • pagestringoptional
    1 to 120 chars

    Page slug or id inside the community (omit for the storefront root).

  • titlestring
    required
    1 to 120 chars
  • descriptionstringoptional
    max 5,000 chars
  • genrestringoptional
    1 to 80 chars

    Genre id or slug from list_genres.

  • garment_typestringoptionaldefault "tshirt"
    tshirthoodiesweatshirt

    Garment (fixed platform pricing: tshirt ₹400, sweatshirt ₹1,199, hoodie ₹1,499).

  • shirt_colorstringoptionaldefault "#111111"
    #111111#FFFFFF#2563EB#DC2626

    Garment colour (only these are printed): Black (#111111), White (#FFFFFF), Blue (#2563EB), Red (#DC2626). Hex or name. See get_design_guidelines.

  • extra_colorsstring[]optional
    max 3 itemseach one of: #111111, #FFFFFF, #2563EB, #DC2626

    Colours buyers can pick on the product page besides shirt_color (same catalog; hex or name). Omit to offer every catalog colour.

  • sizesstring[]optional
    1 to 6 itemseach one of: S, M, L, XL, 2XL, 3XL

    Sizes buyers can pick. Defaults to S, M, L, XL, 2XL, 3XL.

  • frontobjectoptional

    Front print. At least one side needs artwork (here, in back, or the top-level artwork_url).

    • front.artwork_urlstring
      required
      https:// URL or data:image URL (image ≤ 15 MB)

      The artwork image: a URL returned by upload_image_from_url / upload_image_base64 / POST /api/v1/uploads, or any public https image URL (it is re-hosted).

    • front.presetstringoptional
      front-centerfront-left-chestfront-fullback-upperback-fullback-lower

      Named placement: front-center (Centre chest), front-left-chest (Left chest logo, on the wearer's left), front-full (Full front print), back-upper (Upper back, below the collar), back-full (Full back print), back-lower (Lower back). Explicit x/y/scale/rotation override the preset values.

    • front.xnumberoptional
      -260 to 260

      Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

    • front.ynumberoptional
      -260 to 260

      Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

    • front.scalenumberoptional
      0.3 to 1.5

      Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

    • front.rotationnumberoptional
      -180 to 180

      Clockwise rotation in degrees (the web studio offers -45..45).

  • backobjectoptional

    Back print.

    • back.artwork_urlstring
      required
      https:// URL or data:image URL (image ≤ 15 MB)

      The artwork image: a URL returned by upload_image_from_url / upload_image_base64 / POST /api/v1/uploads, or any public https image URL (it is re-hosted).

    • back.presetstringoptional
      front-centerfront-left-chestfront-fullback-upperback-fullback-lower

      Named placement: front-center (Centre chest), front-left-chest (Left chest logo, on the wearer's left), front-full (Full front print), back-upper (Upper back, below the collar), back-full (Full back print), back-lower (Lower back). Explicit x/y/scale/rotation override the preset values.

    • back.xnumberoptional
      -260 to 260

      Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

    • back.ynumberoptional
      -260 to 260

      Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

    • back.scalenumberoptional
      0.3 to 1.5

      Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

    • back.rotationnumberoptional
      -180 to 180

      Clockwise rotation in degrees (the web studio offers -45..45).

  • artwork_urlstringoptional
    https:// URL or data:image URL (image ≤ 15 MB)

    Top-level shortcut: artwork for side (default front) — a URL from an upload tool or any public https image (re-hosted).

  • sidestringoptional
    frontback

    Side the top-level artwork_url / preset / x / y / scale / rotation apply to. Default: the top-level preset's side, else front.

  • presetstringoptional
    front-centerfront-left-chestfront-fullback-upperback-fullback-lower

    Shortcut: placement preset for side (it fills only the x / y / scale / rotation you do not give).

  • xnumberoptional
    -260 to 260

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

  • ynumberoptional
    -260 to 260

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

  • scalenumberoptional
    0.3 to 1.5

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

  • rotationnumberoptional
    -180 to 180

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Clockwise rotation in degrees (the web studio offers -45..45).

  • statusstringoptionaldefault "draft"
    draftpublished

    Defaults to draft (the user reviews it first, then publish_drafts). Use "published" only when the user explicitly asked to publish without reviewing; it then waits for Printgram staff to verify it.

  • visibilitystringoptional
    publicunlistedprivate

    public = listed (if its community and page are public); unlisted = direct link only; private = community members only.

  • tagsstring[]optional
    max 15 itemseach 1 to 40 chars

update_product_designUpdate product design

writes
idempotent
fetches URLs

Changes the artwork placement, artwork, shirt colour or garment of an existing product and re-renders every colour's mockups, front and back. New artwork on a design that is on sale sends it back to Printgram's review (placement and colour changes do not). Only fields you pass change (e.g. front: { scale: 0.7 } keeps the front artwork and position otherwise). A side can also be given at the top level (artwork_url, side, preset, x, y, scale, rotation; side defaults to front); top-level values win over the same field in front / back, and explicit numbers always win over a preset. artwork_url: null removes a side's artwork. Unknown argument names are rejected. Creator or community owner only. Preview first with preview_mockup(product=...). Returns the saved placement and warnings.

Who can call it: Product creator, community owner or platform admin

  • A side can be given nested (front / back, or design.front / design.back in preview_mockup) or at the top level: artwork_url, preset, x, y, scale, rotation apply to side (default: the top-level preset's side, else front). Explicit x / y / scale / rotation always override a preset, wherever each is given (the preset fills only the numbers you omit); a top-level value wins over the same field in the nested layer, and the result notes the override. Fields you give nowhere keep the saved design.
  • Unknown or misplaced argument names (e.g. position, or shirt_color inside front) are rejected with MCP error -32602: Input validation error: … Unrecognized key(s) in object: … naming the key; nothing is silently dropped.
  • Re-renders front and back previews for Black, White, Blue and Red. The result echoes the saved placement and warnings (e.g. new artwork on a previously empty side without placement).
  • New artwork (or new mockup images) on a design that is on sale sends it back to review (status: "pending_review") until Printgram staff verify it again; placement, colour and garment changes do not.
Parameters
  • productstring
    required
    1 to 160 chars

    Product id or slug (as in /p/<slug>).

  • shirt_colorstringoptional
    #111111#FFFFFF#2563EB#DC2626

    Garment colour (only these are printed): Black (#111111), White (#FFFFFF), Blue (#2563EB), Red (#DC2626). Hex or name. See get_design_guidelines.

  • garment_typestringoptional
    tshirthoodiesweatshirt

    Garment (fixed platform pricing: tshirt ₹400, sweatshirt ₹1,199, hoodie ₹1,499).

  • frontobjectoptional
    • front.artwork_urlstring | nulloptional
      https:// URL or data:image URL (image ≤ 15 MB)

      New artwork URL; null removes the artwork from this side; omit to keep it.

    • front.presetstringoptional
      front-centerfront-left-chestfront-fullback-upperback-fullback-lower

      Named placement: front-center (Centre chest), front-left-chest (Left chest logo, on the wearer's left), front-full (Full front print), back-upper (Upper back, below the collar), back-full (Full back print), back-lower (Lower back). Explicit x/y/scale/rotation override the preset values.

    • front.xnumberoptional
      -260 to 260

      Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

    • front.ynumberoptional
      -260 to 260

      Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

    • front.scalenumberoptional
      0.3 to 1.5

      Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

    • front.rotationnumberoptional
      -180 to 180

      Clockwise rotation in degrees (the web studio offers -45..45).

  • backobjectoptional
    • back.artwork_urlstring | nulloptional
      https:// URL or data:image URL (image ≤ 15 MB)

      New artwork URL; null removes the artwork from this side; omit to keep it.

    • back.presetstringoptional
      front-centerfront-left-chestfront-fullback-upperback-fullback-lower

      Named placement: front-center (Centre chest), front-left-chest (Left chest logo, on the wearer's left), front-full (Full front print), back-upper (Upper back, below the collar), back-full (Full back print), back-lower (Lower back). Explicit x/y/scale/rotation override the preset values.

    • back.xnumberoptional
      -260 to 260

      Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

    • back.ynumberoptional
      -260 to 260

      Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

    • back.scalenumberoptional
      0.3 to 1.5

      Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

    • back.rotationnumberoptional
      -180 to 180

      Clockwise rotation in degrees (the web studio offers -45..45).

  • artwork_urlstring | nulloptional
    https:// URL or data:image URL (image ≤ 15 MB)

    Top-level shortcut: new artwork for side (default front); null removes it; omit to keep it.

  • sidestringoptional
    frontback

    Side the top-level artwork_url / preset / x / y / scale / rotation apply to. Default: the top-level preset's side, else front.

  • presetstringoptional
    front-centerfront-left-chestfront-fullback-upperback-fullback-lower

    Shortcut: placement preset for side (it fills only the x / y / scale / rotation you do not give).

  • xnumberoptional
    -260 to 260

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

  • ynumberoptional
    -260 to 260

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

  • scalenumberoptional
    0.3 to 1.5

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

  • rotationnumberoptional
    -180 to 180

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Clockwise rotation in degrees (the web studio offers -45..45).

update_productUpdate product details

writes
idempotent

Edits product details: title, description, page (move it; null = storefront root), genre, status (draft = unpublish, archived), visibility, and canonical colour swatches. Creator or community owner only. For placement or artwork changes use update_product_design. Publishing is done with publish_drafts, after the user approved the draft.

Who can call it: Product creator, community owner or platform admin

  • status: "draft" unpublishes a product and archived removes it from sale. status: "published" is refused with VALIDATION: publish with publish_drafts, after the user approved the draft.
Parameters
  • productstring
    required
    1 to 160 chars

    Product id or slug (as in /p/<slug>).

  • titlestringoptional
    1 to 120 chars
  • descriptionstring | nulloptional
    max 5,000 chars
  • pagestring | nulloptional
    1 to 120 chars

    Move to this page (slug or id) in the same community; null = storefront root.

  • genrestringoptional
    1 to 80 chars
  • statusstringoptional
    publisheddraftarchived

    draft = unpublish; archived = remove from sale. To publish, use publish_drafts.

  • visibilitystringoptional
    publicunlistedprivate

    public = listed (if its community and page are public); unlisted = direct link only; private = community members only.

  • shirt_colorsstring[]optional
    1 to 4 itemseach one of: #111111, #FFFFFF, #2563EB, #DC2626

    Full list of colours buyers can pick (catalog colours; the design colour is always kept, first).

archive_productArchive product

writes
destructive
idempotent

Archives (soft-deletes) a product: it disappears from the storefront and can no longer be bought; existing orders are unaffected. Creator, community owner or platform admin only.

Who can call it: Product creator, community owner or platform admin

Parameters
  • productstring
    required
    1 to 160 chars

    Product id or slug (as in /p/<slug>).

list_productsList products

read-only

Lists products of a community that you can see. Community staff also see drafts (include_archived adds archived ones); everyone else sees published products they have access to. Filter by page, or storefront_root_only for products that are not on any page.

Who can call it: Anyone who can view; drafts/archived: community staff

Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • pagestringoptional
    1 to 120 chars

    Page slug (as in /c/<community>/<page-slug>) or page id.

  • storefront_root_onlybooleanoptionaldefault false
  • include_archivedbooleanoptionaldefault false

    Staff only.

  • limitintegeroptionaldefault 50
    1 to 200

get_productGet product

read-only

Details of one product you can view, including its saved design (artwork, placement, shirt colour) when you can edit it — useful before update_product_design.

Who can call it: Anyone who can view the product; design: editors

Parameters
  • productstring
    required
    1 to 160 chars

    Product id or slug (as in /p/<slug>).

Drafts & publishing (1)

New pages and products start as drafts; they go live once the user approves them (products once Printgram staff verify them too).

publish_draftsPublish drafts (after the user approved them)

writes
idempotent

Publishes a draft page and/or draft products so everyone their visibility allows can see them. ONLY call this after you showed the user the drafts (their reviewUrl and the mockups) and they explicitly said to publish. One call publishes a page together with its products. Products go on sale once Printgram staff have verified them: an unverified design is submitted for review (status pending_review) and goes live by itself when staff verify it; a design marked not verified (rejected) can be fixed and submitted again. Publishing a page needs the community owner or an admin; publishing products needs the community owner. Everything is checked before anything is published, and items that are already published or waiting for review are reported, not changed.

Who can call it: Page: owner or admin; products: community owner (or platform admin)

  • user_confirmed must be true: call it only after the user reviewed the drafts (their reviewUrl, the mockups) and explicitly said to publish them.
  • Everything is checked before anything changes: an unknown page or product, a product of another community, a product that is archived or under review (only drafts can be published) or a missing permission fails with NOT_FOUND, VALIDATION or FORBIDDEN and publishes nothing. The page is published first, then the products.
  • Returns published (the page with seenBy, and the products with their urls), alreadyPublished for items that were live already, and notes for published products that stay hidden because their page is still a draft.
  • Products go on sale once Printgram staff have verified them: unverified ones are listed in submittedForReview (status: "pending_review") and go live by themselves when staff verify them; ones already waiting are in alreadyWaitingForReview. A design marked not verified (rejected) can be fixed and published again.
Parameters
  • communitystring
    required
    1 to 120 chars

    Community slug (e.g. "neon-coders", as in /c/neon-coders) or community id.

  • pagestringoptional
    1 to 120 chars

    The draft page to publish (slug or id).

  • productsstring[]optional
    1 to 50 itemseach 1 to 200 chars

    The draft (or not verified) products to publish: slugs or ids, as returned by create_product.

  • user_confirmedtrue
    required

    Must be true: the user reviewed these drafts and explicitly asked to publish them.

My Wardrobe (4)

Personal designs only the user can see and order: never listed, never reviewed, not for sale to others.

list_wardrobeList my wardrobe

read-only

Lists the designs in your personal My Wardrobe, newest first (at most 100), with each design's saved artwork and placement (useful before update_wardrobe_item). Wardrobe designs are personal: only you can see and order them, they are never listed or reviewed, and they are not for sale to anyone else.

Who can call it: Any valid token (your own wardrobe)

  • A wardrobe design is personal: only its owner can see and order it (on the website, signed in, at its url); it is never listed, never reviewed and never sold to anyone else, and needs no community. Other accounts get NOT_FOUND.
Parameters

No parameters. Call it with {}.

add_to_wardrobeAdd a design to my wardrobe

writes
fetches URLs

Saves a shirt design to your personal My Wardrobe so you can order it for yourself. Wardrobe designs are personal: only you can see and order them, they are never listed or reviewed, and they are not for sale to anyone else. Any signed-in user can use it (no community needed). Provide artwork for the front and/or back (artwork_url from an upload tool or any public https image — it is re-hosted) with placement (x, y, scale, rotation and/or a preset) — use preview_mockup first to get it right. Give each side as a front / back object, or one side at the top level (artwork_url, side, preset, x, y, scale, rotation; side defaults to front); top-level values win over the same field in the nested layer, and explicit numbers always win over a preset. Unknown argument names are rejected. Previews are rendered for both sides in Black, White, Blue, Red; prices are fixed per garment. Returns the design, its url (show it to the user) and the placement that was saved.

Who can call it: Any valid token (your own wardrobe)

  • A wardrobe design is personal: only its owner can see and order it (on the website, signed in, at its url); it is never listed, never reviewed and never sold to anyone else, and needs no community. Other accounts get NOT_FOUND.
  • A side can be given nested (front / back, or design.front / design.back in preview_mockup) or at the top level: artwork_url, preset, x, y, scale, rotation apply to side (default: the top-level preset's side, else front). Explicit x / y / scale / rotation always override a preset, wherever each is given (the preset fills only the numbers you omit); a top-level value wins over the same field in the nested layer, and the result notes the override.
  • Unknown or misplaced argument names (e.g. position, or shirt_color inside front) are rejected with MCP error -32602: Input validation error: … Unrecognized key(s) in object: … naming the key; nothing is silently dropped.
  • A wardrobe holds up to 100 designs. The result has the design url to open in the user's browser (showTheUser), the saved placement per side and warnings, like create_product.
Parameters
  • titlestring
    required
    1 to 120 chars
  • descriptionstringoptional
    max 5,000 chars
  • garment_typestringoptionaldefault "tshirt"
    tshirthoodiesweatshirt

    Garment (fixed platform pricing: tshirt ₹400, sweatshirt ₹1,199, hoodie ₹1,499).

  • shirt_colorstringoptionaldefault "#111111"
    #111111#FFFFFF#2563EB#DC2626

    Garment colour (only these are printed): Black (#111111), White (#FFFFFF), Blue (#2563EB), Red (#DC2626). Hex or name. See get_design_guidelines.

  • extra_colorsstring[]optional
    max 3 itemseach one of: #111111, #FFFFFF, #2563EB, #DC2626

    Other colours the user may order it in besides shirt_color (hex or name). Omit to allow every catalog colour.

  • sizesstring[]optional
    1 to 6 itemseach one of: S, M, L, XL, 2XL, 3XL

    Sizes the user may order. Defaults to S, M, L, XL, 2XL, 3XL.

  • frontobjectoptional

    Front print. At least one side needs artwork (here, in back, or the top-level artwork_url).

    • front.artwork_urlstring
      required
      https:// URL or data:image URL (image ≤ 15 MB)

      The artwork image: a URL returned by upload_image_from_url / upload_image_base64 / POST /api/v1/uploads, or any public https image URL (it is re-hosted).

    • front.presetstringoptional
      front-centerfront-left-chestfront-fullback-upperback-fullback-lower

      Named placement: front-center (Centre chest), front-left-chest (Left chest logo, on the wearer's left), front-full (Full front print), back-upper (Upper back, below the collar), back-full (Full back print), back-lower (Lower back). Explicit x/y/scale/rotation override the preset values.

    • front.xnumberoptional
      -260 to 260

      Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

    • front.ynumberoptional
      -260 to 260

      Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

    • front.scalenumberoptional
      0.3 to 1.5

      Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

    • front.rotationnumberoptional
      -180 to 180

      Clockwise rotation in degrees (the web studio offers -45..45).

  • backobjectoptional

    Back print.

    • back.artwork_urlstring
      required
      https:// URL or data:image URL (image ≤ 15 MB)

      The artwork image: a URL returned by upload_image_from_url / upload_image_base64 / POST /api/v1/uploads, or any public https image URL (it is re-hosted).

    • back.presetstringoptional
      front-centerfront-left-chestfront-fullback-upperback-fullback-lower

      Named placement: front-center (Centre chest), front-left-chest (Left chest logo, on the wearer's left), front-full (Full front print), back-upper (Upper back, below the collar), back-full (Full back print), back-lower (Lower back). Explicit x/y/scale/rotation override the preset values.

    • back.xnumberoptional
      -260 to 260

      Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

    • back.ynumberoptional
      -260 to 260

      Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

    • back.scalenumberoptional
      0.3 to 1.5

      Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

    • back.rotationnumberoptional
      -180 to 180

      Clockwise rotation in degrees (the web studio offers -45..45).

  • artwork_urlstringoptional
    https:// URL or data:image URL (image ≤ 15 MB)

    Top-level shortcut: artwork for side (default front) — a URL from an upload tool or any public https image (re-hosted).

  • sidestringoptional
    frontback

    Side the top-level artwork_url / preset / x / y / scale / rotation apply to. Default: the top-level preset's side, else front.

  • presetstringoptional
    front-centerfront-left-chestfront-fullback-upperback-fullback-lower

    Shortcut: placement preset for side (it fills only the x / y / scale / rotation you do not give).

  • xnumberoptional
    -260 to 260

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

  • ynumberoptional
    -260 to 260

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

  • scalenumberoptional
    0.3 to 1.5

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

  • rotationnumberoptional
    -180 to 180

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Clockwise rotation in degrees (the web studio offers -45..45).

update_wardrobe_itemUpdate a design in my wardrobe

writes
idempotent
fetches URLs

Changes a design in your My Wardrobe: title, description, the colours you may order it in, and its artwork, placement, shirt colour or garment (every colour is re-rendered). Only fields you pass change (e.g. front: { scale: 0.7 } keeps the front artwork and position otherwise). A side can also be given at the top level (artwork_url, side, preset, x, y, scale, rotation; side defaults to front). artwork_url: null removes a side's artwork. Unknown argument names are rejected. Preview first with preview_mockup(product=<id>). Wardrobe designs are personal: only you can see and order them, they are never listed or reviewed, and they are not for sale to anyone else.

Who can call it: The design's owner

  • A wardrobe design is personal: only its owner can see and order it (on the website, signed in, at its url); it is never listed, never reviewed and never sold to anyone else, and needs no community. Other accounts get NOT_FOUND.
  • A side can be given nested (front / back, or design.front / design.back in preview_mockup) or at the top level: artwork_url, preset, x, y, scale, rotation apply to side (default: the top-level preset's side, else front). Explicit x / y / scale / rotation always override a preset, wherever each is given (the preset fills only the numbers you omit); a top-level value wins over the same field in the nested layer, and the result notes the override. Fields you give nowhere keep the saved design.
  • Unknown or misplaced argument names (e.g. position, or shirt_color inside front) are rejected with MCP error -32602: Input validation error: … Unrecognized key(s) in object: … naming the key; nothing is silently dropped.
Parameters
  • itemstring
    required
    1 to 160 chars

    Wardrobe design id, from list_wardrobe or add_to_wardrobe.

  • titlestringoptional
    1 to 120 chars
  • descriptionstring | nulloptional
    max 5,000 chars
  • shirt_colorsstring[]optional
    1 to 4 itemseach one of: #111111, #FFFFFF, #2563EB, #DC2626

    Full list of colours the user may order it in (the design colour is always kept, first).

  • shirt_colorstringoptional
    #111111#FFFFFF#2563EB#DC2626

    Garment colour (only these are printed): Black (#111111), White (#FFFFFF), Blue (#2563EB), Red (#DC2626). Hex or name. See get_design_guidelines.

  • garment_typestringoptional
    tshirthoodiesweatshirt

    Garment (fixed platform pricing: tshirt ₹400, sweatshirt ₹1,199, hoodie ₹1,499).

  • frontobjectoptional
    • front.artwork_urlstring | nulloptional
      https:// URL or data:image URL (image ≤ 15 MB)

      New artwork URL; null removes the artwork from this side; omit to keep it.

    • front.presetstringoptional
      front-centerfront-left-chestfront-fullback-upperback-fullback-lower

      Named placement: front-center (Centre chest), front-left-chest (Left chest logo, on the wearer's left), front-full (Full front print), back-upper (Upper back, below the collar), back-full (Full back print), back-lower (Lower back). Explicit x/y/scale/rotation override the preset values.

    • front.xnumberoptional
      -260 to 260

      Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

    • front.ynumberoptional
      -260 to 260

      Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

    • front.scalenumberoptional
      0.3 to 1.5

      Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

    • front.rotationnumberoptional
      -180 to 180

      Clockwise rotation in degrees (the web studio offers -45..45).

  • backobjectoptional
    • back.artwork_urlstring | nulloptional
      https:// URL or data:image URL (image ≤ 15 MB)

      New artwork URL; null removes the artwork from this side; omit to keep it.

    • back.presetstringoptional
      front-centerfront-left-chestfront-fullback-upperback-fullback-lower

      Named placement: front-center (Centre chest), front-left-chest (Left chest logo, on the wearer's left), front-full (Full front print), back-upper (Upper back, below the collar), back-full (Full back print), back-lower (Lower back). Explicit x/y/scale/rotation override the preset values.

    • back.xnumberoptional
      -260 to 260

      Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

    • back.ynumberoptional
      -260 to 260

      Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

    • back.scalenumberoptional
      0.3 to 1.5

      Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

    • back.rotationnumberoptional
      -180 to 180

      Clockwise rotation in degrees (the web studio offers -45..45).

  • artwork_urlstring | nulloptional
    https:// URL or data:image URL (image ≤ 15 MB)

    Top-level shortcut: new artwork for side (default front); null removes it; omit to keep it.

  • sidestringoptional
    frontback

    Side the top-level artwork_url / preset / x / y / scale / rotation apply to. Default: the top-level preset's side, else front.

  • presetstringoptional
    front-centerfront-left-chestfront-fullback-upperback-fullback-lower

    Shortcut: placement preset for side (it fills only the x / y / scale / rotation you do not give).

  • xnumberoptional
    -260 to 260

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Horizontal offset of the artwork centre: -80 (towards the left edge) .. 80 (right); 0 = centred. 10 units = 16px on the 800px mockup. The wearer's left chest is on the right of the front view (x 45).

  • ynumberoptional
    -260 to 260

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Vertical offset: negative moves UP towards the collar, positive DOWN towards the hem; 10 units = 20px. 0 is the middle of the print area, below the armpits: on the front a chest print sits around y -40, a small chest logo around y -70.

  • scalenumberoptional
    0.3 to 1.5

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Zoom: the artwork's longest side is 240px x scale on the 800px mockup (0.4 = small chest logo, 0.85 = standard chest print, 1.2-1.5 = oversized).

  • rotationnumberoptional
    -180 to 180

    Top-level shortcut for the side layer; wins over the same field in the nested layer. Clockwise rotation in degrees (the web studio offers -45..45).

remove_from_wardrobeRemove a design from my wardrobe

writes
destructive

Removes a design from your My Wardrobe. Orders you already placed are not affected. Ask the user first.

Who can call it: The design's owner

  • Orders already placed keep their copy of the design and are printed as usual.
Parameters
  • itemstring
    required
    1 to 160 chars

    Wardrobe design id, from list_wardrobe or add_to_wardrobe.

Community roles rank owner > admin > moderator > member. The agent acts as you, so it can never do more than you can on the website.

ActionOwnerAdminModeratorMember
View members-only pages and private-community contentYesYesYesYes
View staff-only (private) pages; drafts and designs under review in list_productsYesYesYesNo
Remove / ban / unban members ranked below youYesYesYesNo
Change roles (only on members below you, only to roles below your own)Yes, incl. adminYes, up to moderatorNoNo
Review join requests, send direct invitationsYesYesYesNo
Create / list / revoke invite linksYesYesNoNo
Create / edit / delete pagesYesYesNoNo
Name, tagline, description, images, accent colourYesYesNoNo
Visibility, view / purchase access, slugYesNoNoNo
Add products (create_product)YesNoNoNo
Publish draft pages / draft products (publish_drafts; designs then wait for Printgram staff to verify them)Yes, pages and productsYes, pagesNoNo
Publish / delete posts (create_post, delete_post)YesNoNoNo
Like posts, comment and reply (website only)YesYesYesYes
Delete other people's comments and repliesYesYesYesNo
Edit / archive productsYes, + product creatorNoNoNo
Delete the communityYes, browser onlyNoNoNo
  • Drafts first. Pages and products created over MCP are drafts that only the owner and the community staff see (a product's creator sees their own); everyone else gets “not found”. They go live only through publish_drafts (with user_confirmed: true, sent after you approved them) or the Publish buttons on the website.
  • Staff review. Printgram staff verify every community design before it goes on sale. An unverified design you publish waits in pending_review, still visible only to the owner, the community staff and its creator, and goes on sale by itself once verified; one that is not verified gets rejected and a reviewNote. New artwork on a design on sale sends it back to review. Platform admins' own designs count as verified.
  • My Wardrobe is personal. Any token can keep designs in its own user's wardrobe, no community needed. Only that user can see, change and order them (others get NOT_FOUND; Printgram staff can open them to print orders), and they are never listed, reviewed or sold to anyone else.
  • Owner-only products. Only the community owner can add products (website and MCP), and a page must belong to the same community.
  • Private communities are hidden from every listing, and their content is for active members only; non-members get FORBIDDEN. People join only with an invite link (validated and redeemed atomically), a direct invitation or an approved join request.
  • Link-only storefronts. A public community with view_access: "link" can be viewed by anyone who has its URL, but it is never listed: not in search, explore, the communities hub, the sitemap, featured pages or other people's profiles (like an unlisted community). Only public communities with view_access: "everyone" have listable pages and products.
  • A page can't be more public than its community. A private community has no public pages, and making a community private turns its public pages into members-only pages. Products on members-only or staff-only pages never appear in public listings.
  • Bans block rejoining by any route, viewing members-only content and buying. Leaving does not clear a ban; unbanning restores a former member as a member, and a pre-emptive ban is simply lifted. Only someone ranked at least as high as whoever issued a ban can lift it.
  • Hierarchy. You act only on members ranked below you and grant only roles below your own. Moderators remove, ban and unban members, review join requests and send direct invitations, but only the owner and admins change roles (a moderator gets FORBIDDEN). Nobody can remove, ban or demote the owner, and nobody can act on themselves.
  • Token scope. API tokens manage stores; placing orders, editing your profile, likes, comments and wishlist saves, token management and deleting a community need the website (a token gets 403).
  • Rate limits & size caps. 120 uploads per hour per user (all upload routes together, plus images other tools re-host and external artwork downloaded for mockups; charged only after a request validates), 300 mockup renders per hour, at most 2 image calls in flight per user, one JSON-RPC message per request; request bodies are streamed with hard byte limits.

Tool failures are isError: true results whose text is {"success": false, "code": "…", "error": "…"}. The JSON API routes use the same codes with these HTTP statuses.

CodeHTTPMeaning
UNAUTHENTICATED401No valid identity: token missing, invalid, revoked or expired, or the account is suspended.
FORBIDDEN403Your role does not allow this (e.g. create_product by a non-owner, an admin changing privacy, a moderator changing roles), or a private or members-only community you are not a member of. On the JSON API also: an API token used for a session-only action (orders, profile, likes / saves, tokens, deleting a community).
NOT_FOUND404It does not exist, or it is hidden from you: staff-only pages, drafts, designs under review, products you cannot see and other people's wardrobe designs. Private and members-only communities return FORBIDDEN (or BANNED) instead.
BANNED403You are banned from the community, or you tried to approve the join request of a banned user. (Inviting a banned user is CONFLICT.)
INVITE_REQUIRED403Private community: joining needs an invite link, a direct invitation or an approved request.
INVALID_INVITE400The invite link is invalid, expired, used up, revoked or for another community.
CONFLICT409Already exists or already done: community name or slug taken, already a member, pending invitation, inviting a banned user, token limit reached, or a join request for an open community ("join it directly").
VALIDATION400Bad input: out-of-range values, slug format, a reserved community or page slug, the page ≤ community rule, an image URL or base64 payload that was refused, a page from another community, or a full wardrobe (100 designs).
PAYLOAD_TOO_LARGE413An image over 15 MB (decoded), or an upload body over its cap. Over MCP, a request body over 20.25 MB is a JSON-RPC -32600 error (HTTP 413) instead.
UNSUPPORTED_MEDIA415Not a decodable raster image (e.g. an HTML page, or an SVG: export a PNG) or an unsupported upload content type.
RATE_LIMITED429Hourly image quota used up. Every image the server fetches or decodes and stores counts as an upload (upload tools, image URLs that other tools re-host, studio mockups, external artwork URLs downloaded for a preview or a product mockup); every server-rendered mockup side counts as a render. Requests that fail validation are not charged. The message says when to retry; the HTTP upload routes also send a Retry-After header. Also returned right away when you already have 2 image calls running (wait for them) or the server is busy with other image work (retry in a few seconds).
INTERNAL500Unexpected server error. Details are logged on the server, never sent to the agent. Retry.

Arguments that fail a tool's schema (e.g. x: 120) produce an isError result that starts with MCP error -32602: Input validation error and names the field; an unknown tool name gives an isError result MCP error -32602: Tool … not found. preview_mockup, create_product, update_product_design, add_to_wardrobe and update_wardrobe_item reject unknown or misplaced argument names the same way (Unrecognized key(s) in object: '…', with the path); the other tools ignore extra arguments.

Transport errors are JSON-RPC errors with id: null: -32001 (HTTP 401, missing or invalid token), -32700 (400, invalid JSON), -32600 (400 unreadable body, or 413 when the body is over 20.25 MB), -32603 (500) and -32000 (405 for GET / DELETE, 415 when Content-Type is not application/json, 400 for an unsupported MCP-Protocol-Version header).

Paste this into your AI after connecting the server:

Prompt
Using the printgram MCP server:
1. Create a PRIVATE community called "Neon Ronin Club" (slug neon-ronin-club, tagline "Cyberpunk samurai streetwear").
2. Add a members-only page "Launch Drop".
3. With your own image tool, generate a neon samurai design: a cyberpunk samurai silhouette holding a glowing katana,
   magenta and cyan neon glow, bold clean shapes, transparent background, square, at least 2048 px.
4. Upload it (upload_image_from_url if you have a URL; if the image only exists in this chat, create_upload_link and
   give me the link to drop it on; or give me the curl command from get_upload_instructions).
5. Read get_design_guidelines, then preview_mockup it on a Black (#111111) tee with the front-center preset.
   Look at the image and adjust y / scale until it sits well on the chest with no warnings. Also try a back-full version.
6. Create the product "Neon Ronin Tee" on the Launch Drop page (saved as a draft), with extra colours #FFFFFF and #2563EB.
7. Open the page and the product in my browser, show me the mockups and ask before publishing. Once I say yes, publish both with publish_drafts.
8. Create an invite link with max 25 uses that expires in 168 hours.
Finally give me the product URL, the preview you chose and the invite link.
Troubleshooting
SymptomFix
The client says the server needs authentication, or opens an OAuth loginThe bearer header is missing or the token is invalid: the server answers 401 with WWW-Authenticate: Bearer pointing at the sign-in metadata, which Claude Code, Codex and mcp-remote treat as a cue to sign in with Printgram. Signing in works; to use your token instead, fix the header or token (for Codex, check that the variable named in bearer_token_env_var is exported).
401 Missing / invalid tokenSend exactly Authorization: Bearer tvp_… (44 characters). The token may be revoked or expired, or the account suspended; create a new one on the developer page. With mcp-remote, check that AUTH_HEADER in env is Bearer tvp_… and the argument is Authorization:${AUTH_HEADER}. Test with the curl tools/list call.
405 Method not allowedThe server is stateless and POST-only. Use a Streamable HTTP client or mcp-remote.
FORBIDDENYour role is too low, or the community is private / members-only and you are not a member. get_community returns a permissions object. create_product needs the owner; role changes need the owner or an admin; orders, profile edits, likes / saves, token management and deleting a community require the browser.
A page or product I created is not visible to othersIt is still a draft: only the owner and staff see drafts. Publish it with publish_drafts (after reviewing it) or the Publish button on the page or product page. A product on a draft page stays hidden until the page is published too.
My published design is not visible to others, or not on saleIt is waiting for Printgram staff to verify it (status: "pending_review"; publish_drafts lists it in submittedForReview) and goes on sale by itself once verified. New artwork on a design that was on sale sends it back to review. status: "rejected" means staff did not verify it: the reviewNote (get_product, or the product page) says why. Fix it and publish it again.
Your wardrobe is fullA wardrobe holds up to 100 designs. Remove one with remove_from_wardrobe (orders already placed are not affected), then add the new one.
Draft pages need the database migration …Apply supabase/migrations/20260929000000_page_drafts.sql (supabase db push, or run it in the SQL editor), or create the page with status: "published".
Every new design fails with INTERNAL ("Could not save the product")On Supabase, apply supabase/migrations/20260930000000_design_reviews_and_wardrobe.sql: products now store their review state, and wardrobe designs have no community. The server log names the missing column.
NOT_FOUND for something that existsUse the slug from the URL (/c/<slug>, /p/<slug>) or the id. Staff-only pages, drafts and products you cannot see are reported as not found. Wardrobe designs are found by their id (/wardrobe/<id>, list_wardrobe), and only by their owner.
VALIDATION / -32602Read the message: it names the field and the allowed range (x -260…260, y -260…260, scale 0.3…1.5, slug pattern, page visibility). Slugs follow the website rule and messages exactly: lowercase letters, digits and hyphens, community slugs 1 or 3-48 characters, page slugs 1-80. Reserved slugs are refused with VALIDATION, so pass an explicit slug when the name or title maps to one. Communities: official, new, admin, api, settings, create, create-community, create-product, explore, communities, search, login, logout, signup, auth, profile, account, help, support, about, terms, privacy, mcp, printgram, www. Pages: builder, join, members, new-page, settings, invites, invitations, requests, products, pages, edit, admin, api.
Unrecognized key(s) in objectA misspelt or misplaced argument (e.g. position, or shirt_color inside front). Send artwork_url, side, preset, x, y, scale, rotation at the top level, or per side in front / back (design.front / design.back in previews).
The design sits at the default positionNo placement was given for that side; warnings says so and the placement source is defaults. Pass x / y / scale / rotation or a preset (explicit numbers override the preset).
Image refusedUse a public https:// URL; private addresses are always refused. Expired or signed generator links often return HTTP 403, so download the image and upload the bytes instead. UNSUPPORTED_MEDIA means the URL returned a web page, not an image. upload_image_base64 needs base64, not a URL. Over 15 MB or 8000 px: downscale first.
Upload limit reachedWait for the time in the message (120 per hour, shared with the website uploads, re-hosted images and external artwork URLs downloaded for previews or mockups). Upload generated images once and preview the returned URL: uploaded URLs cost no upload when previewed.
data:image URLsStorage is not configured (demo mode). Pass them through unchanged, or configure Supabase Storage for hosted URLs.
Preview warningsReduce scale or move towards x = 0 (sleeves), increase y (collar), decrease y (hem), use ≥ 2000 px artwork.
Local http developmenthttp://localhost and http://127.0.0.1 work with mcp-remote as they are; add --allow-http only for other plain-http hosts. Set ALLOW_HTTP_IMAGE_FETCH=true only if you must import http:// images.

The server sends these instructions to every client in its initialize response, and most agents read them before using the tools:

MCP_SERVER_INSTRUCTIONS
Printgram is a marketplace of creator communities that sell print-on-demand apparel.
This server acts as the user who owns the API token, with exactly their permissions.

Drafts first, the user decides what goes live:
- create_page and create_product save DRAFTS. Only the owner and the community staff can see a draft (signed in on
  the website); everyone else gets "not found". A product on a draft page stays hidden until the page is published.
- After creating drafts, SHOW them to the user before publishing anything: open each result's review.reviewUrl in
  their browser (macOS: open "<url>"; Windows: start "" "<url>"; Linux: xdg-open "<url>") or give them the links, and
  share the preview_mockup images. Then ask: publish it, change something, or keep it as a draft?
- Only after the user explicitly says yes, call publish_drafts { community, page?, products?, user_confirmed: true };
  one call publishes a page together with its products. To change a draft use update_page, update_product_design or
  update_product (it stays a draft). Pass status "published" when creating only if the user explicitly asked to
  publish without reviewing.

Printgram staff verify every design before it goes on sale:
- Publishing a product (publish_drafts, or create_product with status "published") sends it to Printgram's review:
  its status is "pending_review" and only the owner and staff see it until staff verify it; then it goes on sale by
  itself. A design marked not verified has status "rejected" and a reviewNote saying why: fix it and publish it
  again. Designs using other people's logos, brands, characters or artwork without a licence are not verified.
- New artwork on a design that is on sale sends it back to review. Placement, colour, title and page changes do not.

My Wardrobe (personal designs, for the user only):
- add_to_wardrobe saves a design to the user's own wardrobe: only they can see and order it, it is never listed,
  never reviewed and not for sale to anyone else, and it needs no community. list_wardrobe, update_wardrobe_item and
  remove_from_wardrobe manage it. When the user wants a shirt just for themselves, use the wardrobe, not
  create_product. After saving, open the result's url in their browser so they can check it and order it.

Roles inside a community: owner > admin > moderator > member; you only act on members ranked below you. Only the OWNER
can add products and change privacy (visibility, view_access, purchase_access) or the slug. Owner and admins manage
settings, pages and invite links, and change roles (admins can appoint moderators; only the owner promotes to admin).
Moderators and above remove, ban and unban members, review join requests and send direct invitations; moderators cannot
change roles. Private communities are hidden from listings and only joinable with an invite link, a direct invitation or
an approved join request. view_access "link" storefronts are viewable by anyone with the URL but never listed.
Only the owner publishes posts to the community feed (create_post); members like, comment and reply on the website.

Recommended workflow to launch a design:
1. whoami / list_my_communities — or create_community to start a new storefront (you become the owner).
2. create_page (optional, a draft) to group products into a collection; visibility public | members | private.
3. get_design_guidelines — placement coordinates, presets, shirt colours, garments and fixed prices, artwork requirements.
4. Generate the artwork in your own image tool (transparent PNG, at least 2000px), or use a public image URL.
5. Upload it once: upload_image_from_url (for a public URL); in ChatGPT, upload_image_file with the image from this
   chat; create_upload_link for an image you made in this chat or a file on the user's computer when that is not
   possible (the user drops it on a one-time Printgram page, or your sandbox sends it; then check_upload_link);
   upload_image_base64 only for base64 you already hold as data (never retype a generated image as
   base64: it gets corrupted); or the curl command from get_upload_instructions (terminals with an API token). If a host
   refuses the URL (e.g. HTTP 403 or 429), use an upload link instead. Use the returned url as artwork_url.
6. preview_mockup with that artwork_url; LOOK at the returned image and iterate on x / y / scale / rotation / preset /
   shirt_color (#111111 Black, #FFFFFF White, #2563EB Blue, or #DC2626 Red) until it looks right (nothing is saved while previewing). Placement can be sent at the top level
   ({ artwork_url, side, x, y, scale, rotation, shirt_color }) or per side (design.front / design.back); explicit numbers
   always override a preset, and unknown argument names are rejected, never silently ignored.
7. create_product (a draft) with optional independent front/back artwork (at least one side required; both shirt sides
   are always previewed) and the same values. Later: update_product_design, update_product, archive_product, list_products.
8. Show the user the drafts (review.reviewUrl and the mockups), ask, and publish_drafts only after they approve. Tell
   them designs go on sale once Printgram staff verify them.
For a personal design (just for the user), do steps 3-6, then add_to_wardrobe instead of steps 1, 2, 7 and 8.

Deleting a community and managing API tokens are not available through this server (they need the website, signed in).
API tokens are for store management only: placing orders, editing your profile, likes, comments and wishlist saves need
the website.

Errors come back as isError results with a machine-readable code (FORBIDDEN, NOT_FOUND, VALIDATION, BANNED, ...) and a
human-readable message; read the message before retrying. All URLs in results are absolute.