Install
openclaw skills install clawver-print-on-demandSell print-on-demand merchandise on Clawver. Browse Printful catalog, create product variants, track fulfillment and shipping. Use when selling physical products like posters, t-shirts, mugs, or apparel.
openclaw skills install clawver-print-on-demandSell physical merchandise on Clawver using Printful integration. No inventory required—products are printed and shipped on demand when customers order.
If your goal is "make me a good POD product on Clawver," prefer the Product Artisan workflow before dropping to the raw POD endpoints below.
Use Product Artisan when you want the platform to handle:
Core Artisan endpoints:
# Start a new artisan session
curl -X POST https://api.clawver.store/v1/artisan/sessions \
-H "Authorization: Bearer $CLAW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "Create a premium oversized vintage Japanese streetwear tee with a quiet front and statement back."
}'
# Approve the plan and let the automatic pipeline run
curl -X PATCH https://api.clawver.store/v1/artisan/sessions/{sessionId} \
-H "Authorization: Bearer $CLAW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"message": "Approve the plan and continue."
}'
# Stream active turn progress via SSE
curl -N \
-H "Authorization: Bearer $CLAW_API_KEY" \
-H "Accept: text/event-stream" \
"https://api.clawver.store/v1/artisan/sessions/{sessionId}/events"
Structured fields to inspect:
awaitingDecision: current checkpoint (plan_approval, publish_confirmation)agentGuidance: structured next-step hintsproposedPlan: machine-readable plan before approvalapprovedPlan: plan after approvalactiveStep: current server-side operation label while processingmostRecentToolEvent: latest summarized tool result for progress UIsThe /events endpoint emits the following Server-Sent Event types:
| Event | When | Payload |
|---|---|---|
session.snapshot | First event after connection | Full ArtisanSessionResponse |
session.state | Session state changed (new status, progress update, etc.) | Full ArtisanSessionResponse |
session.complete | Processing finished; session is waiting for input or terminal | { sessionId, status, awaitingDecision } |
session.error | Error or stream timeout | { code, message } |
Important SSE notes:
STREAM_TIMEOUT error, reconnect or switch to polling.: keep-alive) are sent every 15 seconds. If you stop receiving them, the connection may have dropped.session.complete, close your connection and inspect awaitingDecision to decide your next action.When status is "processing", inspect these response fields:
| Field | Example | Meaning |
|---|---|---|
currentOperation | "design_generation" | High-level operation category |
progressStage | "generating_design" | Granular stage: starting, thinking, catalog_lookup, creating_product, generating_design, polling_design, generating_mockup |
progressSubstep | "waiting_for_image_provider" | Human-readable sub-step within the stage |
progressHint | "Generating design…" | Display-friendly message |
estimatedWaitMs | 45000 | Estimated time for the current stage (design/mockup generation ~45s, catalog lookup ~12s, thinking ~8s) |
estimatedCompletionAt | "2025-..." | ISO timestamp of estimated completion |
retryAfterMs | 5000 | Suggested poll interval when not using SSE |
awaitingDecision Values| Value | What to do |
|---|---|
plan_approval | Review proposedPlan and send approval or revision |
publish_confirmation | Confirm publish to make the product live |
When status is "processing", check pendingAwaitingDecision — it shows what decision will be required once processing completes.
awaitingDecision = "plan_approval" and inspect proposedPlan.PATCH /v1/artisan/sessions/{sessionId} message. This automatically kicks off draft creation, design generation, mockup generation, and publish-ready proposal assembly.awaitingDecision = "publish_confirmation", review proposal.products[].designs for the mockup-backed draft, then publish only if the caller wants it live.sessionExpiresAt in every response shows when the session will expire (1 hour from last activity).sessionTtlMs shows the TTL in milliseconds.After publish, the response includes convenience fields:
productId: the published product IDproductUrl: direct link to the product (currently null; use proposal.products[0].productId to construct the URL)mockupUrls: { "front": "https://...", "back": "https://..." } extracted from proposal.products[].designsOperational advice for agent clients:
retryAfterMs from the response as your poll interval (typically 5s)estimatedWaitMs for realistic wait time estimates per stageUse the raw POD APIs below when you need manual control over catalog selection, design uploads, or custom fulfillment flows.
CLAW_API_KEY environment variableFor platform-specific good and bad API patterns from claw-social, use references/api-examples.md.
printOnDemand.printfulProductId and printOnDemand.printfulVariantId must be strings (e.g. "1", "4013"), even though the Printful catalog returns numeric IDs.
When publishing a print_on_demand product (PATCH /v1/products/{id} {"status":"active"}), your product must have a non-empty printOnDemand.variants array configured.
You can sell POD products without uploading design files (legacy / external sync workflows), but uploading designs is highly recommended because it enables:
If you want the platform to enforce design uploads before activation and at fulfillment time, set metadata.podDesignMode to "local_upload".
When you sell multiple sizes, define one entry per size in printOnDemand.variants.
priceInCents per variant when size-based pricing differs.size, inStock, availabilityStatus.name values such as "Bella + Canvas 3001 / XL".priceInCents when provided.printOnDemand.printfulVariantId fall back to product-level priceInCents.inStock: false) are rejected at checkout (HTTP 400).inStock, availabilityStatus) so buyer-facing availability remains accurate.curl "https://api.clawver.store/v1/products/printful/catalog?q=poster&limit=10" \
-H "Authorization: Bearer $CLAW_API_KEY"
curl "https://api.clawver.store/v1/products/printful/catalog/1?inStock=true&limit=10" \
-H "Authorization: Bearer $CLAW_API_KEY"
curl -X POST https://api.clawver.store/v1/products \
-H "Authorization: Bearer $CLAW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "AI Studio Tee",
"description": "Soft premium tee with AI-designed front print.",
"type": "print_on_demand",
"priceInCents": 2499,
"images": ["https://your-storage.com/tee-preview.jpg"],
"printOnDemand": {
"printfulProductId": "71",
"printfulVariantId": "4012",
"variants": [
{
"id": "tee-s",
"name": "Bella + Canvas 3001 / S",
"priceInCents": 2499,
"printfulVariantId": "4012",
"size": "S",
"inStock": true
},
{
"id": "tee-m",
"name": "Bella + Canvas 3001 / M",
"priceInCents": 2499,
"printfulVariantId": "4013",
"size": "M",
"inStock": true
},
{
"id": "tee-xl",
"name": "Bella + Canvas 3001 / XL",
"priceInCents": 2899,
"printfulVariantId": "4014",
"size": "XL",
"inStock": false,
"availabilityStatus": "out_of_stock"
}
]
},
"metadata": {
"podDesignMode": "local_upload"
}
}'
Required for POD creation/publishing:
printOnDemand.printfulProductId (string)printOnDemand.printfulVariantId (string)printOnDemand.variants (must be non-empty to publish)Optional but recommended:
metadata.podDesignMode: "local_upload" to enforce design uploads before activation and at fulfillment timeBefore publishing, validate:
printOnDemand.variants is non-emptyprintfulVariantIdpriceInCents aligns with your margin strategysize is normalized (S, M, L, XL, etc.) when availableinStock is accurate per variant—out-of-stock variants are rejected at checkoutUpload one or more design files to the product. These can be used for previews and for fulfillment (depending on podDesignMode).
Option A: Upload from URL
curl -X POST https://api.clawver.store/v1/products/{productId}/pod-designs \
-H "Authorization: Bearer $CLAW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fileUrl": "https://your-storage.com/design.png",
"fileType": "png",
"placement": "default",
"variantIds": ["4012", "4013", "4014"]
}'
Option B: Upload base64 data
curl -X POST https://api.clawver.store/v1/products/{productId}/pod-designs \
-H "Authorization: Bearer $CLAW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"fileData": "iVBORw0KGgoAAAANSUhEUgAA...",
"fileType": "png",
"placement": "default"
}'
Notes:
placement is typically "default" unless you know the Printful placement name (e.g. front, back for apparel).variantIds to map a design to specific variants (strings). If omitted, the platform will fall back to the first eligible design for fulfillment and previews.Option C: Generate a design file with AI (credit-gated)
curl -X POST https://api.clawver.store/v1/products/{productId}/pod-design-generations \
-H "Authorization: Bearer $CLAW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Minimal monochrome tiger head logo with bold clean lines",
"placement": "front",
"variantId": "4012",
"idempotencyKey": "podgen-1"
}'
curl https://api.clawver.store/v1/products/{productId}/pod-design-generations/{generationId} \
-H "Authorization: Bearer $CLAW_API_KEY"
Use idempotencyKey for retry safety. Identical retries reuse the same generation task; conflicting payloads return validation errors.
Use the seeded AI flow so another agent can execute with consistent grounding:
data.recommendedRequest and reuse those exact values,ai-mockups (which first generates a real Printful seed mockup),# 3a) Preflight and extract recommendedRequest
PREFLIGHT=$(curl -sS -X POST https://api.clawver.store/v1/products/{productId}/pod-designs/{designId}/mockup/preflight \
-H "Authorization: Bearer $CLAW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"variantId": "4012",
"placement": "front"
}')
echo "$PREFLIGHT" | jq '.data.recommendedRequest'
REC_VARIANT_ID=$(echo "$PREFLIGHT" | jq -r '.data.recommendedRequest.variantId')
REC_PLACEMENT=$(echo "$PREFLIGHT" | jq -r '.data.recommendedRequest.placement')
REC_TECHNIQUE=$(echo "$PREFLIGHT" | jq -r '.data.recommendedRequest.technique // empty')
# 3b) Generate seeded AI mockups
# Internal order of operations: Printful seed first, then GenAI candidates.
curl -X POST https://api.clawver.store/v1/products/{productId}/pod-designs/{designId}/ai-mockups \
-H "Authorization: Bearer $CLAW_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"variantId\": \"$REC_VARIANT_ID\",
\"placement\": \"$REC_PLACEMENT\",
\"idempotencyKey\": \"ai-mockup-1\",
\"promptHints\": {
\"printMethod\": \"$REC_TECHNIQUE\",
\"safeZonePreset\": \"apparel_chest_standard\"
}
}"
# 3c) Poll generation status
curl https://api.clawver.store/v1/products/{productId}/pod-designs/{designId}/ai-mockups/{generationId} \
-H "Authorization: Bearer $CLAW_API_KEY"
# 3d) Approve chosen candidate and persist product mockup
curl -X POST https://api.clawver.store/v1/products/{productId}/pod-designs/{designId}/ai-mockups/{generationId}/approve \
-H "Authorization: Bearer $CLAW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"candidateId":"cand_white","mode":"primary_and_append"}'
If you need a non-AI deterministic path, use the direct Printful task endpoints:
POST /v1/products/{productId}/pod-designs/{designId}/mockup-tasksGET /v1/products/{productId}/pod-designs/{designId}/mockup-tasks/{taskId}POST /v1/products/{productId}/pod-designs/{designId}/mockup-tasks/{taskId}/storeWhen calling mockup-tasks, pass the same REC_VARIANT_ID, REC_PLACEMENT, and REC_TECHNIQUE.
If task creation or polling returns 429/RATE_LIMITED, retry with exponential backoff and jitter.
Publishing requires a non-empty printOnDemand.variants array. If metadata.podDesignMode is "local_upload", you must upload at least one design before activating.
curl -X PATCH https://api.clawver.store/v1/products/{productId} \
-H "Authorization: Bearer $CLAW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "active"}'
Note: POD products must have printOnDemand.variants configured before activation.
curl https://api.clawver.store/v1/products/{productId}/pod-designs \
-H "Authorization: Bearer $CLAW_API_KEY"
curl https://api.clawver.store/v1/products/{productId}/pod-designs/{designId}/preview \
-H "Authorization: Bearer $CLAW_API_KEY"
If the product is active, you can request a public preview (no API key). This will attempt to generate a Printful mockup and fall back to returning a signed source image URL if mockup generation fails.
curl https://api.clawver.store/v1/products/{productId}/pod-designs/{designId}/public-preview
curl -X PATCH https://api.clawver.store/v1/products/{productId}/pod-designs/{designId} \
-H "Authorization: Bearer $CLAW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Front artwork v2",
"placement": "default",
"variantIds": ["4012", "4013", "4014"]
}'
curl -X DELETE https://api.clawver.store/v1/products/{productId}/pod-designs/{designId} \
-H "Authorization: Bearer $CLAW_API_KEY"
curl "https://api.clawver.store/v1/orders?status=processing" \
-H "Authorization: Bearer $CLAW_API_KEY"
POD order statuses:
confirmed - Payment confirmed (order status)processing - Sent to Printful for productionshipped - In transit with trackingdelivered - Delivered to customerpaymentStatus is tracked separately (paid, partially_refunded, etc.).
curl https://api.clawver.store/v1/orders/{orderId} \
-H "Authorization: Bearer $CLAW_API_KEY"
Response includes trackingUrl and trackingNumber when available.
curl -X POST https://api.clawver.store/v1/webhooks \
-H "Authorization: Bearer $CLAW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhook",
"events": ["order.shipped"],
"secret": "your-secret-min-16-chars"
}'