# Neuroartist API Gateway > Unified public gateway for AI image/video generation: credit-based billing, scoped API keys, session/agent auth, and asset re-hosting on Russian CDN. Base: https://dashboard.neuroartist.ru Docs: [HTML](https://dashboard.neuroartist.ru/) | [OpenAPI JSON](https://dashboard.neuroartist.ru/openapi.json) ## Auth Methods - KEY: `Authorization: ` (header) - KEY: `better-auth.session_token: ` (cookie) ## Conventions - Auth: JWT = Bearer token, KEY = API Key, JWT|KEY = either, NONE = no auth - `*` after field name = required, all fields string unless noted with `:type` - Common errors: 400/401/404 return `{success:false, error}` --- ## Health Liveness probes ### GET /health - Gateway liveness probe | NONE 200: `{status*, db*, uptime*:number, timestamp*}` ## Models Public model catalog ### GET /models - List public models | NONE Query: ?category ?search ?limit:integer ?offset:integer 200: `{items*:[{modelId*, displayName*, category*, description*, creditCost*:integer, priceRub*:number, priceUnit*, createdAt*}], total*:integer, limit*:integer, offset*:integer}` ### GET /models/{modelId} - Get model details | NONE Path: :modelId (Model identifier. May contain slashes — not URL-encoded.) 200: `{modelId*, displayName*, category*, description*, creditCost*:integer, priceRub*:number, priceUnit*, createdAt*, schema, schemaFetchedAt*, updatedAt*}` ### POST /models/{modelId}/estimate - Cost estimate for a generation request | KEY Path: :modelId (Model identifier. May contain slashes — not URL-encoded.) Body: `{}` 200: `{modelId*, modelAlias*, estimatedCostRub*:integer, priceUnit*, quantity*:number, currency*}` ## Generation AI image/video generation (auth required) ### POST /queue/{modelId} - Submit a generation job | KEY Path: :modelId (Model identifier (alias). May contain slashes — pass as-is, not URL-encoded.) Query: ?webhook Body: `{}` 200: `{requestId*, status*, queuePosition:integer}` ### GET /queue/{modelId}/requests/{requestId} - Get generation result | KEY Path: :modelId :requestId Query: ?passthrough 200: `{request_id*, images:[{url*, content_type, width:integer, height:integer, file_size:integer}], video:{url*, content_type}}` ### GET /queue/{modelId}/requests/{requestId}/status - Poll generation status | KEY Path: :modelId :requestId Query: ?logs 200: `{status*, request_id*, logs:[{message*, timestamp*}]}` ### GET /queue/{modelId}/requests/{requestId}/status/stream - SSE status stream | KEY Path: :modelId :requestId Query: ?logs ### GET /queue/{modelId}/requests/{requestId}/progress/stream - Gateway-originated SSE progress stream | KEY Path: :modelId :requestId ### PUT /queue/{modelId}/requests/{requestId}/cancel - Cancel a pending job | KEY Path: :modelId :requestId 200: `{ok:boolean, status}` ### POST /run/{modelId} - Generate (synchronous) | KEY Path: :modelId (Model identifier (alias). May contain slashes — pass as-is, not URL-encoded.) Query: ?passthrough Body: `{}` 200: `{request_id*, images:[{url*, content_type, width:integer, height:integer, file_size:integer}], video:{url*, content_type}}` ## Me Current user dashboard (auth required) ### GET /me - Current user profile | KEY 200: `{user*:{id*, email*, name*, role*}, auth*:{source*, apiKeyId*}}` ### PATCH /me - Update profile | KEY Body: `{name, image, lowBalanceEmailOptOut:boolean}` 200: `{user*:{id*, email*, name*, image*, role*, createdAt*}}` ### GET /me/sessions - List active sessions | KEY 200: `{sessions*:[{id*, expiresAt*, ipAddress, userAgent, createdAt*, updatedAt*}]}` ### DELETE /me/sessions/{id} - Revoke session | KEY Path: :id ### GET /me/balance - Current credit balance | KEY 200: `{userId*, balance*:integer}` ### GET /me/transactions - List credit transactions | KEY Query: ?limit:integer ?offset:integer ?type 200: `{items*:[{id*, userId*, type*, amount*:integer, balanceAfter:integer, description*, createdAt*}], total*:integer, limit*:integer, offset*:integer}` ### GET /me/usage - Usage snapshot | KEY Query: ?windows 200: `{balance*:integer, windows*:[{window*, credits*:integer, count*:integer, byModel*:[{modelId*, credits*:integer, count*:integer}]}]}` ### GET /me/usage/history - Usage time series | KEY Query: ?from ?to ?granularity 200: `{granularity*, from*, to*, series*:[{bucket*, credits*:integer, count*:integer}]}` ### GET /me/usage/by-model - Usage grouped by model | KEY Query: ?window 200: `{items*:[{modelId*, displayName*, priceUnit*, credits*:integer, count*:integer}]}` ### GET /me/usage/by-key - Usage grouped by API key | KEY Query: ?window 200: `{items*:[{apiKeyId*, keyName*, partialKey*, credits*:integer, count*:integer, lastUsedAt*}]}` ### GET /me/activity - Per-request activity feed | KEY Query: ?limit:integer ?offset:integer ?modelId ?apiKeyId ?operation ?status 200: `{items*:[{id*, modelId*, operation*, credits*:integer, status*, errorCode*, requestId*, apiKeyId*, durationMs*:integer, createdAt*}], total*:integer, limit*:integer, offset*:integer}` ### GET /me/activity/{falRequestId} - Single request detail | KEY Path: :falRequestId (Provider request id returned by /submit) 200: `{record*:{id*, modelId*, operation*, credits*:integer, status*, errorCode*, requestId*, apiKeyId*, durationMs*:integer, createdAt*}, assets*:[{id*, falRequestId*, sourceUrl*, s3Key*, s3Bucket*, contentType*, sizeBytes*:integer, status*, error*, publicUrl*, createdAt*}]}` ### GET /me/keys - List API keys | KEY 200: `{keys*:[{id*, name*, prefix*, start*, enabled*:boolean, expiresAt*, createdAt*, lastRequest*, requestCount*:integer, requests:integer, lastUsedAt, lifetimeCostRub:integer, monthlyLimitRub:integer}]}` ### POST /me/keys - Create API key | KEY Body: `{name, expiresIn:integer}` 201: `{id*, name*, key*, prefix*, start*, expiresAt*, createdAt*}` ### DELETE /me/keys/{id} - Delete API key | KEY Path: :id ### PATCH /me/keys/{id} - Update API key | KEY Path: :id Body: `{name, metadata:{monthlyLimitRub:integer}}` 200: `{id*, name*, prefix*, start*, enabled*:boolean, expiresAt*, createdAt*, lastRequest*, requestCount*:integer, requests:integer, lastUsedAt, lifetimeCostRub:integer, monthlyLimitRub:integer}` ### GET /me/payment-methods - List saved payment methods | KEY 200: `{items*:[{id*, cardLast4*, cardBrand*, isDefault*:boolean, createdAt*}]}` ### DELETE /me/payment-methods/{id} - Delete saved payment method | KEY Path: :id ### POST /me/payment-methods/{id}/default - Set default payment method | KEY Path: :id 200: `{method*:{id*, cardLast4*, cardBrand*, isDefault*:boolean, createdAt*}}` ### PATCH /me/auto-topup - Configure auto-topup | KEY Body: `{enabled:boolean, thresholdRub:integer, amountRub:integer}` 200: `{thresholdRub*:integer, amountRub*:integer}` ### GET /me/webhook-secret - Get webhook signing secret | KEY 200: `{secret*}` ### POST /me/webhook-secret/rotate - Rotate webhook signing secret | KEY 200: `{secret*}` ### POST /me/uploads - Upload an input file (image or mp4) | KEY Body: `{file}` 201: `{assetId*, url*, contentType*, sizeBytes*:integer}` ## Billing Credit packs and payments ### POST /billing/topup - Create a top-up payment | KEY Body: `{amountRub*:integer, returnUrl*}` 201: `{payment*:{id*, yookassaId*, status*, creditsToGrant*:integer, amountKopecks*:integer, currency*, description*, createdAt*}, confirmationUrl*}` ### GET /billing/payments - List user payments | KEY Query: ?limit:integer ?offset:integer 200: `{items*:[{id*, yookassaId*, status*, creditsToGrant*:integer, amountKopecks*:integer, currency*, description*, createdAt*}], total*:integer, limit*:integer, offset*:integer}` ### GET /billing/payments/{id} - Get one payment | KEY Path: :id (YooKassa payment id) 200: `{payment*:{id*, yookassaId*, status*, creditsToGrant*:integer, amountKopecks*:integer, currency*, description*, createdAt*}}` ### GET /billing/payments/{id}/receipt - Redirect to YooKassa receipt | KEY Path: :id (YooKassa payment id) ## Admin Admin-only operations (session + role=admin) ### GET /admin/stats - Global counters | KEY 200: `{users*:integer, activeApiKeys*:integer, models*:integer, credits*:{issued*:integer, used*:integer}}` ### GET /admin/users - List users | KEY Query: ?limit:integer ?offset:integer ?q ?role ?banned:boolean 200: `{items*:[{id*, email*, name*, role*, banned*:boolean, createdAt*, balance*:integer}], total*:integer, limit*:integer, offset*:integer}` ### GET /admin/users/{id} - Get user detail | KEY Path: :id 200: `{user*:{id*, email*, name*, image*, role*, banned*:boolean, banReason*, banExpires*, createdAt*, updatedAt*}, balance*:integer, usage*:[{window*, credits*:integer, count*:integer, byModel*:[{modelId*, credits*:integer, count*:integer}]}]}` ### POST /admin/users/{id}/ban - Ban user | KEY Path: :id Body: `{reason, expiresAt}` 200: `{ok*:boolean}` ### POST /admin/users/{id}/unban - Unban user | KEY Path: :id 200: `{ok*:boolean}` ### POST /admin/users/{id}/credits/grant - Grant credits | KEY Path: :id Body: `{amount*:integer, description}` 201: `{transaction*:{id*, userId*, type*, amount*:integer, balanceAfter:integer, description*, createdAt*}}` ### POST /admin/users/{id}/credits/clawback - Clawback credits | KEY Path: :id Body: `{amount*:integer, description}` 201: `{transaction*:{id*, userId*, type*, amount*:integer, balanceAfter:integer, description*, createdAt*}}` ### GET /admin/users/{id}/transactions - User transactions | KEY Path: :id Query: ?limit:integer ?offset:integer ?type 200: `{items*:[{id*, userId*, type*, amount*:integer, balanceAfter:integer, description*, createdAt*}], total*:integer, limit*:integer, offset*:integer}` ### GET /admin/users/{id}/usage - User usage snapshot | KEY Path: :id Query: ?windows 200: `{windows*:[{window*, credits*:integer, count*:integer, byModel*:[{modelId*, credits*:integer, count*:integer}]}]}` ### GET /admin/users/{id}/sessions - List user sessions | KEY Path: :id 200: `{sessions*:[{id*, expiresAt*, ipAddress, userAgent, createdAt*, updatedAt*}]}` ### DELETE /admin/users/{id}/sessions - Revoke all user sessions | KEY Path: :id ### GET /admin/users/{id}/keys - List user API keys | KEY Path: :id 200: `{keys*:[{id*, name*, prefix*, start*, enabled*:boolean, expiresAt*, createdAt*, lastRequest*, requestCount*:integer, requests:integer, lastUsedAt, lifetimeCostRub:integer, monthlyLimitRub:integer}]}` ### DELETE /admin/users/{id}/keys/{keyId} - Delete a user API key | KEY Path: :id :keyId ### PATCH /admin/users/{id}/keys/{keyId} - Tune API key | KEY Path: :id :keyId Body: `{name, metadata:{monthlyLimitRub:integer}}` 200: `{key*:{id*, name*, enabled*:boolean, rateLimitEnabled*:boolean, rateLimitTimeWindow*:integer, rateLimitMax*:integer, remaining*:integer, expiresAt*, updatedAt*}}` ### GET /admin/transactions - Global transactions log | KEY Query: ?limit:integer ?offset:integer ?userId ?type 200: `{items*:[{id*, userId*, type*, amount*:integer, balanceAfter:integer, description*, createdAt*}], total*:integer, limit*:integer, offset*:integer}` ### GET /admin/usage/top-users - Top credit consumers | KEY Query: ?window ?limit:integer 200: `{window*, items*:[{userId*, email*, name*, credits*:integer, count*:integer}]}` ### GET /admin/models - List all models (incl. hidden) | KEY Query: ?limit:integer ?offset:integer ?category ?visibleOnly:boolean 200: `{items*:[{modelId*, alias*, displayName*, category*, description*, creditCost*:integer, visible*:boolean, schemaFetchedAt*, createdAt*, updatedAt*}], total*:integer, limit*:integer, offset*:integer}` ### POST /admin/models - Create or update a model | KEY Body: `{modelId*, alias, displayName, category, description, creditCost:integer, visible:boolean}` 201: `{model*:{modelId*, alias*, displayName*, category*, description*, creditCost*:integer, visible*:boolean, schemaFetchedAt*, createdAt*, updatedAt*}}` ### DELETE /admin/models - Remove a model | KEY Body: `{modelId*}` ### POST /admin/models/sync - Sync model schema from provider | KEY Body: `{modelId*}` 200: `{model*:{modelId*, alias*, displayName*, category*, description*, creditCost*:integer, visible*:boolean, schemaFetchedAt*, createdAt*, updatedAt*}}` ### POST /admin/models/sync-catalog - Force full catalog re-sync | KEY 200: `{before*:integer, after*:integer, added*:integer, elapsedMs*:integer}` ## Webhooks Incoming webhooks (signature-verified) ### POST /webhooks/provider - Provider webhook receiver | NONE Body: `{request_id, gateway_request_id, status*, error, payload, payload_error}` 200: `{ok*:boolean, status, deduped:boolean, unmapped:boolean, assets:{found*:integer, uploaded*:integer, failed*:integer}}` ### POST /webhooks/yookassa/notify - YooKassa payment webhook | NONE Body: `{type, event*, object*:{id*}}` 200: `{ok*:boolean, processed:boolean, reason, deduped:boolean}` ## Changelog Public changelog feed ### GET /changelog.json - Public changelog feed | NONE 200: `{entries*:[{version*, date*, notes*:string[]}]}` ## Integrations Partner-specific integration adapters (custom request/response contracts). Auth required via platform API keys. ### POST /integrations/yourtunes - YourTunes: generate album cover | KEY Body: `{artist*, release_name*, genre*, text*}` 200: `{success*:boolean, message*, artist*, release_name*, genre*, text_length*:integer, cover_image_url*, cover_image_prompt*}` ### POST /integrations/yourtunes-edit - YourTunes: add text overlay to cover | KEY Body: `{artist*, release_name*, genre*, render_track_text, render_artist_text, reference_image}` 200: `{success*:boolean, message*, artist*, release_name*, genre*, text_length*:integer, cover_image_url*, cover_image_prompt*}`