Dokumentáció: REST API v1

Fejlesztői dokumentáció

Partner REST API (v1)

A FotóAI stabil, verziózott REST API-ja: OAuth 2.1 partnereknek, személyes API-kulcs, végpontok, feltöltés, webhookok.

Alapok

Alapcím
https://fotoai.hu
Formátum
Csak JSON, UTF-8. Az idő ISO 8601 UTC. A pénz egész szám millikreditben (1 kredit = 1000 mc).
Hitelesítés
Authorization: Bearer fai_oat_… (OAuth) vagy Bearer fai_live_… (személyes API-kulcs). Böngészős munkamenet itt nem azonosít.
Verzió
v1 – a stabil, nyilvános szerződés. Új integrációhoz ezt használd; az MCP-eszközök alakja az MCP-kliensek igényeit követi.

Az API, az MCP-szerver és a CLI ugyanazzal a kóddal áraz és generál, mint a web, ezért soha nem mondanak mást egy árról vagy egy csomagkorlátról.

OAuth 2.1 partnereknek

A FotóAI az engedélyezési szerver, a partner bizalmas (titkos kulccsal rendelkező) kliens. Metaadatok: https://fotoai.hu/.well-known/oauth-authorization-server (RFC 8414). Kliensregisztráció nálunk történik – dinamikus regisztráció nincs; írj, ha partneralkalmazást építesz.

  1. Engedélyezés. Irányítsd a felhasználót ide. A PKCE (S256) kötelező, a titkos kulccsal rendelkező kliensnek is.
  2. Jóváhagyás. A felhasználó belép (Google vagy e-mailes link), és a magyar nyelvű jóváhagyó képernyőn engedélyez. Ha már van érvényes, a kért jogokat lefedő engedélye, a képernyő kimarad.
  3. Visszairányítás. redirect_uri?code=fai_oac_…&state=…, elutasításkor error=access_denied.
  4. Token. A kódot (egyszer használható, 60 mp) a token-végponton váltod be.
1. Engedélyezési cím
https://fotoai.hu/oauth/authorize?response_type=code
  &client_id=<client_id>
  &redirect_uri=<pontosan a regisztrált cím>
  &scope=profile%20read%20generate
  &state=<véletlen>
  &code_challenge=<base64url(SHA-256(verifier))>
  &code_challenge_method=S256
4. Token kérése
curl -s https://fotoai.hu/oauth/token \
  -u "<client_id>:<client_secret>" \
  -d grant_type=authorization_code -d code=fai_oac_… \
  -d redirect_uri=<ugyanaz> -d code_verifier=<verifier>
Válasz
{ "access_token": "fai_oat_…", "token_type": "Bearer", "expires_in": 3600,
  "refresh_token": "fai_ort_…", "scope": "profile read generate" }
Token / műveletSzabály
Hozzáférési tokenfai_oat_…, 1 óra
Frissítő tokenfai_ort_…, 90 nap, minden használatkor cserélődik; egy már lecserélt token újbóli bemutatása a teljes engedélyt visszavonja.
Frissítésgrant_type=refresh_token&refresh_token=fai_ort_… – a scope szűkíthető, nem bővíthető.
VisszavonásPOST /oauth/revoke (RFC 7009) bármelyik tokennel: a teljes engedély megszűnik.
Kliens-hitelesítésclient_secret_basic vagy client_secret_post – egyszerre csak az egyik.

A felhasználó a kapcsolatot a Beállítások → Kapcsolt alkalmazások oldalon bármikor leválaszthatja; a tokenek azonnal érvénytelenné válnak.

Jogosultságok (scope)

ScopeMit enged
profileE-mail és név (GET /api/v1/me).
readEgyenleg, modellek, stílusok, árajánlatok, generálások és kimenetek.
generateGenerálás és lemondás, feltöltés – kreditet költ.

A személyes API-kulcs read és generate jogú, és a /api/v1/me-nél profile-nak is számít. A böngészőhöz kötött útvonalak (számlázás, fiók, API-kulcsok, kapcsolt alkalmazások) tokennel 403 FORBIDDEN (session_only) választ adnak.

Végpontok

MetódusÚtvonalScopeLeírás
GET/api/v1/meprofile és/vagy readFiók: e-mail, név (profile), egyenleg (read).
GET/api/v1/models?category=image|video|audio|toolreadModellek módokkal, paraméterekkel és ártáblával.
GET/api/v1/stylesreadStílusok és fotócsomagok.
POST/api/v1/quoteread vagy generateÁrajánlat, 10 percig tárolva (quote_id).
POST/api/v1/generationsgenerateGenerálás indítása (202).
GET/api/v1/generations?cursor=&limit=readA legutóbbi generálások, minden forrásból.
GET/api/v1/generations/:idreadÁllapot és kimenetek (:id lehet csomagfuttatás group_id-ja is).
DELETE/api/v1/generations/:idgenerateLemondás, a lefoglalt kredit felszabadul.
POST/api/v1/uploadsgenerateElőre aláírt PUT-cím feltöltéshez.
POST/api/v1/uploads/:id/completegenerateFeltöltés lezárása → asset_id.

Árajánlat és generálás

Előbb kérj árat. A quote_id 10 percig érvényes, és felső korlátként kötöd a generáláshoz: a szerver újra áraz, és ha az ár a korlát fölé menne, 409 PRICE_CHANGED a válasz, és semmi nem indul el.

Árajánlat
curl -s https://fotoai.hu/api/v1/quote \
  -H "Authorization: Bearer $FOTOAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"image.seedream-5-lite","params":{"aspect":"3:4"},"prompt":"portré egy kávézóban"}'
Válasz
{ "millicredits": 1000, "credits": 1, "breakdown": [{ "label": "…", "millicredits": 1000 }],
  "quote_id": "uuid", "expires_at": "…", "catalog_hash": "…", "estimated": false,
  "insufficient": false, "available_millicredits": 3000 }
Generálás
curl -s https://fotoai.hu/api/v1/generations \
  -H "Authorization: Bearer $FOTOAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"image.seedream-5-lite","params":{"aspect":"3:4"},"prompt":"portré egy kávézóban",
       "quote_id":"<a quote_id>","idempotency_key":"'"$(uuidgen)"'"}'
Válasz (202)
{ "id": "jobId", "status": "running", "estimated_seconds": 12,
  "reserved_millicredits": 1000, "client_reference": null }
  • quote_id vagy max_millicredits kötelező (vagy mindkettő – ilyenkor a kisebbik a korlát).
  • idempotency_key kötelező: ugyanazzal a kulccsal ismételt kérés ugyanazt a feladatot adja vissza 200-zal, és nem foglal le kétszer kreditet.
  • Bemenetek: inputs.reference_asset_ids, start_asset_id, end_asset_id, reference_video_asset_ids, script; stílus: style_id. Hangmodellnél a prompt a felolvasandó szöveg, ha nincs inputs.script.
  • Fotócsomag-stílusnál a generálás csomagfuttatás: pack_size darab kép, képenkénti áron, egy group_id alatt. Ha az egyenleg nem fedezi az összeset, 402 INSUFFICIENT_CREDITS, és egy sem indul.

Állapot lekérdezése

GET /api/v1/generations/:id – a kész kimenetek url-je hitelesítés nélkül letölthető (nyilvános cím, vagy 6 óráig érvényes aláírt cím url_expires_at-tel).

Válasz
{ "id": "…", "status": "completed", "model": "image.seedream-5-lite", "mode": "t2i",
  "source": "api", "reserved_millicredits": 1000, "charged_millicredits": 1000,
  "outputs": [{ "asset_id": "…", "kind": "image", "url": "https://media.fotoai.hu/…",
                "mime": "image/png", "width": 1024, "height": 1024 }] }

Feltöltés

Referenciaképet, kezdő- és zárókockát bájtokként töltesz fel – URL-ről a szerver nem tölt le. Korlát: 120 feltöltés 10 percenként felhasználónként (a webbel közösen).

Terminál
# 1. PUT-cím kérése
curl -s https://fotoai.hu/api/v1/uploads -H "Authorization: Bearer $FOTOAI_API_KEY" \
  -H "Content-Type: application/json" -d '{"kind":"image","mime":"image/jpeg","bytes":482113}'
# → { "upload_id": "…", "put_url": "https://…", "headers": { "Content-Type": "image/jpeg" }, "expires_at": "…" }

# 2. a fájl feltöltése pontosan ezekkel a fejlécekkel
curl -s -X PUT "<put_url>" -H "Content-Type: image/jpeg" --data-binary @szelfi.jpg

# 3. lezárás
curl -s -X POST https://fotoai.hu/api/v1/uploads/<upload_id>/complete -H "Authorization: Bearer $FOTOAI_API_KEY"
# → { "asset_id": "…" }

Webhookok

Ha egy partnerként (OAuth) indított feladat végállapotba ér, a FotóAI a kliens regisztrált webhookcímére POST-ol (soha nem kérésből kapott címre). Események: generation.completed, generation.failed, generation.cancelled. A törzs a GET /api/v1/generations/:id válasza, kiegészítve az event, event_id és client_reference mezőkkel.

FotoAI-Signature
t=<unix idő>,v1=<hex hmac_sha256(webhookSecret, t + "." + rawBody)>
FotoAI-Event-Id
evt_… – feladatonként és eseményenként állandó; erre deduplikálj.
Kézbesítés
Azonnal (5 mp időkorlát), majd újrapróbálás 1 p, 5 p, 15 p, 1 ó, 3 ó, 6 ó és 12 ó után (8 kísérlet, ~24 óra). Bármely 2xx siker; átirányítást nem követünk.
Aláírás ellenőrzése (Node.js)
const [t, v1] = header.split(",").map((p) => p.split("=")[1]);
const ok =
  Math.abs(Date.now() / 1000 - Number(t)) <= 300 &&
  crypto.timingSafeEqual(
    Buffer.from(v1),
    Buffer.from(crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex"))
  );

A webhook kényelmi funkció

Legjobb szándékkal kézbesítjük; a megbízható forrás mindig a GET /api/v1/generations/:id lekérdezés. A személyes API-kulccsal indított feladatokhoz nincs webhook.