Backend Integration OpenAPI JSON

Fanfux.app API Expansion

These endpoints let an independent frontend client use the same XNudes database core for account balance, subscription tier, generation history, cached image actions, and gallery feed data.

Base URL: https://xnudes.app Version: /api/v1 Auth: API key + Firebase bearer token Response: JSON

Quick Start

Frontends should authenticate users with Firebase, pass the ID token to the API, and let the backend resolve or create the shared database user by verified email.

1. Sign in Use Firebase Auth and require a verified email address.
2. Send API key and bearer token Include X-Fanfux-Api-Key on every call and Authorization: Bearer <id_token> on user profile and generation calls.
3. Use shared balance Token purchases and tier updates apply to the shared database account for that email.

Minimal request flow

const partnerApiKey = "provided privately by XNudes";
const token = await firebase.auth().currentUser.getIdToken();

const profile = await fetch("https://xnudes.app/api/v1/user/profile", {
  headers: {
    "X-Fanfux-Api-Key": partnerApiKey,
    Authorization: `Bearer ${token}`,
  },
}).then((res) => res.json());

const reveal = await fetch("https://xnudes.app/api/v1/generation/reveal", {
  method: "POST",
  headers: {
    "X-Fanfux-Api-Key": partnerApiKey,
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model_id: "00000000-0000-4000-8000-000000000000",
    prompt_data: {
      mood: "playful",
      lighting: "soft studio",
      outfit_style: "black dress",
    },
    is_hd: false,
  }),
}).then((res) => res.json());

Authentication

Protected endpoints verify a Firebase ID token and require the Firebase user to have a verified email. All Fanfux API endpoints also require a private integration key.

Fanfux.app and XNudes do not share browser cookies or sessions. The API normalizes the verified email address and resolves the database user by email. This is what makes token balance and tier status appear instantly across products.

Integration header

X-Fanfux-Api-Key: <partner_api_key>

User header

Authorization: Bearer <firebase_id_token>

Why both are needed

The integration key proves the caller is an approved client. The Firebase token proves which verified user is making the request, so the API can sync balance and subscription tier by email.

Key handling

The actual integration key is not published in these docs. If it is placed in browser JavaScript, users can inspect and copy it. For strongest protection, call these endpoints from your backend or proxy.

GET /api/v1/user/profile

Returns profile identity, subscription tier, Stripe customer linkage, token balance, and free reveal usage.

Request

curl https://xnudes.app/api/v1/user/profile \
  -H "X-Fanfux-Api-Key: $FANFUX_API_KEY" \
  -H "Authorization: Bearer $FIREBASE_ID_TOKEN"

Response

{
  "user": {
    "id": "firebase-or-db-user-id",
    "email": "user@example.com",
    "name": "user@example.com",
    "emailVerified": true,
    "createdAt": "2026-06-22T08:00:00.000Z",
    "updatedAt": "2026-06-22T08:00:00.000Z"
  },
  "subscription": {
    "tier": "free",
    "unlimitedTokensEndDate": null,
    "stripeCustomerId": null
  },
  "balance": {
    "tokens": 25,
    "freeTokens": 2,
    "totalTokens": 27
  },
  "generation": {
    "freeRevealsUsed": 0,
    "freeRevealsRemaining": 2
  }
}
POST /api/v1/generation/reveal

Reveals a model image. If the user has fewer than two free reveals used, the API returns the model static image immediately instead of starting a live GPU job.

Field Type Required Notes
model_id or modelId UUID string Yes Must match a row in models.
prompt_data or promptData JSON object No Accepts keys such as mood, lighting, and outfit style.
is_hd or isHd Boolean No Live generations use higher image size and token cost when true.

Request

curl https://xnudes.app/api/v1/generation/reveal \
  -X POST \
  -H "X-Fanfux-Api-Key: $FANFUX_API_KEY" \
  -H "Authorization: Bearer $FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "model_id": "00000000-0000-4000-8000-000000000000",
    "prompt_data": {
      "mood": "playful",
      "lighting": "soft studio",
      "outfit_style": "black dress"
    },
    "is_hd": false
  }'

Free static reveal response

{
  "generationId": "11111111-1111-4111-8111-111111111111",
  "modelId": "00000000-0000-4000-8000-000000000000",
  "actionHash": "f3dc7e7a...",
  "imageUrl": "https://...",
  "cached": true,
  "source": "static_reveal",
  "freeRevealsUsed": 1,
  "freeRevealsRemaining": 1
}

Live generation response after free reveals

{
  "generationId": "22222222-2222-4222-8222-222222222222",
  "workerImageId": "image-worker-id",
  "modelId": "00000000-0000-4000-8000-000000000000",
  "actionHash": "f3dc7e7a...",
  "imageUrl": "https://...",
  "cached": false,
  "source": "live_generation",
  "balance": {
    "tokens": 23,
    "freeTokens": 2,
    "totalTokens": 25,
    "unlimited": false
  }
}
POST /api/v1/generation/customize

Generates or retrieves a customized image using deterministic prompt hashing. The server computes md5(model_id + ":" + JSON.stringify(normalized prompt_data)) and checks stored generations before using the GPU worker.

Request

curl https://xnudes.app/api/v1/generation/customize \
  -X POST \
  -H "X-Fanfux-Api-Key: $FANFUX_API_KEY" \
  -H "Authorization: Bearer $FIREBASE_ID_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "model_id": "00000000-0000-4000-8000-000000000000",
    "prompt_data": {
      "mood": "confident",
      "lighting": "neon",
      "outfit_style": "latex"
    },
    "is_hd": true
  }'

Cached response

{
  "generationId": "33333333-3333-4333-8333-333333333333",
  "modelId": "00000000-0000-4000-8000-000000000000",
  "actionHash": "0f6a9b6d...",
  "imageUrl": "https://...",
  "cached": true
}

Cache miss response

{
  "generationId": "44444444-4444-4444-8444-444444444444",
  "workerImageId": "image-worker-id",
  "modelId": "00000000-0000-4000-8000-000000000000",
  "actionHash": "0f6a9b6d...",
  "imageUrl": "https://...",
  "cached": false,
  "balance": {
    "tokens": 21,
    "freeTokens": 2,
    "totalTokens": 23,
    "unlimited": false
  }
}

Data Model

These are the database concepts exposed by the API. The API uses validated email to keep shared user balances consistent between Fanfux and XNudes.

Table Important fields Purpose
user email, tokens, free_tokens, tier, free_reveals_used, stripe_customer_id Shared identity, subscription tier, and token balance.
themes id, name, is_premium_only Groups models and gates premium-only content.
models id, theme_id, name, base_image_url, likes_count Source model cards and static free reveal images.
user_generations user_id, model_id, generated_image_url, prompt_data, is_hd, action_hash Generation history and cache lookup source.
favorites user_id, generation_id, model_id Vault entries. Free accounts are limited to 10 rows; premium and elite tiers are unlimited.

Errors

Error responses use JSON with an error string. Treat unknown errors as retryable only when the HTTP status is 5xx.

Status Error Meaning
401 missing_api_key, invalid_api_key Missing or invalid Fanfux integration key.
503 fanfux_api_key_not_configured The server has no Fanfux integration key configured.
401 missing_bearer_token, invalid_token Authentication header is missing or Firebase rejected the token.
403 email_not_verified Firebase user exists but the email is not verified.
403 premium_theme_required Free tier attempted to use a premium-only model theme.
400 invalid_json_body, invalid_model_id Request body is malformed or the model id is not a UUID.
404 model_not_found The requested model id does not exist.
5xx generation_failed, invalid_generation_response Generation service or storage failed.

Implementation Notes

Details that frontend and backend teams should preserve when integrating another client.

  • Do not rely on cross-domain cookies. Always pass the Firebase ID token and let the API resolve the account by verified email.
  • prompt_data is normalized before hashing, so key order does not change the cache key.
  • Storage URLs may be direct URLs, base64 payloads, or signed Supabase URLs generated by the API.
  • Premium-only themes require subscription.tier to be premium or elite.