{"components":{"parameters":{"CursorParam":{"in":"query","name":"cursor","schema":{"type":"string"}},"IdempotencyKeyHeader":{"in":"header","name":"Idempotency-Key","schema":{"maxLength":255,"type":"string"}},"LimitParam":{"in":"query","name":"limit","schema":{"default":50,"maximum":200,"minimum":1,"type":"integer"}}},"responses":{"OpenAIBadGateway":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIErrorEnvelope"}}},"description":"The upstream answered but its response could not be used (OpenAI error envelope): code upstream_response_too_large when the vector payload exceeded the per-request size limit, or upstream_error for a malformed / misaligned response. Not retryable as-is for upstream_response_too_large — send fewer inputs."},"OpenAIBadRequest":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIErrorEnvelope"}}},"description":"Invalid request (OpenAI error envelope)."},"OpenAIForbidden":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIErrorEnvelope"}}},"description":"API key lacks the required scope (OpenAI error envelope)."},"OpenAIGone":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIErrorEnvelope"}}},"description":"The requested artifact has expired and is no longer available (OpenAI error envelope)."},"OpenAIImages":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImagesResponse"}}},"description":"OpenAI-shaped image generation response (b64_json only)."},"OpenAIInternalError":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIErrorEnvelope"}}},"description":"Internal server error (OpenAI error envelope)."},"OpenAIModel":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Model"}}},"description":"OpenAI-shaped single model."},"OpenAIModelsList":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelList"}}},"description":"OpenAI-shaped model list."},"OpenAINotFound":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIErrorEnvelope"}}},"description":"Model or resource not found (OpenAI error envelope)."},"OpenAIPaymentRequired":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIErrorEnvelope"}}},"description":"Insufficient balance (OpenAI error envelope, SERV-06)."},"OpenAIRateLimited":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIErrorEnvelope"}}},"description":"Rate limit exceeded (OpenAI error envelope). Retry-After header indicates seconds to wait.","headers":{"Retry-After":{"description":"Seconds the client should wait before retrying.","schema":{"minimum":1,"type":"integer"}}}},"OpenAIRequestTooLarge":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIErrorEnvelope"}}},"description":"Request body too large (OpenAI error envelope, code request_too_large)."},"OpenAIUnauthorized":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIErrorEnvelope"}}},"description":"Missing or invalid API key (OpenAI error envelope)."},"OpenAIUnprocessableEntity":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIErrorEnvelope"}}},"description":"Request validation failed (OpenAI error envelope)."},"OpenAIUpstreamTimeout":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIErrorEnvelope"}}},"description":"Upstream provider exceeded the synchronous per-attempt deadline (OpenAI\nerror envelope). 504 semantic — distinct from 503 OpenAIUpstreamUnavailable\nwhich signals a connection or routing failure rather than a timeout.\n"},"OpenAIUpstreamUnavailable":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIErrorEnvelope"}}},"description":"Upstream provider unavailable (OpenAI error envelope). Retry-After header may indicate seconds to wait.","headers":{"Retry-After":{"description":"Seconds the client should wait before retrying.","schema":{"minimum":1,"type":"integer"}}}},"OpenAIVideo":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoJob"}}},"description":"A video generation job object."},"OpenAIVideoList":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoList"}}},"description":"A page of video generation jobs."},"Problem":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Error response (RFC 7807)"},"RateLimited":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Rate limit exceeded (RFC 7807). Retry-After header indicates seconds to wait.","headers":{"Retry-After":{"description":"Seconds the client should wait before retrying.","schema":{"minimum":1,"type":"integer"}}}},"UsagePage":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsagePage"}}},"description":"OK"},"WebhookEndpointPage":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointPage"}}},"description":"OK"}},"schemas":{"Balance":{"description":"The organization's credit balance with its auto-pay configuration and live burn rate. Mirrors the dashboard's balance contract field for field. No payment-instrument detail appears here — saved cards live behind separate admin-only routes.\n","properties":{"auto_pay_amount_cents":{"description":"Amount auto-pay charges when it triggers. Meaningful only when auto_pay_enabled.","format":"int64","type":"integer"},"auto_pay_enabled":{"type":"boolean"},"auto_pay_threshold_cents":{"description":"Auto-pay triggers when the balance falls below this. Meaningful only when auto_pay_enabled.","format":"int64","type":"integer"},"balance_cents":{"description":"Remaining credit in USD cents. May be negative when usage has outrun the balance.","format":"int64","type":"integer"},"balance_dollars":{"description":"The same figure in dollars, for display.","type":"number"},"burn_cents_per_hour":{"description":"Live burn rate in USD cents per hour: the committed hourly price of every instance that is live, counted from launch — so instances still creating, provisioning or booting are included, not only those that have reached running. 0 when nothing is live.\n\n`null` means the burn rate could not be determined. Treat that as unknown, never as 0: reading an unavailable burn rate as \"nothing running\" reports unbounded runway to an organization that may be minutes from empty.","format":"int64","type":["integer","null"]}},"required":["balance_cents","balance_dollars","auto_pay_enabled","auto_pay_threshold_cents","auto_pay_amount_cents","burn_cents_per_hour"],"type":"object"},"ChatChoice":{"properties":{"finish_reason":{"enum":["stop","length","content_filter","tool_calls"],"type":"string"},"index":{"type":"integer"},"message":{"$ref":"#/components/schemas/ChatMessage"}},"type":"object"},"ChatCompletionRequest":{"properties":{"max_tokens":{"minimum":1,"nullable":true,"type":"integer"},"messages":{"items":{"$ref":"#/components/schemas/ChatMessage"},"type":"array"},"model":{"description":"Canonical model id, e.g. \"deeprelay/qwen2.5-7b-instruct\".","type":"string"},"stream":{"default":false,"type":"boolean"},"stream_options":{"$ref":"#/components/schemas/StreamOptions"},"temperature":{"nullable":true,"type":"number"}},"required":["model","messages"],"type":"object"},"ChatCompletionResponse":{"properties":{"choices":{"items":{"$ref":"#/components/schemas/ChatChoice"},"type":"array"},"created":{"description":"Unix timestamp (seconds).","type":"integer"},"id":{"type":"string"},"model":{"description":"Canonical (deeprelay/...) model id, NEVER the upstream HF path.","type":"string"},"object":{"enum":["chat.completion"],"type":"string"},"usage":{"$ref":"#/components/schemas/Usage"}},"required":["id","object","created","model","choices","usage"],"type":"object"},"ChatMessage":{"properties":{"content":{"type":"string"},"role":{"enum":["system","user","assistant"],"type":"string"}},"required":["role","content"],"type":"object"},"CryptoDeposit":{"description":"A stablecoin deposit. Mirrors the Go DTO field for field. Like that struct, this schema deliberately has NO payment-processor property — the processor is an implementation detail and is structurally absent from the customer contract, not merely filtered out.\n","properties":{"amount_usd_cents":{"description":"The amount requested, in USD cents. Never the settled or credited value.","type":"integer"},"asset":{"enum":["usdc","usdt"],"type":"string"},"chain":{"enum":["ethereum","base","arbitrum","polygon","solana"],"type":"string"},"created_at":{"format":"date-time","type":"string"},"credited_cents":{"description":"USD cents actually credited to the balance. Absent until credited.","type":"integer"},"deposit_id":{"type":"string"},"expires_at":{"description":"RFC3339 UTC. After this, send nothing to pay_address.","format":"date-time","type":"string"},"pay_address":{"description":"The address to send funds to. Unique to this deposit.","type":"string"},"pay_amount":{"description":"The exact token amount to send, as a decimal string (never a number — the value must not round-trip through a float).\n","type":"string"},"status":{"description":"Lifecycle state: created, detected, confirming, settled, screening, held_screening, credited, credited_underpaid, credited_overpaid, expired, expired_received, failed, failed_screening.\n","type":"string"},"tx_hash":{"type":"string"}},"required":["deposit_id","status","chain","asset","amount_usd_cents","created_at"],"type":"object"},"Embedding":{"properties":{"embedding":{"description":"The embedding vector as 32-bit floats.","items":{"format":"float","type":"number"},"type":"array"},"index":{"description":"Position of this vector's input in the request.","type":"integer"},"object":{"enum":["embedding"],"type":"string"}},"required":["object","embedding","index"],"type":"object"},"EmbeddingsRequest":{"properties":{"dimensions":{"description":"Requested output dimensionality, for models that support truncated (Matryoshka) embeddings. Passed through to the model when set.","minimum":1,"type":"integer"},"encoding_format":{"default":"float","description":"Only \"float\" is supported. A value of \"base64\" is rejected with invalid_request_error / unsupported_parameter.","enum":["float"],"type":"string"},"input":{"description":"Text to embed: either a single string or an array of up to 2048 strings. Token-id arrays are not supported. The per-request ceiling is bounded so the response stays under 64 MiB — about 680 inputs at 4096 dimensions, fewer at a larger `dimensions` value; an over-limit batch is rejected with 400 invalid_request_error whose message states the limit, before any processing.","oneOf":[{"type":"string"},{"items":{"type":"string"},"maxItems":2048,"type":"array"}]},"model":{"description":"Canonical embedding model id, e.g. \"deeprelay/qwen3-embedding-8b\".","type":"string"},"user":{"description":"Opaque end-user identifier for abuse tracing. Optional.","type":"string"}},"required":["model","input"],"type":"object"},"EmbeddingsResponse":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Embedding"},"type":"array"},"model":{"description":"Canonical (deeprelay/...) model id, NEVER the upstream identifier.","type":"string"},"object":{"enum":["list"],"type":"string"},"usage":{"$ref":"#/components/schemas/EmbeddingsUsage"}},"required":["object","data","model","usage"],"type":"object"},"EmbeddingsUsage":{"description":"Embeddings are billed on input tokens only, so this block carries no completion_tokens field — an embeddings call emits none, and total_tokens always equals prompt_tokens.","properties":{"prompt_tokens":{"type":"integer"},"total_tokens":{"type":"integer"}},"required":["prompt_tokens","total_tokens"],"type":"object"},"ImagesGenerationsRequest":{"properties":{"model":{"description":"Customer-facing image model id (e.g. deeprelay/flux.1-schnell) or alias.","type":"string"},"n":{"default":1,"description":"Number of images to generate. Defaults to 1.","minimum":1,"type":"integer"},"prompt":{"description":"Text description of the image(s) to generate.","type":"string"},"response_format":{"default":"b64_json","description":"Only b64_json is supported. A value of \"url\" is rejected with invalid_request_error until S3-backed URL delivery lands.","enum":["b64_json"],"type":"string"},"size":{"description":"Output image dimensions as WxH (e.g. 1024x1024).","type":"string"}},"required":["model","prompt"],"type":"object"},"ImagesResponse":{"properties":{"created":{"description":"Unix timestamp (seconds) when the response was created.","type":"integer"},"data":{"items":{"properties":{"b64_json":{"description":"Base64-encoded image bytes.","type":"string"}},"required":["b64_json"],"type":"object"},"type":"array"},"usage":{"properties":{"image_count":{"description":"Number of images generated (the billable unit).","type":"integer"}},"required":["image_count"],"type":"object"}},"required":["created","data","usage"],"type":"object"},"InferencePreflight":{"description":"The verdict for one (organization, model) pair: what would happen if the model were invoked right now.\n","properties":{"funded":{"description":"Whether a pay-as-you-go request could be paid for right now: a positive credit balance AND no self-set spending cap already at its limit. A boolean and never a figure — this route is on the inference read scope and must not disclose the balance.","type":"boolean"},"message":{"description":"A ready-to-print sentence, phrased for this exact plan/credit combination. Prefer it over composing one from verdict and reason: it improves without a client release.","type":"string"},"model":{"description":"The canonical catalog id the request resolved to.","type":"string"},"plan_covered":{"description":"Whether the subscription tier covers this model. A property of the PLATFORM, not of the caller — it stays true for an organization that has no subscription, which is what makes the \"this is included in the plan\" upsell honest.","type":"boolean"},"reason":{"description":"Stable code explaining the verdict, for clients that need their own wording. Absent on a plain `ok` that needs no explanation.","enum":["plan_covered","payg","not_plan_covered","subscription_quota_exhausted","insufficient_balance","daily_limit_reached","monthly_limit_reached","preflight_unavailable"],"type":"string"},"subscribed":{"description":"Whether the organization is entitled to the plan right now.","type":"boolean"},"verdict":{"description":"`ok` — the request will be served (covered by the plan, or payable). `warn` — it will be served AND charged in a way the caller may not expect. `block` — it will be refused (402 or 429).","enum":["ok","warn","block"],"type":"string"}},"required":["model","verdict","message","plan_covered","subscribed","funded"],"type":"object"},"Model":{"properties":{"aliases":{"items":{"type":"string"},"type":"array"},"author":{"description":"Model creator/organization (e.g. \"Meta\", \"Qwen\", \"Black Forest Labs\").","type":"string"},"category":{"description":"Browse-facing category; mirrors modality (chat/image/video/embedding).","type":"string"},"context_length":{"type":"integer"},"created":{"type":"integer"},"fine_tunable":{"description":"Whether this model can be used as the base of a managed fine-tuning job. The tunable allowlist is operator-curated and much narrower than the serving catalog, so this is false for almost every model. Absent means false.","type":"boolean"},"fine_tune_base_model":{"description":"The exact id to send as `model` when creating a fine-tuning job for this entry. Present only when `fine_tunable` is true, and deliberately distinct from `id`: the allowlist matches the customer-facing base id (e.g. `qwen2.5-7b-instruct`), which a served model normally carries as an alias rather than as its namespaced catalog id.","type":"string"},"id":{"type":"string"},"modality":{"enum":["chat","image","video","embedding"],"type":"string"},"object":{"enum":["model"],"type":"string"},"owned_by":{"type":"string"},"parameters":{"description":"Typed parameter descriptors for media (image/video) models — name, type, enum/bounds/default. Playgrounds render input forms from these.","items":{"properties":{"default":{"description":"Default value for the field (any JSON scalar)."},"enum":{"items":{"type":"string"},"type":"array"},"max":{"type":"integer"},"min":{"type":"integer"},"name":{"type":"string"},"type":{"enum":["enum","int","string","bool"],"type":"string"}},"required":["name","type"],"type":"object"},"type":"array"},"plan_covered":{"description":"Whether the flat subscription tier covers this model. A covered model rides the plan's quota while the subscription is entitled and has quota left; everything else bills pay-as-you-go at list rates, as does a covered model once the quota is spent. Absent means false.\n\nCoverage is a property of the platform, not of the caller: the flag reads the same for every organization, subscribed or not. Use `/billing/subscription` for what the caller's own plan has left.","type":"boolean"},"pricing":{"properties":{"cached_input_per_1m_tokens_cents":{"description":"cached_input_per_1m_tokens_microcents rounded to whole cents; omitted when the exact rate rounds below half a cent.","type":"integer"},"cached_input_per_1m_tokens_microcents":{"description":"Exact per-1M-token rate in micro-cents (1e-6 cent) for the cache-HIT part of a chat prompt (usage.prompt_tokens_details.cached_tokens). Present only on models with a published cached-input rate; absent means no discount — every prompt token bills at input_per_1m_tokens_cents. Treat this field as the presence signal: cached rates are routinely sub-cent, so the rounded cents field below is often omitted.","type":"integer"},"currency":{"type":"string"},"input_per_1m_tokens_cents":{"type":"integer"},"off_peak":{"$ref":"#/components/schemas/ModelRate"},"output_per_1m_tokens_cents":{"type":"integer"},"peak":{"$ref":"#/components/schemas/ModelRate"},"peak_windows_utc":{"description":"The daily UTC ranges during which the `peak` rates apply, start inclusive and end exclusive (\"24:00\" is a valid end; a range whose end sorts before its start wraps midnight). Present only on a model priced by time of day.","items":{"$ref":"#/components/schemas/PeakWindow"},"type":"array"},"per_image_cents":{"type":"integer"},"per_image_microcents":{"description":"Exact per-image rate in micro-cents (1e-6 cent). Prefer this for image models — per_image_cents rounds sub-cent prices to 0.","type":"integer"},"per_mpxl_microcents":{"description":"Price per output megapixel in micro-cents (1e-6 cent) for image models metered per megapixel. Mutually exclusive with the per-image fields.","type":"integer"},"per_video_microcents":{"description":"Representative per-clip price in micro-cents (1e-6 cent) for video models — a \"from\" price taken from the provider's published example rate.","type":"integer"},"per_video_second_cents":{"type":"integer"},"per_video_second_microcents":{"description":"Exact per-second-of-output-video rate in micro-cents (1e-6 cent). Prefer this for video models.","type":"integer"},"period":{"description":"Present only on a model priced by time of day. Which side of the peak schedule the instant this response was rendered falls on; the flat rate fields above are the rates in effect at that instant. A request is billed at the rate in effect when it ARRIVES (UTC), so a caller planning a call for later should price it from `peak` / `off_peak` and `peak_windows_utc`.","enum":["peak","off_peak"],"type":"string"}},"type":"object"},"status":{"enum":["active","coming_soon","deprecated"],"type":"string"},"supported_parameters":{"items":{"type":"string"},"type":"array"}},"required":["id","object","owned_by","modality","status","pricing"],"type":"object"},"ModelList":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Model"},"type":"array"},"object":{"enum":["list"],"type":"string"}},"required":["object","data"],"type":"object"},"ModelRate":{"description":"One per-1M-token rate triple, in rounded cents and exact micro-cents (1e-6 cent). Prefer the micro-cent fields for arithmetic; cached rates are routinely sub-cent and their cents field is then omitted.","properties":{"cached_input_per_1m_tokens_cents":{"type":"integer"},"cached_input_per_1m_tokens_microcents":{"type":"integer"},"input_per_1m_tokens_cents":{"type":"integer"},"input_per_1m_tokens_microcents":{"type":"integer"},"output_per_1m_tokens_cents":{"type":"integer"},"output_per_1m_tokens_microcents":{"type":"integer"}},"type":"object"},"OpenAIErrorEnvelope":{"properties":{"error":{"properties":{"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"type":"string"},"param":{"nullable":true,"type":"string"},"type":{"description":"OpenAI error class (e.g. invalid_request_error, authentication_error, billing_error).","type":"string"}},"required":["message","type","code"],"type":"object"}},"required":["error"],"type":"object"},"Operation":{"properties":{"completed_at":{"format":"date-time","nullable":true,"type":"string"},"created_at":{"format":"date-time","type":"string"},"error":{"nullable":true,"properties":{"code":{"type":"string"},"detail":{"type":"string"}},"type":"object"},"kind":{"enum":["instance.create","instance.delete"],"type":"string"},"operation_id":{"format":"uuid","type":"string"},"resource_id":{"nullable":true,"type":"string"},"result":{"description":"Terminal-success payload. Carries the per-instance web-access coordinates — app_url for template deploys, terminal_url for instances provisioned with the browser web console, and the basic-auth username shared by both. The basic-auth PASSWORD is never included here — retrieve it via GET /instances/{id}?include=credentials (connection.app_password).","properties":{"app_basic_auth_user":{"type":"string"},"app_url":{"type":"string"},"terminal_url":{"type":"string"}},"type":"object"},"state":{"enum":["pending","in_progress","succeeded","failed","cancelled"],"type":"string"},"updated_at":{"format":"date-time","type":"string"},"warnings":{"description":"Create-time advisories, present only on the synchronous 202 from POST /instances (never on GET /operations/{id} polls) — e.g. the model-size guard failing open because a template deploy's MODEL is a gated or unknown Hugging Face repo whose size could not be verified.","items":{"type":"string"},"type":"array"}},"required":["operation_id","kind","state","created_at"],"type":"object"},"PeakWindow":{"description":"One daily UTC time range during which a model's peak rates apply.","properties":{"end":{"description":"\"HH:MM\" UTC, exclusive (\"24:00\" means until midnight).","type":"string"},"start":{"description":"\"HH:MM\" UTC, inclusive.","type":"string"}},"required":["start","end"],"type":"object"},"Problem":{"properties":{"code":{"enum":["unauthenticated","invalid_api_key","insufficient_scope","not_found","idempotency_conflict","idempotency_mismatch","validation_failed","invalid_gpu_type","disk_unavailable","disk_exceeds_offering","model_too_large","gpu_vram_too_small","price_changed","insufficient_balance","quota_exceeded","rate_limited","creator_check_unavailable","internal_error","operation_failed"],"type":"string"},"detail":{"type":"string"},"request_id":{"type":"string"},"status":{"type":"integer"},"title":{"type":"string"},"type":{"format":"uri","type":"string"}},"required":["type","title","status","detail","code","request_id"],"type":"object"},"SpendingLimit":{"properties":{"auto_terminate_hours":{"nullable":true,"type":"integer"},"billing_cycle_start":{"format":"date-time","type":"string"},"current_day_spend_cents":{"type":"integer"},"current_month_spend_cents":{"type":"integer"},"current_month_spend_dollars":{"type":"number"},"daily_limit_cents":{"nullable":true,"type":"integer"},"daily_limit_dollars":{"nullable":true,"type":"number"},"enforcement":{"enum":["stop_running","block_launch","notify_only"],"type":"string"},"monthly_limit_cents":{"type":"integer"},"monthly_limit_dollars":{"type":"number"},"percent_used":{"type":"number"}},"required":["monthly_limit_cents","monthly_limit_dollars","current_month_spend_cents","current_month_spend_dollars","percent_used","billing_cycle_start","enforcement","current_day_spend_cents"],"type":"object"},"StreamOptions":{"properties":{"include_usage":{"default":true,"description":"When true, the final stream chunk carries usage totals (CHAT-03).","type":"boolean"}},"type":"object"},"Subscription":{"description":"The organization's flat subscription tier: entitlement, billing period, the configured quota, and consumption against it. The console and this endpoint assemble it from the same source, so the two surfaces cannot report different numbers.\n","properties":{"cancel_at_period_end":{"description":"Whether the subscription stops at the end of the current period instead of renewing.","type":"boolean"},"current_period_end":{"description":"End of the current billing period. Absent under the same condition as current_period_start.","format":"date-time","type":"string"},"current_period_start":{"description":"Start of the current billing period. Absent until the billing provider has reported a period for this subscription.","format":"date-time","type":"string"},"plan":{"$ref":"#/components/schemas/SubscriptionPlan"},"quota":{"$ref":"#/components/schemas/SubscriptionQuota"},"status":{"description":"Raw subscription status — \"active\", \"trialing\", \"past_due\", \"canceled\" and so on — or \"none\" when the organization never subscribed.","type":"string"},"subscribed":{"description":"Whether the organization is entitled to the plan right now. Only an `active` or `trialing` subscription entitles, including a short grace window past the period end that absorbs renewal webhook lag; a failed payment (`past_due`) cuts access immediately. Render from this; do not re-derive entitlement from `status` and the period bounds.","type":"boolean"},"usage":{"$ref":"#/components/schemas/SubscriptionUsage"}},"required":["subscribed","status","cancel_at_period_end","plan","quota"],"type":"object"},"SubscriptionCheckoutRequest":{"description":"Optional overrides for a checkout mint. Every field has a deployment default, so an empty body is the normal call.\n","properties":{"cancel_url":{"description":"Where the provider returns if checkout is abandoned. Defaults to the deployment's configured URL.","type":"string"},"plan_key":{"description":"Pins the plan the client displayed. An unknown key is a 400 rather than a silent purchase of a different tier.","type":"string"},"success_url":{"description":"Where the provider returns after a completed payment. Defaults to the deployment's configured URL.","type":"string"}},"type":"object"},"SubscriptionCheckoutSession":{"properties":{"checkout_url":{"description":"The hosted page the customer must open to pay.","type":"string"},"plan":{"$ref":"#/components/schemas/SubscriptionPlan"},"session_id":{"description":"Checkout session id, for support correlation.","type":"string"}},"required":["checkout_url","session_id","plan"],"type":"object"},"SubscriptionPlan":{"description":"What the organization is — or would be — subscribing to. Always present, including for an organization that never subscribed: it is what a point of purchase renders before sending anyone to pay.\n","properties":{"key":{"description":"Stable identifier for the tier. Send it as `plan_key` on /billing/subscription/checkout to pin the plan you displayed.","type":"string"},"name":{"description":"Human-facing plan name.","type":"string"},"price":{"$ref":"#/components/schemas/SubscriptionPrice"}},"required":["key","name"],"type":"object"},"SubscriptionPortalRequest":{"description":"Optional overrides for a billing-portal mint.","properties":{"return_url":{"description":"Where the provider returns when the customer leaves the portal. Defaults to the deployment's configured URL.","type":"string"}},"type":"object"},"SubscriptionPortalSession":{"properties":{"portal_url":{"description":"The hosted billing portal — cancel, resume, change payment method, download invoices.","type":"string"}},"required":["portal_url"],"type":"object"},"SubscriptionPrice":{"description":"The plan's recurring charge, read live from the billing provider — the single source of truth, never a number embedded in code.\n\nABSENT means \"price unavailable right now\" (billing unconfigured, or the provider could not be reached). It NEVER means free. Render the plan without a figure rather than substituting one; the hosted checkout page always shows the real amount.\n","properties":{"amount_cents":{"description":"Amount in the currency's minor unit (299 = $2.99).","format":"int64","type":"integer"},"currency":{"description":"ISO 4217 currency code, lower-case (e.g. \"usd\").","type":"string"},"interval":{"description":"Recurrence unit — \"day\", \"week\", \"month\" or \"year\".","type":"string"},"interval_count":{"description":"How many intervals between charges (\"month\", 1 = monthly).","format":"int64","type":"integer"}},"required":["amount_cents","currency","interval","interval_count"],"type":"object"},"SubscriptionQuota":{"description":"The plan's configured allowances — the denominators of the usage meter. Always present, including for an organization that has not subscribed, where it describes what subscribing would provide.\n\nToken figures are WEIGHTED tokens: every model carries a usage factor, so a model at factor 2 spends two of these per token it serves.\n","properties":{"input_tokens_monthly":{"description":"Weighted input tokens included per billing period.","format":"int64","type":"integer"},"max_usage_micro_cents":{"description":"Per-period backstop on list-price metered cost, in micro-cents (1e-6 cent). 0 means no backstop is configured on this deployment.","format":"int64","type":"integer"},"output_tokens_monthly":{"description":"Weighted output tokens included per billing period. Reasoning tokens are output tokens.","format":"int64","type":"integer"},"payg_discount_bp":{"deprecated":true,"description":"Deprecated and always 0. There is no subscriber discount on pay-as-you-go spend: a request that exhausts the plan quota bills at list rates, the same rates an org without a subscription pays. The field is retained so existing deserializers keep working and will be removed in the next breaking revision. Do not display it.","format":"int64","type":"integer"},"weekly_tokens":{"description":"Weighted input+output tokens allowed over a ROLLING 7-day window. This cap slides with the clock; it does not reset with the billing period.","format":"int64","type":"integer"}},"required":["input_tokens_monthly","output_tokens_monthly","weekly_tokens","max_usage_micro_cents","payg_discount_bp"],"type":"object"},"SubscriptionUsage":{"description":"Consumption against the quota, from the same factor-weighted read the request-time gate evaluates — so this meter and enforcement cannot disagree.\n\nPresent only for an entitled subscription whose meter could be read. Its absence is never a statement that nothing was used.\n","properties":{"cost_micro_cents":{"description":"List-price metered cost of this period's plan traffic in micro-cents (1e-6 cent), against `quota.max_usage_micro_cents`.","format":"int64","type":"integer"},"period_end":{"description":"End of that window.","format":"date-time","type":"string"},"period_start":{"description":"Start of the window the period figures were summed over: the subscription's current period, or the UTC calendar month when the billing provider has not reported one yet.","format":"date-time","type":"string"},"weekly_weighted_tokens":{"description":"Weighted input+output tokens over the trailing 7 days, against `quota.weekly_tokens`. A rolling figure, not a period one — it can be non-zero moments after a period rolls over.","format":"int64","type":"integer"},"weighted_input_tokens":{"description":"Weighted input tokens spent this period, against `quota.input_tokens_monthly`.","format":"int64","type":"integer"},"weighted_output_tokens":{"description":"Weighted output tokens spent this period, against `quota.output_tokens_monthly`.","format":"int64","type":"integer"}},"required":["period_start","period_end","weighted_input_tokens","weighted_output_tokens","weekly_weighted_tokens","cost_micro_cents"],"type":"object"},"Usage":{"properties":{"completion_tokens":{"type":"integer"},"prompt_tokens":{"type":"integer"},"total_tokens":{"type":"integer"}},"required":["prompt_tokens","completion_tokens","total_tokens"],"type":"object"},"UsageBucket":{"properties":{"bucket_start":{"format":"date-time","type":"string"},"cost_cents":{"type":"integer"},"gpu_seconds":{"type":"integer"},"gpu_type":{"nullable":true,"type":"string"},"instance_id":{"nullable":true,"type":"string"}},"required":["bucket_start","gpu_seconds","cost_cents"],"type":"object"},"UsagePage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/UsageBucket"},"type":"array"},"next_cursor":{"type":["string","null"]}},"required":["data","next_cursor"],"type":"object"},"VideoCreateRequest":{"properties":{"model":{"description":"Customer-facing video model id (e.g. deeprelay/wan-2.2-t2v) or alias.","type":"string"},"prompt":{"description":"Text description of the video to generate.","type":"string"},"seconds":{"description":"Output video duration in seconds (the billable unit).","minimum":1,"type":"integer"},"size":{"description":"Output video dimensions as WxH (e.g. 1280x720).","type":"string"}},"required":["model","prompt"],"type":"object"},"VideoJob":{"properties":{"completed_at":{"description":"Unix timestamp (seconds) when the job completed.","nullable":true,"type":"integer"},"cost_cents":{"description":"Final billed cost in cents; present once the job completes (failed/cancelled jobs are never billed).","nullable":true,"type":"integer"},"created_at":{"description":"Unix timestamp (seconds) when the job was created.","type":"integer"},"error":{"description":"Canonical failure code when status is failed.","nullable":true,"type":"string"},"expires_at":{"description":"Unix timestamp (seconds) when the artifact expires (24h after completion).","nullable":true,"type":"integer"},"id":{"type":"string"},"model":{"type":"string"},"object":{"enum":["video"],"type":"string"},"progress":{"description":"Generation progress 0-100.","type":"integer"},"seconds":{"type":"integer"},"size":{"type":"string"},"status":{"enum":["queued","in_progress","completed","failed","cancelled","expired"],"type":"string"}},"required":["id","object","model","status","progress","created_at"],"type":"object"},"VideoList":{"properties":{"data":{"items":{"$ref":"#/components/schemas/VideoJob"},"type":"array"},"has_more":{"type":"boolean"},"object":{"enum":["list"],"type":"string"}},"required":["object","data","has_more"],"type":"object"},"WebhookEndpoint":{"properties":{"created_at":{"format":"date-time","type":"string"},"enabled":{"type":"boolean"},"event_types":{"items":{"enum":["instance.creating","instance.running","instance.terminated","instance.failed","video.completed","video.failed"],"type":"string"},"type":"array"},"id":{"format":"uuid","type":"string"},"secret":{"description":"Returned ONLY at creation; subsequent reads omit.","type":"string"},"url":{"format":"uri","type":"string"}},"required":["id","url","event_types","enabled","created_at"],"type":"object"},"WebhookEndpointPage":{"properties":{"data":{"items":{"$ref":"#/components/schemas/WebhookEndpoint"},"type":"array"},"next_cursor":{"type":["string","null"]}},"required":["data","next_cursor"],"type":"object"}},"securitySchemes":{"bearerAuth":{"bearerFormat":"deeprelay_live_\u003c24-base62\u003e","scheme":"bearer","type":"http"}}},"info":{"contact":{"email":"support@deeprelay.ai","name":"deeprelay Support"},"description":"Programmatic API for deeprelay. Authenticate with API keys minted from\nthe dashboard. See https://deeprelay.ai/docs for details.\n","license":{"identifier":"Apache-2.0","name":"Apache-2.0"},"title":"deeprelay Public Developer API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/billing/balance":{"get":{"description":"Returns the organization's credit balance, its auto-pay configuration, and the live burn rate. Requires the `billing:read` scope. The organization is taken from the authenticated API key, never from a parameter.\n\nDivide `balance_cents` by `burn_cents_per_hour` for the remaining runway in hours. The quotient is undefined in three cases a caller must keep apart: `burn_cents_per_hour` is 0 (nothing running, so the balance funds unbounded idle time), it is `null` (the burn rate could not be determined — NOT the same as idle), or the balance is already at or below zero.\n\nThis is the balance itself — for spend against a configured cap see `/billing/spending-limit`, and for past consumption see `/usage`.\n","operationId":"getBalance","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Balance"}}},"description":"OK"},"429":{"$ref":"#/components/responses/RateLimited"},"default":{"$ref":"#/components/responses/Problem"}},"summary":"Get the org credit balance","tags":["Billing"]}},"/billing/deposits":{"get":{"description":"Returns the organization's stablecoin deposits, newest first, capped at 50. Requires the `billing:read` scope. The organization is taken from the authenticated API key, never from a parameter. Returns 404 when stablecoin deposits are not enabled for this deployment.\n","operationId":"listDeposits","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"deposits":{"items":{"$ref":"#/components/schemas/CryptoDeposit"},"type":"array"},"min_deposit_cents":{"description":"The effective minimum deposit in USD cents (admin override, else the deployment's configured floor). Validate amounts against this live value rather than hardcoding the default.","example":500,"format":"int64","type":"integer"}},"required":["deposits","min_deposit_cents"],"type":"object"}}},"description":"OK"},"404":{"$ref":"#/components/responses/Problem"},"429":{"$ref":"#/components/responses/RateLimited"},"default":{"$ref":"#/components/responses/Problem"}},"summary":"List stablecoin deposits","tags":["Billing"]}},"/billing/deposits/crypto":{"post":{"description":"Creates a deposit intent and returns the payment address and the exact token amount to send. Requires the `billing:write` scope; any member of the organization may add funds. Send the exact `pay_amount` of `asset` on `chain` and no other network — funds sent on a different network are not detected automatically. Returns 404 when stablecoin deposits are not enabled for this deployment.\n","operationId":"createCryptoDeposit","requestBody":{"content":{"application/json":{"schema":{"properties":{"amount_cents":{"description":"Amount to deposit in USD cents. Must be at least the configured minimum (500 = $5.00 by default).\n","type":"integer"},"asset":{"enum":["usdc","usdt"],"type":"string"},"chain":{"enum":["ethereum","base","arbitrum","polygon","solana"],"type":"string"}},"required":["amount_cents","chain","asset"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CryptoDeposit"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"`validation-error` — `amount_cents` is below the configured minimum. `unsupported-chain-asset` — the chain/asset pair is not available. `invalid-request` — malformed JSON body.\n"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"`org-frozen` — the organization cannot add funds; contact support."},"404":{"$ref":"#/components/responses/Problem"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"`payment-source-unavailable` — deposits are temporarily unavailable; retry shortly."},"default":{"$ref":"#/components/responses/Problem"}},"summary":"Create a stablecoin deposit","tags":["Billing"]}},"/billing/deposits/{id}":{"get":{"description":"Returns a single deposit belonging to the caller's organization. Requires the `billing:read` scope. A deposit belonging to another organization returns the same `404 deposit-not-found` as one that does not exist — deliberately, so this endpoint cannot be used to test whether a deposit id is real. Also 404 when stablecoin deposits are not enabled for this deployment.\n","operationId":"getDeposit","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CryptoDeposit"}}},"description":"OK"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"`deposit-not-found` — no such deposit, or it belongs to another organization. `not-found` — the feature is not enabled.\n"},"429":{"$ref":"#/components/responses/RateLimited"},"default":{"$ref":"#/components/responses/Problem"}},"summary":"Get one stablecoin deposit","tags":["Billing"]},"parameters":[{"description":"The deposit id.","in":"path","name":"id","required":true,"schema":{"type":"string"}}]},"/billing/spending-limit":{"get":{"description":"Returns the organization's monthly spending limit and opt-in daily spend cap, with the current month and day spend. Requires the `billing:read` scope. Returns 404 when no limit is configured.\n","operationId":"getSpendingLimit","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendingLimit"}}},"description":"OK"},"404":{"$ref":"#/components/responses/Problem"},"429":{"$ref":"#/components/responses/RateLimited"},"default":{"$ref":"#/components/responses/Problem"}},"summary":"Get the org spending limit","tags":["Billing"]},"put":{"description":"Sets the monthly spending limit and optionally sets or clears the opt-in daily spend cap. Requires the `billing:write` scope AND org-admin privileges (a non-admin member gets 403). `daily_limit_dollars` uses pointer semantics: omit to leave the cap unchanged, 0 to clear it, a positive value to set it.\n","operationId":"updateSpendingLimit","requestBody":{"content":{"application/json":{"schema":{"properties":{"auto_terminate_hours":{"description":"Hours after the limit is reached before auto-terminate; null = never.","nullable":true,"type":"integer"},"daily_limit_dollars":{"description":"Opt-in daily cap in USD. Omit = unchanged, 0 = clear, \u003e 0 = set.","type":"number"},"enforcement":{"enum":["stop_running","block_launch","notify_only"],"type":"string"},"monthly_limit_dollars":{"description":"Monthly limit in USD (minimum 1.00).","type":"number"}},"required":["monthly_limit_dollars"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendingLimit"}}},"description":"OK"},"403":{"$ref":"#/components/responses/Problem"},"422":{"$ref":"#/components/responses/Problem"},"429":{"$ref":"#/components/responses/RateLimited"},"default":{"$ref":"#/components/responses/Problem"}},"summary":"Set the org spending limit","tags":["Billing"]}},"/billing/subscription":{"get":{"description":"Returns the organization's flat-tier subscription: whether it is entitled, the current billing period, the plan's configured quota, and — for an entitled subscription — consumption against the four limits the server enforces on every request. Requires the `billing:read` scope. The organization is taken from the authenticated API key, never from a parameter.\n\nAn organization that never subscribed is NOT an error. It gets 200 with `subscribed: false`, `status: \"none\"` and the quota the plan would provide, so a client can answer \"what does the plan include\" without a second endpoint and without an error path.\n\nRead `subscribed`, not `status`, to decide whether the plan applies: `status` carries the billing provider's vocabulary, and the two can disagree — an `active` subscription just past its period end still entitles for a short grace window (renewal-webhook lag), while `past_due` and `canceled` never entitle.\n\n`usage` is present only for an entitled subscription whose meter could be read. An absent `usage` NEVER means \"nothing used\" — reading it as zero would report a full quota to an organization that has none left.\n\nThis is the plan's meter. For pay-as-you-go credit see `/billing/balance`, for spend against a self-set cap see `/billing/spending-limit`, and for past consumption see `/usage`.\n","operationId":"getSubscription","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}},"description":"OK"},"429":{"$ref":"#/components/responses/RateLimited"},"default":{"$ref":"#/components/responses/Problem"}},"summary":"Get the org subscription status and quota usage","tags":["Billing"]}},"/billing/subscription/checkout":{"post":{"description":"Opens a hosted checkout session for the flat tier and returns its URL. Requires the `billing:write` scope AND organization-admin privileges — subscribing spends organization money.\n\nThis endpoint does NOT subscribe anyone. Checkout is a hosted page that needs a browser and a card, so the caller's job is to put the returned URL in front of a human. The subscription becomes active when payment completes, which is not synchronous with this call: poll `/billing/subscription` to confirm.\n\n`success_url` and `cancel_url` are optional and fall back to the deployment's configured redirects, which is what lets a command-line client start a purchase without having any URLs of its own. An empty request body is valid and means \"use every default\".\n\n`plan_key`, when sent, pins the plan the client DISPLAYED: an unknown key is a 400 rather than a silent purchase of a different tier. Today there is one tier, so the only accepted value is its key — but sending it is the forward-compatible choice.\n\nOne flat tier means at most one subscription per organization: a second checkout while an entitling subscription exists is a 409.\n","operationId":"createSubscriptionCheckout","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionCheckoutRequest"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionCheckoutSession"}}},"description":"OK"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Not an organization admin"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"The organization already has an active subscription"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Subscription billing is not configured on this deployment"},"default":{"$ref":"#/components/responses/Problem"}},"summary":"Start a subscription checkout session","tags":["Billing"]}},"/billing/subscription/portal":{"post":{"description":"Returns a URL for the hosted billing portal: where a customer cancels the subscription, resumes one they cancelled, changes payment method, or downloads invoices. Requires the `billing:write` scope AND organization-admin privileges.\n\nCancellation lives here rather than on its own endpoint because it is one surface with the rest of the billing lifecycle. The common reason a subscription is about to lapse is a declined card, and the fix for that is a new card, not a cancellation — sending a customer somewhere that can only cancel would lose renewals.\n\nCancelling in the portal ends the subscription at the close of the current period; coverage continues until then and `/billing/subscription` reports `cancel_at_period_end: true`.\n\nAn organization that has never paid for anything gets 404: there is no billing account to manage, and this endpoint deliberately does not create one as a side effect of looking.\n","operationId":"createSubscriptionPortal","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionPortalRequest"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionPortalSession"}}},"description":"OK"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Not an organization admin"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"The organization has no billing account yet"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Subscription billing is not configured on this deployment"},"default":{"$ref":"#/components/responses/Problem"}},"summary":"Open the billing portal to cancel or manage the subscription","tags":["Billing"]}},"/chat/completions":{"post":{"operationId":"createChatCompletion","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatCompletionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatCompletionResponse"}},"text/event-stream":{"schema":{"description":"Server-Sent Events stream of ChatCompletionChunk objects, terminated by `data: [DONE]`.","format":"binary","type":"string"}}},"description":"Successful response. application/json for non-streaming requests;\ntext/event-stream for streaming requests (stream=true).\n"},"400":{"$ref":"#/components/responses/OpenAIBadRequest"},"401":{"$ref":"#/components/responses/OpenAIUnauthorized"},"402":{"$ref":"#/components/responses/OpenAIPaymentRequired"},"403":{"$ref":"#/components/responses/OpenAIForbidden"},"404":{"$ref":"#/components/responses/OpenAINotFound"},"422":{"$ref":"#/components/responses/OpenAIUnprocessableEntity"},"429":{"$ref":"#/components/responses/OpenAIRateLimited"},"500":{"$ref":"#/components/responses/OpenAIInternalError"},"503":{"$ref":"#/components/responses/OpenAIUpstreamUnavailable"}},"security":[{"bearerAuth":[]}],"summary":"Create a chat completion (OpenAI-compatible)","tags":["Inference"]}},"/embeddings":{"post":{"description":"Synchronous text embeddings. Accepts a single string or an array of up to 2048 strings, bounded so the response stays under 64 MiB (about 680 inputs at 4096 dimensions; fewer at a larger `dimensions` value) — over-limit requests are rejected with 400 before any processing. Returns one float vector per input, in request order. Billed on the upstream's reported prompt tokens at the model's listed input rate, rounded up to the next whole cent per request; an embeddings call emits no completion tokens, so `usage` carries `prompt_tokens` and `total_tokens` only. `encoding_format` accepts only `float` — a value of `base64` is rejected with `unsupported_parameter`. A chat model id on this route returns 404 `model_not_found`: the id is valid, but not on this surface.","operationId":"createEmbeddings","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbeddingsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbeddingsResponse"}}},"description":"One embedding vector per input, in request order."},"400":{"$ref":"#/components/responses/OpenAIBadRequest"},"401":{"$ref":"#/components/responses/OpenAIUnauthorized"},"402":{"$ref":"#/components/responses/OpenAIPaymentRequired"},"403":{"$ref":"#/components/responses/OpenAIForbidden"},"404":{"$ref":"#/components/responses/OpenAINotFound"},"413":{"$ref":"#/components/responses/OpenAIRequestTooLarge"},"429":{"$ref":"#/components/responses/OpenAIRateLimited"},"500":{"$ref":"#/components/responses/OpenAIInternalError"},"502":{"$ref":"#/components/responses/OpenAIBadGateway"},"503":{"$ref":"#/components/responses/OpenAIUpstreamUnavailable"},"504":{"$ref":"#/components/responses/OpenAIUpstreamTimeout"}},"security":[{"bearerAuth":[]}],"summary":"Create embeddings (OpenAI-compatible)","tags":["Inference"]}},"/health":{"get":{"operationId":"getHealth","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"status":{"const":"ok","type":"string"}},"type":"object"}}},"description":"OK"}},"security":[],"summary":"Liveness probe","tags":["Meta"]}},"/images/generations":{"post":{"description":"Synchronous text-to-image generation. Returns base64-encoded images only (`response_format` is restricted to `b64_json`); a value of `url` is rejected with `invalid_request_error` until S3-backed URL delivery lands. Pass an `Idempotency-Key` header to make a retried request replay the original response without a second charge.","operationId":"createImage","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyHeader"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImagesGenerationsRequest"}}},"required":true},"responses":{"200":{"$ref":"#/components/responses/OpenAIImages"},"400":{"$ref":"#/components/responses/OpenAIBadRequest"},"401":{"$ref":"#/components/responses/OpenAIUnauthorized"},"402":{"$ref":"#/components/responses/OpenAIPaymentRequired"},"403":{"$ref":"#/components/responses/OpenAIForbidden"},"404":{"$ref":"#/components/responses/OpenAINotFound"},"422":{"$ref":"#/components/responses/OpenAIUnprocessableEntity"},"429":{"$ref":"#/components/responses/OpenAIRateLimited"},"500":{"$ref":"#/components/responses/OpenAIInternalError"},"503":{"$ref":"#/components/responses/OpenAIUpstreamUnavailable"},"504":{"$ref":"#/components/responses/OpenAIUpstreamTimeout"}},"security":[{"bearerAuth":[]}],"summary":"Create image (OpenAI-compatible)","tags":["Inference"]}},"/inference/preflight":{"get":{"description":"Answers \"what happens if I call this model right now?\" before the call is made: whether the plan covers the model, whether the organization is subscribed, and whether there is credit to pay if it is not covered. Requires the `serverless:read` scope.\n\nNothing is sent, counted, charged, or reserved. The verdict is computed from the state of the SAME gates that judge the real request — plan coverage, subscription entitlement, remaining plan quota, credit balance and self-set spending caps — so the advice cannot drift from enforcement. It is deliberately NOT a dry run: no per-request cost estimate is quoted, because that figure changes with every prompt and quoting it would invite clients to cache it.\n\nThe gates consulted depend on the model's MODALITY, because the endpoints do not all meet the same ones. Chat, embeddings and image requests meet the full gate (balance, then the organization's opt-in daily cap, then its monthly cap). Video creation meets only the balance check, so an organization past its own spending cap but holding credit is reported as fundable for video — which is what the video endpoint will in fact do. Predicting the strictest gate rather than the applicable one would make this endpoint refuse requests the API accepts.\n\nThe same is true of the plan: video creation does not run the subscription gate, so `plan_covered` is false for a video model even if an operator has placed it on the plan's covered list. That is a deliberate divergence from the same-named field on `/v1/models`, which reports the platform's configuration. Here it means \"the plan covers this REQUEST\" — describing what will happen is the entire job of a preflight.\n\nOne caveat on \"read-only\": resolving the balance creates the organization's balance row if it has never had one (idempotent, org-scoped, and the same row the first real request would create). Nothing else is written.\n\nThe case this exists for is `warn` / `not_plan_covered`. A subscriber calling a model outside the plan IS served and IS charged pay-as-you-go, and nothing in the response to that request says so — the first signal used to be the invoice.\n\n`funded` is a boolean and never a figure. This route is on the inference read scope, so it must not disclose the organization's balance; use `/billing/balance` for the number.\n\nFailure posture is the opposite of the request gate's: any gate that cannot be read degrades the verdict toward `ok`, never toward `block`. A false `block` would stop a customer whose request would have succeeded, while a false `ok` costs them one honest error from the real call.\n","operationId":"inferencePreflight","parameters":[{"description":"Model id or alias, optionally with a `:economy` tier suffix. It is resolved through the catalog exactly as the inference endpoints resolve it, so an alias and a tier view answer for the model that would actually serve.","in":"query","name":"model","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferencePreflight"}}},"description":"OK"},"404":{"$ref":"#/components/responses/OpenAINotFound"},"429":{"$ref":"#/components/responses/OpenAIRateLimited"}},"summary":"Check whether a model request would be served, and at whose expense","tags":["Inference"]}},"/models":{"get":{"operationId":"listModels","parameters":[{"in":"query","name":"modality","required":false,"schema":{"enum":["chat","image","video","embedding"],"type":"string"}}],"responses":{"200":{"$ref":"#/components/responses/OpenAIModelsList"},"400":{"$ref":"#/components/responses/OpenAIBadRequest"},"401":{"$ref":"#/components/responses/OpenAIUnauthorized"},"403":{"$ref":"#/components/responses/OpenAIForbidden"},"429":{"$ref":"#/components/responses/OpenAIRateLimited"}},"security":[{"bearerAuth":[]}],"summary":"List available models (OpenAI-compatible)","tags":["Inference"]}},"/models/{id}":{"get":{"operationId":"getModel","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"$ref":"#/components/responses/OpenAIModel"},"401":{"$ref":"#/components/responses/OpenAIUnauthorized"},"403":{"$ref":"#/components/responses/OpenAIForbidden"},"404":{"$ref":"#/components/responses/OpenAINotFound"},"429":{"$ref":"#/components/responses/OpenAIRateLimited"}},"security":[{"bearerAuth":[]}],"summary":"Get a specific model (OpenAI-compatible)","tags":["Inference"]}},"/openapi.json":{"get":{"description":"The machine-readable specification of the PUBLISHED API surface — the same document the client SDKs are generated from. Endpoints marked `x-gpu-supply-paused` in the authoritative spec are absent from it while GPU supply is paused (D-28), so this response and the SDKs always describe the same surface.","operationId":"getOpenApiSpec","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"OK"}},"security":[],"summary":"OpenAPI 3.1 specification","tags":["Meta"]}},"/operations/{id}":{"get":{"operationId":"getOperation","parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Operation"}}},"description":"OK"},"429":{"$ref":"#/components/responses/RateLimited"},"default":{"$ref":"#/components/responses/Problem"}},"summary":"Get an async operation status","tags":["Operations"]}},"/usage":{"get":{"operationId":"listUsage","parameters":[{"in":"query","name":"bucket","schema":{"enum":["hour","day","week","month"],"type":"string"}},{"in":"query","name":"group_by","schema":{"enum":["instance_id","gpu_type"],"type":"string"}},{"in":"query","name":"start","schema":{"format":"date-time","type":"string"}},{"in":"query","name":"end","schema":{"format":"date-time","type":"string"}},{"$ref":"#/components/parameters/CursorParam"},{"$ref":"#/components/parameters/LimitParam"}],"responses":{"200":{"$ref":"#/components/responses/UsagePage"},"429":{"$ref":"#/components/responses/RateLimited"},"default":{"$ref":"#/components/responses/Problem"}},"summary":"Time-bucketed usage","tags":["Usage"]}},"/videos":{"get":{"operationId":"listVideos","parameters":[{"in":"query","name":"after","required":false,"schema":{"type":"string"}},{"in":"query","name":"limit","required":false,"schema":{"maximum":100,"minimum":1,"type":"integer"}}],"responses":{"200":{"$ref":"#/components/responses/OpenAIVideoList"},"401":{"$ref":"#/components/responses/OpenAIUnauthorized"},"403":{"$ref":"#/components/responses/OpenAIForbidden"},"429":{"$ref":"#/components/responses/OpenAIRateLimited"}},"security":[{"bearerAuth":[]}],"summary":"List video generation jobs","tags":["Inference"]},"post":{"description":"Submit an asynchronous text-to-video generation job. Returns a job object with status `queued`; poll GET /videos/{id} until `completed`, then stream the result from GET /videos/{id}/content. Pass an `Idempotency-Key` header to make a retried request replay the original response without a second charge.","operationId":"createVideo","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyHeader"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VideoCreateRequest"}}},"required":true},"responses":{"200":{"$ref":"#/components/responses/OpenAIVideo"},"400":{"$ref":"#/components/responses/OpenAIBadRequest"},"401":{"$ref":"#/components/responses/OpenAIUnauthorized"},"402":{"$ref":"#/components/responses/OpenAIPaymentRequired"},"403":{"$ref":"#/components/responses/OpenAIForbidden"},"404":{"$ref":"#/components/responses/OpenAINotFound"},"429":{"$ref":"#/components/responses/OpenAIRateLimited"},"500":{"$ref":"#/components/responses/OpenAIInternalError"}},"security":[{"bearerAuth":[]}],"summary":"Create a video generation job (async)","tags":["Inference"]}},"/videos/{id}":{"get":{"operationId":"getVideo","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"$ref":"#/components/responses/OpenAIVideo"},"401":{"$ref":"#/components/responses/OpenAIUnauthorized"},"403":{"$ref":"#/components/responses/OpenAIForbidden"},"404":{"$ref":"#/components/responses/OpenAINotFound"},"429":{"$ref":"#/components/responses/OpenAIRateLimited"}},"security":[{"bearerAuth":[]}],"summary":"Get a video generation job","tags":["Inference"]}},"/videos/{id}/cancel":{"post":{"operationId":"cancelVideo","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"$ref":"#/components/responses/OpenAIVideo"},"401":{"$ref":"#/components/responses/OpenAIUnauthorized"},"403":{"$ref":"#/components/responses/OpenAIForbidden"},"404":{"$ref":"#/components/responses/OpenAINotFound"},"409":{"$ref":"#/components/responses/OpenAIBadRequest"},"429":{"$ref":"#/components/responses/OpenAIRateLimited"}},"security":[{"bearerAuth":[]}],"summary":"Cancel a video generation job","tags":["Inference"]}},"/videos/{id}/content":{"get":{"description":"Streams the generated MP4 for a completed job. Returns 410 Gone once the artifact has expired (24h retention).","operationId":"getVideoContent","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"video/mp4":{"schema":{"format":"binary","type":"string"}}},"description":"The generated video stream."},"401":{"$ref":"#/components/responses/OpenAIUnauthorized"},"403":{"$ref":"#/components/responses/OpenAIForbidden"},"404":{"$ref":"#/components/responses/OpenAINotFound"},"410":{"$ref":"#/components/responses/OpenAIGone"},"429":{"$ref":"#/components/responses/OpenAIRateLimited"}},"security":[{"bearerAuth":[]}],"summary":"Download a completed video artifact","tags":["Inference"]}},"/webhook-endpoints":{"get":{"operationId":"listWebhookEndpoints","parameters":[{"$ref":"#/components/parameters/CursorParam"},{"$ref":"#/components/parameters/LimitParam"}],"responses":{"200":{"$ref":"#/components/responses/WebhookEndpointPage"},"429":{"$ref":"#/components/responses/RateLimited"},"default":{"$ref":"#/components/responses/Problem"}},"summary":"List webhook endpoints","tags":["Webhooks"]},"post":{"operationId":"createWebhookEndpoint","parameters":[{"$ref":"#/components/parameters/IdempotencyKeyHeader"}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"event_types":{"items":{"enum":["instance.creating","instance.running","instance.terminated","instance.failed","video.completed","video.failed"],"type":"string"},"type":"array"},"url":{"format":"uri","type":"string"}},"required":["url","event_types"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}},"description":"Created"},"429":{"$ref":"#/components/responses/RateLimited"},"default":{"$ref":"#/components/responses/Problem"}},"summary":"Create a webhook endpoint","tags":["Webhooks"]}},"/webhook-endpoints/{id}":{"delete":{"operationId":"deleteWebhookEndpoint","responses":{"204":{"description":"Deleted"},"429":{"$ref":"#/components/responses/RateLimited"},"default":{"$ref":"#/components/responses/Problem"}},"summary":"Delete a webhook endpoint","tags":["Webhooks"]},"get":{"operationId":"getWebhookEndpoint","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}},"description":"OK"},"429":{"$ref":"#/components/responses/RateLimited"},"default":{"$ref":"#/components/responses/Problem"}},"summary":"Get a webhook endpoint","tags":["Webhooks"]},"parameters":[{"in":"path","name":"id","required":true,"schema":{"format":"uuid","type":"string"}}]}},"security":[{"bearerAuth":[]}],"servers":[{"description":"Production","url":"https://api.deeprelay.ai/v1"},{"description":"Demo","url":"https://api.demo.deeprelay.ai/v1"}],"tags":[{"description":"OpenAI-compatible inference: chat completions, image generation, video generation, and the model catalog.","name":"Inference"},{"description":"Reusable launch templates that capture an instance configuration for one-call launches.","name":"Templates"},{"description":"OpenAI-compatible fine-tuning: create and manage fine-tuning jobs and upload JSONL training files.","name":"FineTuning"},{"description":"Community Cloud supplier program: enroll as a supplier, register and manage your machines, and track your earnings.","name":"Community"},{"description":"Service metadata: the liveness probe and the machine-readable OpenAPI specification. No authentication required.","name":"Meta"},{"description":"The GPU hardware catalog — every GPU type available on the platform. No authentication required.","name":"GpuTypes"},{"description":"Live pricing offers per GPU type, GPU count, region, and tier, with current availability. No authentication required.","name":"Pricing"},{"description":"The launch environment catalog: certified frameworks, selectable versions, \"What's included\" software lists, and the raw-VM OS images accepted by instance launch. No authentication required.","name":"Environments"},{"description":"GPU instances: launch, list, inspect, update, and terminate. Mutating calls return an async operation to poll for completion.","name":"Instances"},{"description":"Async operation tracking. Instance launches and terminations return an operation id; poll it here until the operation reaches a terminal state.","name":"Operations"},{"description":"SSH public keys registered with your organization for instance access.","name":"SshKeys"},{"description":"Webhook endpoints for event delivery. The signing secret is returned once at creation; verify every delivery against it.","name":"Webhooks"},{"description":"Watch rules: get notified when a GPU model's price drops below a number you set, or when it comes back in stock. Watch rules only notify — they never launch capacity and never spend.","name":"Watch"},{"description":"Auto-launch rules: standing, pre-approved purchase orders that launch a frozen instance spec automatically when a GPU model hits your target, bounded by a whole-instance hourly ceiling and a maximum runtime you approve up front. A separate resource from watch rules — watch notifies, auto-launch spends, and no field converts one into the other.","name":"Autolaunch"},{"description":"GPU Market history: deeprelay's own price and obtainable capacity for a GPU model over time, sampled every 15 minutes and retained for a year. A null price or count is a bucket we did not sample, which is NOT the same as a zero.","name":"Market"},{"description":"Time-bucketed usage aggregates for your organization, optionally grouped by instance or GPU type.","name":"Usage"},{"description":"Billing controls and funding: the organization spending limit and account deposits.","name":"Billing"}]}