Photolyx

API & MCP server

Generate product photos, infographic cards and videos from your own code — or let an AI assistant do it through the MCP server. Same engine, same prices as the web app.

Quickstart

Create a key in the app under Workspace → API, then queue your first generation. Every request is billed in lyx from the same balance the web app uses.

curl -X POST https://app.photolyx.com/api/v1/generate \
  -H "Authorization: Bearer plx_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "White ceramic mug on a marble kitchen counter, morning light, e-commerce product shot",
    "model": "gpt-image-2.5-flare",
    "size": "1200x1600"
  }'
{
  "id": "0f2c1a4e-…",
  "status": "queued",
  "created_at": "2026-08-07T12:00:00.000Z"
}

Generation is asynchronous: a photo takes 20–60 seconds, a video up to a few minutes. Poll the job or receive a webhook.

Authentication

Pass the key in the Authorization header. X-Api-Key is accepted as an alternative.

Authorization: Bearer plx_live_…

The key is shown once, at creation. We store only its hash, so a lost key cannot be recovered — issue a new one and revoke the old. Up to 10 active keys per account. A key inherits the account’s balance and, for team members, the owner’s shared pool.

POST /api/v1/generate

Queues a generation and answers 202 with the job.

FieldTypeNotes
promptstringRequired, up to 4000 characters. Any language.
modelstringDefaults to gpt-image-2.5-flare. See GET /api/v1/models.
sizestring1024x1024, 1200x1600 (marketplace card), 1152x2048, 2048x1152, wide 1920x1008 (link preview), 2400x960, 2304x768 and more. Models that lack an aspect ratio draw the nearest one and the result is center-cropped.
qualitystringlow · medium · high · xhigh · max (the last two — GPT Image 2.5 only, other models fall back to high). auto is treated as medium. Affects price.
countinteger1–4 variants in one request (images only).
imagestringhttps URL or data:image/…;base64,…. Turns the call into image-to-image: the product is preserved, the scene is rebuilt. With a video model it becomes the first frame; next to video_url — a look reference (@Image1).
durationintegerVideo length in seconds: seedance-2.5 4–30, wan3.0-video 2–30, grok-imagine-video-1.5 1–15. Other video models are fixed at 6 s. Affects price.
aspect_ratiostringVideo aspect ratio: 16:9, 9:16, 1:1, 4:3, 3:4.
video_urlstringSource video for seedance-2.5 (selected automatically): https URL, MP4/MOV/WebM, 2–30 s (longer clips are trimmed to the first 30 s), up to 100 MB. Videos with real people’s faces are rejected by the provider. Price counts the source seconds plus the result seconds.
video_modestringWith video_url: edit (default) — change something in the video, the result keeps its length and aspect ratio (needs 4+ s, no duration); extend — continue the video from its last frame for duration seconds, prompt optional; reference — a new video that copies the motion and camera of the source, with your aspect_ratio and duration.
backgroundstringtransparent for a cut-out PNG (OpenAI models).
webhook_urlstringOverrides the key’s default callback for this job.

GET /api/v1/jobs/:id

curl https://app.photolyx.com/api/v1/jobs/0f2c1a4e-… \
  -H "Authorization: Bearer plx_live_…"
{
  "id": "0f2c1a4e-…",
  "status": "succeeded",
  "created_at": "2026-08-07T12:00:00.000Z",
  "finished_at": "2026-08-07T12:00:34.000Z",
  "result": {
    "items": [
      { "id": 91422, "url": "/uploads/gen-….png", "type": "image" }
    ],
    "model": "gpt-image-2.5-flare",
    "credits_spent": 5,
    "credits_balance": 995
  }
}

status is one of queued, running, succeeded, failed. URLs are relative to https://app.photolyx.com and are publicly readable, so you can hand them straight to a marketplace. Failed jobs are never charged: lyx are returned automatically.

GET /api/v1/jobs lists your recent jobs.

Webhooks

Set a default callback per key in the cabinet, or pass webhook_url per request. We POST the same body as GET /api/v1/jobs/:id, wrapped in an event, and retry twice (after 5 and 30 seconds) until you answer 2xx.

POST https://your-service.com/photolyx
x-photolyx-timestamp: 1786000000
x-photolyx-signature: sha256=…

{ "event": "job.succeeded", "data": { … } }

Verify the signature — otherwise anyone can forge a callback. It is an HMAC-SHA256 of {timestamp}.{raw body} keyed with the webhook secret shown when the key was created.

import { createHmac, timingSafeEqual } from "crypto";

const expected = "sha256=" + createHmac("sha256", process.env.PHOTOLYX_WEBHOOK_SECRET)
  .update(req.headers["x-photolyx-timestamp"] + "." + rawBody)
  .digest("hex");

const ok = expected.length === signature.length &&
  timingSafeEqual(Buffer.from(expected), Buffer.from(signature));

Idempotency

Send an Idempotency-Key header with any generation request. A repeat with the same key returns the original job with status 200 instead of creating (and charging) a second one — so a client timeout never costs you twice.

Idempotency-Key: order-4471-main-photo

Balance & models

GET /api/v1/balance  → { "balance": 980, "currency": "lyx", … }
GET /api/v1/models   → { "data": [ { "id": "gpt-image-2.5-flare", "kind": "image",
                                     "price_lyx": { "low": 5, "medium": 5, "high": 20, "max": 75 } } ], … }

Prices come from the same calculation that charges you, so the catalogue can never drift from the invoice. 1 lyx = $0.01 at list price.

Errors & limits

Every error has the same shape:

{ "error": { "code": "insufficient_credits", "message": "…" } }
unauthorized401 — missing, malformed or revoked key.
invalid_request400 — bad parameters.
insufficient_credits402 — top up the balance.
moderation_blocked422 — the provider’s content filter rejected it. Rephrase.
rate_limited429 — 120 requests per minute per key.
too_many_jobs429 — at most 8 jobs pending at once. Poll, then submit more.
generation_failed502 — provider failure. Lyx are refunded.

MCP server

Photolyx also speaks the Model Context Protocol (stateless HTTP; the revision is negotiated with your client, 2024-11-05 through 2026-07-28). Point any MCP host — Claude Code, Claude Desktop, ChatGPT, Cursor — at the endpoint below and your assistant can shoot product photos and update marketplace cards on its own. The API key is the only credential: nothing to install, no OAuth round-trip.

Claude Code — one command:

claude mcp add --transport http photolyx https://app.photolyx.com/api/mcp \
  --header "Authorization: Bearer plx_live_…"

Cursor, Windsurf, ChatGPT developer mode and anything else configured by file:

{
  "mcpServers": {
    "photolyx": {
      "type": "http",
      "url": "https://app.photolyx.com/api/mcp",
      "headers": { "Authorization": "Bearer plx_live_…" },
      "timeout": 600000
    }
  }
}

Keep "type": "http" — an entry that has a url but no type is read as a local stdio server and skipped.timeout (ms) is optional but recommended: hosts cut off a single request after 60 seconds by default, and video jobs run longer than that.

Claude Desktop and claude.ai: Settings → Connectors → Add custom connector, the URL above, then a request header authorization with the value Bearer plx_live_….

Tools exposed:

  • generate_image — product photos, cards, icons, illustrations; image-to-image editing; videos, including editing, extending or re-shooting your own clip with Seedance 2.5 (video_url + video_mode). Returns the images inline when the job finishes within ~50 seconds, otherwise a job_id for get_job.
  • build_marketplace_card_prompt — reads the current Wildberries top for a query, including recurring complaints in its reviews, and returns a compliant infographic prompt plus three gallery slides. Free.
  • list_wb_products · set_wb_main_photo — read the seller’s catalogue and replace a card’s main photo.
  • get_job · get_balance.

Marketplace tools require the account to be linked in the app first (API tab → Stores).