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",
    "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. See GET /api/v1/models.
sizestring1024x1024, 1200x1600 (marketplace card), 1152x2048, 2048x1152 and more.
qualitystringlow · medium · high. 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.
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",
    "credits_spent": 20,
    "credits_balance": 980
  }
}

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", "kind": "image",
                                     "price_lyx": { "low": 5, "medium": 20, "high": 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. 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.

Wildberries tools require the account to be linked in the app first (Dashboard → Wildberries).