Visual Commerce
Pi’s visual commerce suite covers the full static ad lifecycle: generate a hero campaign image, refine it with targeted edits, spin off localized variants for every market, and create brand avatars for editorial and social use.
Every creation endpoint returns 202 Accepted + a job_id. Poll GET /api/v1/jobs/:id for the result.
Campaign generation
POST /api/v1/campaigns/generate produces premium static campaign ads. Pass an optional brand_id (from brand extraction) to ground the output in extracted brand identity.
export BASE="https://api.example.com"
export API_KEY="pi_live_***"
generate_job_id=$(curl -sS -X POST "$BASE/api/v1/campaigns/generate" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Generate a Gen Z Coca Cola static ad for summer vibes in Paris",
"reference_images": ["https://example.com/ref1.jpg"],
"brand_id": "11111111-2222-3333-4444-555555555555",
"output": { "aspect_ratio": "4:5", "resolution": "1K", "thinking_intensity": "high" }
}' | jq -r '.data.job_id')
# Poll for result
curl -sS "$BASE/api/v1/jobs/$generate_job_id?wait_for_completion=true&timeout_seconds=30&expand=ad" \
-H "Authorization: Bearer $API_KEY"
Response (202)
{
"id": "req_pi_...",
"object": "job",
"status": "queued",
"created_at": 1760000000,
"data": { "job_id": "11111111-2222-3333-4444-555555555555" }
}
When completed, the job payload contains data.ad.image_url (when using expand=ad) or data.payload.image_url.
Pass brand_id from a prior extraction to automatically ground every visual in extracted brand colors, typography rules, and style conditioning. See the Brand Pipeline guide.
OpenAI-compatible image generation
POST /api/v1/images/generations is the OpenAI-compatible endpoint for general async image creation.
curl -sS -X POST "$BASE/api/v1/images/generations" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A minimalist product shot on a white background, studio lighting",
"output": { "aspect_ratio": "1:1", "resolution": "1K" }
}'
Use POST /api/v1/campaigns/generate when you need brand-grounded campaign quality. Use POST /api/v1/images/generations for general image generation with the same async job pattern.
Editing generated images
Pass the job_id from a prior generation (or a previous edit) as source_job_id. Pi resolves the source image automatically — no need to extract or copy image URLs.
edit_job_id=$(curl -sS -X POST "$BASE/api/v1/campaigns/edit" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"source_job_id\": \"$generate_job_id\",
\"prompt\": \"Change environment to rooftop sunset; keep product placement and layout identical\"
}" | jq -r '.data.job_id')
source_job_id accepts both generation and previous edit job IDs. Chain as many edits as you need — each edit references the previous step’s job_id.
Localizing ads for markets
POST /api/v1/campaigns/localize-ad adapts copy, cultural cues, and currency for a target market while preserving composition. Pass source_job_id from a generate or edit job.
fr_job_id=$(curl -sS -X POST "$BASE/api/v1/campaigns/localize-ad" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"prompt\": \"Keep composition exactly the same\",
\"source_job_id\": \"$generate_job_id\",
\"target_culture\": \"French urban premium\",
\"target_language\": \"fr\",
\"target_currency\": \"EUR\",
\"brand_id\": \"$brand_id\"
}" | jq -r '.data.job_id')
# Poll localized result
curl -sS "$BASE/api/v1/jobs/$fr_job_id?wait_for_completion=true&timeout_seconds=30&expand=ad" \
-H "Authorization: Bearer $API_KEY"
To localize to multiple markets in parallel, fire multiple localize-ad calls from the same source_job_id before any of them complete:
# Fire all three in parallel — all reference the same source_job_id
fr_job_id=$(curl -sS -X POST "$BASE/api/v1/campaigns/localize-ad" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"source_job_id\":\"$generate_job_id\",\"target_language\":\"fr\",\"target_currency\":\"EUR\",\"target_culture\":\"French urban premium\",\"prompt\":\"Keep composition exactly.\"}" \
| jq -r '.data.job_id') &
ar_job_id=$(curl -sS -X POST "$BASE/api/v1/campaigns/localize-ad" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d "{\"source_job_id\":\"$generate_job_id\",\"target_language\":\"ar\",\"target_currency\":\"AED\",\"target_culture\":\"Gulf luxury lifestyle\",\"prompt\":\"Keep composition exactly.\"}" \
| jq -r '.data.job_id') &
wait
Avatar generation
POST /api/v1/avatars/generate creates marketing-style avatar images. Supply a prompt (and optional hints) or bring your own reference_images.
avatar_job_id=$(curl -sS -X POST "$BASE/api/v1/avatars/generate" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "confident founder vibe for a fintech app, modest professional outfit",
"hints": {
"filter_culture": "middle_east",
"avatar_composition": "Portrait",
"art_direction": "Ultra Realistic"
},
"output": {
"aspect_ratio": "auto",
"resolution": "1K",
"thinking_intensity": "minimal"
},
"client_reference_id": "n8n-run-2026-03-26T12:00:00Z-step-7"
}' | jq -r '.data.job_id')
# Poll for avatar
curl -X GET "$BASE/api/v1/jobs/$avatar_job_id?wait_for_completion=true&timeout_seconds=30&expand=avatar" \
-H "Authorization: Bearer pi_live_***"
Saving avatars for reuse
Once a generation job is complete, persist the avatar so you can reuse its URL without regenerating:
curl -X POST "$BASE/api/v1/avatars/save" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_id":"<job_id>","label":"CEO hero v2"}'
List saved avatars with GET /api/v1/avatars and retrieve one by id with GET /api/v1/avatars/:id.
Polling pattern
All visual commerce endpoints share the same job lifecycle:
| Status | Meaning |
|---|
queued | Job accepted, waiting for a worker |
processing | Worker is running |
completed | Result is available in payload.output |
failed | Error logged in error_log |
# Long-poll — Pi holds the connection for up to timeout_seconds
curl -sS "$BASE/api/v1/jobs/$job_id?wait_for_completion=true&timeout_seconds=30&expand=ad" \
-H "Authorization: Bearer $API_KEY"
Use Idempotency-Key on all mutation calls. If your network request times out before receiving the 202, retrying with the same key and body is safe — Pi replays the original queued response.