Skip to main content

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:
StatusMeaning
queuedJob accepted, waiting for a worker
processingWorker is running
completedResult is available in payload.output
failedError 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.