{"components":{"parameters":{"NodeId":{"in":"path","name":"nodeId","required":true,"schema":{"type":"string"}},"OrgId":{"in":"path","name":"orgId","required":true,"schema":{"type":"string"}},"ResourceId":{"in":"path","name":"id","required":true,"schema":{"type":"string"}},"VmId":{"in":"path","name":"vmId","required":true,"schema":{"type":"string"}}},"responses":{"BadGateway":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"An upstream dependency answered badly; retry"},"BadRequest":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request is invalid"},"CapacityUnavailable":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request is valid but the capacity it needs is momentarily unavailable: a transient saturation that clears on node or VM turnover, not a durable conflict. Codes: NODE_AT_CAPACITY, INSUFFICIENT_GPU_CAPACITY, INSUFFICIENT_CPU_CAPACITY, INSUFFICIENT_IP_CAPACITY. Retry the same request with exponential backoff; the Retry-After header suggests how long to wait.","headers":{"Retry-After":{"description":"Suggested seconds to wait before retrying.","schema":{"type":"integer"}}}},"Conflict":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The request conflicts with existing state"},"Forbidden":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"API key lacks the required scope"},"Gone":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"The resource existed but is no longer usable (e.g. an expired invite)"},"InferenceBadRequest":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferenceError"}}},"description":"The request is invalid"},"InferenceConflict":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferenceError"}}},"description":"The resource is in a state that rejects this action"},"InferenceNotFound":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferenceError"}}},"description":"Not found (also returned when the organization does not have Batch API access)"},"InferenceRateLimited":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferenceError"}}},"description":"Too many concurrent uploads; retry shortly"},"InferenceTooLarge":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferenceError"}}},"description":"The upload exceeds the 200 MB limit"},"InferenceUnauthorized":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferenceError"}}},"description":"Missing or invalid API key"},"InferenceUnavailable":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferenceError"}}},"description":"The action could not be dispatched; retry"},"NotFound":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Resource not found"},"Unauthorized":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or invalid API key"},"Unavailable":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"A required integration (e.g. payments) is not configured"}},"schemas":{"AcceptInviteResult":{"properties":{"organizationId":{"type":"string"},"role":{"type":"string"}},"required":["organizationId","role"],"type":"object"},"AcceptTransferRequest":{"properties":{"sshKeyIds":{"items":{"type":"string"},"type":"array"}},"type":"object"},"AddMemberRequest":{"properties":{"email":{"type":"string"},"role":{"enum":["admin","member","viewer"],"type":"string"}},"required":["email","role"],"type":"object"},"ApiKeySummary":{"properties":{"createdAt":{"type":"string"},"expiresAt":{"type":"string"},"id":{"type":"string"},"keyPrefix":{"type":"string"},"lastUsedAt":{"type":"string"},"name":{"type":"string"},"revokedAt":{"type":"string"},"scopes":{"items":{"type":"string"},"type":"array"}},"required":["id","name","keyPrefix","scopes","createdAt"],"type":"object"},"ApplyForProviderRequest":{"properties":{"note":{"type":"string"}},"required":["note"],"type":"object"},"AttachSshKeyRequest":{"properties":{"sshKeyId":{"type":"string"}},"required":["sshKeyId"],"type":"object"},"AutoRechargeSettings":{"properties":{"amountCents":{"description":"Amount charged per auto-recharge (within deposit bounds $10-$500).","type":"integer"},"enabled":{"description":"When on, the balance is topped up automatically below the threshold.","type":"boolean"},"thresholdCents":{"description":"Recharge fires when available balance drops below this.","type":"integer"}},"required":["enabled","thresholdCents","amountCents"],"type":"object"},"Balance":{"properties":{"accruedUsageCents":{"description":"Usage cost not yet settled into a transaction: the current month, plus any recently ended month whose settlement debit has not been written yet (briefly higher than currentMonthUsageCents right after a month rollover).","type":"integer"},"availableCents":{"description":"Spendable balance = balanceCents − accruedUsageCents. This is what the deploy gate checks; it can be below balanceCents while usage is running, and slightly negative between auto-recharge ticks.","type":"integer"},"balanceCents":{"description":"Raw ledger balance (deposits + credits − settled debits).","type":"integer"},"currentMonthUsageCents":{"description":"Usage cost accrued in the current calendar month only. Use this for \"spend this month\" displays; use accruedUsageCents for balance math.","type":"integer"},"minDepositCents":{"description":"Smallest deposit the org can make right now, in cents: the flat $10 floor, raised to the 3-day-runway requirement while the org has open usage burning, clamped to the org's per-deposit cap. This is the exact minimum the deposit endpoint enforces (INSUFFICIENT_RUNWAY on a lower amount), so clients should render it rather than re-derive it.","type":"integer"},"monthlySpendLimitCents":{"description":"The org's monthly spend cap, or null if no limit is set. Drives the \"used of limit\" budget gauge in the dashboard.","nullable":true,"type":"integer"},"transactions":{"items":{"$ref":"#/components/schemas/Transaction"},"type":"array"}},"required":["balanceCents","availableCents","accruedUsageCents","currentMonthUsageCents","transactions"],"type":"object"},"BatchList":{"description":"One page of batches, newest first (OpenAI list envelope).","properties":{"data":{"items":{"$ref":"#/components/schemas/BatchObject"},"type":"array"},"first_id":{"description":"Id of the first batch on this page.","type":"string"},"has_more":{"type":"boolean"},"last_id":{"description":"Id of the last batch on this page; pass it as `after` to fetch the next page.","type":"string"},"object":{"enum":["list"],"type":"string"}},"required":["object","data","has_more"],"type":"object"},"BatchObject":{"description":"A batch inference job (OpenAI-compatible).","properties":{"cancelled_at":{"format":"int64","type":"integer"},"completed_at":{"format":"int64","type":"integer"},"completion_window":{"enum":["24h"],"type":"string"},"created_at":{"description":"Unix timestamp (seconds).","format":"int64","type":"integer"},"endpoint":{"description":"The API family every record in the batch calls.","type":"string"},"error_file_id":{"description":"Present once results are written; fetch its content for failed records.","type":"string"},"expired_at":{"format":"int64","type":"integer"},"expires_at":{"description":"When the completion window closes (created_at + 24h).","format":"int64","type":"integer"},"failed_at":{"format":"int64","type":"integer"},"id":{"description":"Batch id (batch_…).","type":"string"},"input_file_id":{"type":"string"},"metadata":{"additionalProperties":{"type":"string"},"description":"Your key-value pairs, echoed back unchanged.","type":"object"},"model":{"description":"Model id, settled from the input file during validation.","type":"string"},"object":{"enum":["batch"],"type":"string"},"output_file_id":{"description":"Present once results are written; fetch its content for successful records.","type":"string"},"request_counts":{"$ref":"#/components/schemas/BatchRequestCounts"},"status":{"description":"Lifecycle state. validating → in_progress → finalizing → completed | failed | expired; cancelling → cancelled.\n","enum":["validating","in_progress","finalizing","completed","failed","expired","cancelling","cancelled"],"type":"string"},"usage":{"$ref":"#/components/schemas/BatchUsage"}},"required":["id","object","endpoint","input_file_id","completion_window","status","created_at","request_counts"],"type":"object"},"BatchOpenRelayExtensions":{"description":"The reserved \"openrelay\" object on a batch record body: the one top-level key OpenRelay request extensions live under, mirroring the \"openrelay\" response namespace that carries tool_rounds. Additive, so new keys may appear without a breaking change. The worker strips the keys it owns from the body before every model invocation.\n","properties":{"tool_config":{"$ref":"#/components/schemas/BatchToolConfig"}},"type":"object"},"BatchRequestCounts":{"description":"Progress counters, settled as the batch validates and shards complete.","properties":{"completed":{"format":"int64","type":"integer"},"failed":{"format":"int64","type":"integer"},"total":{"format":"int64","type":"integer"}},"required":["total","completed","failed"],"type":"object"},"BatchRequestRecord":{"description":"One batch request: the same object shape used for each line of a JSONL input file and for each entry of the inline requests array.\n","properties":{"body":{"description":"The request body you would send to that endpoint online, for example a {model, messages} object for /v1/chat/completions. Streaming is not supported, so \"stream\": true fails that record. A chat record may also carry OpenRelay request extensions under the reserved \"openrelay\" key. A bare top-level tool_config is the retired spelling of that extension and fails the record with code tool_config_moved.\n","properties":{"openrelay":{"$ref":"#/components/schemas/BatchOpenRelayExtensions"}},"type":"object"},"custom_id":{"description":"Your identifier for this request, echoed on the matching result line so you can join results back to inputs. Unique within the batch.\n","type":"string"},"method":{"description":"HTTP method for the record; only POST is supported.","enum":["POST"],"type":"string"},"url":{"description":"The endpoint this record targets; must match the batch's endpoint.","type":"string"}},"type":"object"},"BatchToolConfig":{"description":"The OpenRelay tool-calling extension, carried at body.openrelay.tool_config on a /v1/chat/completions record: where to execute the model's tool calls between turns. When a turn ends with finish_reason \"tool_calls\", the batch worker POSTs the calls to this endpoint, appends the assistant and tool messages, and re-invokes the model, up to max_rounds times. The field is stripped from the body before every model invocation and never appears in result files. A malformed value fails that one record with code invalid_tool_config.\n","properties":{"authorization":{"description":"Sent verbatim as the Authorization header on every executor call.","type":"string"},"context":{"description":"Opaque JSON forwarded to the executor on every call.","type":"object"},"max_rounds":{"default":8,"description":"Maximum tool round-trips for this record. 0 or omitted takes the default of 8; values above 16 are clamped to 16. A record can therefore cost up to max_rounds + 1 model invocations.\n","maximum":16,"minimum":0,"type":"integer"},"timeout_ms":{"default":30000,"description":"Per-executor-call timeout in milliseconds. 0 or omitted takes the default of 30000; values above 120000 are clamped to 120000.\n","maximum":120000,"minimum":0,"type":"integer"},"type":{"default":"http","description":"Executor protocol. \"http\" is the plain POST contract ({custom_id, context, tool_calls} in, {results:[{tool_call_id, content}]} out); \"mcp\" is a Model Context Protocol server speaking Streamable HTTP. Defaults to \"http\".\n","enum":["http","mcp"],"type":"string"},"url":{"description":"Your tool-executor endpoint. HTTPS only, because the request carries your authorization value. Endpoints resolving to internal or private addresses are rejected.\n","format":"uri","type":"string"}},"required":["url"],"type":"object"},"BatchUsage":{"description":"Rolled-up token and cost totals, present once any progress is recorded. cost_nano_usd is the price you pay (batch discount applied), in nano-USD so sub-cent batches stay exact.\n","properties":{"cost_nano_usd":{"format":"int64","type":"integer"},"input_tokens":{"format":"int64","type":"integer"},"output_tokens":{"format":"int64","type":"integer"}},"required":["input_tokens","output_tokens","cost_nano_usd"],"type":"object"},"BootstrapRequest":{"properties":{"orgName":{"type":"string"}},"type":"object"},"BootstrapResult":{"properties":{"organizationId":{"type":"string"},"role":{"type":"string"}},"required":["organizationId","role"],"type":"object"},"ClassAvailability":{"description":"Availability for one consumption mode of a model: \"vm\" = VFIO passthrough (exact, from per-node unit state); \"cluster\" = container device-request (capacity minus cluster commitment).","properties":{"freeGpus":{"type":"integer"},"maxPlaceableGpus":{"description":"Largest single allocation possible right now: for a VM the biggest tiling on any one node, for a cluster the largest per-replica device count on any one node. A workload can never exceed this even if freeGpus is larger (it lands on a single node).","type":"integer"},"nodes":{"items":{"$ref":"#/components/schemas/NodeClassCap"},"type":"array"},"placeableGpuCounts":{"description":"GPU counts actually placeable on \u003e=1 node right now. Frontends drive the count picker from this so offered counts can't 409/deploy-timeout.","items":{"type":"integer"},"type":"array"},"reservedFreeGpus":{"description":"How much of freeGpus is reserved exclusively for your organization: capacity you contracted for, as opposed to capacity you would be renting from the open market. Absent or 0 when none of it is yours.","type":"integer"},"spotFreeGpus":{"description":"Free GPUs released from a node reservation to the spot market. NOT included in freeGpus, because you can only get them by asking for tier=spot, which accepts that the capacity owner may terminate the workload at any time to take the hardware back.","type":"integer"},"spotPlaceableGpuCounts":{"description":"GPU counts placeable right now at tier=spot (released capacity plus ordinary public capacity). A superset of placeableGpuCounts; drive a spot buyer's count picker from this.","items":{"type":"integer"},"type":"array"},"tileLattice":{"description":"Structural set of counts this model's units can ever tile to (free or not): the hardware shape. vm view only; clusters are step-1.","items":{"type":"integer"},"type":"array"},"totalGpus":{"description":"free + claimed","type":"integer"}},"required":["totalGpus","freeGpus","maxPlaceableGpus"],"type":"object"},"ClusterDetail":{"properties":{"allowFallback":{"type":"boolean"},"containerPort":{"type":"integer"},"createdAt":{"type":"string"},"deployToken":{"type":"string"},"diskSizeGb":{"type":"integer"},"endpointUrl":{"type":"string"},"envVars":{"additionalProperties":{"type":"string"},"type":"object"},"gpuModel":{"$ref":"#/components/schemas/GpuModel"},"gpuModelId":{"type":"string"},"gpusPerReplica":{"type":"integer"},"healthCheckPath":{"type":"string"},"healthCheckPort":{"type":"integer"},"healthChecks":{"items":{"type":"string"},"type":"array"},"id":{"type":"string"},"imageUrl":{"type":"string"},"name":{"type":"string"},"notes":{"type":"string"},"organizationId":{"type":"string"},"preferredRegions":{"items":{"type":"string"},"type":"array"},"privileged":{"type":"boolean"},"replicaCount":{"type":"integer"},"replicas":{"items":{"$ref":"#/components/schemas/ClusterReplicaItem"},"type":"array"},"resourceSize":{"type":"string"},"status":{"type":"string"},"statusReason":{"description":"human-readable cause for failed/stuck states","type":"string"},"tier":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","status","gpusPerReplica","resourceSize","replicaCount","tier","allowFallback","diskSizeGb","privileged","replicas","createdAt","updatedAt"],"type":"object"},"ClusterPage":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ClusterSummary"},"type":"array"},"nextCursor":{"type":"string"}},"required":["items"],"type":"object"},"ClusterReplicaItem":{"properties":{"consecutiveFailures":{"type":"integer"},"createdAt":{"type":"string"},"diskTotalBytes":{"format":"int64","type":"integer"},"diskUpdatedAt":{"type":"string"},"diskUsedBytes":{"format":"int64","type":"integer"},"endpointUrl":{"type":"string"},"gpuCount":{"type":"integer"},"healthy":{"type":"boolean"},"id":{"type":"string"},"internalIp":{"type":"string"},"lastHealthCheckAt":{"type":"string"},"nodeHostname":{"type":"string"},"nodeId":{"type":"string"},"nodeLocation":{"type":"string"},"nodeRegion":{"type":"string"},"nvlinkEnabled":{"type":"boolean"},"port":{"type":"integer"},"sshPort":{"type":"integer"},"startedAt":{"type":"string"},"status":{"type":"string"}},"required":["id","gpuCount","status","healthy","nvlinkEnabled","createdAt"],"type":"object"},"ClusterSummary":{"properties":{"activeReplicas":{"type":"integer"},"createdAt":{"type":"string"},"endpointUrl":{"type":"string"},"gpuModelId":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organizationId":{"type":"string"},"replicaCount":{"type":"integer"},"status":{"type":"string"}},"required":["id","organizationId","name","status"],"type":"object"},"CpuPrice":{"properties":{"pricePerHourCents":{"type":"integer"},"ramMb":{"description":"guest RAM in MiB a machine of this size boots with","type":"integer"},"resourceSize":{"type":"string"},"vcpu":{"description":"vCPU cores a machine of this size boots with","type":"integer"}},"required":["resourceSize","pricePerHourCents","vcpu","ramMb"],"type":"object"},"CreateApiKeyRequest":{"properties":{"expiresInSeconds":{"description":"Optional server-side expiry, in seconds from now. When set (and \u003e 0) the key stops authenticating after this many seconds, capping the leak window regardless of any client-side cleanup. Omitted or \u003c= 0 means the key never expires (the default).","format":"int64","type":"integer"},"name":{"type":"string"},"scopes":{"description":"Supported values: clusters:read, clusters:write, vms:read, vms:write, inference.","items":{"type":"string"},"type":"array"}},"required":["name"],"type":"object"},"CreateApiKeyResult":{"properties":{"createdAt":{"type":"string"},"id":{"type":"string"},"key":{"description":"Plaintext key, shown once","type":"string"},"keyPrefix":{"type":"string"},"name":{"type":"string"},"organizationId":{"description":"The organization this key is bound to. Every API key belongs to exactly one organization; keep this id alongside the key so callers can build org-scoped request paths without a separate lookup (GET /v1/whoami returns the same id for an existing key).","type":"string"},"scopes":{"items":{"type":"string"},"type":"array"}},"required":["id","key","keyPrefix","name","scopes","createdAt","organizationId"],"type":"object"},"CreateClusterRequest":{"properties":{"allowFallback":{"type":"boolean"},"containerCommand":{"items":{"type":"string"},"type":"array"},"containerPort":{"type":"integer"},"diskSizeGb":{"type":"integer"},"envVars":{"additionalProperties":{"type":"string"},"type":"object"},"gpuModelId":{"type":"string"},"gpusPerReplica":{"type":"integer"},"healthCheckPath":{"type":"string"},"healthCheckPort":{"type":"integer"},"healthChecks":{"items":{"type":"string"},"type":"array"},"imageUrl":{"type":"string"},"kernelModules":{"items":{"type":"string"},"type":"array"},"name":{"type":"string"},"preferredRegions":{"items":{"type":"string"},"type":"array"},"privileged":{"type":"boolean"},"registryCredentialId":{"type":"string"},"replicaCount":{"type":"integer"},"resourceSize":{"type":"string"},"templateId":{"type":"string"},"tier":{"type":"string"}},"required":["name"],"type":"object"},"CreateInferenceCatalogBackendRequest":{"properties":{"auth":{"description":"Auth STYLE the gateway applies to this backend's requests. anthropic sends x-api-key + anthropic-version headers (required by Anthropic- dialect upstreams); api_key sends Authorization: Bearer.","enum":["none","bedrock_api_key","api_key","anthropic"],"type":"string"},"endpoint":{"type":"string"},"maxConcurrency":{"minimum":0,"type":"integer"},"nativeFamilies":{"items":{"type":"string"},"minItems":1,"type":"array"},"provider":{"description":"Managed providers only; self-hosted capacity attaches through serving deployments","enum":["bedrock","openrouter","openai","anthropic"],"type":"string"},"ref":{"description":"Provider-side model id","type":"string"},"transport":{"enum":["direct","tunnel","privatelink"],"type":"string"},"weight":{"default":0,"description":"Starting weight; 0 keeps the backend in the overflow tail so attaching moves no traffic","minimum":0,"type":"integer"}},"required":["provider","ref","endpoint","nativeFamilies"],"type":"object"},"CreateNodeReservationRequest":{"properties":{"allowedOrgIds":{"description":"The audience. The platform ops org is added automatically so we can validate the node.","items":{"type":"string"},"type":"array"},"contractRef":{"type":"string"},"endsAt":{"description":"Omit for open-ended","type":"string"},"managingOrgIds":{"description":"Orgs allowed to release, reclaim, and terminate spot workloads on these units on the owner's behalf. Omit unless a second org manages this capacity commercially.","items":{"type":"string"},"type":"array"},"reason":{"type":"string"},"startsAt":{"description":"Defaults to now","type":"string"},"unitIds":{"description":"GPU unit ids to reserve (from the node's inventory). Must be vfio-group units.","items":{"type":"string"},"type":"array"}},"required":["unitIds","allowedOrgIds"],"type":"object"},"CreateOrgRequest":{"properties":{"billingEmail":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"required":["name"],"type":"object"},"CreatePodRequest":{"description":"Request to create a compute-fabric pod: a RunPod-style bring-your-own container image + SSH keys guest. A focused subset of CreateVmRequest (no qcow2 template / kernel modules / privileged mode).","properties":{"computeClass":{"default":"on_demand","description":"purchase option. on_demand (pay per second, no commitment) is the only class sold; reserved is a deprecated alias that is stored as on_demand. interruptible is retired and rejected with 400 COMPUTE_CLASS_RETIRED; it remains listed only until the deprecation window closes.","enum":["on_demand","reserved","interruptible"],"type":"string"},"envVars":{"additionalProperties":{"type":"string"},"type":"object"},"gpuCount":{"default":1,"description":"how many whole GPUs this pod gets. All of them are claimed on ONE node and passed to the container together; the pod is priced for gpuCount GPUs. Omitted = 1. A request larger than any single node can currently tile is rejected at admission (see freeUnits / maxGpuCount on /v1/pods/capacity).","maximum":8,"minimum":1,"type":"integer"},"gpuModelId":{"type":"string"},"httpPort":{"default":80,"description":"in-guest HTTP port exposed on the endpoint","type":"integer"},"image":{"description":"tenant container image ref (required)","type":"string"},"name":{"type":"string"},"public":{"default":false,"description":"true = open endpoint (no data-plane auth); default false requires an org API key","type":"boolean"},"shmSizeGb":{"description":"size of the pod's private /dev/shm in GiB (docker --shm-size). Optional; omitted lets the platform pick a safe default (a generous share for a pod that owns its whole node, otherwise 2 GiB per GPU). The maximum a create may set is the pod's memory entitlement on its placement node (its GPU-proportional share of node RAM); a larger value is rejected at admission. Raise this when a multi-GPU NCCL / PyTorch DataLoader workload needs more shared memory than the default.","maximum":8192,"minimum":1,"type":"integer"},"sshKeys":{"description":"tenant SSH public keys injected into the guest","items":{"type":"string"},"type":"array"}},"required":["image"],"type":"object"},"CreateRegistryCredentialRequest":{"properties":{"authType":{"enum":["basic","aws_ecr","gcp_gcr"],"type":"string"},"credentials":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"type":"string"},"server":{"type":"string"}},"required":["name","server","authType","credentials"],"type":"object"},"CreateRunnerPoolRequest":{"properties":{"diskSizeGb":{"type":"integer"},"githubOwner":{"type":"string"},"githubOwnerType":{"type":"string"},"grant":{"type":"string"},"installationId":{"format":"int64","type":"integer"},"name":{"type":"string"},"poolRamGb":{"type":"integer"}},"required":["installationId"],"type":"object"},"CreateServingDeploymentRequest":{"properties":{"acknowledgeLaneJoin":{"default":false,"description":"Required (true) to create an independent deployment into a lane that already has live deployments. Without it such a create is rejected with LANE_INTENT_REQUIRED naming the lane's current members, because \"add capacity beside the live lane\" and \"replace the live lane\" must never be the same request body. Not stored: it describes this request, not the deployment.","type":"boolean"},"backendWeight":{"description":"Initial routing weight; defaults to 0 (standby)","type":"integer"},"dtype":{"type":"string"},"engine":{"description":"Must resolve to a known engine (e.g. vllm, sglang)","type":"string"},"engineImage":{"description":"Engine container image override","type":"string"},"env":{"additionalProperties":{"type":"string"},"type":"object"},"extraArgs":{"items":{"type":"string"},"type":"array"},"gpuCountPerReplica":{"type":"integer"},"gpuModelId":{"type":"string"},"isolated":{"default":false,"description":"Launch and health-check replicas without ever publishing their pool as a catalog backend. Requires backendWeight 0.","type":"boolean"},"maxConcurrencyPerReplica":{"type":"integer"},"maxModelLen":{"type":"integer"},"maxPromptBytes":{"description":"Exclusive text-request byte ceiling for this deployment's routing lane; 0 or absent = unbounded","type":"integer"},"maxUnavailable":{"description":"Rolling only: how many replicas may be simultaneously not-ready during a rolling rollout (absolute count; surfaces show the percentage of desired). Defaults to 1 when rolloutStrategy is rolling and this is omitted. Ignored by blue/green.","minimum":1,"type":"integer"},"minPromptBytes":{"description":"Inclusive text-request byte floor for this deployment's routing lane; 0 or absent = unbounded","type":"integer"},"nativeFamilies":{"description":"Defaults to chat_completions at registration","items":{"type":"string"},"type":"array"},"nodePool":{"description":"Stable logical capacity lane, e.g. rtx5090 or h100. Deployments in different node pools keep independent routing weights.","type":"string"},"organizationId":{"description":"Org the managed replicas run (and meter) under","type":"string"},"publicModelId":{"description":"Catalog model the pool backend registers on. Must already be seeded, with pricing","type":"string"},"quantization":{"type":"string"},"replicaRuntime":{"description":"How each replica is placed. Omit (or \"\" or \"vm\") for a QEMU/VFIO VM on the baremetal fleet (the legacy default). Set \"pod\" to place replicas as hardened GPU pods on provider-onboarded runc nodes; a pod deployment REQUIRES weightsRef (pods never fall back to a hub download). Unknown values are rejected, never coerced to a runtime.","enum":["","vm","pod"],"type":"string"},"replicasDesired":{"description":"Must lie within replicasMin..replicasMax","type":"integer"},"replicasMax":{"type":"integer"},"replicasMin":{"type":"integer"},"rolloutRole":{"description":"Omit (or \"\") to create an INDEPENDENT weighted capacity member of the lane. Set \"green\" to start a blue/green rollout that ramps traffic onto this version and eventually demotes the lane's older deployments. A green must start at backendWeight 0, and only one may be live per lane.","enum":["","green"],"type":"string"},"rolloutStrategy":{"description":"How a version bump reaches this deployment's fleet. Omit (or blue_green) for the default sibling-and-ramp rollout. \"rolling\" replaces THIS deployment's replicas in place one at a time (start via the rolling-rollout verb); maxUnavailable bounds how many may be down.","enum":["blue_green","rolling"],"type":"string"},"servedModelId":{"description":"Must equal the catalog backend ref the gateway rewrites to","type":"string"},"version":{"type":"string"},"weightsRef":{"description":"Staged-checkpoint reference; omit to let the engine load from its own source","type":"string"}},"required":["organizationId","publicModelId","engine","servedModelId","gpuModelId","gpuCountPerReplica","replicasMin","replicasMax","replicasDesired"],"type":"object"},"CreateSnapshotRequest":{"properties":{"name":{"type":"string"}},"required":["name"],"type":"object"},"CreateSshKeyRequest":{"properties":{"name":{"type":"string"},"publicKey":{"type":"string"}},"required":["name","publicKey"],"type":"object"},"CreateVmRequest":{"properties":{"allowFallback":{"type":"boolean"},"cloudInit":{"description":"Startup script run inside the guest the first time this VM boots. Must begin with a shebang (e.g. \"#!/bin/bash\"); a raw cloud-config document is rejected. Runs once per disk, not once per boot: a stop/start keeps the disk and does not re-run it, while a VM that lands on a fresh disk does. It runs as root once the network and your SSH keys are up, and its output goes to the VM's logs. A script that fails does not stop the VM from starting or from being billed, so check the logs rather than assuming it succeeded.","maxLength":16384,"type":"string"},"containerCommand":{"items":{"type":"string"},"type":"array"},"containerPort":{"type":"integer"},"diskSizeGb":{"type":"integer"},"envVars":{"additionalProperties":{"type":"string"},"type":"object"},"gpuCount":{"type":"integer"},"gpuModelId":{"type":"string"},"healthCheckPath":{"type":"string"},"healthCheckPort":{"type":"integer"},"healthChecks":{"items":{"type":"string"},"type":"array"},"imageUrl":{"type":"string"},"kernelModules":{"items":{"type":"string"},"type":"array"},"name":{"type":"string"},"nodeId":{"description":"Place this VM on a specific node. Required to use capacity reserved to your org; omit to use the public pool. There is no fallback: if the node has no room for this GPU count the request is refused rather than placed elsewhere. A node you have no rights on reads as not found.","type":"string"},"preferredRegions":{"items":{"type":"string"},"type":"array"},"privileged":{"type":"boolean"},"public":{"description":"true = open endpoint (no data-plane auth); default false requires an org API key","type":"boolean"},"registryCredentialId":{"type":"string"},"resourceSize":{"type":"string"},"sshKeyIds":{"items":{"type":"string"},"type":"array"},"templateId":{"type":"string"},"tier":{"type":"string"}},"required":["name"],"type":"object"},"CreateWebhookRequest":{"properties":{"events":{"items":{"type":"string"},"type":"array"},"name":{"type":"string"},"url":{"type":"string"}},"required":["name","url"],"type":"object"},"CreatedResource":{"properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"}},"required":["id","name","status"],"type":"object"},"CurrentUsage":{"properties":{"autoStopFloorCents":{"description":"available-balance floor (cents) below which the org is auto-stopped","type":"integer"},"breakdown":{"items":{"$ref":"#/components/schemas/UsageBreakdownItem"},"type":"array"},"burnPerHourCents":{"description":"sum of the locked rates of every open usage record in the org (live burn per hour)","type":"integer"},"projectedRunwayHours":{"description":"hours until available balance hits the auto-stop floor at the current burn: (available - autoStopFloorCents) / burnPerHourCents; null when burn is 0, clamped to 0 when already at or below the floor","format":"double","nullable":true,"type":"number"},"usageCents":{"type":"integer"},"usageHours":{"type":"number"}},"required":["usageCents","usageHours","breakdown"],"type":"object"},"DailyUsage":{"properties":{"daily":{"description":"one point per day, oldest first, zero-filled so the series is continuous","items":{"$ref":"#/components/schemas/DailyUsagePoint"},"type":"array"},"previousCents":{"description":"total spend in the same-length window immediately before this one","type":"integer"},"previousSegments":{"items":{"$ref":"#/components/schemas/UsageSegment"},"type":"array"}},"required":["daily","previousCents","previousSegments"],"type":"object"},"DailyUsagePoint":{"properties":{"cents":{"description":"total spend attributed to this day","type":"integer"},"date":{"description":"UTC calendar day, YYYY-MM-DD","type":"string"},"segments":{"items":{"$ref":"#/components/schemas/UsageSegment"},"type":"array"}},"required":["date","cents","segments"],"type":"object"},"DeleteRunnerPoolResult":{"properties":{"status":{"type":"string"}},"required":["status"],"type":"object"},"DepositRequest":{"properties":{"amountCents":{"description":"Amount to charge the saved card, in cents (bounds $10-$500).","type":"integer"},"idempotencyKey":{"description":"Client-generated id for this logical deposit; reused verbatim on retry so a network/client retry can't double-charge. Optional: the server generates one if absent (then retries won't dedupe).","type":"string"},"promoCode":{"description":"Optional first-deposit promo code. When valid on the org's first deposit, amountCents is the deposit the customer wants credited and the saved card is charged the DISCOUNTED amount; the discount is credited as free (usage-only) credit by the webhook. Invalid / already-used codes are rejected (400) before any charge.","type":"string"}},"required":["amountCents"],"type":"object"},"DepositResult":{"properties":{"clientSecret":{"description":"Set only when status is requires_action (on-session SCA).","type":"string"},"paymentIntentId":{"type":"string"},"status":{"description":"Stripe PaymentIntent status. \"succeeded\" → credit lands via webhook; \"requires_action\" → complete SCA on-session with clientSecret; \"requires_payment_method\" → the card was declined.","type":"string"}},"required":["paymentIntentId","status"],"type":"object"},"EarningsBreakdownGpu":{"properties":{"billableGrossCents":{"description":"Customer gross attributable to legacy revenue-share paid-funded hours. Omitted for fixed-only providers; mixed providers exclude fixed-contract customer economics.","type":"integer"},"billableHours":{"description":"Legacy revenue-share hours that fell in the paid band. Omitted for fixed-only providers.","type":"number"},"earningHours":{"description":"Hours owed to the provider under fixed-hourly or legacy revenue-share terms.","type":"number"},"earningsCents":{"type":"integer"},"gpuModelId":{"type":"string"},"grossEarningsCents":{"description":"Customer sell gross for legacy revenue-share records. Omitted for fixed-only providers; mixed providers exclude fixed-contract customer economics.","type":"integer"},"hours":{"description":"Total hours hosted.","type":"number"},"name":{"type":"string"}},"required":["gpuModelId","name","earningsCents","hours"],"type":"object"},"EarningsBreakdownNode":{"properties":{"billableGrossCents":{"description":"Customer gross attributable to legacy revenue-share paid-funded hours. Omitted for fixed-only providers; mixed providers exclude fixed-contract customer economics.","type":"integer"},"billableHours":{"description":"Legacy revenue-share hours that fell in the customer's paid-funding band. Omitted for fixed-only providers.","type":"number"},"earningHours":{"description":"Hours owed to the provider. Fixed-hourly contracts use eligible hosted hours; legacy revenue share uses paid-funded hours.","type":"number"},"earningsCents":{"type":"integer"},"gpuCount":{"description":"GPU count on this node for the utilization denominator: sellable catalog inventory (Σ NodeGpu.Quantity, excluding unrecognised hardware), falling back to raw detected accelerators (Σ DetectedGpusRaw.Count) when no sellable rows exist. 0 only for a genuine CPU-only node.","type":"integer"},"grossEarningsCents":{"description":"Customer sell gross for legacy revenue-share records. Omitted for fixed-only providers; mixed providers exclude fixed-contract customer economics.","type":"integer"},"hostname":{"type":"string"},"hours":{"description":"Total hours hosted (paid + free-credit + unpaid).","type":"number"},"nodeId":{"type":"string"},"rentedGpuHoursMonth":{"description":"GPU-hours rented on this node so far this calendar month (Σ over GPU records of month-clamped hours × the record's gpuCount). The utilizationPercent numerator. NOTE: usage records written before per-record gpuCount was stamped count as 1 GPU (a conservative floor).","type":"number"},"utilizationPercent":{"description":"Month-to-date utilization: rentedGpuHoursMonth / (gpuCount × hours elapsed this month) × 100, clamped to [0,100]. Null when gpuCount is 0 (CPU node) or the month just started (no elapsed time to measure against). CAVEAT: the denominator uses the node's CURRENT gpuCount, so a node whose GPU inventory changed mid-month is exact only from the change onward (added GPUs understate; removed GPUs can saturate at 100).","nullable":true,"type":"number"}},"required":["nodeId","hostname","earningsCents","hours"],"type":"object"},"EarningsBreakdownStream":{"properties":{"billableGrossCents":{"description":"Customer gross attributable to legacy revenue-share paid-funded hours in this stream. Omitted for fixed-only providers; mixed providers exclude fixed-contract customer economics.","type":"integer"},"billableHours":{"description":"Legacy revenue-share hours in this stream that fell in the paid band. Omitted for fixed-only providers.","type":"number"},"earningHours":{"description":"Hours owed to the provider in this stream.","type":"number"},"earningsCents":{"description":"Provider earnings for this stream, calculated from the locked fixed-hourly rate or the legacy revenue share.","type":"integer"},"grossEarningsCents":{"description":"Customer sell gross for legacy revenue-share records in this stream. Omitted for fixed-only providers; mixed providers exclude fixed-contract customer economics.","type":"integer"},"hours":{"description":"Total hours hosted in this stream.","type":"number"},"stream":{"description":"Income stream: 'gpu' (GPU rentals) or 'cpu' (CPU-only rentals).","type":"string"}},"required":["stream","earningsCents","hours"],"type":"object"},"EarningsDailyPoint":{"properties":{"billableGrossCents":{"description":"Customer gross attributable to legacy revenue-share paid-funded hours this day. Omitted for fixed-only providers; mixed providers exclude fixed-contract customer economics.","type":"integer"},"billableHours":{"description":"Legacy revenue-share hours in the customer's paid-funding band this day; zero for fixed-contract records.","type":"number"},"date":{"description":"UTC day, YYYY-MM-DD.","type":"string"},"earningHours":{"description":"Hours owed to the provider this day.","type":"number"},"earningsCents":{"description":"Provider earnings attributed to this day. Prorated across day boundaries for records spanning days.","type":"integer"},"freeHours":{"description":"Legacy revenue-share hours funded by customer free credits this day; zero for fixed-contract records.","type":"number"},"hostedHours":{"description":"Total hours hosted this day (all bands).","type":"number"},"unpaidHours":{"description":"Legacy revenue-share hours beyond the customer's free+paid funding this day; zero for fixed-contract records.","type":"number"}},"required":["date","earningsCents","hostedHours","billableHours","freeHours","unpaidHours"],"type":"object"},"EnrollNodeBundle":{"properties":{"dataPlaneCa":{"description":"CertIssuer CA the gateway pins; optional","type":"string"},"dataPlaneCert":{"description":"node data-plane mTLS cert (gateway tunnel); optional","type":"string"},"dataPlaneKey":{"description":"node data-plane mTLS key; optional","type":"string"},"datacenter":{"type":"string"},"gatewayAddr":{"description":"host:port the node-agent dials (QUIC/UDP); optional","type":"string"},"gatewayServerName":{"description":"expected CN on the gateway server cert; optional","type":"string"},"nebulaCa":{"type":"string"},"nebulaCert":{"type":"string"},"nebulaLighthouses":{"items":{"properties":{"endpoint":{"description":"publicIp:4242","type":"string"},"nebulaIp":{"type":"string"}},"required":["nebulaIp","endpoint"],"type":"object"},"type":"array"},"nebulaOverlayIp":{"type":"string"},"nodeAgentToken":{"description":"durable node token for the node-agent self-update fetch (GET /v1/nodes/agent); optional","type":"string"},"nodeId":{"type":"string"},"nomadCa":{"type":"string"},"nomadClientCert":{"type":"string"},"nomadClientKey":{"type":"string"},"nomadClientReset":{"description":"The control plane has determined the node's Nomad record is gone (record GC'd past node_gc_threshold, or purged) and no live workload is pinned here, so enroll.sh must reset the local Nomad client (stop nomad, kill orphaned VM processes, delete the client state DB) before the fresh introduction token can register a new record. Never set while any workload is placed, so a running VM's processes are never stranded.","type":"boolean"},"nomadIntroToken":{"type":"string"},"nomadPool":{"type":"string"},"nomadServerName":{"description":"stable FQDN whose A records track the live Nomad server overlay IPs; enroll.sh prefers a retry_join on this name (re-resolves on every retry) and falls back to nomadServers when it is empty or the node cannot resolve it; optional","type":"string"},"nomadServers":{"description":"server Nebula RPC addrs (ip:4647); literal fallback used when nomadServerName is unset or does not resolve on the node","items":{"type":"string"},"type":"array"}},"required":["nodeId","nebulaCert","nebulaCa","nebulaOverlayIp","nebulaLighthouses","nomadCa","nomadClientCert","nomadClientKey","nomadServers","nomadIntroToken","nomadPool","datacenter","dataPlaneCert","dataPlaneKey","dataPlaneCa","gatewayAddr","gatewayServerName","nomadClientReset"],"type":"object"},"EnrollNodeRequest":{"properties":{"cpuCores":{"type":"integer"},"cpuModel":{"type":"string"},"gpuInventory":{"$ref":"#/components/schemas/GpuInventory"},"gpus":{"description":"LEGACY nvidia-smi detection (name + pci.bus_id BDF, NOT a vendor:device id despite the example). Kept for old scripts; gpuInventory supersedes it.","items":{"properties":{"name":{"type":"string"},"pciId":{"description":"nvidia-smi pci.bus_id (a BDF like 00000000:04:00.0)","type":"string"}},"required":["pciId"],"type":"object"},"type":"array"},"hardwareFingerprint":{"type":"string"},"hostShape":{"$ref":"#/components/schemas/HostShape"},"hostname":{"type":"string"},"identityPubkey":{"description":"Node ed25519 identity public key (base64 raw, 32 bytes). The collision-safe node anchor: the box generates this once, keeps the private key at /etc/vectorlay/identity.key, and signs every enroll with it. Required by the server: an enroll without it is refused (401 BAD_SIGNATURE).","type":"string","x-go-type-skip-optional-pointer":true},"machineId":{"description":"Stable per-box identity (SMBIOS product_uuid / serial / cloud instance-id). Primary global node-identity key; fingerprint is the fallback when empty.","type":"string"},"nebulaPubkey":{"description":"Node Nebula public key PEM (from nebula-cert keygen)","type":"string"},"ramGb":{"type":"integer"},"runtime":{"description":"Node runtime, detected on the box by enroll.sh from the host shape: qemu (VFIO/KVM passthrough VM host) on baremetal, runc (GPU-container Pod node) on a cloud guest. The control plane refuses an enroll whose runtime is absent or contradicts hostShape (matched case-insensitively).","type":"string"},"signature":{"description":"base64 ed25519 signature proving possession of identityPubkey over the identity-critical enroll fields (token hash, identity pubkey, nebula pubkey hash, fingerprint, machineId, hostname, signedAt). Required by the server: an enroll without it is refused (401 BAD_SIGNATURE).","type":"string","x-go-type-skip-optional-pointer":true},"signedAt":{"description":"Unix seconds when the enroll body was signed. Required by the server: an unsigned enroll is refused (401 BAD_SIGNATURE); rejected if more than 300s from server time.","type":"integer","x-go-type-skip-optional-pointer":true},"token":{"description":"Provisioning token (vtk_...)","type":"string"}},"required":["token","hardwareFingerprint","nebulaPubkey"],"type":"object"},"Error":{"properties":{"code":{"type":"string"},"error":{"type":"string"}},"required":["error"],"type":"object"},"FileObject":{"description":"A file stored for the Batch API (OpenAI-compatible).","properties":{"bytes":{"description":"File size in bytes.","format":"int64","type":"integer"},"created_at":{"description":"Unix timestamp (seconds).","format":"int64","type":"integer"},"expires_at":{"description":"Unix timestamp (seconds); files are retained for 30 days.","format":"int64","type":"integer"},"filename":{"type":"string"},"id":{"description":"File id (file-…). Pass it as input_file_id when creating a batch.","type":"string"},"object":{"enum":["file"],"type":"string"},"purpose":{"description":"\"batch\" for inputs, \"batch_output\" / \"batch_error\" for result files.","type":"string"}},"required":["id","object","bytes","created_at","filename","purpose"],"type":"object"},"ForkRequest":{"properties":{"containerPort":{"type":"integer"},"diskSizeGb":{"type":"integer"},"envVars":{"additionalProperties":{"type":"string"},"type":"object"},"gpuCount":{"type":"integer"},"gpuModelId":{"type":"string"},"imageUrl":{"type":"string"},"name":{"type":"string"},"resourceSize":{"type":"string"},"snapshotId":{"type":"string"}},"required":["snapshotId"],"type":"object"},"GenerateProvisioningTokenRequest":{"description":"Every token lives 24 hours from creation and enrolls any number of nodes in that window; neither is a request input. The node runtime is not a token property; each node detects its own shape at enroll (baremetal becomes qemu, a cloud guest becomes runc). Expired and revoked tokens stay listed for 7 days after creation, then fall out.","properties":{"name":{"type":"string"}},"type":"object"},"GpuAvailability":{"description":"Per-model GPU availability, answered separately for the two consumption modes because a physical GPU belongs to exactly one alloc-class (its driver binding): \"vm\" (vfio-group passthrough) and \"cluster\" (device-request).","properties":{"cluster":{"$ref":"#/components/schemas/ClassAvailability"},"formFactor":{"description":"Board type, see GpuModel.formFactor. Distinguishes SKUs that share a die.","type":"string"},"gpuModelId":{"type":"string"},"name":{"description":"Catalog display name, echoed here so a client picking from availability alone (the orl interactive picker) can show something other than a UUID.","type":"string"},"vm":{"$ref":"#/components/schemas/ClassAvailability"},"vramGb":{"type":"integer"}},"required":["gpuModelId","vm","cluster"],"type":"object"},"GpuInventory":{"description":"Versioned, driverless GPU detection (lspci) collected by enroll.sh. Works on VFIO hosts with no NVIDIA driver and on non-NVIDIA accelerators. source=none means the node could not detect (no lspci): the control plane must NOT treat that as \"no GPUs\".\n","properties":{"devices":{"items":{"properties":{"bdfs":{"description":"full BDFs of the group's devices","items":{"type":"string"},"type":"array"},"count":{"type":"integer"},"pciClass":{"description":"PCI class code, e.g. 0300/0302/1200","type":"string"},"pciDeviceId":{"description":"vendor:device, e.g. 10de:2204","type":"string"},"pciName":{"description":"lspci device name string","type":"string"}},"required":["pciDeviceId","count"],"type":"object"},"type":"array"},"driverVersion":{"type":"string"},"iommuGroups":{"description":"Per-IOMMU-group VFIO units: each entry is one IOMMU group containing an NVIDIA/AMD GPU. A group's GPUs are indivisible (must be passed together). Empty when IOMMU is off (runc pod / CPU nodes): those GPUs are fed via the Nomad device fingerprint instead. Kept out of the hardware fingerprint (BDFs renumber across reboots).\n","items":{"properties":{"gpus":{"description":"GPU-class (0300/0302) NVIDIA/AMD devices in this group, the sellable + passthrough set","items":{"properties":{"bdf":{"description":"full BDF, e.g. 0000:04:00.0","type":"string"},"deviceId":{"description":"vendor:device, e.g. 10de:2204","type":"string"}},"required":["bdf","deviceId"],"type":"object"},"type":"array"},"group":{"description":"IOMMU group number (provenance)","type":"integer"}},"required":["group","gpus"],"type":"object"},"type":"array"},"nvlinkActive":{"description":"The DRIVER reports live NVLink between GPUs (NV# links in nvidia-smi topo -m). True on a virtualised SXM guest whose nvswitchCount is 0. Absent on older node scripts.","type":"boolean"},"nvswitchBdfs":{"description":"Full BDFs of the node's NVSwitch functions (PCI class 0680, vendor 10de), e.g. \"0000:83:00.0\". Full-node (all-GPU) VM placement appends these to the passthrough list so the guest gets the NVLink fabric; partial allocations never receive them. Absent/empty on non-SXM boxes and old enroll scripts.\n","items":{"type":"string"},"type":"array"},"nvswitchCount":{"description":"NVSwitch bridges (PCI class 0680, vendor 10de) VISIBLE ON THIS MACHINE'S PCI BUS. This answers \"can we pass the switches through to a VM?\", not \"does NVLink work?\": on a virtualised SXM platform the host keeps the switch functions and gives the guest an NVLink partition, so this is 0 while NVLink is fully live. Read nvlinkActive for the latter.","type":"integer"},"source":{"description":"none | lspci | lspci+nvidia-smi","type":"string"},"version":{"type":"integer"}},"required":["version","source","devices","nvswitchCount"],"type":"object"},"GpuModel":{"properties":{"architecture":{"type":"string"},"createdAt":{"type":"string"},"formFactor":{"description":"Board type: \"sxm\", \"pcie\", \"nvl\", \"oam\", or absent when unknown. One GPU die ships in several boards that differ in memory type, TDP and peer bandwidth, so they are separate models at separate prices. Read this instead of parsing name.","type":"string"},"hidden":{"type":"boolean"},"id":{"type":"string"},"interconnect":{"description":"Peer fabric: \"nvlink\", \"nvswitch\", \"infinity-fabric\", or absent for PCIe-only parts. Present here so a client can tell an NVLink-capable SKU from a PCIe one without parsing name.","type":"string"},"manufacturer":{"type":"string"},"name":{"type":"string"},"vramGb":{"type":"integer"}},"required":["id","name","manufacturer","vramGb","hidden"],"type":"object"},"GpuPrice":{"properties":{"gpuModelId":{"type":"string"},"gpuModelName":{"type":"string"},"pricePerHourCents":{"type":"integer"},"tier":{"type":"string"},"vramGb":{"type":"integer"}},"required":["gpuModelId","tier","pricePerHourCents"],"type":"object"},"Health":{"properties":{"service":{"type":"string"},"status":{"type":"string"}},"required":["status","service"],"type":"object"},"HostShape":{"description":"What the machine ACTUALLY is, detected by enroll.sh. The platform supports two shapes and each has exactly one correct runtime: baremetal =\u003e qemu (we convert the GPUs to vfio-pci and rent VMs), guest =\u003e runc (we keep the native NVIDIA driver and run pods). The runtime follows the shape on the box, and the control plane refuses any enroll whose runtime contradicts it instead of recording a node that cannot serve its own product. Absent on older node scripts, which the control plane refuses (re-run the bootstrap).\n","properties":{"hasDevKvm":{"description":"/dev/kvm exists. A node without it cannot host VMs no matter what its runtime says.","type":"boolean"},"hypervisor":{"description":"Raw systemd-detect-virt output (kvm, vmware, none, ...); provenance for the shape.","type":"string"},"iommuGroupCount":{"description":"Entries in /sys/kernel/iommu_groups. 0 on a typical cloud guest, which is why pod nodes mint one unit per GPU instead of per IOMMU group.","type":"integer"},"shape":{"description":"baremetal | guest | unknown. unknown means systemd-detect-virt was unavailable; it is NOT treated as baremetal, because that is the direction that tears down a working driver.","type":"string"}},"type":"object"},"InferenceCatalogBackend":{"description":"One place the model can be served, as a traffic surface renders it.","properties":{"accelerator":{"description":"Hardware class tag, when known","type":"string"},"endpoint":{"description":"pool://\u003cpoolId\u003e for self-hosted capacity; an external URL for managed providers","type":"string"},"healthy":{"description":"false = excluded from routing entirely (the kill switch)","type":"boolean"},"maxConcurrency":{"description":"Per-backend in-flight cap; 0 = model default","type":"integer"},"maxPromptBytes":{"type":"integer"},"minPromptBytes":{"type":"integer"},"nativeFamilies":{"items":{"type":"string"},"type":"array"},"pools":{"description":"Self-hosted only: the deployments registering into this pool","items":{"properties":{"deploymentId":{"type":"string"},"nodePool":{"type":"string"},"status":{"type":"string"},"version":{"type":"string"}},"type":"object"},"type":"array"},"provider":{"description":"selfhosted|bedrock|openrouter|openai|anthropic (openrelay pools report selfhosted)","type":"string"},"ref":{"description":"Provider-side model id / deployment id","type":"string"},"standby":{"description":"true = held out of even the overflow tail (an unramped rollout green)","type":"boolean"},"transport":{"description":"direct|tunnel|privatelink","type":"string"},"version":{"description":"Low-cardinality rollout tag, when set","type":"string"},"weight":{"description":"Live routing weight (0 = overflow tail)","type":"integer"}},"required":["provider","ref","endpoint","weight","healthy"],"type":"object"},"InferenceCatalogModel":{"description":"The endpoint-level routing view of one catalog model.","properties":{"backends":{"items":{"$ref":"#/components/schemas/InferenceCatalogBackend"},"type":"array"},"enabled":{"type":"boolean"},"modelId":{"type":"string"},"routing":{"properties":{"families":{"additionalProperties":{"properties":{"maxAttempts":{"type":"integer"},"strategy":{"type":"string"}},"type":"object"},"type":"object"},"maxAttempts":{"type":"integer"},"strategy":{"description":"first_healthy|weighted|sticky (empty = gateway default)","type":"string"}},"type":"object"},"status":{"type":"string"},"updatedAt":{"type":"string"}},"required":["modelId","backends"],"type":"object"},"InferenceCatalogModelList":{"properties":{"models":{"items":{"$ref":"#/components/schemas/InferenceCatalogModel"},"type":"array"}},"required":["models"],"type":"object"},"InferenceError":{"description":"Error shape returned by the inference endpoint (OpenAI-compatible).","properties":{"error":{"properties":{"code":{"description":"Machine code for the specific failure.","type":"string"},"message":{"description":"Human-readable explanation.","type":"string"},"type":{"description":"Error class, e.g. invalid_request_error.","type":"string"}},"required":["message","type"],"type":"object"}},"required":["error"],"type":"object"},"InferenceModelUsage":{"properties":{"batch":{"type":"boolean"},"dimensions":{"additionalProperties":{"pattern":"^[0-9]+$","type":"string"},"description":"Exact decimal token quantities. Current keys are input, cached_input, cache_write, and output; the map shape reserves room for future metered units.","type":"object"},"model":{"type":"string"},"requestCount":{"description":"Exact decimal count; encoded as a string so browser clients do not lose int64 precision.","pattern":"^[0-9]+$","type":"string"}},"required":["model","requestCount","batch","dimensions"],"type":"object"},"InferenceTransactionDetails":{"properties":{"carriedNanoUSD":{"description":"Customer charge represented by these metered requests that remains as sub-cent accrual for a later debit.","pattern":"^[0-9]+$","type":"string"},"models":{"items":{"$ref":"#/components/schemas/InferenceModelUsage"},"type":"array"},"partial":{"description":"True when some of the debit is not itemized in this row, including legacy usage or sub-cent cost carried from a prior row.","type":"boolean"}},"required":["models","partial","carriedNanoUSD"],"type":"object"},"InitiateTransferRequest":{"properties":{"note":{"type":"string"},"resourceId":{"type":"string"},"resourceType":{"enum":["cluster","vm"],"type":"string"},"targetOrgSlug":{"type":"string"}},"required":["resourceType","resourceId","targetOrgSlug"],"type":"object"},"Invite":{"properties":{"email":{"type":"string"},"expiresAt":{"description":"RFC3339; the invite cannot be accepted after this time. Re-invite to refresh.","type":"string"},"invitedAt":{"type":"string"},"invitedBy":{"type":"string"},"role":{"type":"string"}},"required":["email","role","invitedAt"],"type":"object"},"Location":{"properties":{"city":{"type":"string"},"country":{"type":"string"},"id":{"type":"string"},"latitude":{"type":"number"},"longitude":{"type":"number"},"name":{"type":"string"},"region":{"type":"string"}},"required":["id","name","city","country","region"],"type":"object"},"Me":{"properties":{"organizations":{"items":{"$ref":"#/components/schemas/OrgMembership"},"type":"array"},"user":{"$ref":"#/components/schemas/UserProfile"}},"required":["user","organizations"],"type":"object"},"Member":{"properties":{"avatarUrl":{"type":"string"},"email":{"type":"string"},"fullName":{"type":"string"},"invitedAt":{"type":"string"},"joinedAt":{"type":"string"},"role":{"type":"string"},"status":{"enum":["active","invited"],"type":"string"},"userId":{"type":"string"}},"required":["userId","role"],"type":"object"},"Modalities":{"description":"Input and output modalities the model accepts and produces. Values are free-form strings (text, image, audio, video, and novel ones such as a world model's state/actions) so new modalities need no schema change. This is what distinguishes text→text, vision (image→text), text→image, image→image, TTS/STT, and video/world models.\n","properties":{"input":{"description":"What the model accepts, e.g. [text], [text, image].","items":{"type":"string"},"type":"array"},"output":{"description":"What the model produces, e.g. [text], [image], [video].","items":{"type":"string"},"type":"array"}},"required":["input","output"],"type":"object"},"Model":{"description":"A public model in the OpenRelay catalog (OpenAI-compatible).","properties":{"batch_eligible":{"description":"Whether this model can be run through the Batch API (POST /v1/batches). Omitted/false = online only. Batch pricing is ~50% of the online rate.\n","type":"boolean"},"created":{"description":"Unix timestamp (seconds) the model was added to the catalog.","format":"int64","type":"integer"},"id":{"description":"Public model id, send this in the `model` field of a request.","type":"string"},"modalities":{"$ref":"#/components/schemas/Modalities"},"object":{"enum":["model"],"type":"string"},"owned_by":{"description":"Always \"openrelay\".","type":"string"},"pricing":{"$ref":"#/components/schemas/ModelPricing"},"status":{"description":"available = usable now. request_access = listed for discovery but not yet invocable for your organization; request access to enable it.\n","enum":["available","request_access"],"type":"string"},"supported_apis":{"description":"API families this model serves, e.g. chat_completions, images, embeddings.","items":{"type":"string"},"type":"array"}},"required":["id","object","created","owned_by","supported_apis","modalities","status"],"type":"object"},"ModelList":{"properties":{"data":{"items":{"$ref":"#/components/schemas/Model"},"type":"array"},"object":{"enum":["list"],"type":"string"}},"required":["object","data"],"type":"object"},"ModelPricing":{"description":"Public per-1M-token price for this model in USD (the online rate; batch runs ~50% of it). Present only for a model whose price is not published elsewhere, e.g. an org-scoped endpoint the catalog serves directly to your organization. A widely-listed model omits it (its rate is documented on the pricing page).\n","properties":{"input":{"description":"USD per 1M input tokens.","type":"number"},"output":{"description":"USD per 1M output tokens.","type":"number"}},"type":"object"},"MyInvite":{"properties":{"expiresAt":{"type":"string"},"invitedAt":{"type":"string"},"inviterEmail":{"description":"Email of the user who sent the invite, when resolvable.","type":"string"},"organizationId":{"type":"string"},"organizationName":{"type":"string"},"role":{"type":"string"}},"required":["organizationId","role","invitedAt"],"type":"object"},"NodeClassCap":{"properties":{"freeGpus":{"type":"integer"},"freeUnits":{"type":"integer"},"nodeId":{"type":"string"},"reserved":{"description":"This node's free capacity in your view is held exclusively for your organization under a node reservation.","type":"boolean"},"spot":{"description":"This node carries capacity released from a reservation to the spot market: buyable at tier=spot only, and terminable without notice when the capacity owner wants it back.","type":"boolean"},"unitSizes":{"description":"Free unit sizes on this node (for display/tiling).","items":{"type":"integer"},"type":"array"}},"required":["nodeId","freeUnits","freeGpus"],"type":"object"},"NodeGpuItem":{"properties":{"gpuName":{"type":"string"},"quantity":{"type":"integer"},"vramGb":{"type":"integer"}},"required":["gpuName","vramGb","quantity"],"type":"object"},"NodeGpuUnitItem":{"description":"One allocatable GPU unit on a node: the thing a reservation is scoped to. The id is what create/release/reclaim take.","properties":{"allocClass":{"description":"vfio-group is the VM passthrough path and the only reservable one. device-request is scheduled by Nomad, so exclusivity cannot be enforced.","type":"string"},"claimedBy":{"description":"What holds it, e.g. vm:\u003cid\u003e. Present when claimed.","type":"string"},"gpuModelId":{"type":"string"},"gpuModelName":{"type":"string"},"id":{"description":"Unit id, e.g. 0000:04:00.0. Pass this to create/release/reclaim.","type":"string"},"notReservableReason":{"description":"Why not, when reservable is false.","type":"string"},"released":{"description":"On the spot market right now (double sell).","type":"boolean"},"reservable":{"description":"Whether this unit can be put in a reservation.","type":"boolean"},"reservationId":{"description":"The reservation governing it, when any.","type":"string"},"sellableGpus":{"description":"GPUs this unit sells; indivisible, they go together","type":"integer"},"state":{"description":"free | claimed","type":"string"}},"required":["id","gpuModelId","sellableGpus","allocClass","state","reservable"],"type":"object"},"NodeReservation":{"description":"Exclusive placement rights for a set of orgs over specific GPU units of one node, for a term. Scoped to units, not GPU counts, because units are the indivisible allocation primitive.","properties":{"allowedOrgIds":{"description":"The audience: orgs that may place workloads on the reserved units.","items":{"type":"string"},"type":"array"},"contractRef":{"type":"string"},"createdAt":{"type":"string"},"endsAt":{"description":"Absent = open-ended until cancelled","type":"string"},"id":{"type":"string"},"managingOrgIds":{"description":"Orgs that may release these units to the market, reclaim them, and terminate the spot workloads on them, on the node owner's behalf. Being in the audience does not confer this.","items":{"type":"string"},"type":"array"},"nodeId":{"type":"string"},"reason":{"type":"string"},"releasedUnitIds":{"description":"The subset of reservedUnitIds currently on the spot market (double sell). Still this reservation's capacity, sellable to anyone at tier=spot only, and reclaimable at any time.","items":{"type":"string"},"type":"array"},"reservedUnitIds":{"items":{"type":"string"},"type":"array"},"skipped":{"description":"Reclaim only: VM ids left running because they are not spot.","items":{"type":"string"},"type":"array"},"startsAt":{"type":"string"},"status":{"description":"needs-review means the node's GPU topology changed under the reservation: it grants nothing (and withholds its units from everyone) until an operator re-affirms it.","enum":["pending","active","expired","cancelled","needs-review"],"type":"string"},"terminated":{"description":"Reclaim only: spot VM ids terminated to free the reclaimed units.","items":{"type":"string"},"type":"array"},"topologyGen":{"description":"Node topology generation this reservation was written against","type":"integer"},"updatedAt":{"type":"string"},"wouldSkip":{"description":"Reclaim dry run only: VM ids that would be left running.","items":{"type":"string"},"type":"array"},"wouldTerminate":{"description":"Reclaim dry run only: the spot VM ids this call would terminate.","items":{"type":"string"},"type":"array"}},"required":["id","nodeId","reservedUnitIds","allowedOrgIds","startsAt","status"],"type":"object"},"NodeReservationSummary":{"description":"Reservation state of this node, from the caller's point of view. Present only when some of the node's GPU units are reserved.","properties":{"releasedUnitCount":{"description":"GPU units this node's reservations have released to the spot market. They are still the reservation's capacity: anyone may buy them, but only at tier=spot, and the capacity owner can reclaim them at any time.","type":"integer"},"reservations":{"items":{"$ref":"#/components/schemas/NodeReservation"},"type":"array"},"reservedUnitCount":{"description":"GPU units held exclusively for the audience right now","type":"integer"}},"required":["reservedUnitCount","releasedUnitCount"],"type":"object"},"OrgFeatures":{"description":"Per-org feature entitlements the dashboard gates its UI on.","properties":{"batch":{"description":"The org is allowlisted for the Batch API (batch-api.md §8).","type":"boolean"},"fabric":{"description":"The org is allowlisted for the compute-fabric Pod product (FABRIC_ENABLED fleet-wide or FABRIC_ORGS). Gates the dashboard Pods surface (shadow rollout).","type":"boolean"},"perVmSshKeys":{"description":"The org is allowlisted for per-VM SSH key scoping (the gateway honors each VM's sshKeyIds instead of accepting any org key). Gates the dashboard's per-VM key management (shadow rollout).","type":"boolean"},"publicEndpoint":{"deprecated":true,"description":"Vestigial, always false. The public-endpoint feature is GA (ungated); this field is kept only to avoid a breaking response-contract change and will be removed in a follow-up deprecation PR.","type":"boolean"}},"required":["batch","publicEndpoint","fabric","perVmSshKeys"],"type":"object"},"OrgMembership":{"properties":{"features":{"$ref":"#/components/schemas/OrgFeatures"},"name":{"type":"string"},"organizationId":{"type":"string"},"role":{"type":"string"}},"required":["organizationId","name","role","features"],"type":"object"},"Organization":{"properties":{"accountStatus":{"description":"User-facing account state derived from the org's trust \u0026 safety risk state. \"active\" = normal; \"restricted\" = new deploys paused pending review (existing workloads keep running, funding still allowed); \"suspended\" = read-only, workloads stopped, pending review; \"banned\" = disabled. Drives the dashboard account-status banner. Omitted/\"active\" means no banner.\n","type":"string"},"billingEmail":{"type":"string"},"createdAt":{"type":"string"},"id":{"type":"string"},"isProvider":{"type":"boolean"},"name":{"type":"string"},"plan":{"type":"string"},"shortId":{"type":"string"},"slug":{"type":"string"},"taxCountry":{"description":"ISO 3166-1 alpha-2 country whose tax scheme taxId belongs to (e.g. US, GB, SG, DE).","type":"string"},"taxId":{"description":"Customer tax identifier stored on the organization (US EIN, EU/UK VAT, SG GST, etc.), shown on the billing settings page. Requires taxCountry.","type":"string"}},"required":["id","shortId","name","slug","billingEmail","plan"],"type":"object"},"PatchInferenceCatalogBackendRequest":{"properties":{"healthy":{"type":"boolean"},"maxConcurrency":{"minimum":0,"type":"integer"},"maxPromptBytes":{"minimum":0,"type":"integer"},"minPromptBytes":{"minimum":0,"type":"integer"},"reason":{"description":"Audit note, required when flipping the kill switch","type":"string"},"weight":{"minimum":0,"type":"integer"}},"type":"object"},"PatchInferenceCatalogRoutingRequest":{"properties":{"families":{"additionalProperties":{"properties":{"maxAttempts":{"maximum":10,"minimum":1,"type":"integer"},"strategy":{"enum":["first_healthy","weighted","sticky"],"type":"string"}},"type":"object"},"description":"Per-family overrides, keyed by family constant","type":"object"},"maxAttempts":{"maximum":10,"minimum":1,"type":"integer"},"strategy":{"enum":["first_healthy","weighted","sticky"],"type":"string"}},"type":"object"},"PatchServingDeploymentRequest":{"description":"In-place edit of routing-class fields: which requests this deployment is eligible for. Only the provided fields change. Fields this schema does not define are NOT applied and are not silently accepted either: the decoder drops them, so a body carrying only such a field names no routing field and is rejected with 400 REQUIRES_NEW_GENERATION rather than answering 200 on a row it did not change. An empty patch is rejected for the same reason. Sending the values the deployment already has is a no-op: 200 with the current row and no write. The patch applies to the one deployment named in the path, never to its lane peer.","properties":{"maxPromptBytes":{"description":"Exclusive text-request byte ceiling; 0 = unbounded","type":"integer"},"minPromptBytes":{"description":"Inclusive text-request byte floor; 0 = unbounded","type":"integer"},"nativeFamilies":{"items":{"type":"string"},"type":"array"}},"type":"object"},"PaymentMethodCard":{"properties":{"brand":{"type":"string"},"expMonth":{"type":"integer"},"expYear":{"type":"integer"},"id":{"type":"string"},"last4":{"type":"string"}},"required":["id","brand","last4","expMonth","expYear"],"type":"object"},"PaymentMethods":{"properties":{"defaultPaymentMethodId":{"type":"string"},"paymentMethods":{"items":{"$ref":"#/components/schemas/PaymentMethodCard"},"type":"array"}},"required":["paymentMethods"],"type":"object"},"PayoutItem":{"properties":{"amountCents":{"type":"integer"},"createdAt":{"type":"string"},"description":{"nullable":true,"type":"string"},"id":{"type":"string"},"type":{"type":"string"}},"required":["id","type","amountCents","createdAt"],"type":"object"},"PendingCount":{"properties":{"count":{"type":"integer"}},"required":["count"],"type":"object"},"PodCapacityItem":{"description":"Live pod capacity for one GPU model. freeUnits counts what a create can claim RIGHT NOW (free device-request units on online, non-delisted nodes); rateOnDemandCentsPerHour is the server-computed price so clients never re-derive pricing.","properties":{"freeUnits":{"description":"GPU units claimable right now (each pod takes one whole GPU)","type":"integer"},"gpuModelId":{"type":"string"},"maxGpuCount":{"description":"largest gpuCount a single create can be granted right now: the biggest exact per-node tiling of free placeable units (0 = nothing claimable)","type":"integer"},"name":{"type":"string"},"rateInterruptibleCentsPerHour":{"deprecated":true,"description":"deprecated: the interruptible class is retired, so this equals rateOnDemandCentsPerHour","type":"integer"},"rateOnDemandCentsPerHour":{"description":"on-demand rate, cents per GPU-hour: what a create is charged","type":"integer"},"rateReservedCentsPerHour":{"deprecated":true,"description":"deprecated alias of rateOnDemandCentsPerHour, carrying the same value","type":"integer"},"totalUnits":{"description":"all pod units on online nodes, including ones currently claimed","type":"integer"},"vramGb":{"type":"integer"}},"required":["gpuModelId","name","vramGb","freeUnits","totalUnits","maxGpuCount","rateOnDemandCentsPerHour","rateReservedCentsPerHour","rateInterruptibleCentsPerHour"],"type":"object"},"PresignUploadResponse":{"description":"A minted file id and a signed URL for a direct browser upload.","properties":{"expires_at":{"description":"Unix timestamp (seconds) the signed URL stops working.","format":"int64","type":"integer"},"file_id":{"description":"File id to register with the complete call after the PUT succeeds.","type":"string"},"put_url":{"description":"Signed URL; PUT the raw JSONL bytes to it.","type":"string"}},"required":["file_id","put_url","expires_at"],"type":"object"},"Pricing":{"properties":{"cpu":{"items":{"$ref":"#/components/schemas/CpuPrice"},"type":"array"},"gpu":{"items":{"$ref":"#/components/schemas/GpuPrice"},"type":"array"},"vmDisk":{"$ref":"#/components/schemas/VmDiskLimits"}},"required":["gpu","cpu","vmDisk"],"type":"object"},"ProviderEarnings":{"properties":{"activeRentals":{"description":"Currently-running rentals on this provider's nodes (open usage records).","type":"integer"},"availableForPayoutCents":{"type":"integer"},"billableGrossCents":{"description":"Total customer gross attributable to legacy revenue-share paid-funded hours. Omitted for fixed-only providers; mixed providers exclude fixed-contract customer economics.","type":"integer"},"billableHours":{"description":"Legacy revenue-share hours in customers' paid-funding band. Omitted for fixed-only providers.","type":"number"},"byGpuModel":{"items":{"$ref":"#/components/schemas/EarningsBreakdownGpu"},"type":"array"},"byNode":{"items":{"$ref":"#/components/schemas/EarningsBreakdownNode"},"type":"array"},"byStream":{"description":"CPU vs GPU income streams.","items":{"$ref":"#/components/schemas/EarningsBreakdownStream"},"type":"array"},"commissionPercent":{"description":"Default legacy provider revenue-share percentage. Relevant to legacy records in revenue_share_legacy and mixed responses; fixed records use their locked hourly rate.","type":"integer"},"compensationMode":{"description":"How provider earnings in this response were calculated. Optional for backward compatibility; absent means revenue_share_legacy. mixed indicates the selected history contains both fixed-hourly and legacy records.","enum":["revenue_share_legacy","fixed_hourly","mixed"],"type":"string"},"currentMonthEarningsCents":{"type":"integer"},"currentMonthHours":{"type":"number"},"daily":{"description":"Last-30-UTC-day earnings series, oldest first, one point per day (zero-filled). A record's band hours/earnings are prorated onto each day by its share of the record's hosted hours (band placement itself is whole-of-life, so a day's split is an attribution, not a re-run of the waterfall per day).","items":{"$ref":"#/components/schemas/EarningsDailyPoint"},"type":"array"},"earningHours":{"description":"Hours owed to the provider. Fixed-hourly contracts accrue across eligible hosted utilization; legacy revenue share accrues only across paid-funded hours.","type":"number"},"freeCreditHours":{"description":"Legacy revenue-share hours funded by customer free credits. Omitted for fixed-only providers and excludes fixed-contract hours in mixed responses.","type":"number"},"grossEarningsCents":{"description":"Total customer sell gross for legacy revenue-share records. Omitted for fixed-only providers; mixed providers exclude fixed-contract customer economics.","type":"integer"},"pendingEarningsCents":{"type":"integer"},"recentPayouts":{"items":{"$ref":"#/components/schemas/PayoutItem"},"type":"array"},"totalEarningsCents":{"type":"integer"},"totalHours":{"description":"Total hours hosted across all of this provider's usage.","type":"number"},"totalPaidOutCents":{"type":"integer"},"unpaidHours":{"description":"Legacy revenue-share hours beyond customer free credit and paid balance. Omitted for fixed-only providers and excludes fixed-contract hours in mixed responses.","type":"number"}},"required":["totalEarningsCents","totalHours","currentMonthEarningsCents","currentMonthHours","pendingEarningsCents","availableForPayoutCents","totalPaidOutCents","commissionPercent","byNode","byGpuModel","recentPayouts"],"type":"object"},"ProviderNode":{"properties":{"cpuCores":{"nullable":true,"type":"integer"},"cpuModel":{"nullable":true,"type":"string"},"createdAt":{"type":"string"},"gpus":{"items":{"$ref":"#/components/schemas/NodeGpuItem"},"type":"array"},"hostname":{"type":"string"},"id":{"type":"string"},"locationCity":{"nullable":true,"type":"string"},"locationCountry":{"nullable":true,"type":"string"},"nodeType":{"type":"string"},"ramGb":{"nullable":true,"type":"integer"},"reservation":{"$ref":"#/components/schemas/NodeReservationSummary"},"runtime":{"description":"Node runtime: qemu (VFIO VM host) or runc (GPU-container Pod node). Empty reads as qemu.","type":"string"},"status":{"type":"string"},"tdxEnabled":{"type":"boolean"}},"required":["id","hostname","nodeType","tdxEnabled","status","createdAt","gpus"],"type":"object"},"ProviderStats":{"properties":{"activeTokens":{"type":"integer"},"offlineNodes":{"type":"integer"},"onlineNodes":{"type":"integer"},"totalNodes":{"type":"integer"}},"required":["totalNodes","onlineNodes","offlineNodes","activeTokens"],"type":"object"},"ProviderStatus":{"properties":{"applicationStatus":{"nullable":true,"type":"string"},"applicationSubmittedAt":{"nullable":true,"type":"string"},"isProvider":{"type":"boolean"}},"required":["isProvider"],"type":"object"},"ProvisioningTokenCreated":{"properties":{"expiresAt":{"description":"24 hours after creation.","type":"string"},"id":{"type":"string"},"name":{"nullable":true,"type":"string"},"nodePool":{"type":"string"},"token":{"type":"string"},"tokenPrefix":{"type":"string"},"warning":{"type":"string"}},"required":["id","token","tokenPrefix","expiresAt","warning"],"type":"object"},"ProvisioningTokenItem":{"properties":{"createdAt":{"type":"string"},"enrollmentCount":{"description":"Nodes enrolled with this token so far. There is no cap: a token enrolls any number of nodes while it is valid.","type":"integer"},"expiresAt":{"description":"24 hours after createdAt. Every token expires; the lifetime is fixed.","type":"string"},"id":{"type":"string"},"lastUsedAt":{"nullable":true,"type":"string"},"name":{"nullable":true,"type":"string"},"nodePool":{"type":"string"},"nodeType":{"type":"string"},"revokedAt":{"nullable":true,"type":"string"},"status":{"description":"active | expired | revoked. A token that has enrolled nodes is still active until it expires or is revoked.","type":"string"},"tokenPrefix":{"type":"string"}},"required":["id","tokenPrefix","nodeType","status","createdAt","expiresAt","enrollmentCount"],"type":"object"},"PutInferenceCatalogAllocationRequest":{"description":"Percentage traffic targets over EVERY backend of the model.","properties":{"allocations":{"items":{"properties":{"endpoint":{"type":"string"},"weightPct":{"description":"Percent of traffic; all entries must sum to 100","format":"double","minimum":0,"type":"number"}},"required":["endpoint","weightPct"],"type":"object"},"type":"array"}},"required":["allocations"],"type":"object"},"PutShadowCaptureScopeRequest":{"description":"Creates or replaces the scope for (orgId, modelId). A second PUT for the same pair overwrites the first (upsert), which is how an operator extends a scope's TTL: PUT again with a fresh ttlMinutes.","properties":{"allowAllOrgs":{"description":"Must be true when orgId is \"*\"; the second-confirm switch fleet-wide capture requires","type":"boolean"},"captureResponse":{"description":"Defaults to false (request + metadata only) when omitted","type":"boolean"},"enabled":{"type":"boolean"},"modelId":{"description":"Public catalog model to scope capture to, or \"*\" for every model","type":"string"},"note":{"type":"string"},"orgId":{"description":"Org to scope capture to, or \"*\" for every org (requires allowAllOrgs: true)","type":"string"},"sampleRate":{"description":"Defaults to 1.0 (capture every matching request) when omitted","format":"double","maximum":1,"minimum":0,"type":"number"},"ttlMinutes":{"description":"Required. How long the scope stays live from now; the design's mandatory auto-off (max 24h).","maximum":1440,"minimum":1,"type":"integer"}},"required":["orgId","modelId","enabled","ttlMinutes"],"type":"object"},"ReclaimSpotWorkloadsRequest":{"properties":{"dryRun":{"description":"Report what would be terminated, and terminate nothing.","type":"boolean"},"reason":{"description":"Recorded in the audit trail.","type":"string"},"unitIds":{"description":"Limit to these units. Omit to target every spot workload on the node.","items":{"type":"string"},"type":"array"}},"type":"object"},"ReclaimSpotWorkloadsResult":{"properties":{"skipped":{"description":"VM ids left alone because they are not spot","items":{"type":"string"},"type":"array"},"terminated":{"description":"VM ids terminated (or, on a dry run, that would be)","items":{"type":"string"},"type":"array"}},"required":["terminated"],"type":"object"},"RegistryCredentialSummary":{"properties":{"authType":{"type":"string"},"createdAt":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"server":{"type":"string"}},"required":["id","name","server","authType","createdAt"],"type":"object"},"ReservationReclaimRequest":{"description":"Reclaim takes units back off the spot market and terminates the spot workloads on them, so unlike release it can destroy someone's running VM: hence the dry run.","properties":{"dryRun":{"description":"Report which spot workloads would be terminated, and change nothing: no terminations, and the capacity boundary stays where it is.","type":"boolean"},"reason":{"description":"Recorded in the audit trail.","type":"string"},"unitIds":{"items":{"type":"string"},"type":"array"}},"required":["unitIds"],"type":"object"},"ReservationUnitsRequest":{"properties":{"reason":{"description":"Recorded in the audit trail.","type":"string"},"unitIds":{"items":{"type":"string"},"type":"array"}},"required":["unitIds"],"type":"object"},"ResizeDiskRequest":{"properties":{"newSizeGb":{"type":"integer"}},"required":["newSizeGb"],"type":"object"},"ResizeRunnerPoolRequest":{"properties":{"poolRamGb":{"type":"integer"}},"required":["poolRamGb"],"type":"object"},"ReviseServingDeploymentRequest":{"description":"The next generation's spec, expressed as overrides on the source deployment: a field that is present overrides, a field that is absent is inherited. Rollout mechanics (backendWeight, isolated, rolloutRole, acknowledgeLaneJoin) are deliberately absent from this body: the server owns them, and a revision is always a weight-0 green in the source's lane. Structural fields ARE accepted by the schema so the server can reject them by name: changing one would make a different lane or a different tenant, which is a create, not a revision.","properties":{"changeCause":{"description":"Why this revision exists. Stored on the row and shown in the revision history.","type":"string"},"dtype":{"type":"string"},"engine":{"type":"string"},"engineImage":{"type":"string"},"env":{"additionalProperties":{"type":"string"},"type":"object"},"extraArgs":{"items":{"type":"string"},"type":"array"},"gpuCountPerReplica":{"type":"integer"},"gpuModelId":{"type":"string"},"maxConcurrencyPerReplica":{"type":"integer"},"maxModelLen":{"type":"integer"},"maxPromptBytes":{"type":"integer"},"minPromptBytes":{"type":"integer"},"nativeFamilies":{"items":{"type":"string"},"type":"array"},"nodePool":{"description":"Structural: rejected. A different node pool is a different lane.","type":"string"},"organizationId":{"description":"Structural: rejected. A different org is a different tenant.","type":"string"},"publicModelId":{"description":"Structural: rejected. A different model is a different lane.","type":"string"},"quantization":{"type":"string"},"replicaRuntime":{"description":"Changing the runtime is a replica-class change like any other, so it rolls out rather than mutating in place.","enum":["","vm","pod"],"type":"string"},"replicasDesired":{"type":"integer"},"replicasMax":{"type":"integer"},"replicasMin":{"type":"integer"},"servedModelId":{"type":"string"},"version":{"description":"Rollout tag for the new generation: the low-cardinality `version` metric label the gateway reports, which is what separates the two sides of the roll. ABSENT CLEARS IT rather than inheriting the source's, because a green carrying blue's tag registers the identical label and collapses both sides into one series. Cleared, the green reports as \"green\" against blue's \"blue\". Present sets it explicitly.","type":"string"},"weightsRef":{"type":"string"}},"type":"object"},"RunnerConnectUrl":{"properties":{"installUrl":{"type":"string"},"oauth":{"type":"boolean"},"url":{"type":"string"}},"required":["url","installUrl","oauth"],"type":"object"},"RunnerInstallation":{"properties":{"account":{"type":"string"},"accountType":{"type":"string"},"alreadyConnected":{"type":"boolean"},"connectGrant":{"type":"string"},"installationId":{"format":"int64","type":"integer"}},"required":["installationId","account","accountType","alreadyConnected","connectGrant"],"type":"object"},"RunnerInstallationsRequest":{"properties":{"code":{"type":"string"},"state":{"type":"string"}},"required":["code"],"type":"object"},"RunnerInstallationsResult":{"properties":{"installations":{"items":{"$ref":"#/components/schemas/RunnerInstallation"},"type":"array"}},"required":["installations"],"type":"object"},"RunnerJob":{"properties":{"completedAt":{"type":"string"},"cpus":{"type":"integer"},"failureReason":{"type":"string"},"githubRepo":{"type":"string"},"id":{"type":"string"},"nodeId":{"type":"string"},"organizationId":{"type":"string"},"provisioningAt":{"type":"string"},"queuedAt":{"type":"string"},"ramMb":{"type":"integer"},"runnerName":{"type":"string"},"runnerPoolId":{"type":"string"},"runnerSizeLabel":{"type":"string"},"runningAt":{"type":"string"},"status":{"type":"string"}},"required":["id","runnerPoolId","organizationId","status","ramMb","cpus","queuedAt"],"type":"object"},"RunnerMetrics":{"properties":{"cost":{"$ref":"#/components/schemas/RunnerMetricsCost"},"daily":{"items":{"$ref":"#/components/schemas/RunnerMetricsDaily"},"type":"array"},"summary":{"$ref":"#/components/schemas/RunnerMetricsSummary"}},"required":["daily","summary","cost"],"type":"object"},"RunnerMetricsCost":{"properties":{"githubCostCents":{"type":"integer"},"savedCents":{"type":"integer"},"vectorlayCostCents":{"type":"integer"}},"required":["githubCostCents","vectorlayCostCents","savedCents"],"type":"object"},"RunnerMetricsDaily":{"properties":{"cancelled":{"type":"integer"},"completed":{"type":"integer"},"date":{"type":"string"},"failed":{"type":"integer"},"running":{"type":"integer"}},"required":["date","completed","failed","cancelled","running"],"type":"object"},"RunnerMetricsSummary":{"properties":{"avgDurationSec":{"type":"integer"},"avgQueueTimeSec":{"type":"integer"},"completedJobs":{"type":"integer"},"failedJobs":{"type":"integer"},"totalJobs":{"type":"integer"}},"required":["totalJobs","completedJobs","failedJobs","avgDurationSec","avgQueueTimeSec"],"type":"object"},"RunnerPool":{"properties":{"createdAt":{"type":"string"},"currentPeriodEnd":{"type":"string"},"diskSizeGb":{"type":"integer"},"githubInstallationId":{"format":"int64","type":"integer"},"githubOwner":{"type":"string"},"githubOwnerType":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organizationId":{"type":"string"},"poolRamGb":{"type":"integer"},"runtime":{"type":"string"},"status":{"type":"string"},"subscriptionStatus":{"type":"string"},"trialEndsAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","githubInstallationId","githubOwner","githubOwnerType","poolRamGb","diskSizeGb","status","createdAt","updatedAt"],"type":"object"},"RunnerPoolItem":{"properties":{"activeRunners":{"type":"integer"},"createdAt":{"type":"string"},"currentPeriodEnd":{"type":"string"},"diskSizeGb":{"type":"integer"},"githubInstallationId":{"format":"int64","type":"integer"},"githubOwner":{"type":"string"},"githubOwnerType":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"organizationId":{"type":"string"},"poolRamGb":{"type":"integer"},"queuedRamMb":{"type":"integer"},"queuedRunners":{"type":"integer"},"runtime":{"type":"string"},"status":{"type":"string"},"subscriptionStatus":{"type":"string"},"trialEndsAt":{"type":"string"},"updatedAt":{"type":"string"},"usedRamMb":{"type":"integer"}},"required":["id","organizationId","name","githubInstallationId","githubOwner","githubOwnerType","poolRamGb","diskSizeGb","status","createdAt","updatedAt","activeRunners","usedRamMb","queuedRunners","queuedRamMb"],"type":"object"},"ScaleRequest":{"properties":{"replicaCount":{"minimum":1,"type":"integer"}},"required":["replicaCount"],"type":"object"},"ScaleServingDeploymentRequest":{"properties":{"desired":{"description":"Target replica count; must lie within replicasMin..replicasMax (as adjusted by this request)","type":"integer"},"replicasMax":{"description":"Optional new upper bound for the replica band. Omitted = keep the current value. desired is validated against the adjusted band.","type":"integer"},"replicasMin":{"description":"Optional new lower bound for the replica band. Omitted = keep the current value. desired is validated against the adjusted band.","type":"integer"}},"required":["desired"],"type":"object"},"ServingDeployment":{"description":"A self-hosted serving deployment: the durable intent to run N replicas of an inference engine for one public catalog model, plus the runtime state the reconcile loop maintains while converging on it.","properties":{"autoscalePaused":{"description":"True while an operator has frozen the load-driven autoscaler. The min/max band is kept as bounds and reconcile still converges the fleet to replicasDesired; the autoscaler just stops moving replicasDesired on its own. An operator can still hand-set replicasDesired within the band through /scale. Absent when unset.","type":"boolean"},"backendCatalogWeight":{"description":"Live catalog routing weight of this deployment's backend. Only present when backendRegistered is true. May differ from backendWeight (the row mirror) while a rollout ramp is in flight.","type":"integer"},"backendHealthy":{"description":"Live catalog health of this deployment's backend (the kill switch). Only present when backendRegistered is true.","type":"boolean"},"backendRegistered":{"description":"Whether this deployment's pool:// backend is currently present on the catalog model row. Absent when the live catalog state could not be read; false until the first replica passes readiness.","type":"boolean"},"backendVersion":{"description":"The rollout tag this deployment's backend reports to the gateway as the `version` metric label on ig_model_requests_total and ig_stream_ttft_seconds. Use it to separate the two sides of a rollout in a dashboard query. Only present when backendRegistered is true.","type":"string"},"backendWeight":{"description":"Routing weight of the catalog backend. 0 = standby (blue/green starts here)","type":"integer"},"changeCause":{"description":"Operator-supplied reason for the revision that created this deployment","type":"string"},"createdAt":{"type":"string"},"dtype":{"type":"string"},"engine":{"description":"Inference engine, e.g. vllm or sglang","type":"string"},"engineImage":{"description":"Engine container image override; absent = the engine's default","type":"string"},"env":{"additionalProperties":{"type":"string"},"type":"object"},"extraArgs":{"items":{"type":"string"},"type":"array"},"generation":{"description":"1 for a hand-created deployment, parent+1 for each revision. History ordering only: the controller never reads it, because rollout adoption is the explicit rolloutRole opt-in and nothing else.","type":"integer"},"gpuCountPerReplica":{"type":"integer"},"gpuModelId":{"type":"string"},"id":{"type":"string"},"isolated":{"description":"True when replicas and their private endpoint pool are for direct operator testing only. Isolated deployments never register a catalog backend, never participate in blue/green rollout, and cannot receive gateway traffic.","type":"boolean"},"maxConcurrencyPerReplica":{"type":"integer"},"maxModelLen":{"type":"integer"},"maxPromptBytes":{"description":"Exclusive text-request byte ceiling for routing eligibility; 0 or absent = unbounded. Routing hint only, not a context limit.","type":"integer"},"maxUnavailable":{"description":"Rolling only: how many replicas may be simultaneously not-ready (absolute count; surfaces show the percentage of desired).","type":"integer"},"minPromptBytes":{"description":"Inclusive text-request byte floor for routing eligibility; 0 or absent = unbounded. Routing hint only, not a context limit.","type":"integer"},"nativeFamilies":{"items":{"type":"string"},"type":"array"},"nodePool":{"description":"Stable logical capacity lane within the public model. Blue/green only replaces versions in the same node pool; empty preserves legacy model-wide grouping.","type":"string"},"organizationId":{"description":"Org that owns the managed replicas (and their metering)","type":"string"},"parentDeploymentId":{"description":"The deployment this one was revised from. Set only by /revise, so an absent value means this deployment was created by hand.","type":"string"},"poolId":{"description":"Endpoint pool the deployment's replicas register in","type":"string"},"publicModelId":{"description":"Catalog model this deployment serves capacity for","type":"string"},"quantization":{"type":"string"},"readyReplicas":{"type":"integer"},"replicaRuntime":{"description":"How each replica is placed. Absent or \"\" or \"vm\" = a QEMU/VFIO VM on the baremetal fleet (the legacy shape; every pre-existing deployment reads as this). \"pod\" = a hardened GPU pod on a provider-onboarded runc node. One deployment is one runtime; a public model mixes runtimes through lanes.","enum":["","vm","pod"],"type":"string"},"replicaVmIds":{"items":{"type":"string"},"type":"array"},"replicasDesired":{"type":"integer"},"replicasMax":{"type":"integer"},"replicasMin":{"type":"integer"},"rollingActive":{"description":"True while an in-place rolling rollout is in flight on this deployment.","type":"boolean"},"rollout":{"$ref":"#/components/schemas/ServingRolloutState"},"rolloutPaused":{"description":"True while an operator has paused the ramp. Traffic is untouched: the deployment keeps the weight it holds and keeps serving, and the controller takes no rollout action until resume. The pause is also on rollout.paused; this raw column is what a surface falls back to when the lane could not be read at all.","type":"boolean"},"rolloutRole":{"description":"Opt-in blue/green role. Empty (default) = an INDEPENDENT weighted capacity member of its lane, never blue or green. \"green\" = the lane's successor generation: the controller ramps it against the older members under a capacity guard and cuts them over once it reaches capacity parity. Rollouts are never implicit.","enum":["","green"],"type":"string"},"rolloutStrategy":{"description":"How a version bump reaches the fleet. blue_green (default) mints a ramped sibling; rolling replaces this deployment's replicas in place one at a time.","enum":["blue_green","rolling"],"type":"string"},"servedModelId":{"description":"Model id the engine serves; must equal the catalog backend ref","type":"string"},"status":{"enum":["pending","deploying","ready","degraded","scaling","draining","stopped","failed"],"type":"string"},"statusReason":{"description":"Human-readable cause for degraded/failed states","type":"string"},"updatedAt":{"type":"string"},"version":{"description":"Generation marker for blue/green within one public model and node pool; the target a rolling rollout replaces replicas to","type":"string"},"weightsRef":{"description":"Staged-checkpoint reference; absent = the engine loads from its own source","type":"string"}},"required":["id","organizationId","publicModelId","engine","servedModelId","gpuModelId","gpuCountPerReplica","replicasMin","replicasMax","replicasDesired","backendWeight","isolated","status","readyReplicas","createdAt","updatedAt"],"type":"object"},"ServingDeploymentHealthRequest":{"properties":{"healthy":{"description":"false = emergency kill (backend excluded from routing entirely); true = clear the kill","type":"boolean"}},"required":["healthy"],"type":"object"},"ServingDeploymentPage":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ServingDeployment"},"type":"array"},"nextCursor":{"description":"Opaque; pass back to continue. Absent/empty = no more pages","type":"string"}},"required":["items"],"type":"object"},"ServingDeploymentWeightRequest":{"properties":{"weight":{"description":"Catalog backend routing weight; 0 = standby (NOT a kill switch)","type":"integer"}},"required":["weight"],"type":"object"},"ServingLane":{"description":"One capacity lane (public model + node pool) and its members, derived by the same code that drives rollouts.","properties":{"greenId":{"description":"The lane's opted-in green; absent when the lane is not rolling","type":"string"},"lane":{"description":"The lane's label, publicModelId/nodePool","type":"string"},"members":{"description":"Oldest first","items":{"$ref":"#/components/schemas/ServingLaneMember"},"type":"array"},"nextAction":{"description":"What the rollout controller will do next on this lane, in one sentence for an operator. The same value the green's rollout state reports. Absent when the lane is not rolling.","type":"string"},"nodePool":{"description":"Absent or empty = the legacy model-wide lane","type":"string"},"phase":{"allOf":[{"$ref":"#/components/schemas/ServingRolloutPhase"}],"description":"The lane's rollout phase, derived by the same decision the controller executes. One per LANE, not per member: every member of a rolling lane is in the same rollout. Absent when the lane is not rolling."},"policyMismatch":{"description":"The lane's members run different pool member-selection policies","type":"boolean"},"publicModelId":{"type":"string"},"rolling":{"description":"The lane has a green with something to replace","type":"boolean"},"totalWeight":{"description":"Sum of the lane members' live catalog weights","type":"integer"}},"required":["publicModelId","lane","totalWeight","rolling","members"],"type":"object"},"ServingLaneList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ServingLane"},"type":"array"}},"required":["items"],"type":"object"},"ServingLaneMember":{"description":"One deployment's position in its lane.","properties":{"backendHealthy":{"description":"Whether this member's catalog backend is marked healthy. Absent when the catalog could not be read or the backend is not registered at all. Read it with liveWeight: a healthy member at weight 0 is a standby, an unhealthy one has been pulled out of rotation.","type":"boolean"},"id":{"type":"string"},"liveWeight":{"description":"The catalog weight this member's backend currently carries. 0 for an unregistered backend and for a demoted blue, which are different states with the same number: read role and status to tell them apart.","type":"integer"},"readyReplicas":{"type":"integer"},"replicasDesired":{"type":"integer"},"role":{"$ref":"#/components/schemas/ServingRolloutRole"},"share":{"description":"This member's percentage of the lane's total weight, rounded. 0 when the lane carries no weight at all.","type":"integer"},"status":{"description":"The deployment's lifecycle status","type":"string"}},"required":["id","role","status","liveWeight","share","readyReplicas","replicasDesired"],"type":"object"},"ServingMemberWeightRequest":{"properties":{"hostname":{"description":"The pool-member key (the replica's run.* hostname)","type":"string"},"weight":{"description":"Ranking share in (0,1]; 1 restores full share","format":"double","type":"number"}},"required":["hostname","weight"],"type":"object"},"ServingPoolRoutingRequest":{"description":"Pool routing knobs; only the provided fields change. policy switches the member-selection algorithm; affinityGuardUtil bounds how full a conversation's home member may run before prefix_affinity overflows to load-only P2C (0 clears it back to the gateway default).","properties":{"affinityGuardUtil":{"format":"double","maximum":1,"minimum":0,"type":"number"},"policy":{"enum":["p2c_util","prefix_affinity"],"type":"string"}},"type":"object"},"ServingPoolShedding":{"description":"The pool's OPTIONAL load-shedding override, set and read as one whole block. Every field zero-value (or absent) = inherit the gateway's global/env behavior, so an all-empty block is \"no override\": setting one clears the override back to inherit. As a request body this REPLACES the pool's whole block, so send every field the pool should keep, not just the one changed. Edits the POOL (deployments sharing a pool share it) and reach every gateway task in ~2s via the config generation stamp. Default-off is unchanged: a deployment that never sets this keeps today's exact behavior.","properties":{"brownoutTtftP95Sec":{"description":"Arms the pool brownout breaker at this rolling-TTFT-p95 threshold; 0 = disabled (the default).","minimum":0,"type":"integer"},"concurrencyMode":{"description":"Overrides global concurrency enforcement for this pool's member caps. \"enforce\" makes the fleet counter decide, \"shadow\" keeps the per-task ceiling deciding, \"\" (or absent) inherits the global decision.","enum":["","enforce","shadow"],"type":"string"},"exclusionMode":{"description":"Overrides global member-health exclusion for this pool. \"enforce\" removes failing members whatever the global env says, \"shadow\" only records, \"\" (or absent) inherits the global decision.","enum":["","enforce","shadow"],"type":"string"},"firstAttemptTtftSec":{"description":"Caps attempt 1 of a streaming request when the plan has \u003e= 2 dial targets, so one failover stays affordable. 0 = inherit the global default; capped at half the effective TTFT budget by the gateway.","minimum":0,"type":"integer"},"maxExcludedFraction":{"description":"Panic threshold: health exclusion may never remove more than this share of active members at once. Must stay in [0,1) (the exclusive upper bound is enforced by the server); 0 = the gateway default (0.5).","format":"double","minimum":0,"type":"number"},"ttftBudgetSec":{"description":"Per-request first-token budget for this pool's streaming attempts; 0 = the global default.","minimum":0,"type":"integer"}},"type":"object"},"ServingPreflight":{"description":"Read-only pre-deployment checks: everything that must already be true for a serving deployment with the given spec to admit, place, and register. Every check the query parameters make applicable must pass for ok.","properties":{"capacity":{"$ref":"#/components/schemas/ServingPreflightCapacity"},"model":{"$ref":"#/components/schemas/ServingPreflightModel"},"nodes":{"$ref":"#/components/schemas/ServingPreflightNodes"},"ok":{"type":"boolean"},"reportStoreConfigured":{"description":"The node-agent serving-report store (Redis) is configured. Without it the leader reads no readiness reports and pool membership holds forever, so a replica boots, stages, and runs but never joins the pool.","type":"boolean"},"weights":{"$ref":"#/components/schemas/ServingPreflightWeights"}},"required":["ok","model","weights","capacity","nodes","reportStoreConfigured"],"type":"object"},"ServingPreflightCapacity":{"description":"The serving capacity budget, evaluated for the requested footprint.","properties":{"committedOtherGpus":{"description":"GPUs other active serving deployments of this model already commit","type":"integer"},"evaluated":{"description":"gpuModelId, gpuCountPerReplica and replicasDesired were all passed","type":"boolean"},"fleetTotalGpus":{"description":"Structural fleet size of this GPU model (free + claimed units on online nodes)","type":"integer"},"heldReplicas":{"description":"replicasDesired minus placableReplicas: what dispatch would hold with a StatusReason until capacity frees up. 0 when ok. Present when evaluated.","type":"integer"},"laneCommittedGpus":{"description":"GPUs the target lane's existing live deployments already commit for this GPU model. Present only when publicModelId (and nodePool) were passed.","type":"integer"},"ok":{"description":"true when the footprint fits the fleet total","type":"boolean"},"placableReplicas":{"description":"How many of replicasDesired fit the fleet budget right now (clamp-to-fit admission). Present when evaluated.","type":"integer"},"reason":{"description":"The hold reason dispatch would surface, when not ok","type":"string"},"requestedGpus":{"description":"replicasDesired x gpuCountPerReplica","type":"integer"},"rolloutOk":{"description":"Whether rolloutPeakGpus fits the serving budget. This is a WARNING, not a blocker: `ok` still reflects the single-deployment verdict, because running a deliberately tight rollout and draining the old side as the new one ramps is a legitimate plan.","type":"boolean"},"rolloutPeakGpus":{"description":"Peak GPU demand while a rollout is in flight: requestedGpus plus laneCommittedGpus, because the old and new generations both hold real hardware until the old one is drained.","type":"integer"},"rolloutReason":{"description":"Why the rollout peak does not fit, in lane terms, when rolloutOk is false","type":"string"}},"required":["evaluated","ok"],"type":"object"},"ServingPreflightModel":{"description":"Catalog-row checks for the target public model.","properties":{"backendCount":{"description":"Backends already on the row (other providers, prior deployments)","type":"integer"},"enabled":{"type":"boolean"},"found":{"description":"The catalog row exists (the registrar refuses to create one)","type":"boolean"},"hasPricing":{"description":"The row carries at least one pricing dimension; deployments must never invent a price","type":"boolean"},"provided":{"description":"publicModelId was passed; the remaining fields are only meaningful when true","type":"boolean"}},"required":["provided"],"type":"object"},"ServingPreflightNodes":{"description":"Node-prep checks for the requested GPU model.","properties":{"evaluated":{"description":"gpuModelId was passed","type":"boolean"},"gpuNodeCount":{"description":"Active nodes with sellable units of this GPU model","type":"integer"},"virtiofsdReadyCount":{"description":"Of those, Nomad-ready nodes carrying the vectorlay_virtiofsd meta. Weights-backed replicas can only place on these; a node enrolled before virtiofsd support must re-enroll. Absent when Nomad was not reachable or weightsRef was not passed (the constraint only applies to weights-backed VMs).","type":"integer"}},"required":["evaluated"],"type":"object"},"ServingPreflightWeights":{"description":"Weights-distribution checks for the requested weightsRef.","properties":{"configured":{"description":"The weights distributor is wired on this control plane (S3_WEIGHTS_BUCKET)","type":"boolean"},"digest":{"type":"string"},"provided":{"description":"weightsRef was passed","type":"boolean"},"published":{"description":"The ref resolves to a digest in the weights manifest","type":"boolean"},"totalBytes":{"format":"int64","type":"integer"}},"required":["configured","provided"],"type":"object"},"ServingReplica":{"description":"One replica of a serving deployment: the managed VM row joined with its pool-member state and last scraped engine load.","properties":{"capacity":{"description":"In-flight bound the engine was launched with (= pool member capacity)","type":"integer"},"createdAt":{"type":"string"},"drainHold":{"description":"An operator has taken this replica out of rotation. The reconcile loop keeps its pool member draining while set, instead of re-activating it once it is ready again.","type":"boolean"},"drainHoldReason":{"description":"Why the replica was held out of rotation","type":"string"},"gpuCount":{"type":"integer"},"hostname":{"description":"run.* hostname the gateway dials; the pool-member key","type":"string"},"load":{"$ref":"#/components/schemas/ServingReplicaLoad"},"memberCapacity":{"description":"Capacity currently advertised on the pool member; present only when a member exists","type":"integer"},"memberState":{"description":"Pool membership: none = not (yet) a member of the deployment's pool","enum":["active","draining","out","none"],"type":"string"},"memberWeight":{"description":"Ranking weight on the pool member (the warmup/canary dial); absent = full share (1.0)","format":"double","type":"number"},"name":{"type":"string"},"nodeId":{"description":"Node the replica placed on; absent before placement","type":"string"},"nodeName":{"description":"Memorable display label for nodeId (adjective-noun-hex); absent before placement","type":"string"},"nomadAllocId":{"type":"string"},"provisioningStage":{"type":"string"},"runtime":{"description":"What backs this replica. A LABEL only, derived from the replica row's capability tier: every operator action here is runtime-neutral, because a pool member is a hostname and the pool does not know what serves it.","enum":["vm","pod"],"type":"string","x-enum-varnames":["ServingReplicaRuntimeVm","ServingReplicaRuntimePod"]},"status":{"description":"VM lifecycle status (deploying, running, stopped, terminated, failed); \"unreadable\" when the row could not be read this request","type":"string"},"statusReason":{"description":"Defer/failure reason from dispatch (weights staging, image staging, placement, ...)","type":"string"},"vmId":{"type":"string"}},"required":["vmId","status","memberState"],"type":"object"},"ServingReplicaList":{"properties":{"affinityGuardUtil":{"description":"prefix_affinity overflow bound; absent = the gateway default","format":"double","type":"number"},"items":{"items":{"$ref":"#/components/schemas/ServingReplica"},"type":"array"},"policy":{"description":"The pool's member-selection policy (p2c_util when the pool row does not exist yet)","enum":["p2c_util","prefix_affinity"],"type":"string"},"poolId":{"description":"The deployment's endpoint pool id","type":"string"},"shedding":{"allOf":[{"$ref":"#/components/schemas/ServingPoolShedding"}],"description":"The pool's load-shedding override; absent when the pool inherits every global/env default (no override set)."},"strayMembers":{"description":"Pool member hostnames no current replica owns (pending the reconcile loop's stray sweep). Usually empty.","items":{"type":"string"},"type":"array"}},"required":["items","poolId","policy"],"type":"object"},"ServingReplicaLoad":{"description":"The replica's last scraped engine load, as published to the gateways. Absent fields mean the engine did not expose the metric; a fabricated zero is never reported.","properties":{"atMs":{"description":"Publish time, unix milliseconds; readings expire after ~45s","format":"int64","type":"integer"},"hasKvUtil":{"type":"boolean"},"hasWaiting":{"type":"boolean"},"kvUtil":{"description":"KV-cache utilization 0..1; present only when hasKvUtil","type":"number"},"running":{"description":"Requests currently executing on the engine","type":"integer"},"waiting":{"description":"Requests queued on the engine; present only when hasWaiting","type":"integer"}},"required":["running","atMs"],"type":"object"},"ServingRevision":{"description":"One generation in a deployment's revision chain.","properties":{"deployment":{"$ref":"#/components/schemas/ServingDeployment"},"diffFromParent":{"description":"What this generation changed against its parent. Absent for the root generation, and for a generation whose parent row no longer exists.","items":{"$ref":"#/components/schemas/ServingSpecDiffEntry"},"type":"array"}},"required":["deployment"],"type":"object"},"ServingRevisionPage":{"description":"One deployment's whole generation chain. Not paged: the chain is bounded by the walk itself (50 generations), and a history an operator cannot see whole is not a history.","properties":{"items":{"items":{"$ref":"#/components/schemas/ServingRevision"},"type":"array"}},"required":["items"],"type":"object"},"ServingRevisionResult":{"allOf":[{"$ref":"#/components/schemas/ServingRolloutCapacity"},{"properties":{"deployment":{"$ref":"#/components/schemas/ServingDeployment"},"diff":{"description":"Every field that differs from the source generation","items":{"$ref":"#/components/schemas/ServingSpecDiffEntry"},"type":"array"}},"required":["deployment","diff"],"type":"object"}],"description":"The outcome of a revise: the new generation's deployment row, what changed against its source, and the rollout capacity warning for running both generations at once.","required":["deployment","diff"],"type":"object"},"ServingRollingRolloutRequest":{"properties":{"action":{"enum":["start","abort"],"type":"string"},"cause":{"description":"Free-text reason, recorded in the version history","type":"string"},"version":{"description":"Required for start: the target version tag replicas are replaced to","maxLength":32,"type":"string"}},"required":["action"],"type":"object"},"ServingRolloutActionRequest":{"properties":{"action":{"description":"pause freezes the ramp at its current share without touching traffic. resume clears the freeze. rollback marks the green's backend unhealthy, which is the one true rollback lever: weight 0 keeps a backend as the overflow tail and would route to the broken version exactly when the lane saturates. cutover arms a supervised force of the capacity gates. finalize drains the demoted blues and returns the lane to steady state.","enum":["pause","resume","rollback","cutover","finalize"],"type":"string","x-enum-varnames":["ServingRolloutActionPause","ServingRolloutActionResume","ServingRolloutActionRollback","ServingRolloutActionCutover","ServingRolloutActionFinalize"]},"confirm":{"description":"Typed confirmation for the actions that cannot be undone cheaply (cutover, finalize): must equal the deployment id.","type":"string"},"reason":{"description":"Why. Required for cutover, recorded in the admin audit log with the actor for every action.","type":"string"}},"required":["action"],"type":"object"},"ServingRolloutCapacity":{"description":"What a rollout costs in hardware while BOTH generations run. Preflight asks before anything exists and revise answers as the generation is minted, but the four numbers and the arithmetic behind them are identical, so they are one schema: two copies is how the two surfaces end up warning about different peaks.","properties":{"laneCommittedGpus":{"description":"GPUs the target lane's existing live deployments already commit for this GPU model.","type":"integer"},"rolloutOk":{"description":"Whether rolloutPeakGpus fits the serving budget. A WARNING, not a blocker: running a tight rollout and draining the old side as the new one ramps is a legitimate plan.","type":"boolean"},"rolloutPeakGpus":{"description":"Peak GPU demand while the rollout is in flight: the new generation's footprint plus laneCommittedGpus, because both generations hold real hardware until the old one is drained.","type":"integer"},"rolloutReason":{"description":"Why the rollout peak does not fit, in lane terms, when rolloutOk is false","type":"string"}},"type":"object"},"ServingRolloutPhase":{"description":"Where a roll is. capacity_hold is the state the capacity guard produces: green is ramped as far as its serving capacity justifies and will not advance until it is scaled up. paused is an operator hold: the ramp is frozen at its current share and traffic is untouched, which is categorically weaker than rolled_back.","enum":["none","waiting_for_replicas","ramping","capacity_hold","at_full_weight","cut_over","rolled_back","paused"],"type":"string","x-enum-varnames":["RolloutPhaseViewNone","RolloutPhaseViewWaitingForReplicas","RolloutPhaseViewRamping","RolloutPhaseViewCapacityHold","RolloutPhaseViewAtFullWeight","RolloutPhaseViewCutOver","RolloutPhaseViewRolledBack","RolloutPhaseViewPaused"]},"ServingRolloutRole":{"description":"A deployment's part in its lane RIGHT NOW. independent = carries its own weight and is not in a rollout (also what every lane member is when no green exists). blue = an older member a green is ramping against. green = the opted-in successor.","enum":["independent","blue","green"],"type":"string","x-enum-varnames":["RolloutRoleViewIndependent","RolloutRoleViewBlue","RolloutRoleViewGreen"]},"ServingRolloutState":{"description":"DERIVED rollout state for this deployment's lane (public model + node pool). Computed by the same code that drives the rollout, so an operator surface can never narrate a different story than the controller will execute. Absent when the deployment is isolated, terminal, or the live catalog could not be read.\nforced / forceExpired / forcedBy / forcedReason are the ONLY read of a force-cutover: the arm expires, so the raw stamp on the row answers \"is a force in effect\" wrong roughly as often as it answers it right, and it is deliberately not on the wire.","properties":{"bluePoolPolicy":{"description":"Member-selection policy on the blue side's endpoint pool. Absent when the lane has no blue with a pool.","type":"string"},"blueSlots":{"description":"The blues' combined ready serving capacity, same unit","type":"integer"},"cutoverReady":{"description":"Whether green has the capacity parity cutover requires (\u003e=95% of blue's slots)","type":"boolean"},"forceExpired":{"description":"Force-cutover was armed but its window has elapsed, so it is inert. Set instead of forced, so a stale arm reads as expired rather than as an override nobody notices is still in effect.","type":"boolean"},"forced":{"description":"Force-cutover is armed AND still inside its window, so the slot-derived gates (the weight ceiling and the parity bar) are being overridden this pass. Readiness, the step limit, and the two-pass separation are never overridden.","type":"boolean"},"forcedBy":{"description":"Actor who armed force-cutover","type":"string"},"forcedReason":{"description":"Why force-cutover was armed","type":"string"},"greenPoolPolicy":{"description":"Member-selection policy on the green's endpoint pool","type":"string"},"greenSlots":{"description":"Green's ready serving capacity: readyReplicas x maxConcurrencyPerReplica","type":"integer"},"lane":{"description":"The lane's label, publicModelId/nodePool","type":"string"},"laneMemberIds":{"description":"Every live deployment in this lane, oldest first","items":{"type":"string"},"type":"array"},"laneWeight":{"description":"The lane's full weight: the largest weight any blue carries","type":"integer"},"nextAction":{"description":"One sentence naming what the controller will do next and why, written by the deciding code itself.","type":"string"},"paused":{"description":"The green row carries an operator pause. The ramp is frozen at its current share; traffic is untouched.","type":"boolean"},"peerId":{"description":"The other side of the roll: for a green, the newest blue it replaces; for a blue, the green replacing it. Absent when the lane is not rolling.","type":"string"},"phase":{"$ref":"#/components/schemas/ServingRolloutPhase"},"policyMismatch":{"description":"The two sides run different member-selection policies, so the rollout is not a controlled comparison: whatever the new version measures includes the balancing change.","type":"boolean"},"role":{"$ref":"#/components/schemas/ServingRolloutRole"},"weightCeiling":{"description":"The highest weight green may carry right now under the capacity guard (its share of the lane's ready serving slots, scaled onto laneWeight).","type":"integer"}},"required":["role","lane","phase"],"type":"object"},"ServingSpecDiffEntry":{"description":"One field that differs between two generations, with the class that decides how it can be changed. Values are stringified for display, so a diff is never a re-usable spec fragment. env diffs report KEYS ONLY (added, removed or changed key names): environment values can carry credentials and are never returned by this API.","properties":{"class":{"$ref":"#/components/schemas/ServingSpecFieldClass"},"field":{"type":"string"},"from":{"description":"The source generation's value, stringified; absent when unset","type":"string"},"to":{"description":"The new generation's value, stringified; absent when unset","type":"string"}},"required":["field","class"],"type":"object"},"ServingSpecField":{"properties":{"class":{"$ref":"#/components/schemas/ServingSpecFieldClass"},"field":{"type":"string"}},"required":["field","class"],"type":"object"},"ServingSpecFieldClass":{"description":"How a spec field may be changed. structural makes a different lane or tenant and is rejected everywhere. routing is safe to mutate under a running replica (PATCH). sizing is mutable by adding or removing replicas (/scale). replica requires new processes on new hardware, which is the blue/green trigger, and is the default for any field not listed.","enum":["structural","routing","sizing","replica"],"type":"string","x-enum-varnames":["ServingSpecClassStructural","ServingSpecClassRouting","ServingSpecClassSizing","ServingSpecClassReplica"]},"ServingSpecFieldList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ServingSpecField"},"type":"array"}},"required":["items"],"type":"object"},"SetServingDeploymentAutoscaleRequest":{"properties":{"paused":{"description":"true freezes the load-driven autoscaler (the band is kept as bounds and reconcile still converges to replicasDesired); false resumes it.","type":"boolean"}},"required":["paused"],"type":"object"},"SetVmVisibilityRequest":{"properties":{"public":{"description":"true = open endpoint (no data-plane auth); false = require an org API key","type":"boolean"}},"required":["public"],"type":"object"},"SetupIntent":{"properties":{"clientSecret":{"type":"string"},"publishableKey":{"type":"string"}},"required":["clientSecret","publishableKey"],"type":"object"},"ShadowCaptureScope":{"description":"One shadow-mode prompt-capture toggle for the inference gateway (notes/shadow-capture-design.md): while active, the gateway mirrors matching (orgId, modelId) request/response payloads to Firehose -\u003e S3 for offline replay/dataset building. orgId and/or modelId may be \"*\" (wildcard); an all-orgs scope additionally requires allowAllOrgs (a second confirm field, so a fat-fingered orgId can never silently become fleet-wide capture).","properties":{"allowAllOrgs":{"description":"Required (true) for a scope whose orgId is \"*\"; ignored otherwise","type":"boolean"},"captureResponse":{"description":"Also mirror the response payload (unary: scrubbed body; streaming: usage + timing only, see the gateway's capture-point limitation note). false captures the request + metadata only.","type":"boolean"},"createdAt":{"type":"string"},"createdBy":{"type":"string"},"enabled":{"type":"boolean"},"expiresAt":{"description":"RFC3339 UTC; the scope self-expires here regardless of the DynamoDB TTL sweep timing. Mandatory: a capture toggle that outlives its purpose is a privacy incident.","type":"string"},"modelId":{"description":"Public catalog model this scope governs, or \"*\" for every model","type":"string"},"note":{"description":"Free-text operator note, e.g. what dataset this scope is for","type":"string"},"orgId":{"description":"Org this scope governs, or \"*\" for every org (requires allowAllOrgs)","type":"string"},"sampleRate":{"description":"Fraction of matching requests captured: 0 captures nothing, 1 captures every match","format":"double","maximum":1,"minimum":0,"type":"number"},"updatedAt":{"type":"string"}},"required":["orgId","modelId","enabled","sampleRate","captureResponse","expiresAt"],"type":"object"},"ShadowCaptureScopeList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ShadowCaptureScope"},"type":"array"}},"required":["items"],"type":"object"},"Snapshot":{"properties":{"chainDepth":{"type":"integer"},"containerPort":{"type":"integer"},"createdAt":{"type":"string"},"deletedAt":{"type":"string"},"diskPath":{"type":"string"},"diskSizeGb":{"type":"integer"},"envVars":{"additionalProperties":{"type":"string"},"type":"object"},"errorMessage":{"type":"string"},"gpuCount":{"type":"integer"},"gpuModelId":{"type":"string"},"id":{"type":"string"},"imageUrl":{"type":"string"},"name":{"type":"string"},"nodeId":{"type":"string"},"organizationId":{"type":"string"},"resourceSize":{"type":"string"},"sizeBytes":{"format":"int64","type":"integer"},"sourceVmId":{"type":"string"},"status":{"type":"string"},"templateId":{"type":"string"}},"required":["id","organizationId","name","status","diskSizeGb","chainDepth","createdAt"],"type":"object"},"SshKeySummary":{"properties":{"attachedVmCount":{"description":"Number of live (non-terminated) VMs this key is attached to. Zero means the key is not scoped to any VM. The dashboard shows an \"in use\" indicator and warns before deleting a key that is in use.","type":"integer"},"blockingVms":{"description":"Names of VMs for which this is the only key that still resolves to a live org key. Deleting the key is refused (409) while this is non-empty; attach another key to those VMs first.","items":{"type":"string"},"type":"array"},"createdAt":{"type":"string"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"publicKey":{"type":"string"}},"required":["id","name","publicKey","fingerprint","createdAt"],"type":"object"},"TargetOrg":{"properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"}},"required":["id","name","slug"],"type":"object"},"TelegramLinkResult":{"properties":{"createdAt":{"type":"string"},"linkedAt":{"type":"string"},"linkingCode":{"type":"string"},"present":{"type":"boolean"},"telegramUserId":{"format":"int64","type":"integer"},"telegramUsername":{"type":"string"}},"required":["present"],"type":"object"},"ToggleWebhookRequest":{"properties":{"enabled":{"type":"boolean"}},"required":["enabled"],"type":"object"},"Transaction":{"properties":{"amountCents":{"type":"integer"},"createdAt":{"type":"string"},"description":{"type":"string"},"id":{"type":"string"},"inferenceDetails":{"$ref":"#/components/schemas/InferenceTransactionDetails"},"source":{"type":"string"},"type":{"type":"string"}},"required":["id","type","source","amountCents","createdAt"],"type":"object"},"TransferItem":{"properties":{"clusterId":{"type":"string"},"completedAt":{"type":"string"},"createdAt":{"type":"string"},"expiresAt":{"type":"string"},"id":{"type":"string"},"note":{"type":"string"},"resourceName":{"type":"string"},"resourceType":{"type":"string"},"respondedAt":{"type":"string"},"sourceOrgName":{"type":"string"},"sourceOrgSlug":{"type":"string"},"status":{"type":"string"},"targetOrgName":{"type":"string"},"targetOrgSlug":{"type":"string"},"vmId":{"type":"string"}},"required":["id","resourceType","status","resourceName","createdAt"],"type":"object"},"TransferRequest":{"properties":{"clusterId":{"type":"string"},"completedAt":{"type":"string"},"createdAt":{"type":"string"},"expiresAt":{"type":"string"},"id":{"type":"string"},"initiatedBy":{"type":"string"},"note":{"type":"string"},"resourceType":{"type":"string"},"respondedAt":{"type":"string"},"respondedBy":{"type":"string"},"sourceOrganizationId":{"type":"string"},"status":{"type":"string"},"targetOrganizationId":{"type":"string"},"vmId":{"type":"string"}},"required":["id","resourceType","sourceOrganizationId","targetOrganizationId","status","createdAt"],"type":"object"},"UpdateMemberRoleRequest":{"properties":{"role":{"enum":["owner","admin","member","viewer"],"type":"string"}},"required":["role"],"type":"object"},"UpdateNodeReservationRequest":{"properties":{"allowedOrgIds":{"description":"Replace the audience","items":{"type":"string"},"type":"array"},"endsAt":{"description":"Move the term end. Empty string makes it open-ended.","type":"string"},"managingOrgIds":{"description":"Replace the managing orgs. An empty array removes the delegation.","items":{"type":"string"},"type":"array"},"reaffirm":{"description":"Re-pin to the node's current topology and reactivate (clears needs-review)","type":"boolean"}},"type":"object"},"UpdateOrgRequest":{"properties":{"billingEmail":{"type":"string"},"name":{"type":"string"},"taxCountry":{"description":"ISO 3166-1 alpha-2 (e.g. US, GB). Empty string clears it.","type":"string"},"taxId":{"description":"Tax identifier to store, max 64 chars. Requires taxCountry (in this request or already on the org). Empty string clears it.","type":"string"}},"type":"object"},"UpdateProfileRequest":{"properties":{"avatarUrl":{"type":"string"},"defaultOrganizationId":{"type":"string"},"fullName":{"type":"string"}},"type":"object"},"UpdateWebhookRequest":{"properties":{"events":{"items":{"type":"string"},"type":"array"},"name":{"type":"string"},"url":{"type":"string"}},"type":"object"},"UsageBreakdownItem":{"properties":{"cents":{"type":"integer"},"hours":{"type":"number"},"key":{"type":"string"}},"required":["key","hours","cents"],"type":"object"},"UsageSegment":{"properties":{"cents":{"type":"integer"},"key":{"description":"display segment: a GPU model name, \"CPU\", or \"Inference\"","type":"string"}},"required":["key","cents"],"type":"object"},"UserProfile":{"properties":{"avatarUrl":{"type":"string"},"defaultOrganizationId":{"type":"string"},"email":{"type":"string"},"fullName":{"type":"string"},"id":{"type":"string"}},"required":["id","email"],"type":"object"},"VmBurn":{"properties":{"accruedSessionCents":{"description":"cost accrued by the open session so far (session span times the locked rate, the same span math settlement uses); omitted when no session is open","type":"integer"},"autoStopFloorCents":{"description":"available-balance floor (cents) below which the org is auto-stopped","type":"integer"},"diskBilled":{"description":"always false today: disk is included in the bundled hourly rate and never billed as a separate line item","type":"boolean"},"gpuCount":{"description":"set when rateBasis is gpu","type":"integer"},"nonBillable":{"description":"This VM's usage is recorded but never charged (a provider consuming its own reserved node), so pricePerHourCents is informational and every cents figure below is 0 for it. Without this a client cannot tell \"not charged\" from \"nothing running\".","type":"boolean"},"orgAccruedCents":{"description":"the org's un-settled usage across all workloads: the current month, plus any prior month not yet settled","type":"integer"},"orgAccruedMonthCents":{"deprecated":true,"description":"deprecated alias of orgAccruedCents (same value; the name predates un-settled prior months being included); will be removed in a future spec version","type":"integer"},"orgAvailableCents":{"description":"spendable balance = ledger balance minus orgAccruedCents (can be negative)","type":"integer"},"orgBurnPerHourCents":{"description":"sum of the locked rates of every open usage record in the org","type":"integer"},"perGpuCents":{"description":"pricePerHourCents / gpuCount; set when rateBasis is gpu","type":"integer"},"pricePerHourCents":{"description":"the VM's bundled hourly rate: the open usage record's locked rate when a session is accruing, else the current catalog rate for the VM's shape","type":"integer"},"projectedRunwayHours":{"description":"(orgAvailableCents - autoStopFloorCents) / orgBurnPerHourCents; null when orgBurnPerHourCents is 0, clamped to 0 when available is already at or below the floor","format":"double","nullable":true,"type":"number"},"rateBasis":{"description":"gpu = per-GPU catalog price times gpuCount; cpu = flat per-resource-size rate","enum":["gpu","cpu"],"type":"string"},"rateSource":{"description":"locked = read from the open usage record (exactly what settlement will charge); catalog = no open session, the rate that would apply on next start","enum":["locked","catalog"],"type":"string"},"sessionStartedAt":{"description":"start of the open usage session; omitted when none","type":"string"},"vmId":{"type":"string"}},"required":["vmId","pricePerHourCents","rateSource","rateBasis","diskBilled","orgAccruedCents","orgAccruedMonthCents","orgAvailableCents","orgBurnPerHourCents","projectedRunwayHours","autoStopFloorCents"],"type":"object"},"VmDetail":{"properties":{"capabilityTier":{"description":"compute-fabric capability tier (e.g. \"pod\"); empty for a plain VM","type":"string"},"computeClass":{"description":"compute-fabric purchase option (on_demand; pods created before the rename carry the legacy reserved or interruptible values); empty for a plain VM","type":"string"},"containerPort":{"type":"integer"},"createdAt":{"type":"string"},"diskSizeGb":{"type":"integer"},"diskTotalBytes":{"format":"int64","type":"integer"},"diskUsedBytes":{"format":"int64","type":"integer"},"endpointUrl":{"type":"string"},"gpuCount":{"type":"integer"},"gpuInfo":{"$ref":"#/components/schemas/VmGpuInfo"},"gpuModelId":{"type":"string"},"healthy":{"type":"boolean"},"hostDiskFreeGb":{"description":"pods only: free disk on the pod's host node, shared across the pods on that node. Pods have no per-pod quota, and pod storage does not persist across terminate. Absent when the node reports no live disk signal.","type":"integer"},"hostDiskTotalGb":{"description":"pods only: total disk on the pod's host node, shared across the pods on that node. Pods have no per-pod quota, and pod storage does not persist across terminate. Absent when the node reports no live disk signal.","type":"integer"},"id":{"type":"string"},"imageUrl":{"type":"string"},"internalIp":{"type":"string"},"name":{"type":"string"},"nodeId":{"type":"string"},"nodeInfo":{"$ref":"#/components/schemas/VmNodeInfo"},"nonBillable":{"description":"This VM's usage is recorded but never charged: a provider org consuming GPUs on a node it owns under its own reservation. pricePerHourCents still reports the real rate (the measurement is kept, the charge is waived) so a client showing a rate MUST also show this or it will display a bill that never arrives.","type":"boolean"},"organizationId":{"type":"string"},"osType":{"type":"string"},"persistentDiskPath":{"type":"string"},"pricePerHourCents":{"description":"bundled hourly rate. When the VM has an open usage session this is the rate LOCKED at session open (what settlement charges); otherwise the rate a session WOULD lock: the fabric admission snapshot for a pod, else the current catalog rate for the VM's shape.","type":"integer"},"provisioningStage":{"type":"string"},"public":{"description":"true = open endpoint (no data-plane auth); false = require an org API key","type":"boolean"},"rdpPassword":{"type":"string"},"rdpPort":{"type":"integer"},"resourceSize":{"type":"string"},"serviceType":{"description":"managed-service tag; empty for a plain VM","type":"string"},"sshCommand":{"description":"ready-to-paste ssh command (ssh \u003chandle\u003e@\u003chost\u003e)","type":"string"},"sshHandle":{"description":"RunPod-style SSH handle (first label of endpointUrl); used as the SSH username","type":"string"},"sshHost":{"description":"data-plane SSH gateway host, e.g. ssh.run.beta.openrelay.inc","type":"string"},"sshPort":{"type":"integer"},"sshReady":{"description":"true when SSH should work now. Observed: a node-side probe saw the guest sshd answer. Falls back to the running-status guess on nodes not yet reporting probes (sshState=unverified).","type":"boolean"},"sshState":{"description":"why sshReady is what it is. booting = the VM is running but its guest OS has not started sshd yet (GPU VMs take minutes); unverified = no probe signal for this node, sshReady is a guess; offline = the VM is not running.","enum":["ready","booting","unverified","offline"],"type":"string"},"sshStateReason":{"description":"human-readable explanation of sshState, safe to show to the customer","type":"string"},"sshUser":{"description":"login user the gateway lands as inside the VM (e.g. ubuntu)","type":"string"},"startedAt":{"type":"string"},"status":{"type":"string"},"statusReason":{"description":"human-readable cause for failed/stuck states","type":"string"},"templateInfo":{"$ref":"#/components/schemas/VmTemplateInfo"},"terminatedAt":{"description":"RFC3339 time the VM was terminated. Set only once status is \"terminated\" (a stopped/failed VM can restart, so it has none); absent while the VM is live. On a terminated VM, healthy is false and provisioningStage is absent.","type":"string"},"tier":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","organizationId","name","status","gpuCount","resourceSize","tier","diskSizeGb","healthy","createdAt","updatedAt"],"type":"object"},"VmDiskLimits":{"description":"Live VM boot-disk size bounds the control plane accepts. Runtime env-tunable, so clients read these instead of hardcoding a ceiling that would drift from the server. A request outside the range is a 400.\n","properties":{"maxGb":{"description":"largest boot disk a VM may request","type":"integer"},"minGb":{"description":"smallest boot disk a VM may request","type":"integer"}},"required":["minGb","maxGb"],"type":"object"},"VmGpuInfo":{"properties":{"name":{"type":"string"},"pricePerHourCents":{"description":"current catalog per-GPU-hour rate for this model/tier (informational; the billed rate is VmDetail.pricePerHourCents)","type":"integer"},"vramGb":{"type":"integer"}},"required":["name","vramGb"],"type":"object"},"VmNodeInfo":{"properties":{"hostname":{"type":"string"},"location":{"type":"string"}},"required":["hostname"],"type":"object"},"VmPage":{"properties":{"items":{"items":{"$ref":"#/components/schemas/VmSummary"},"type":"array"},"nextCursor":{"type":"string"}},"required":["items"],"type":"object"},"VmSshKeyItem":{"properties":{"attached":{"type":"boolean"},"crossOrg":{"type":"boolean"},"fingerprint":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"orgName":{"nullable":true,"type":"string"}},"required":["id","name","fingerprint","attached","crossOrg"],"type":"object"},"VmSummary":{"properties":{"capabilityTier":{"description":"compute-fabric capability tier (e.g. \"pod\"); empty for a plain VM","type":"string"},"computeClass":{"description":"compute-fabric purchase option (on_demand; pods created before the rename carry the legacy reserved or interruptible values); empty for a plain VM","type":"string"},"createdAt":{"type":"string"},"diskSizeGb":{"type":"integer"},"endpointUrl":{"type":"string"},"gpuCount":{"type":"integer"},"gpuModelId":{"type":"string"},"gpuModelName":{"type":"string"},"id":{"type":"string"},"imageUrl":{"description":"tenant container image (pods)","type":"string"},"managedBy":{"description":"platform owner of this row (\"serving\" = inference serving replica, read-only on customer surfaces); empty for a customer-launched VM","type":"string"},"name":{"type":"string"},"organizationId":{"type":"string"},"pricePerHourCents":{"type":"integer"},"provisioningStage":{"type":"string"},"public":{"description":"true = open endpoint (no data-plane auth); false = require an org API key","type":"boolean"},"resourceSize":{"type":"string"},"serviceType":{"description":"managed-service tag; empty for a plain VM","type":"string"},"status":{"type":"string"},"statusReason":{"description":"human-readable cause for failed/stuck states","type":"string"},"tier":{"type":"string"}},"required":["id","organizationId","name","status"],"type":"object"},"VmTemplate":{"properties":{"compatibleGpuModelIds":{"description":"GPU model ids this template supports; null = all GPUs allowed","items":{"type":"string"},"nullable":true,"type":"array"},"description":{"type":"string"},"driverVersion":{"type":"string"},"filename":{"type":"string"},"id":{"type":"string"},"isDefault":{"type":"boolean"},"name":{"type":"string"},"osType":{"type":"string"},"osVersion":{"type":"string"},"requiresGpu":{"type":"boolean"},"slug":{"type":"string"},"sortOrder":{"type":"integer"}},"required":["id","name","slug","filename","requiresGpu","osType"],"type":"object"},"VmTemplateInfo":{"properties":{"name":{"type":"string"},"osType":{"type":"string"},"osVersion":{"type":"string"}},"required":["name","osType"],"type":"object"},"WebhookDeliveryItem":{"properties":{"attemptNumber":{"type":"integer"},"createdAt":{"type":"string"},"deliveredAt":{"type":"string"},"errorMessage":{"type":"string"},"eventId":{"type":"string"},"eventType":{"type":"string"},"id":{"type":"string"},"status":{"type":"string"},"statusCode":{"type":"integer"}},"required":["id","eventType","eventId","attemptNumber","status","createdAt"],"type":"object"},"WebhookSecret":{"properties":{"id":{"type":"string"},"secret":{"type":"string"},"secretPrefix":{"type":"string"}},"required":["id","secret","secretPrefix"],"type":"object"},"WebhookSummary":{"properties":{"createdAt":{"type":"string"},"enabled":{"type":"boolean"},"events":{"items":{"type":"string"},"type":"array"},"id":{"type":"string"},"name":{"type":"string"},"secretPrefix":{"type":"string"},"updatedAt":{"type":"string"},"url":{"type":"string"}},"required":["id","name","url","secretPrefix","events","enabled","createdAt","updatedAt"],"type":"object"},"WebhookToggleResult":{"properties":{"enabled":{"type":"boolean"},"id":{"type":"string"}},"required":["id","enabled"],"type":"object"},"WebhookUpdateResult":{"properties":{"events":{"items":{"type":"string"},"type":"array"},"id":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"}},"required":["id","name","url","events"],"type":"object"},"WebhookWithSecret":{"properties":{"createdAt":{"type":"string"},"enabled":{"type":"boolean"},"events":{"items":{"type":"string"},"type":"array"},"id":{"type":"string"},"name":{"type":"string"},"secret":{"type":"string"},"secretPrefix":{"type":"string"},"url":{"type":"string"}},"required":["id","name","url","secretPrefix","events","enabled","createdAt","secret"],"type":"object"},"WeightsRefList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/WeightsRefSummary"},"type":"array"}},"required":["items"],"type":"object"},"WeightsRefSummary":{"description":"One published checkpoint in the weights manifest.","properties":{"digest":{"description":"Content address of the whole checkpoint (sha256 over the sorted file list)","type":"string"},"fileCount":{"type":"integer"},"ref":{"description":"The reference deployments use as weightsRef","type":"string"},"totalBytes":{"format":"int64","type":"integer"}},"required":["ref","digest","totalBytes","fileCount"],"type":"object"},"Whoami":{"properties":{"availableOrgs":{"description":"Every org the user belongs to (session only).","items":{"$ref":"#/components/schemas/OrgMembership"},"type":"array"},"email":{"description":"Set for a user session.","type":"string"},"organizationId":{"description":"The org this request acts in (fixed for a key, the active org for a session; may be empty for a session mid-onboarding).","type":"string"},"organizationName":{"type":"string"},"principalType":{"description":"Whether the caller authenticated with an API key, a user session, or ADMIN_API_TOKEN.","enum":["api_key","user","platform_admin"],"type":"string"},"role":{"description":"The user's role in the acting org (session only).","type":"string"},"scopes":{"description":"The API key's scopes (key principal only).","items":{"type":"string"},"type":"array"},"userId":{"description":"Set for a user session.","type":"string"}},"required":["principalType","organizationId"],"type":"object"}},"securitySchemes":{"apiKey":{"description":"OpenRelay API key. Send it as `Authorization: Bearer or_…` (legacy `vl_…` keys still work).","scheme":"bearer","type":"http"}}},"info":{"description":"The OpenRelay control-plane REST API. Deploy GPU VMs and inference clusters, manage organizations and billing, and automate your infrastructure. All requests are authenticated with an API key (`or_…`; legacy `vl_…` keys still work) unless noted otherwise.\n","title":"OpenRelay API","version":"0.1.0"},"openapi":"3.1.0","paths":{"/health":{"get":{"operationId":"getHealth","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}},"description":"Service is up"}},"summary":"Liveness probe (no auth)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/catalog":{"get":{"description":"The endpoint-page population in ONE request: each catalog model with its routing policy and backends (including pool linkage for self-hosted), instead of N+1 per-model fetches.","operationId":"listInferenceCatalogModels","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferenceCatalogModelList"}}},"description":"Models"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"List every catalog model's routing view (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/catalog/{modelId}":{"get":{"description":"The endpoint-level view a traffic surface renders: the model's routing policy and every backend with its live weight, health, standby flag, and rollout tag. Self-hosted pool:// backends additionally carry the deployment ids and node pools that register into them, so the fleet breakdown under an aggregated \"OpenRelay\" row comes from the same read.","operationId":"getInferenceCatalogModel","parameters":[{"in":"path","name":"modelId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferenceCatalogModel"}}},"description":"The model's routing view"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Read one catalog model's routing view (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/catalog/{modelId}/allocation":{"put":{"description":"Percentage-based traffic split over the model's backends. Every backend must be listed; an unlisted backend is rejected rather than silently zeroed. Targets are percentages (they must sum to 100); the server normalizes them to integer weights once, with largest-remainder rounding, so every consumer of this API shares one deterministic rule. A PUT that targets a self-hosted pool whose lane has a live rollout campaign answers 409: pause the rollout, then edit the allocation.","operationId":"putInferenceCatalogAllocation","parameters":[{"in":"path","name":"modelId","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"allocations":[{"endpoint":"pool://us-west-b200","weightPct":70},{"endpoint":"https://bedrock-mantle.us-east.amazonaws.com","weightPct":20},{"endpoint":"https://api.lambda.ai/v1","weightPct":10}]},"schema":{"$ref":"#/components/schemas/PutInferenceCatalogAllocationRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferenceCatalogModel"}}},"description":"Applied; echoes the persisted integer weights"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Replace the endpoint's traffic allocation across ALL backends (platform admin)","tags":["Internal"],"x-internal":true,"x-openrelay-mcp":{"exclude":true}}},"/v1/admin/inference/catalog/{modelId}/backends":{"post":{"description":"Adds one managed third-party backend (bedrock/openrouter/openai/anthropic) to the catalog row. Self-hosted capacity is NOT created here: pool:// endpoints are registrar-owned by serving deployments and attaching one by hand is refused. New backends start healthy at the given weight (default 0, the overflow tail), so attaching never moves traffic by itself.","operationId":"createInferenceCatalogBackend","parameters":[{"in":"path","name":"modelId","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"auth":"api_key","endpoint":"https://api.lambda.ai/v1","nativeFamilies":["chat_completions"],"provider":"bedrock","ref":"llama-4-maverick"},"schema":{"$ref":"#/components/schemas/CreateInferenceCatalogBackendRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferenceCatalogModel"}}},"description":"Attached"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Attach a managed-provider backend to the model (platform admin)","tags":["Internal"],"x-internal":true,"x-openrelay-mcp":{"exclude":true}}},"/v1/admin/inference/catalog/{modelId}/backends/{backendEndpoint}":{"delete":{"description":"Deletes a managed backend entry. Refused while the backend is healthy: flip it dark first (healthy=false is the reversible off state), because delete is immediate and lossy while the kill switch is instant AND undoable. Self-hosted pool:// backends are registrar-owned and cannot be deleted from here.","operationId":"deleteInferenceCatalogBackend","parameters":[{"in":"path","name":"modelId","required":true,"schema":{"type":"string"}},{"in":"path","name":"backendEndpoint","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferenceCatalogModel"}}},"description":"Removed"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Remove a MANAGED backend from the model (platform admin)","tags":["Internal"],"x-internal":true,"x-openrelay-mcp":{"destructiveHint":true,"exclude":true}},"patch":{"description":"Weight, health kill switch, concurrency cap, and prompt-byte lane on one managed backend. Identity fields (provider, endpoint) and self-hosted pool:// backends are refused here: pool state belongs to the serving deployments that own it (their /weight and /health endpoints).","operationId":"patchInferenceCatalogBackend","parameters":[{"in":"path","name":"modelId","required":true,"schema":{"type":"string"}},{"in":"path","name":"backendEndpoint","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"healthy":false,"reason":"investigating elevated error rates"},"schema":{"$ref":"#/components/schemas/PatchInferenceCatalogBackendRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferenceCatalogModel"}}},"description":"Applied"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Edit a MANAGED backend's knobs (platform admin)","tags":["Internal"],"x-internal":true,"x-openrelay-mcp":{"exclude":true}}},"/v1/admin/inference/catalog/{modelId}/routing":{"patch":{"description":"Sets RoutingConfig.Strategy / MaxAttempts (and optionally per-family overrides) on the catalog row. Rides the generation-stamp cache, so the change propagates to every gateway task without a deploy.","operationId":"patchInferenceCatalogRouting","parameters":[{"in":"path","name":"modelId","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{"maxAttempts":2,"strategy":"weighted"},"schema":{"$ref":"#/components/schemas/PatchInferenceCatalogRoutingRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferenceCatalogModel"}}},"description":"Applied"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Edit the model's Layer-1 routing policy (platform admin)","tags":["Internal"],"x-internal":true,"x-openrelay-mcp":{"exclude":true}}},"/v1/admin/inference/deployments":{"get":{"description":"Cursor-paginated. Filterable by status and by publicModelId; with no filter every status partition is walked in declared order behind one opaque cursor.","operationId":"listServingDeployments","parameters":[{"in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"in":"query","name":"status","required":false,"schema":{"type":"string"}},{"in":"query","name":"publicModelId","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingDeploymentPage"}}},"description":"Page of deployments"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"List serving deployments (platform admin)","tags":["Internal"],"x-internal":true},"post":{"description":"Records the operator's intent to run N replicas of an inference engine for one public catalog model. The spec is validated hard at admission (the engine must exist and the replica must plan) so a row that could never dispatch is rejected here instead of failing later. The new deployment starts in status pending; the serving reconcile loop admits replicas, stages weights, probes readiness, and registers the pool backend from there.","operationId":"createServingDeployment","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateServingDeploymentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingDeployment"}}},"description":"Created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Create a self-hosted serving deployment (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}":{"delete":{"description":"Stops the deployment: detaches its pool backend from the catalog model, removes its pool members, and terminates its replica VMs. The row is kept as history in status stopped; deleting an already-terminal deployment removes the row itself.","operationId":"deleteServingDeployment","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingDeployment"}}},"description":"Stopped (or removed)"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Stop a serving deployment (platform admin)","tags":["Internal"],"x-internal":true,"x-openrelay-mcp":{"destructiveHint":true,"exclude":true}},"get":{"description":"The full row, including runtime state the reconcile loop maintains: status, statusReason, readyReplicas, poolId, and the replica VM ids.","operationId":"getServingDeployment","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingDeployment"}}},"description":"The deployment"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Serving deployment detail (platform admin)","tags":["Internal"],"x-internal":true},"patch":{"description":"In-place edit of the routing-class fields (prompt-size band, native API families): these select which requests the deployment is eligible for and no engine process cares, so they change under running replicas. The reconcile loop propagates them to the catalog backend on its next pass. No other field is accepted, and none is silently applied: fields outside this schema are dropped by the decoder, so a body that names only such a field (a replica-class field belonging on /revise, or the replica band belonging on /scale) contains no routing field and is rejected with 400 REQUIRES_NEW_GENERATION. An empty patch is rejected the same way, because a 200 on a request that changed nothing reads as \"applied\". A patch whose values already match the row returns 200 without writing. Applies to the one deployment named in the path, never to its lane peer.","operationId":"patchServingDeployment","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchServingDeploymentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingDeployment"}}},"description":"Updated"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Edit a serving deployment's routing fields in place (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/autoscale":{"post":{"description":"Sets autoscalePaused. When true the autoscaler stops moving replicasDesired on load, keeping the replicasMin..replicasMax band as bounds; the reconcile loop still converges the fleet to replicasDesired, and /scale can still hand-set it within the band. false resumes load-driven scaling. Idempotent: setting the current state is a no-op. Rejected on a terminal or draining deployment.","operationId":"setServingDeploymentAutoscale","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetServingDeploymentAutoscaleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingDeployment"}}},"description":"Updated"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Freeze or resume a serving deployment's load-driven autoscaler (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/drain":{"post":{"description":"Moves the deployment to draining. The reconcile loop drains its pool members so no replica takes new traffic while in-flight requests finish. Idempotent on an already-draining deployment.","operationId":"drainServingDeployment","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingDeployment"}}},"description":"Draining"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Drain a serving deployment (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/health":{"post":{"description":"The emergency control. healthy=false takes this deployment's backend out of routing entirely; the gateway excludes unhealthy backends from the live set. This is categorically stronger than weight 0, which keeps the backend as the standby/overflow tail and still routes to it exactly when the weighted backends are saturated or failing. healthy=true clears the kill. The setting survives restarts and reconcile passes until cleared.","operationId":"setServingDeploymentHealth","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingDeploymentHealthRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingDeployment"}}},"description":"Updated"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Set the deployment backend's health (the emergency kill switch)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/member-weight":{"post":{"description":"The per-replica warmup/canary dial: weight \u003c 1 makes the member look fuller so it receives proportionally less traffic; 1 restores its full share. The member's hard capacity cap is untouched, and the reconcile loop preserves the weight across re-adds. Returns the refreshed replica view.","operationId":"setServingMemberWeight","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingMemberWeightRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingReplicaList"}}},"description":"Updated"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Set one pool member's ranking weight (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/pool-routing":{"post":{"description":"Sets the endpoint pool's member-selection policy and/or the prefix_affinity load guard. This edits the POOL (deployments sharing a pool share the setting) and reaches every gateway task in ~2s via the config generation stamp; setting policy back to p2c_util is the instant rollback. Returns the refreshed replica view.","operationId":"setServingPoolRouting","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingPoolRoutingRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingReplicaList"}}},"description":"Updated"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Set the deployment pool's member-selection routing (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/pool-shedding":{"post":{"description":"Replaces the endpoint pool's whole load-shedding override (PoolShedding). Every field zero-value = inherit the global/env behavior, so an all-empty body clears the override. This edits the POOL (deployments sharing a pool share it) and reaches every gateway task in ~2s via the config generation stamp; on a subsequent revise the new generation's pool inherits it, so set it on the CURRENT generation to have the next one carry it forward. Returns the refreshed replica view.","operationId":"setServingPoolShedding","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingPoolShedding"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingReplicaList"}}},"description":"Updated"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Set the deployment pool's load-shedding override (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/replicas":{"get":{"description":"Joins each replica VM row with its pool-member state and the last scraped engine load, so an operator can see where every replica is stuck (placement, weights staging, image staging, readiness) and what traffic it is taking, without walking Nomad and Redis by hand.","operationId":"listServingDeploymentReplicas","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingReplicaList"}}},"description":"The deployment's replicas"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Per-replica runtime view of a serving deployment (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/replicas/{vmId}/drain":{"post":{"description":"Drains this replica's pool member so it takes no new traffic while in-flight requests finish, and records a durable hold on the replica so the reconcile loop keeps it drained instead of re-activating it on its next pass. Keyed by vmId because that is the runtime-neutral replica identity: a pod replica is the same row shape as a VM replica. Returns the refreshed replica list.","operationId":"drainServingReplica","parameters":[{"$ref":"#/components/parameters/ResourceId"},{"$ref":"#/components/parameters/VmId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingReplicaList"}}},"description":"Draining"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Take one serving replica out of rotation (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/replicas/{vmId}/undrain":{"post":{"description":"Clears the operator drain hold. The reconcile loop re-activates the pool member on its next pass, once the replica is ready. Idempotent on a replica that is not held. Returns the refreshed replica list.","operationId":"undrainServingReplica","parameters":[{"$ref":"#/components/parameters/ResourceId"},{"$ref":"#/components/parameters/VmId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingReplicaList"}}},"description":"Returned to rotation"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Return one serving replica to rotation (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/revise":{"post":{"description":"Server-side \"next generation from this one\": every spec field in the body overrides the source, every absent field is inherited, and the result is created as a weight-0 green in the source's lane. Structural overrides (organizationId, publicModelId, nodePool) are rejected: they would make a different lane or tenant. A delta touching only routing or sizing fields is also rejected, naming PATCH or /scale, because no new replicas are needed and a new generation would be waste. The new green and its lane lock are written in one transaction, so two simultaneous revises of one lane cannot both become green. The response carries the field diff and the rollout capacity warning for running both generations at once.","operationId":"reviseServingDeployment","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviseServingDeploymentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingRevisionResult"}}},"description":"The new generation"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Mint the next generation of a serving deployment (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/revisions":{"get":{"description":"The revision history: this deployment's ancestry and its descendants in the same lane, each with the field diff against its parent, class labeled. Terminal generations are included, because they ARE the history: \"what changed between the generation that worked and this one\" is the question the chain exists to answer. env diffs report key names only. Returns the whole chain in one response: the walk is bounded at 50 generations, so there is nothing to page through.","operationId":"listServingDeploymentRevisions","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingRevisionPage"}}},"description":"The generation chain"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"List a serving deployment's generation chain (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/rolling-rollout":{"post":{"description":"start bumps the deployment's Version IN PLACE and replaces its replicas one at a time; drain, bounded in-flight window, terminate, admit into the freed units, wait ready; so a saturated pool can revise without capacity headroom (which is exactly what blue/green needs and rolling does not). Requires the deployment's rolloutStrategy to be \"rolling\". The lane lock is held for the campaign's lifetime: create/revise answer 409 while it runs. pause/resume reuse the existing rollout verb. abort stops the campaign and releases the lane; drained victims re-activate on the next reconcile pass (membership heals level-triggered), so abort never leaves a replica out of rotation permanently.","operationId":"rollingRolloutServingDeployment","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"example":{"action":"start","cause":"bump to patched engine build","version":"v8"},"schema":{"$ref":"#/components/schemas/ServingRollingRolloutRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingDeployment"}}},"description":"The refreshed deployment"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Start or abort an in-place rolling rollout (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/rollout":{"post":{"description":"The rollout control verb, targeting the lane's green. pause freezes the ramp at its current share while leaving traffic alone; resume clears it. rollback marks the green's backend unhealthy, which removes it from routing entirely. cutover arms a supervised override of the capacity ceiling and the parity bar (readiness, the per-pass step limit and the two-pass separation are never overridden), requires a reason and a typed confirmation, and expires. finalize is valid once the lane has cut over: it drains the demoted blues and returns the green to an ordinary independent deployment, which also ends the instant-rollback window. Every action is recorded in the admin audit log with the actor and reason. All of these are rejected on a deployment that is not a rolling green, so they can never touch an independent deployment by accident.","operationId":"rolloutServingDeployment","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingRolloutActionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingDeployment"}}},"description":"The refreshed green"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Drive a blue/green rollout (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/scale":{"post":{"description":"Sets replicasDesired. The value must lie within the deployment's replicasMin..replicasMax band; out-of-range values are rejected rather than silently clamped. The reconcile loop converges the fleet.","operationId":"scaleServingDeployment","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScaleServingDeploymentRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingDeployment"}}},"description":"Updated"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Set a serving deployment's desired replica count (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/deployments/{id}/weight":{"post":{"description":"The traffic ramp: sets the catalog weight of this deployment's pool backend. Weight 0 is standby, not off; the gateway still uses weight-0 backends as the overflow tail when weighted backends saturate or fail. Requires the backend to be registered (at least one replica has been ready).","operationId":"setServingDeploymentWeight","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingDeploymentWeightRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingDeployment"}}},"description":"Updated"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Set the deployment backend's routing weight (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/serving/lanes":{"get":{"description":"The lane view, derived by the same pipeline that drives rollouts, so a surface can never narrate a different split than the controller executes: per lane, its members oldest first with their live catalog weight and share, the green if the lane is rolling, and a flag when the two sides run different pool policies. Filter by publicModelId and nodePool to answer \"does this lane already have a green\" before creating one.","operationId":"listServingLanes","parameters":[{"in":"query","name":"publicModelId","required":false,"schema":{"type":"string"}},{"in":"query","name":"nodePool","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingLaneList"}}},"description":"Lanes"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"List serving capacity lanes and their members (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/serving/preflight":{"get":{"description":"Read-only. Evaluates everything that must already be true for a deployment with the given spec to admit, place, and register: the catalog row is seeded with pricing, the weights ref is published, the GPU footprint fits the serving capacity budget, prepared nodes exist, and readiness probing is configured. Each check is only applied when its query parameters are present, so the UI can preflight incrementally as a form is filled in.","operationId":"getServingPreflight","parameters":[{"in":"query","name":"publicModelId","required":false,"schema":{"type":"string"}},{"in":"query","name":"gpuModelId","required":false,"schema":{"type":"string"}},{"in":"query","name":"gpuCountPerReplica","required":false,"schema":{"type":"integer"}},{"in":"query","name":"replicasDesired","required":false,"schema":{"type":"integer"}},{"in":"query","name":"weightsRef","required":false,"schema":{"type":"string"}},{"description":"The capacity lane this deployment would join, with publicModelId. Enables the rollout-peak capacity check.","in":"query","name":"nodePool","required":false,"schema":{"type":"string"}},{"description":"Pass \"green\" to also evaluate the ROLLOUT peak: a rollout runs the old and new generations at once, so it needs the new footprint on top of what the lane already holds.","in":"query","name":"rolloutRole","required":false,"schema":{"enum":["","green"],"type":"string"}},{"description":"The runtime the deployment would use, mirroring the create request. Selects which fleet the capacity budget is evaluated against: \"pod\" sizes against the device-request fleet, omit (or \"\" or \"vm\") against the VFIO/VM fleet (the default).","in":"query","name":"replicaRuntime","required":false,"schema":{"enum":["","vm","pod"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingPreflight"}}},"description":"Preflight result"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Pre-deployment checks for a serving spec (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/serving/spec-fields":{"get":{"description":"The server's classification of every deployment spec field: structural (rejected everywhere), routing (PATCH), sizing (/scale), or replica (a blue/green rollout). Served so surfaces render the server's truth instead of keeping a copy that can drift: a field the server calls replica must be a field the UI calls replica. Any field not listed is replica.","operationId":"listServingSpecFields","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServingSpecFieldList"}}},"description":"Spec field classification"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"List serving spec fields and how each may be changed (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/shadow-capture":{"delete":{"description":"Removes the (orgId, modelId) scope so the gateway stops honoring it on its next cache refresh (~10s), rather than waiting for ttlMinutes to elapse. Idempotent: deleting an absent scope still returns 200.","operationId":"deleteShadowCaptureScope","parameters":[{"in":"query","name":"orgId","required":true,"schema":{"type":"string"}},{"in":"query","name":"modelId","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Removed (or already absent)"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Immediately turn off a shadow-mode prompt-capture scope (platform admin)","tags":["Internal"],"x-internal":true,"x-openrelay-mcp":{"destructiveHint":true,"exclude":true}},"get":{"description":"Every scope currently on the config table, including a stale one an operator forgot to disable (still visible here; expiresAt tells the operator whether the gateway is still honoring it, an expired-but-not- yet-swept row is inert even though it's still listed).","operationId":"listShadowCaptureScopes","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShadowCaptureScopeList"}}},"description":"Active + recent scopes"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"List shadow-mode prompt-capture scopes (platform admin)","tags":["Internal"],"x-internal":true},"put":{"description":"Upserts the (orgId, modelId) capture scope the inference gateway polls (notes/shadow-capture-design.md): while live, matching request/response payloads are mirrored to Firehose -\u003e S3 for offline replay/dataset building. ttlMinutes is required (max 1440/24h): a capture toggle that outlives its purpose is a privacy incident, so every scope self-expires. A second PUT for the same (orgId, modelId) overwrites the first, which is how an operator extends a scope's TTL.","operationId":"putShadowCaptureScope","requestBody":{"content":{"application/json":{"example":{"captureResponse":true,"enabled":true,"modelId":"openrelay/gemma-4-31b-32k","note":"hillclimb dataset capture","orgId":"org_01hzy3tailwind","sampleRate":1,"ttlMinutes":45},"schema":{"$ref":"#/components/schemas/PutShadowCaptureScopeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShadowCaptureScope"}}},"description":"Saved"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Create or replace a shadow-mode prompt-capture scope (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/admin/inference/weights":{"get":{"description":"The weights manifest as a list: every checkpoint ref a deployment can use as weightsRef, with its content digest and size. Publishing itself stays with cmd/weightspublish (it needs AWS credentials and the raw checkpoint); this is the read side for operators.","operationId":"listServingWeights","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeightsRefList"}}},"description":"Published weights refs"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"502":{"$ref":"#/components/responses/BadGateway"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"List published weights refs (platform admin)","tags":["Internal"],"x-internal":true}},"/v1/batches":{"get":{"operationId":"listBatches","parameters":[{"description":"Page size, 1-100 (default 20).","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"A batch id; returns batches submitted after it (use the previous page's last_id).","in":"query","name":"after","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchList"}}},"description":"OK"},"400":{"$ref":"#/components/responses/InferenceBadRequest"},"401":{"$ref":"#/components/responses/InferenceUnauthorized"},"404":{"$ref":"#/components/responses/InferenceNotFound"}},"security":[{"apiKey":[]}],"summary":"List batches, newest first","tags":["Batches"]},"post":{"description":"Submit a batch from an uploaded input file (input_file_id) or inline request objects (requests); provide exactly one of the two. The batch completes within the 24h window at a discounted rate; poll it with get, then download results via the output and error file ids.\n","operationId":"createBatch","requestBody":{"content":{"application/json":{"schema":{"properties":{"completion_window":{"description":"Time budget for the batch; only \"24h\" is supported.","enum":["24h"],"type":"string"},"endpoint":{"description":"The API family every record in the batch calls.","enum":["/v1/chat/completions","/v1/completions","/v1/embeddings"],"type":"string"},"input_file_id":{"description":"Id of an uploaded JSONL input file (from the file upload).","type":"string"},"metadata":{"additionalProperties":{"type":"string"},"description":"Up to 16 key-value pairs echoed back on the batch object.","type":"object"},"model":{"description":"Optional display hint shown on the batch immediately; validation settles the authoritative value from the input file.\n","type":"string"},"requests":{"description":"Inline request objects (max 50,000), each with a custom_id and the request body for the chosen endpoint. Alternative to input_file_id for small batches.\n","items":{"$ref":"#/components/schemas/BatchRequestRecord"},"type":"array"}},"required":["endpoint","completion_window"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchObject"}}},"description":"OK"},"400":{"$ref":"#/components/responses/InferenceBadRequest"},"401":{"$ref":"#/components/responses/InferenceUnauthorized"},"404":{"$ref":"#/components/responses/InferenceNotFound"},"413":{"$ref":"#/components/responses/InferenceTooLarge"},"429":{"$ref":"#/components/responses/InferenceRateLimited"},"503":{"$ref":"#/components/responses/InferenceUnavailable"}},"security":[{"apiKey":[]}],"summary":"Create a batch inference job","tags":["Batches"]},"servers":[{"description":"Production","url":"https://inference.openrelay.inc"},{"description":"Beta","url":"https://inference.beta.openrelay.inc"}]},"/v1/batches/{id}":{"get":{"operationId":"getBatch","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchObject"}}},"description":"OK"},"401":{"$ref":"#/components/responses/InferenceUnauthorized"},"404":{"$ref":"#/components/responses/InferenceNotFound"}},"security":[{"apiKey":[]}],"summary":"Get a batch by id","tags":["Batches"]},"servers":[{"description":"Production","url":"https://inference.openrelay.inc"},{"description":"Beta","url":"https://inference.beta.openrelay.inc"}]},"/v1/batches/{id}/cancel":{"post":{"description":"In-flight records finish, unstarted records are skipped, and partial results are written; the batch settles to cancelled within a few minutes. Completed work is billed. Batches already in a terminal state return 409.\n","operationId":"cancelBatch","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchObject"}}},"description":"OK"},"401":{"$ref":"#/components/responses/InferenceUnauthorized"},"404":{"$ref":"#/components/responses/InferenceNotFound"},"409":{"$ref":"#/components/responses/InferenceConflict"},"503":{"$ref":"#/components/responses/InferenceUnavailable"}},"security":[{"apiKey":[]}],"summary":"Cancel a running batch","tags":["Batches"],"x-openrelay-mcp":{"destructiveHint":true}},"servers":[{"description":"Production","url":"https://inference.openrelay.inc"},{"description":"Beta","url":"https://inference.beta.openrelay.inc"}]},"/v1/clusters/{id}":{"get":{"operationId":"getCluster","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterSummary"}}},"description":"The cluster"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Get a cluster by id","tags":["Clusters"]}},"/v1/clusters/{id}/detail":{"get":{"operationId":"getClusterDetail","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterDetail"}}},"description":"The cluster detail"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Get a cluster with its replicas and GPU model","tags":["Clusters"],"x-openrelay-mcp":{"default":true}}},"/v1/clusters/{id}/restart":{"post":{"operationId":"restartCluster","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterSummary"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Restart a cluster","tags":["Clusters"]}},"/v1/clusters/{id}/scale":{"post":{"operationId":"scaleCluster","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"example":{"replicaCount":4},"schema":{"$ref":"#/components/schemas/ScaleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterSummary"}}},"description":"OK"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/CapacityUnavailable"}},"security":[{"apiKey":[]}],"summary":"Scale a cluster's replica count","tags":["Clusters"]}},"/v1/clusters/{id}/stop":{"post":{"operationId":"stopCluster","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterSummary"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Stop a cluster","tags":["Clusters"]}},"/v1/clusters/{id}/terminate":{"post":{"operationId":"terminateCluster","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterSummary"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Terminate a cluster","tags":["Clusters"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/files":{"post":{"description":"Send multipart/form-data with a `file` part (the JSONL bytes, up to 200 MB and 50,000 records) and a `purpose` field set to `batch`. Each line is one request object with the BatchRequestRecord shape (including the optional `openrelay.tool_config` extension on chat bodies); the returned file id goes in `input_file_id` when creating a batch.\n","operationId":"uploadFile","requestBody":{"content":{"multipart/form-data":{"schema":{"properties":{"file":{"description":"The JSONL file contents.","format":"binary","type":"string"},"purpose":{"enum":["batch"],"type":"string"}},"required":["file","purpose"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileObject"}}},"description":"OK"},"400":{"$ref":"#/components/responses/InferenceBadRequest"},"401":{"$ref":"#/components/responses/InferenceUnauthorized"},"404":{"$ref":"#/components/responses/InferenceNotFound"},"413":{"$ref":"#/components/responses/InferenceTooLarge"},"429":{"$ref":"#/components/responses/InferenceRateLimited"}},"security":[{"apiKey":[]}],"summary":"Upload a JSONL input file for the Batch API","tags":["Files"],"x-openrelay-mcp":{"exclude":true}},"servers":[{"description":"Production","url":"https://inference.openrelay.inc"},{"description":"Beta","url":"https://inference.beta.openrelay.inc"}]},"/v1/files/presign":{"post":{"description":"For browser clients that upload the file straight to storage instead of through the API: returns a file id and a signed PUT URL (valid 15 minutes). After the PUT succeeds, register the file with the complete call. CLI and SDK clients should use the plain upload instead.\n","operationId":"presignFileUpload","requestBody":{"content":{"application/json":{"schema":{"properties":{"bytes":{"description":"Exact size of the upcoming upload in bytes (max 200 MB).","format":"int64","type":"integer"},"filename":{"description":"Display name; not persisted.","type":"string"}},"required":["bytes"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresignUploadResponse"}}},"description":"OK"},"400":{"$ref":"#/components/responses/InferenceBadRequest"},"401":{"$ref":"#/components/responses/InferenceUnauthorized"},"404":{"$ref":"#/components/responses/InferenceNotFound"}},"security":[{"apiKey":[]}],"summary":"Mint a signed URL for a direct JSONL upload","tags":["Files"],"x-openrelay-cli":{"hidden":true},"x-openrelay-mcp":{"exclude":true}},"servers":[{"description":"Production","url":"https://inference.openrelay.inc"},{"description":"Beta","url":"https://inference.beta.openrelay.inc"}]},"/v1/files/{id}":{"get":{"operationId":"getFile","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileObject"}}},"description":"OK"},"401":{"$ref":"#/components/responses/InferenceUnauthorized"},"404":{"$ref":"#/components/responses/InferenceNotFound"}},"security":[{"apiKey":[]}],"summary":"Get a file's metadata","tags":["Files"]},"servers":[{"description":"Production","url":"https://inference.openrelay.inc"},{"description":"Beta","url":"https://inference.beta.openrelay.inc"}]},"/v1/files/{id}/complete":{"post":{"description":"Validates the uploaded object (size, line count) and registers it as a batch input file. Idempotent; completing an already-registered id returns the existing file.\n","operationId":"completeFileUpload","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileObject"}}},"description":"OK"},"400":{"$ref":"#/components/responses/InferenceBadRequest"},"401":{"$ref":"#/components/responses/InferenceUnauthorized"},"404":{"$ref":"#/components/responses/InferenceNotFound"}},"security":[{"apiKey":[]}],"summary":"Register a presigned upload after the PUT succeeds","tags":["Files"],"x-openrelay-cli":{"hidden":true},"x-openrelay-mcp":{"exclude":true}},"servers":[{"description":"Production","url":"https://inference.openrelay.inc"},{"description":"Beta","url":"https://inference.beta.openrelay.inc"}]},"/v1/files/{id}/content":{"get":{"description":"Streams the file bytes. Use it to fetch batch results: the batch object's output_file_id (successful records) and error_file_id (failed records) each point at a JSONL file of `{custom_id, response | error}` lines.\n","operationId":"getFileContent","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/octet-stream":{"schema":{"format":"binary","type":"string"}}},"description":"The file's raw JSONL bytes"},"401":{"$ref":"#/components/responses/InferenceUnauthorized"},"404":{"$ref":"#/components/responses/InferenceNotFound"}},"security":[{"apiKey":[]}],"summary":"Download a file's raw JSONL content","tags":["Files"],"x-openrelay-mcp":{"exclude":true}},"servers":[{"description":"Production","url":"https://inference.openrelay.inc"},{"description":"Beta","url":"https://inference.beta.openrelay.inc"}]},"/v1/gpu-availability":{"get":{"description":"Live availability per GPU model. Responses are served from a short-lived cache.","operationId":"getGpuAvailability","responses":{"200":{"content":{"application/json":{"example":[{"cluster":{"freeGpus":8,"maxPlaceableGpus":8,"totalGpus":8},"gpuModelId":"rtx-4090","vm":{"freeGpus":5,"maxPlaceableGpus":4,"totalGpus":12}}],"schema":{"items":{"$ref":"#/components/schemas/GpuAvailability"},"type":"array"}}},"description":"Availability by gpu model"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"GPU availability by model","tags":["Catalog"],"x-openrelay-mcp":{"default":true}}},"/v1/gpu-models":{"get":{"operationId":"listGpuModels","responses":{"200":{"content":{"application/json":{"example":[{"hidden":false,"id":"rtx-4090","manufacturer":"NVIDIA","name":"RTX 4090","vramGb":24}],"schema":{"items":{"$ref":"#/components/schemas/GpuModel"},"type":"array"}}},"description":"GPU models"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"apiKey":[]}],"summary":"GPU model catalog","tags":["Catalog"],"x-openrelay-mcp":{"default":true}}},"/v1/locations":{"get":{"operationId":"listLocations","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Location"},"type":"array"}}},"description":"Locations"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"apiKey":[]}],"summary":"Locations / regions","tags":["Catalog"]}},"/v1/me":{"get":{"description":"Returns the signed-in user's profile and organization memberships. Requires a dashboard session token; API keys cannot call this endpoint.","operationId":"getMe","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}},"description":"The caller's profile + orgs"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"apiKey":[]}],"summary":"Current user profile and org memberships","tags":["Account"],"x-openrelay-cli":{"hidden":true},"x-openrelay-mcp":{"exclude":true}}},"/v1/me/invites":{"get":{"description":"Returns unexpired pending invites whose invited email matches the session's email. Requires a dashboard session token; API keys are org service accounts and cannot hold or accept invites.","operationId":"listMyInvites","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/MyInvite"},"type":"array"}}},"description":"Pending invites for the caller"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"apiKey":[]}],"summary":"Pending org invites addressed to the signed-in user's email","tags":["Account"],"x-openrelay-cli":{"hidden":true},"x-openrelay-mcp":{"exclude":true}}},"/v1/me/invites/{orgId}/accept":{"post":{"description":"Creates the membership for the invite addressed to the session's email in the given org, then consumes the invite. This is the only way an invite becomes a membership: the invitee must be authenticated with the invited email and act explicitly.","operationId":"acceptOrgInvite","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptInviteResult"}}},"description":"Joined the organization"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"410":{"$ref":"#/components/responses/Gone"}},"security":[{"apiKey":[]}],"summary":"Accept a pending org invite (session only)","tags":["Account"],"x-openrelay-cli":{"hidden":true},"x-openrelay-mcp":{"exclude":true}}},"/v1/me/invites/{orgId}/decline":{"post":{"operationId":"declineOrgInvite","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"204":{"description":"Declined (idempotent)"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"apiKey":[]}],"summary":"Decline a pending org invite (session only)","tags":["Account"],"x-openrelay-cli":{"hidden":true},"x-openrelay-mcp":{"exclude":true}}},"/v1/me/profile":{"patch":{"description":"Requires a signed-in dashboard session token; API keys cannot call this endpoint.","operationId":"updateProfile","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProfileRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfile"}}},"description":"Updated"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Update the caller's profile","tags":["Account"],"x-openrelay-cli":{"hidden":true},"x-openrelay-mcp":{"exclude":true}}},"/v1/models":{"get":{"description":"Returns the public model catalog. Free, not balance-gated. Models with status \"available\" can be used immediately in /v1/chat/completions; \"request_access\" models are listed for discovery but are not yet invocable.\n","operationId":"listModels","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelList"}}},"description":"The public model catalog"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"apiKey":[]}],"summary":"List models in the OpenRelay catalog (OpenAI-compatible)","tags":["Catalog"],"x-openrelay-mcp":{"default":true}}},"/v1/nodes/enroll":{"post":{"operationId":"enrollNode","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollNodeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollNodeBundle"}}},"description":"Enrolled"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid/expired token"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Revoked"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Node busy (reassign blocked), hostname conflict, or a concurrent enroll to retry"}},"security":[],"summary":"Self-enroll a node using a provisioning token (no API key)","tags":["Internal"],"x-internal":true}},"/v1/nodes/{nodeId}/reservations":{"get":{"operationId":"listNodeReservations","parameters":[{"$ref":"#/components/parameters/NodeId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/NodeReservation"},"type":"array"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"List a node's reservations (platform admin)","tags":["Reservations"]},"post":{"description":"Grants exclusive placement rights over specific GPU units for a term. Reserved units disappear from every other org's catalog and cannot be placed on by them. Overlap with an existing reservation is rejected atomically.","operationId":"createNodeReservation","parameters":[{"$ref":"#/components/parameters/NodeId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNodeReservationRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeReservation"}}},"description":"Created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Reserve GPU units on a node for a set of orgs (platform admin)","tags":["Reservations"],"x-openrelay-mcp":{"exclude":true}}},"/v1/nodes/{nodeId}/reservations/units":{"get":{"description":"The allocatable GPU units on one node, with the ids that create, release, and reclaim take. Without this those calls are unusable: they are unit-scoped by design, and nothing else on the API returns a unit id, so the first reservation on a node could not be written at all.\nEach unit is one IOMMU group and is indivisible: a paired unit sells its GPUs together. Only vfio-group units are reservable, and the response says which, and why not, rather than leaving the caller to infer it from a rejection.\nReadable by a platform operator, the org that owns the node, and any org managing a reservation on it. Anyone else gets 404, so the fleet stays unenumerable.","operationId":"listReservationUnits","parameters":[{"$ref":"#/components/parameters/NodeId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/NodeGpuUnitItem"},"type":"array"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"List a node's GPU units, so you can reserve or release them","tags":["Reservations"],"x-openrelay-mcp":{"default":true}}},"/v1/nodes/{nodeId}/reservations/{id}":{"get":{"operationId":"getNodeReservation","parameters":[{"$ref":"#/components/parameters/NodeId"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeReservation"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Get one node reservation (platform admin)","tags":["Reservations"]},"patch":{"operationId":"updateNodeReservation","parameters":[{"$ref":"#/components/parameters/NodeId"},{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateNodeReservationRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeReservation"}}},"description":"OK"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Amend a reservation's term, audience, or topology pin (platform admin)","tags":["Reservations"],"x-openrelay-mcp":{"exclude":true}}},"/v1/nodes/{nodeId}/reservations/{id}/cancel":{"post":{"description":"Returns the GPU units to the public pool immediately. Workloads the tenant is already running keep running; the node's owning org terminates them when it needs the capacity.","operationId":"cancelNodeReservation","parameters":[{"$ref":"#/components/parameters/NodeId"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeReservation"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"End a reservation now (platform admin)","tags":["Reservations"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/nodes/{nodeId}/reservations/{id}/reclaim":{"post":{"description":"Returns the named units to exclusive use AND terminates the spot workloads running on them, in one call. Everything evicted was bought at the spot tier, whose terms are exactly that the capacity owner can reclaim without notice. Pass dryRun to see what would be terminated without changing anything. Callable by a platform operator, the org that owns the node, or an org in the reservation's managingOrgIds.","operationId":"reclaimReservationUnits","parameters":[{"$ref":"#/components/parameters/NodeId"},{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReservationReclaimRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeReservation"}}},"description":"OK"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Take released GPU units back off the market, evicting spot workloads","tags":["Reservations"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/nodes/{nodeId}/reservations/{id}/release":{"post":{"description":"The double-sell operation. The named units stay part of the reservation but go on the market: anyone may buy them, at tier=spot and only at tier=spot, which is what makes them terminable by the capacity owner at any time. An ordinary community or verified request will never be placed on them, so released capacity can always be reclaimed. Callable by a platform operator, the org that owns the node, or an org in the reservation's managingOrgIds.","operationId":"releaseReservationUnits","parameters":[{"$ref":"#/components/parameters/NodeId"},{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReservationUnitsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeReservation"}}},"description":"OK"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Release GPU units to the spot market (double sell)","tags":["Reservations"],"x-openrelay-mcp":{"exclude":true}}},"/v1/onboarding/bootstrap":{"post":{"description":"Requires a signed-in dashboard session token; API keys cannot call this endpoint.","operationId":"bootstrap","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BootstrapRequest"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BootstrapResult"}}},"description":"Bootstrapped (or already existed)"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"apiKey":[]}],"summary":"Create the caller's profile and first org (idempotent)","tags":["Account"],"x-openrelay-cli":{"hidden":true},"x-openrelay-mcp":{"exclude":true}}},"/v1/orgs":{"post":{"description":"Requires a signed-in dashboard session token; API keys cannot call this endpoint.","operationId":"createOrg","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrgRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BootstrapResult"}}},"description":"Created (caller is owner)"},"401":{"$ref":"#/components/responses/Unauthorized"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Create an organization (caller becomes owner)","tags":["Organizations"],"x-openrelay-cli":{"hidden":true},"x-openrelay-mcp":{"exclude":true}}},"/v1/orgs/{orgId}":{"delete":{"description":"Owner-only, reversible soft-delete. Rejects with 409 if the org still has active VMs or a non-zero balance.","operationId":"deleteOrg","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Delete (soft-delete) an organization (owner only)","tags":["Organizations"],"x-openrelay-cli":{"hidden":true},"x-openrelay-mcp":{"destructiveHint":true,"exclude":true}},"get":{"operationId":"getOrg","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organization"}}},"description":"The organization"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Get an organization","tags":["Organizations"]},"patch":{"operationId":"updateOrg","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrgRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organization"}}},"description":"Updated"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Update an organization (owner/admin)","tags":["Organizations"]}},"/v1/orgs/{orgId}/api-keys":{"get":{"operationId":"listOrgApiKeys","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ApiKeySummary"},"type":"array"}}},"description":"API keys"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List an org's API keys (no secrets)","tags":["API Keys"]}},"/v1/orgs/{orgId}/api-keys/create":{"post":{"operationId":"createOrgApiKey","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"example":{"name":"ci-deploy","scopes":["clusters:read","clusters:write","vms:read"]},"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyResult"}}},"description":"Created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Create an API key (returns the plaintext once)","tags":["API Keys"]}},"/v1/orgs/{orgId}/api-keys/{id}":{"delete":{"operationId":"revokeOrgApiKey","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"204":{"description":"Revoked"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Revoke an API key","tags":["API Keys"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/orgs/{orgId}/billing/auto-recharge":{"get":{"operationId":"getAutoRecharge","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoRechargeSettings"}}},"description":"Current settings (defaults when never set)"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Read the org's auto-recharge policy","tags":["Billing"]},"put":{"operationId":"updateAutoRecharge","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoRechargeSettings"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoRechargeSettings"}}},"description":"Saved settings"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Set the org's auto-recharge policy","tags":["Billing"]}},"/v1/orgs/{orgId}/billing/balance":{"get":{"operationId":"getBalance","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"example":{"accruedUsageCents":2120,"availableCents":10430,"balanceCents":12550,"currentMonthUsageCents":2120,"monthlySpendLimitCents":null,"transactions":[{"amountCents":10000,"createdAt":"2026-06-28T14:03:22Z","description":"Card deposit","id":"txn_01hzy3","source":"stripe","type":"stripe_deposit"}]},"schema":{"$ref":"#/components/schemas/Balance"}}},"description":"Balance"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Org balance + recent transactions","tags":["Billing"],"x-openrelay-mcp":{"default":true}}},"/v1/orgs/{orgId}/billing/deposit":{"post":{"description":"Charges the org's default saved card off-session. The balance credit is applied by the payment_intent.succeeded webhook (the authoritative money signal), not this response: poll the balance after success.","operationId":"createBillingDeposit","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"example":{"amountCents":5000,"idempotencyKey":"dep-2026-07-02-a1b2c3"},"schema":{"$ref":"#/components/schemas/DepositRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepositResult"}}},"description":"Charge outcome (status drives the dashboard's next step)"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Charge the saved card to top up prepaid balance","tags":["Billing"]}},"/v1/orgs/{orgId}/billing/payment-methods":{"get":{"operationId":"listBillingPaymentMethods","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethods"}}},"description":"Saved cards"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List saved cards (brand/last4/exp only)","tags":["Billing"]}},"/v1/orgs/{orgId}/billing/payment-methods/{paymentMethodId}":{"delete":{"operationId":"deleteBillingPaymentMethod","parameters":[{"$ref":"#/components/parameters/OrgId"},{"in":"path","name":"paymentMethodId","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Removed"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Remove a saved card","tags":["Billing"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/orgs/{orgId}/billing/payment-methods/{paymentMethodId}/default":{"post":{"operationId":"setBillingPaymentMethod","parameters":[{"$ref":"#/components/parameters/OrgId"},{"in":"path","name":"paymentMethodId","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Default updated"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Set a saved card as the default for charges","tags":["Billing"]}},"/v1/orgs/{orgId}/billing/setup-intent":{"post":{"operationId":"createBillingSetupIntent","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetupIntent"}}},"description":"SetupIntent client secret + publishable key"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"$ref":"#/components/responses/Unavailable"}},"security":[{"apiKey":[]}],"summary":"Start saving a card (Stripe SetupIntent for inline Elements)","tags":["Billing"]}},"/v1/orgs/{orgId}/clusters":{"get":{"operationId":"listOrgClusters","parameters":[{"$ref":"#/components/parameters/OrgId"},{"in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"in":"query","name":"cursor","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterPage"}}},"description":"Page of clusters"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List an org's clusters (cursor-paginated)","tags":["Clusters"],"x-openrelay-mcp":{"default":true}}},"/v1/orgs/{orgId}/clusters/create":{"post":{"operationId":"createCluster","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"example":{"containerPort":8000,"gpuModelId":"rtx-4090","gpusPerReplica":1,"healthCheckPath":"/health","imageUrl":"vllm/vllm-openai:latest","name":"vllm-serving","replicaCount":2},"schema":{"$ref":"#/components/schemas/CreateClusterRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterSummary"}}},"description":"Created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/CapacityUnavailable"}},"security":[{"apiKey":[]}],"summary":"Create a cluster","tags":["Clusters"],"x-openrelay-mcp":{"default":true}}},"/v1/orgs/{orgId}/invites":{"get":{"operationId":"listOrgInvites","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Invite"},"type":"array"}}},"description":"Pending invites"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List an org's pending invites (owner/admin)","tags":["Organizations"]}},"/v1/orgs/{orgId}/invites/{email}":{"delete":{"operationId":"revokeOrgInvite","parameters":[{"$ref":"#/components/parameters/OrgId"},{"in":"path","name":"email","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Revoked"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Revoke a pending invite (owner/admin)","tags":["Organizations"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/orgs/{orgId}/members":{"get":{"operationId":"listOrgMembers","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Member"},"type":"array"}}},"description":"Members"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List an org's members","tags":["Organizations"]}},"/v1/orgs/{orgId}/members/add":{"post":{"description":"Records a pending invite and emails the address. The response status is always \"invited\"; the user becomes a member only after they accept (POST /v1/me/invites/{orgId}/accept). Re-inviting the same email refreshes the invite's role and expiry. 409 if the email already belongs to a member.","operationId":"addOrgMember","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMemberRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Member"}}},"description":"Added"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Invite a member by email (owner/admin)","tags":["Organizations"]}},"/v1/orgs/{orgId}/members/{userId}":{"delete":{"operationId":"removeOrgMember","parameters":[{"$ref":"#/components/parameters/OrgId"},{"in":"path","name":"userId","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Removed"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Remove a member (owner/admin)","tags":["Organizations"],"x-openrelay-mcp":{"destructiveHint":true}},"patch":{"operationId":"updateOrgMemberRole","parameters":[{"$ref":"#/components/parameters/OrgId"},{"in":"path","name":"userId","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMemberRoleRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Member"}}},"description":"Updated"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Update a member's role (owner/admin)","tags":["Organizations"]}},"/v1/orgs/{orgId}/provider/apply":{"post":{"operationId":"applyForProvider","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyForProviderRequest"}}},"required":true},"responses":{"204":{"description":"Submitted"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Submit a provider application","tags":["Provider"]}},"/v1/orgs/{orgId}/provider/earnings":{"get":{"operationId":"getProviderEarnings","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderEarnings"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Provider earnings from usage records (+ payout history)","tags":["Provider"]}},"/v1/orgs/{orgId}/provider/nodes":{"get":{"operationId":"listProviderNodes","parameters":[{"$ref":"#/components/parameters/OrgId"},{"in":"query","name":"includeRemoved","required":false,"schema":{"type":"boolean"}},{"description":"Filter to one status (online|offline|maintenance|draining|removed)","in":"query","name":"status","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ProviderNode"},"type":"array"}}},"description":"OK"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List the provider org's nodes (with GPUs + location)","tags":["Provider"]}},"/v1/orgs/{orgId}/provider/nodes/{nodeId}":{"patch":{"operationId":"updateProviderNode","parameters":[{"$ref":"#/components/parameters/OrgId"},{"in":"path","name":"nodeId","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"status":{"description":"Operator-set lifecycle state. draining/maintenance stop new placement (reconcile leaves them alone, capacity excluded); removed decommissions the node (Phase-1 reaper frees its workloads + GPU units); online clears an operator hold (the next reconcile pass re-derives true liveness from Nomad).\n","enum":["online","draining","maintenance","removed"],"type":"string"}},"required":["status"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderNode"}}},"description":"OK"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Set a node's operator lifecycle status (drain / maintenance / remove / online)","tags":["Provider"]}},"/v1/orgs/{orgId}/provider/nodes/{nodeId}/reclaim-spot":{"post":{"description":"Reclaims capacity by terminating the spot workloads on a node your org owns, or on which your org manages a reservation (managingOrgIds). Scoped strictly to workloads bought at the spot tier: a spot instance is sold on the explicit contract that the capacity owner can terminate it at any time. Non-spot workloads on the node are never touched. Every termination is audited and the affected VM records capacity reclaim as its reason, so the customer is not told it was a platform fault.","operationId":"reclaimSpotWorkloads","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/NodeId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReclaimSpotWorkloadsRequest"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReclaimSpotWorkloadsResult"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Terminate spot workloads running on a node you own or manage","tags":["Provider"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/orgs/{orgId}/provider/stats":{"get":{"operationId":"getProviderStats","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderStats"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Provider node + token stats","tags":["Provider"]}},"/v1/orgs/{orgId}/provider/status":{"get":{"operationId":"getProviderStatus","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderStatus"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Provider status (isProvider + application status)","tags":["Provider"]}},"/v1/orgs/{orgId}/provider/tokens":{"get":{"operationId":"listProvisioningTokens","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ProvisioningTokenItem"},"type":"array"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List the org's provisioning tokens (provider only)","tags":["Provider"]},"post":{"operationId":"generateProvisioningToken","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateProvisioningTokenRequest"}}},"required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningTokenCreated"}}},"description":"Created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Generate a provisioning token (owner/admin session, provider org only)","tags":["Provider"]}},"/v1/orgs/{orgId}/provider/tokens/{id}":{"delete":{"operationId":"revokeProvisioningToken","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"204":{"description":"Revoked"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Revoke a provisioning token (owner/admin)","tags":["Provider"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/orgs/{orgId}/registry-credentials":{"get":{"operationId":"listOrgRegistryCredentials","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/RegistryCredentialSummary"},"type":"array"}}},"description":"Registry credentials"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List an org's registry credentials (no secrets)","tags":["Registry Credentials"]}},"/v1/orgs/{orgId}/registry-credentials/create":{"post":{"operationId":"createOrgRegistryCredential","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRegistryCredentialRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegistryCredentialSummary"}}},"description":"Created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid request"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Create a registry credential (credentials stored encrypted)","tags":["Registry Credentials"]}},"/v1/orgs/{orgId}/registry-credentials/{id}":{"delete":{"operationId":"deleteOrgRegistryCredential","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Delete a registry credential","tags":["Registry Credentials"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/orgs/{orgId}/reservations":{"get":{"operationId":"listOrgReservations","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/NodeReservation"},"type":"array"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"What GPU capacity is reserved to this org, and until when","tags":["Reservations"]}},"/v1/orgs/{orgId}/runner-pools":{"get":{"operationId":"listRunnerPools","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/RunnerPoolItem"},"type":"array"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List runner pools (with active/queued usage)","tags":["Runners"]},"post":{"operationId":"createRunnerPool","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRunnerPoolRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunnerPool"}}},"description":"Created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Create a runner pool","tags":["Runners"]}},"/v1/orgs/{orgId}/runner-pools/connect-url":{"get":{"operationId":"getRunnerConnectUrl","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunnerConnectUrl"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"GitHub connect URL (OAuth authorize when configured, else App install)","tags":["Runners"]}},"/v1/orgs/{orgId}/runner-pools/installations":{"post":{"operationId":"listRunnerInstallations","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunnerInstallationsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunnerInstallationsResult"}}},"description":"OK"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List GitHub installations the user can connect (OAuth code exchange)","tags":["Runners"]}},"/v1/orgs/{orgId}/runner-pools/{id}":{"delete":{"operationId":"deleteRunnerPool","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteRunnerPoolResult"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Delete (or drain) a runner pool","tags":["Runners"],"x-openrelay-mcp":{"destructiveHint":true}},"get":{"operationId":"getRunnerPool","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunnerPoolItem"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Get a runner pool (with usage)","tags":["Runners"]}},"/v1/orgs/{orgId}/runner-pools/{id}/jobs":{"get":{"operationId":"listRunnerJobs","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/ResourceId"},{"in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/RunnerJob"},"type":"array"}}},"description":"OK"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"List runner jobs for a pool","tags":["Runners"]}},"/v1/orgs/{orgId}/runner-pools/{id}/metrics":{"get":{"operationId":"getRunnerJobMetrics","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunnerMetrics"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Runner pool job metrics (last 30 days) + cost comparison","tags":["Runners"]}},"/v1/orgs/{orgId}/runner-pools/{id}/resize":{"post":{"operationId":"resizeRunnerPool","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResizeRunnerPoolRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunnerPool"}}},"description":"OK"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Resize a runner pool's RAM tier","tags":["Runners"]}},"/v1/orgs/{orgId}/snapshots":{"get":{"operationId":"listOrgSnapshots","parameters":[{"$ref":"#/components/parameters/OrgId"},{"in":"query","name":"vmId","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Snapshot"},"type":"array"}}},"description":"OK"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List an org's VM snapshots (optionally filtered by source VM)","tags":["Snapshots"]}},"/v1/orgs/{orgId}/ssh-keys":{"get":{"operationId":"listOrgSshKeys","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/SshKeySummary"},"type":"array"}}},"description":"SSH keys"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List an org's SSH public keys","tags":["SSH Keys"]}},"/v1/orgs/{orgId}/ssh-keys/create":{"post":{"operationId":"createOrgSshKey","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"example":{"name":"work-laptop","publicKey":"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB5ro6Vg2mXKC3jExample user@laptop"},"schema":{"$ref":"#/components/schemas/CreateSshKeyRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SshKeySummary"}}},"description":"Added"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid key"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Add an SSH public key (computes fingerprint)","tags":["SSH Keys"]}},"/v1/orgs/{orgId}/ssh-keys/{id}":{"delete":{"description":"Removes the key from the org and scrubs it from every VM it was attached to. Refused with 409 (code KEY_IN_USE) when it is the only key that still grants access to a VM, since deleting it would strand that VM; attach another key to those VMs first. listOrgSshKeys reports this as blockingVms.","operationId":"deleteOrgSshKey","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Delete an SSH public key","tags":["SSH Keys"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/orgs/{orgId}/transfers":{"post":{"operationId":"initiateTransfer","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InitiateTransferRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransferRequest"}}},"description":"Created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Initiate a resource transfer to another org (owner/admin)","tags":["Transfers"]}},"/v1/orgs/{orgId}/transfers/incoming":{"get":{"operationId":"listIncomingTransfers","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TransferItem"},"type":"array"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Pending incoming transfers (enriched)","tags":["Transfers"]}},"/v1/orgs/{orgId}/transfers/outgoing":{"get":{"operationId":"listOutgoingTransfers","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TransferItem"},"type":"array"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Outgoing transfers, all statuses (enriched)","tags":["Transfers"]}},"/v1/orgs/{orgId}/transfers/pending-count":{"get":{"operationId":"getTransferPendingCount","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingCount"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Count of pending incoming transfers","tags":["Transfers"]}},"/v1/orgs/{orgId}/usage/current":{"get":{"operationId":"getCurrentUsage","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentUsage"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Current-month usage (cents/hours + per gpuModel-tier breakdown)","tags":["Usage"],"x-openrelay-mcp":{"default":true}}},"/v1/orgs/{orgId}/usage/daily":{"get":{"description":"Display aggregation for the dashboard spend chart. Buckets the org's usage-record cost (clamped per UTC day) and drained inference debits by calendar day, with per-product segments. Read-only: rides the same pricing iterator as settlement and the deploy gate, never re-prices.\n","operationId":"getDailyUsage","parameters":[{"$ref":"#/components/parameters/OrgId"},{"in":"query","name":"days","required":false,"schema":{"default":7,"maximum":90,"minimum":1,"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DailyUsage"}}},"description":"OK"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Per-day spend over a trailing window (compute + inference segments)","tags":["Usage"]}},"/v1/orgs/{orgId}/vms":{"get":{"operationId":"listOrgVms","parameters":[{"$ref":"#/components/parameters/OrgId"},{"in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"in":"query","name":"cursor","required":false,"schema":{"type":"string"}},{"description":"Return only non-terminated VMs.","in":"query","name":"activeOnly","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmPage"}}},"description":"Page of VMs"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List an org's VMs (cursor-paginated)","tags":["VMs"],"x-openrelay-mcp":{"default":true}}},"/v1/orgs/{orgId}/vms/create":{"post":{"operationId":"createVm","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"example":{"diskSizeGb":100,"envVars":{"HF_TOKEN":"hf_xxxxxxxx"},"gpuCount":1,"gpuModelId":"rtx-4090","imageUrl":"ghcr.io/acme/cuda-devbox:latest","name":"cuda-devbox","sshKeyIds":["key_2f9c1a"]},"schema":{"$ref":"#/components/schemas/CreateVmRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmSummary"}}},"description":"Created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/CapacityUnavailable"}},"security":[{"apiKey":[]}],"summary":"Create a VM","tags":["VMs"],"x-openrelay-mcp":{"default":true}}},"/v1/orgs/{orgId}/vms/fork":{"post":{"operationId":"forkVm","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedResource"}}},"description":"Created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"503":{"$ref":"#/components/responses/CapacityUnavailable"}},"security":[{"apiKey":[]}],"summary":"Fork a new VM from a snapshot","tags":["Snapshots"],"x-openrelay-cli":{"group":"VMs"}}},"/v1/orgs/{orgId}/webhooks":{"get":{"operationId":"listOrgWebhooks","parameters":[{"$ref":"#/components/parameters/OrgId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/WebhookSummary"},"type":"array"}}},"description":"Webhooks"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List an org's webhooks (no secrets)","tags":["Webhooks"]}},"/v1/orgs/{orgId}/webhooks/create":{"post":{"operationId":"createOrgWebhook","parameters":[{"$ref":"#/components/parameters/OrgId"}],"requestBody":{"content":{"application/json":{"example":{"events":["vm.running","vm.stopped","cluster.degraded"],"name":"deploy-events","url":"https://example.com/hooks/openrelay"},"schema":{"$ref":"#/components/schemas/CreateWebhookRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookWithSecret"}}},"description":"Created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Create a webhook (returns the signing secret once)","tags":["Webhooks"]}},"/v1/orgs/{orgId}/webhooks/{id}":{"delete":{"operationId":"deleteOrgWebhook","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Delete a webhook","tags":["Webhooks"],"x-openrelay-mcp":{"destructiveHint":true}},"patch":{"operationId":"updateOrgWebhook","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookUpdateResult"}}},"description":"Updated"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Update a webhook (name/url/events)","tags":["Webhooks"]}},"/v1/orgs/{orgId}/webhooks/{id}/deliveries":{"get":{"operationId":"listOrgWebhookDeliveries","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/ResourceId"},{"in":"query","name":"limit","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/WebhookDeliveryItem"},"type":"array"}}},"description":"Deliveries"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Recent delivery attempts for a webhook","tags":["Webhooks"]}},"/v1/orgs/{orgId}/webhooks/{id}/regenerate-secret":{"post":{"operationId":"regenerateOrgWebhookSecret","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSecret"}}},"description":"Regenerated"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Regenerate a webhook's signing secret (returned once)","tags":["Webhooks"]}},"/v1/orgs/{orgId}/webhooks/{id}/toggle":{"post":{"operationId":"toggleOrgWebhook","parameters":[{"$ref":"#/components/parameters/OrgId"},{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToggleWebhookRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookToggleResult"}}},"description":"Toggled"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Enable/disable a webhook","tags":["Webhooks"]}},"/v1/pods":{"get":{"description":"Compute-fabric pods for the org resolved from the API key. A pod is a managed_by=fabric Vm row (capabilityTier=pod), so each item is a VmSummary and paging mirrors listOrgVms.","operationId":"listPods","parameters":[{"in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"in":"query","name":"cursor","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmPage"}}},"description":"Page of pods"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"List an org's pods (cursor-paginated)","tags":["Pods"],"x-openrelay-mcp":{"exclude":true}},"post":{"description":"Compute-fabric pod (BYO container image + SSH keys): a hardened GPU container on a whole native-driver GPU. Admission claims a free device-request GPU unit (see listPodCapacity for what is claimable), snapshots the on-demand rate immutably onto the pod, and drives the guest workflow.","operationId":"createPod","requestBody":{"content":{"application/json":{"example":{"computeClass":"on_demand","envVars":{"HF_TOKEN":"hf_xxxxxxxx"},"gpuModelId":"rtx-4090","httpPort":8000,"image":"ghcr.io/acme/cuda-devbox:latest","name":"cuda-devbox","sshKeys":["ssh-ed25519 AAAA... user@host"]},"schema":{"$ref":"#/components/schemas/CreatePodRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmSummary"}}},"description":"Created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Create a pod","tags":["Pods"],"x-openrelay-mcp":{"exclude":true}}},"/v1/pods/capacity":{"get":{"description":"What a pod create can actually claim right now, per GPU model: free native (device-request) GPU units on online, non-delisted nodes (the exact filter admission uses), plus the SERVER-computed on-demand hourly rate. Only models with pod units and a price are returned; a create flow should offer exactly these and render these rates verbatim rather than re-deriving prices from the catalog.","operationId":"listPodCapacity","responses":{"200":{"content":{"application/json":{"schema":{"properties":{"items":{"items":{"$ref":"#/components/schemas/PodCapacityItem"},"type":"array"}},"required":["items"],"type":"object"}}},"description":"Capacity per GPU model, cheapest first"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}},"security":[{"apiKey":[]}],"summary":"Live pod GPU capacity and on-demand rates","tags":["Pods"],"x-openrelay-mcp":{"exclude":true}}},"/v1/pricing":{"get":{"operationId":"getPricing","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pricing"}}},"description":"Pricing"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"apiKey":[]}],"summary":"GPU + CPU pricing","tags":["Catalog"],"x-openrelay-mcp":{"default":true}}},"/v1/provider/bootstrap":{"get":{"description":"Returns a self-contained shell installer (curl | sudo bash). With a bearer org API key it mints a fresh provisioning token (24 hours, any number of nodes); with ?token=vtk_… (e.g. minted from the dashboard) it self-auths and embeds that token instead. name only applies when minting (the token already carries it). Nodes always enroll as QEMU VM hosts in the community pool.\n","operationId":"providerBootstrap","parameters":[{"description":"Pre-minted provisioning token (vtk_…); replaces bearer auth","in":"query","name":"token","required":false,"schema":{"type":"string"}},{"description":"Truthy → run the GPU passthrough (IOMMU + vfio-pci) setup. Safe on CPU-only hosts (self-skips when no GPU is present).","in":"query","name":"gpu","required":false,"schema":{"type":"string"}},{"description":"Token label; mint path only","in":"query","name":"name","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"text/x-shellscript":{"schema":{"type":"string"}}},"description":"The installer script (contains a secret provisioning token, never cached)","headers":{"Cache-Control":{"schema":{"type":"string"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing/invalid credentials"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not an approved provider; a bearer principal without write access to the org (an API key needs clusters:write); or a revoked token"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Node artifact unavailable"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Onboarding not configured"}},"security":[{"apiKey":[]},{}],"summary":"One-command node onboarding installer (auth = org API key bearer, OR a pre-minted provisioning token via ?token=)","tags":["Provider"],"x-openrelay-cli":{"hidden":true},"x-openrelay-mcp":{"exclude":true}}},"/v1/snapshots/{id}":{"delete":{"operationId":"deleteSnapshot","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Soft-delete a snapshot","tags":["Snapshots"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/transfers/target-orgs":{"get":{"operationId":"listTransferTargetOrgs","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TargetOrg"},"type":"array"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"apiKey":[]}],"summary":"Orgs the caller belongs to (for the transfer target dropdown)","tags":["Transfers"]}},"/v1/transfers/{id}/accept":{"post":{"operationId":"acceptTransfer","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptTransferRequest"}}},"required":false},"responses":{"204":{"description":"Accepted"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Accept a pending incoming transfer (target org owner/admin)","tags":["Transfers"]}},"/v1/transfers/{id}/cancel":{"post":{"operationId":"cancelTransfer","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"204":{"description":"Cancelled"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Cancel a pending outgoing transfer (source org)","tags":["Transfers"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/transfers/{id}/reject":{"post":{"operationId":"rejectTransfer","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"204":{"description":"Rejected"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Reject a pending incoming transfer (target org owner/admin)","tags":["Transfers"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/vm-templates":{"get":{"operationId":"listVmTemplates","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/VmTemplate"},"type":"array"}}},"description":"Templates"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"apiKey":[]}],"summary":"VM template catalog","tags":["Catalog"],"x-openrelay-mcp":{"default":true}}},"/v1/vms/{id}":{"get":{"operationId":"getVm","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmSummary"}}},"description":"The VM"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Get a VM by id","tags":["VMs"]}},"/v1/vms/{id}/burn":{"get":{"description":"Every VM bills at one bundled hourly rate (per-GPU catalog price times GPU count for GPU VMs, a flat size rate for CPU VMs); disk is included, never billed separately. The rate is locked when the usage session opens, so this endpoint reads it from the open usage record when one exists (rateSource=locked) and only falls back to the current catalog when nothing is accruing (rateSource=catalog).\n","operationId":"getVmBurn","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"example":{"accruedSessionCents":1028,"autoStopFloorCents":50,"diskBilled":false,"gpuCount":3,"orgAccruedCents":2120,"orgAccruedMonthCents":2120,"orgAvailableCents":10430,"orgBurnPerHourCents":414,"perGpuCents":137,"pricePerHourCents":411,"projectedRunwayHours":25.07,"rateBasis":"gpu","rateSource":"locked","sessionStartedAt":"2026-07-18T09:12:00Z","vmId":"4f6b9a1e-9f1c-4c58-a6a3-1f2f0f6f7a10"},"schema":{"$ref":"#/components/schemas/VmBurn"}}},"description":"The VM's burn breakdown"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Live burn rate, session cost so far, and org runway for a VM","tags":["VMs"],"x-openrelay-mcp":{"default":true}}},"/v1/vms/{id}/detail":{"get":{"operationId":"getVmDetail","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmDetail"}}},"description":"The VM detail"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Get a VM with its GPU/node/template info and price","tags":["VMs"],"x-openrelay-mcp":{"default":true}}},"/v1/vms/{id}/reboot":{"post":{"description":"Beta: the reboot request is validated and accepted, but the in-guest reboot is not yet executed by the data plane.","operationId":"rebootVm","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"204":{"description":"Rebooted"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Reboot a VM","tags":["VMs"]}},"/v1/vms/{id}/resize-disk":{"post":{"description":"Beta: the new disk size is validated and recorded, but the disk is not yet resized on the node.","operationId":"resizeVmDisk","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResizeDiskRequest"}}},"required":true},"responses":{"204":{"description":"Resized"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Resize a stopped VM's disk","tags":["VMs"]}},"/v1/vms/{id}/restart":{"post":{"operationId":"restartVm","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmSummary"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Restart a VM","tags":["VMs"]}},"/v1/vms/{id}/snapshots":{"post":{"description":"Beta: the snapshot record is created, but snapshot capture is not yet enabled on the data plane.","operationId":"createVmSnapshot","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSnapshotRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedResource"}}},"description":"Created"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Create a snapshot of a VM","tags":["Snapshots"],"x-openrelay-cli":{"group":"VMs"}}},"/v1/vms/{id}/ssh-keys":{"get":{"operationId":"getVmSshKeys","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/VmSshKeyItem"},"type":"array"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"SSH keys attached to a VM + the org's keys","tags":["VMs"]},"post":{"operationId":"attachVmSshKey","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AttachSshKeyRequest"}}},"required":true},"responses":{"204":{"description":"Attached"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Attach an org SSH key to a VM","tags":["VMs"]}},"/v1/vms/{id}/ssh-keys/{keyId}":{"delete":{"description":"Removes the key from the VM's allowed set. Detaching a key that is not attached is a no-op (204). The last attached key cannot be detached: an empty set would widen access to every org key (the org-wide fallback), so that returns 400.","operationId":"detachVmSshKey","parameters":[{"$ref":"#/components/parameters/ResourceId"},{"in":"path","name":"keyId","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Detached"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Detach an org SSH key from a VM","tags":["VMs"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/vms/{id}/stop":{"post":{"operationId":"stopVm","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmSummary"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Stop a VM","tags":["VMs"],"x-openrelay-mcp":{"default":true}}},"/v1/vms/{id}/telegram-link":{"get":{"operationId":"getVmTelegramLink","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TelegramLinkResult"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Get a VM's embedded Telegram link","tags":["VMs"]}},"/v1/vms/{id}/terminate":{"post":{"operationId":"terminateVm","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmSummary"}}},"description":"OK"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}},"security":[{"apiKey":[]}],"summary":"Terminate a VM","tags":["VMs"],"x-openrelay-mcp":{"destructiveHint":true}}},"/v1/vms/{id}/visibility":{"post":{"operationId":"setVmVisibility","parameters":[{"$ref":"#/components/parameters/ResourceId"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetVmVisibilityRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VmSummary"}}},"description":"OK"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}},"security":[{"apiKey":[]}],"summary":"Set a VM's endpoint visibility","tags":["VMs"],"x-openrelay-mcp":{"default":true}}},"/v1/whoami":{"get":{"description":"Returns the calling principal (API key or user session) and the organization the request acts in. For an API key that org is fixed by the key; for a session it is the active org, and availableOrgs lists every org the user belongs to. Unlike /v1/me this works for API keys and does not onboard.","operationId":"whoami","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Whoami"}}},"description":"The caller's principal + acting org"},"401":{"$ref":"#/components/responses/Unauthorized"}},"security":[{"apiKey":[]}],"summary":"The current principal and the org it acts in","tags":["Account"],"x-openrelay-mcp":{"default":true}}}},"servers":[{"description":"Production","url":"https://api.openrelay.inc"},{"description":"Beta","url":"https://api.beta.openrelay.inc"},{"description":"Local development","url":"http://localhost:8083"}],"tags":[{"description":"The signed-in user: profile, memberships, and first-time onboarding.","name":"Account"},{"description":"Organizations and their members. Most resources are scoped to an org.","name":"Organizations"},{"description":"Autoscaling inference clusters that serve a container image behind an endpoint.","name":"Clusters"},{"description":"GPU virtual machines: lifecycle, disks, SSH access, and console links.","name":"VMs"},{"description":"GPU pods: bring-your-own-image containers on a whole GPU, with SSH access and an HTTP endpoint.","name":"Pods"},{"description":"Point-in-time VM snapshots and forking new VMs from them.","name":"Snapshots"},{"description":"Org-level SSH public keys that can be attached to VMs.","name":"SSH Keys"},{"description":"Programmatic access keys (or_…; legacy vl_… still valid) used to authenticate with this API.","name":"API Keys"},{"description":"Private container-registry credentials for pulling images.","name":"Registry Credentials"},{"description":"Subscribe to platform events with signed HTTP callbacks.","name":"Webhooks","x-openrelay-docs":{"overview":"webhooks"}},{"description":"Prepaid balance, saved cards, deposits, and auto-recharge.","name":"Billing"},{"description":"Current-period usage and cost breakdowns.","name":"Usage"},{"description":"Move resources between organizations you belong to.","name":"Transfers"},{"description":"For GPU providers: applications, nodes, provisioning tokens, and earnings.","name":"Provider"},{"description":"Long-term reservations of specific GPU units on a node for a set of organizations. Reserved capacity is exclusive to its audience and hidden from everyone else.\n","name":"Reservations"},{"description":"Managed GitHub Actions runner pools.","name":"Runners"},{"description":"Public catalog: GPU models, availability, pricing, templates, and locations.","name":"Catalog"},{"description":"Batch inference jobs: submit a JSONL file of requests, poll status, and download results at a discounted rate. Served by the inference endpoint (inference.openrelay.inc). Batch access is enabled per organization; without it these endpoints return 404.\n","name":"Batches"},{"description":"Input and result files for the Batch API (JSONL, up to 200 MB and 50,000 records). Served by the inference endpoint (inference.openrelay.inc).\n","name":"Files"},{"description":"Machine-to-machine endpoints used by platform infrastructure. Not part of the public API.","name":"Internal","x-internal":true}]}