Build · WooCommerce
Build a WooCommerce try-on plugin
In one sentence
A WooCommerce plugin calls the Photta API over plain HTTP from a WP-Cron event, polls the job on the following tick, and imports the finished image into the media library.
- Credits: 5 credits at 2K · 6 credits at 4K
- Typical time: 1.5–4 min
- Processing: async, polling
Every figure on this page comes from the Photta API docs · checked
No PHP SDK is needed. WP-Cron submits the job, the next tick polls it, and WP-CLI imports the finished image into the media library.
RESPONSE=$(curl -s -X POST https://api.photta.app/api/v1/tryon/apparel \
-H "Authorization: Bearer $PHOTTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"product_type": "top",
"product_images": [
"https://cdn.yourstore.com/products/shirt-front.jpg"
],
"product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
"product_category": "t_shirt",
"mannequin_id": "mnk_f3a9b1",
"pose_id": "pose_c2d4e5",
"background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
"lock_background": true,
"resolution": "2K",
"aspect_ratio": "3:4",
"body_size": "M",
"expression": "neutral",
"user_instruction": "Keep the collar pattern crisp and centered."
}')
ID=$(echo "$RESPONSE" | jq -r '.data.id')const res = await fetch("https://api.photta.app/api/v1/tryon/apparel", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.PHOTTA_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"product_type": "top",
"product_images": [
"https://cdn.yourstore.com/products/shirt-front.jpg"
],
"product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
"product_category": "t_shirt",
"mannequin_id": "mnk_f3a9b1",
"pose_id": "pose_c2d4e5",
"background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
"lock_background": true,
"resolution": "2K",
"aspect_ratio": "3:4",
"body_size": "M",
"expression": "neutral",
"user_instruction": "Keep the collar pattern crisp and centered."
}),
});
const { data } = await res.json(); // data.id, data.statusimport os, requests
res = requests.post(
"https://api.photta.app/api/v1/tryon/apparel",
headers={"Authorization": f"Bearer {os.environ['PHOTTA_API_KEY']}"},
json={
"product_type": "top",
"product_images": [
"https://cdn.yourstore.com/products/shirt-front.jpg"
],
"product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
"product_category": "t_shirt",
"mannequin_id": "mnk_f3a9b1",
"pose_id": "pose_c2d4e5",
"background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
"lock_background": True,
"resolution": "2K",
"aspect_ratio": "3:4",
"body_size": "M",
"expression": "neutral",
"user_instruction": "Keep the collar pattern crisp and centered."
},
)
data = res.json()["data"] # data["id"], data["status"]How it works
A cron tick, a poll and a media import
Plan the plugin, schedule the work, submit, poll on the next tick, import.
Step 1
Plan the plugin
One queue table, one cron hook, one settings screen for the key. Generation never happens during a page request, so nothing a shopper does can hang on it.
Step 2
Get a Photta API key
Read it from the environment in wp-config.php rather than storing it in the options table, so a database export does not carry your credentials.
Step 3
Schedule the cron event
Register a recurring hook and take a handful of queued products per run. A site with real traffic gets frequent ticks; a quiet one needs a real system cron.
Step 4
Submit the product
Send the product image with wp_remote_post — the same URL, headers and JSON body as the cURL sample. Store the returned id on the queue row.
Step 5
Poll on the next tick
Read the status for every row still processing. Because ticks are minutes apart, one poll per tick is usually enough; nothing sleeps inside a request.
Step 6
Import the image
Once a job is complete, import the URL into the media library and attach it to the product. WP-CLI does the download so the plugin never buffers the file.
The plugin’s four moving parts
The Photta calls are rendered from the API documentation; wp_remote_post takes the same URL, headers and body.
# wp-config.php holds the key; the plugin reads it with getenv()
# define( 'PHOTTA_API_KEY', getenv( 'PHOTTA_API_KEY' ) );
export PHOTTA_API_KEY="photta_live_xxx"
export PHOTTA_BASE_URL="https://api.photta.app/api/v1"RESPONSE=$(curl -s -X POST https://api.photta.app/api/v1/tryon/apparel \
-H "Authorization: Bearer $PHOTTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"product_type": "top",
"product_images": [
"https://cdn.yourstore.com/products/shirt-front.jpg"
],
"product_back_image": "https://cdn.yourstore.com/products/shirt-back.jpg",
"product_category": "t_shirt",
"mannequin_id": "mnk_f3a9b1",
"pose_id": "pose_c2d4e5",
"background": "https://cdn.yourstore.com/backgrounds/studio-loft.jpg",
"lock_background": true,
"resolution": "2K",
"aspect_ratio": "3:4",
"body_size": "M",
"expression": "neutral",
"user_instruction": "Keep the collar pattern crisp and centered."
}')
ID=$(echo "$RESPONSE" | jq -r '.data.id')# $ID is data.id from the POST response. Poll every 3 seconds until status is "completed" or "failed".
for i in $(seq 1 120); do
RESULT=$(curl -s "https://api.photta.app/api/v1/tryon/apparel/$ID" -H "Authorization: Bearer $PHOTTA_API_KEY")
STATUS=$(echo "$RESULT" | jq -r '.data.status')
[ "$STATUS" = "completed" ] && break
[ "$STATUS" = "failed" ] && echo "generation failed" && exit 1
sleep 3
done
echo "$RESULT" | jq -r '.data.output_url'# WP-CLI does the download, so the plugin never buffers the file itself
wp media import "$OUTPUT_URL" \
--post_id="$PRODUCT_ID" \
--title="Virtual try-on" \
--featured_imageQuestions developers ask
Questions WordPress developers ask
Is there an official PHP SDK?
No. The API is plain REST over HTTPS with a bearer header, so wp_remote_post covers everything without a dependency to keep updated.
How do I make the call from PHP?
wp_remote_post with the Authorization and Content-Type headers and a JSON body. The cURL sample on this page is the same request, field for field.
Why not generate on page load?
A generation takes longer than a page should. Rendering during a request means a shopper waits, a timeout kills it, and the credits are spent either way.
How many products should one run take?
Take a small slice per tick and let the queue drain across ticks. That keeps every run short and keeps you well inside the published rate limit.
What happens to failed jobs?
Mark the row failed and keep the error type from the response. Credits for a failed generation are refunded automatically, so a retry does not pay twice.
Related pages
Related reading
Capabilities
Virtual try-on API for apparel
REST API that places apparel on AI mannequins. Asynchronous, 1.5–4 minute generation, 5 credits at 2K, 6 at 4K. cURL, Node.js and Python examples.
ReadQuickstart
First Photta image in under five minutes.
Sign up, generate an API key, run your first cURL call, poll until it completes, persist the result. Five steps, documented end-to-end.
ReadLanguages
cURL walkthrough
ReadErrors
Read
Every figure on this page comes from the Photta API docs · checked
A plugin without a dependency tree
Bearer header, JSON body, polling. Everything WordPress already does.