{"openapi":"3.1.0","info":{"title":"Cloud World Model — Agent API","version":"1.0.0","description":"Agent-facing discovery API for Cloud World Model: 54 operations across 50 paths, including 48 metered operations with x402 prices and six operations without a per-call x402 price (wallet challenge and verification, scenario listing, x402 configuration, analysis-job lookup, and AI-job cancellation). Some read and status operations are metered; do not assume all reads are free. Check each operation's x402 price or GET /billing/x402/config for current prices. Anonymous callers can pay for metered requests with USDC on Base via `X-PAYMENT`, or optional Solana USDC via `PAYMENT-SIGNATURE` when advertised by the config endpoint — no account or API key is required for x402 payment.","contact":{"name":"Cloud World Model API Support","url":"https://www.cloudworldmodel.ai","email":"api@cloudworldmodel.ai"},"license":{"name":"MIT","url":"https://opensource.org/licenses/MIT"}},"servers":[{"url":"https://www.cloudworldmodel.ai/api","description":"Production"}],"paths":{"/wallet-auth/challenge":{"x-stability":"stable","post":{"tags":["Wallet Auth"],"summary":"Generate an EIP-191 sign-in challenge","description":"Issues a one-time nonce for the given EVM wallet address. The caller must\nsign the returned `message` string with `personal_sign` (EIP-191) and\nsubmit the hex signature to `POST /wallet-auth/verify` within 5 minutes.\n\nRate-limited to 10 requests per IP per minute to prevent nonce-farming.\nNo authentication required.\n","operationId":"walletAuthChallenge","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["walletAddress"],"properties":{"walletAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"The EVM wallet address requesting a sign-in challenge","example":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"}}}}}},"responses":{"200":{"description":"Challenge nonce issued","content":{"application/json":{"schema":{"type":"object","required":["nonce","message","expiresAt"],"properties":{"nonce":{"type":"string","description":"The random hex nonce (32 hex chars)","example":"a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"},"message":{"type":"string","description":"The full message string to sign with personal_sign","example":"Sign in to Cloud World Model\nNonce: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6\nExpires: 2026-01-01T00:05:00.000Z"},"expiresAt":{"type":"string","format":"date-time","description":"ISO-8601 timestamp when the nonce expires (5 minutes)"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"429":{"description":"Rate limit exceeded — 10 challenges per IP per minute","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/wallet-auth/verify":{"x-stability":"stable","post":{"tags":["Wallet Auth"],"summary":"Generate a session token from an EIP-191 signature","description":"Verifies the EIP-191 personal_sign signature for the challenge previously\nissued by `POST /wallet-auth/challenge`. On success, returns a 24-hour\nJWT session token.\n\nUse the token as `Authorization: Bearer <token>` on `GET /simulations`\nand `GET /simulations/{simulationId}` to list and retrieve simulations\nclaimed by this wallet address without a traditional API key.\n\nThe nonce is single-use and consumed on successful verification.\nNo authentication required.\n","operationId":"walletAuthVerify","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["walletAddress","signature"],"properties":{"walletAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","description":"The EVM wallet address that signed the challenge","example":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"},"signature":{"type":"string","description":"Hex-encoded EIP-191 signature from personal_sign (0x-prefixed, 65 bytes)","example":"0x1234...ef01b"}}}}}},"responses":{"200":{"description":"Signature verified — session token issued","content":{"application/json":{"schema":{"type":"object","required":["token","walletAddress","expiresIn"],"properties":{"token":{"type":"string","description":"JWT session token (HS256, 24-hour TTL)"},"walletAddress":{"type":"string","description":"The lowercase verified wallet address","example":"0xd8da6bf26964af9d7eed9e03e53415d37aa96045"},"expiresIn":{"type":"integer","description":"Token lifetime in seconds (86400 = 24 hours)","example":86400}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"description":"Invalid or expired signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/wallet-auth/x402-session":{"x-stability":"stable","post":{"tags":["Wallet Auth"],"summary":"Generate a wallet session JWT via x402 micro-payment","description":"Issues a 24-hour wallet session JWT in exchange for a small USDC\nmicro-payment on Base (x402 protocol). Designed for managed/MPC\nwallets that cannot sign EIP-191 challenges directly.\n\n**Flow:**\n1. Call this endpoint with no `X-PAYMENT` header — the server returns\n `402 Payment Required` with the USDC amount and payment details.\n2. Submit the x402 payment and resend the request with the\n `X-PAYMENT` header populated.\n3. On success the response body includes both a `settlement` block\n (confirmed transaction hash) and a `walletSession` block\n containing the JWT token and its expiry.\n\nThe issuance is idempotent per transaction hash — replaying the same\ntransaction hash returns the same session: the returned token carries\nthe same `jti` (session nonce) as the original issuance. No new\nsession record is created. The token's `iat`/`exp` timestamps differ\nbetween calls (each JWT is freshly signed), but the session identity\nbound by `jti` is stable across all replays of the same hash.\n\n**Token lifetime and persistence:** Tokens are valid for 24 hours and\nreusable across requests until they expire. Token validity **survives\nserver restarts only when the `WALLET_AUTH_JWT_SECRET` environment\nvariable is set to a stable, persistent value**. Without it, an\nephemeral per-process secret is used and all sessions are invalidated\non every redeploy. The server logs a startup warning when this variable\nis absent.\n\n**Authentication:** x402 micro-payment only (no API key required).\n","operationId":"walletAuthX402Session","x-x402-call-type":"wallet_session","x-x402-price-usdc":"$0.0030","security":[{"x402":[]},{"x402Solana":[]}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":false}}}},"responses":{"200":{"description":"Session JWT issued alongside x402 settlement","content":{"application/json":{"schema":{"type":"object","properties":{"settlement":{"type":"object","description":"x402 on-chain settlement details","properties":{"status":{"type":"string","example":"confirmed"},"transactionHash":{"type":"string","example":"0xabc123..."}}},"walletSession":{"type":"object","description":"Issued wallet session JWT (24-hour TTL). Tokens are reusable\nacross requests until expiry. Token validity survives server\nrestarts only when the `WALLET_AUTH_JWT_SECRET` environment\nvariable is set to a stable, persistent value — without it\nsessions are invalidated on every redeploy.\n","properties":{"token":{"type":"string","description":"JWT session token (HS256, 24-hour TTL, reusable until expiry)"},"expiresAt":{"type":"string","format":"date-time","description":"ISO-8601 expiry timestamp"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/scenarios":{"x-stability":"stable","get":{"tags":["Discover"],"summary":"Search available simulation templates","description":"Returns the list of pre-built infrastructure scenario templates. Scenarios\ndefine a complete simulation configuration (resources, traffic, connections)\nthat can be loaded directly into a new simulation via the browser UI or the\nAPI.\n\nOptional filters narrow the catalog by provider, category, and difficulty.\nOmit all filters to receive the complete, unfiltered scenario catalog.\nCategory matching is case-insensitive.\n\n**No authentication required.**\n","operationId":"listScenarios","security":[],"parameters":[{"name":"provider","in":"query","required":false,"description":"Return scenarios containing resources from this cloud provider.","schema":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"]},"example":"aws"},{"name":"category","in":"query","required":false,"description":"Return scenarios in this category. Matching is case-insensitive.\nSupported catalog categories are cost, failure, networking, reliability,\nand scaling.\n","schema":{"type":"string","enum":["cost","failure","networking","reliability","scaling"]},"example":"reliability"},{"name":"difficulty","in":"query","required":false,"description":"Return scenarios at this difficulty level.","schema":{"type":"string","enum":["beginner","intermediate","advanced"]},"example":"intermediate"}],"responses":{"200":{"description":"Array of scenario templates","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Scenario"}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/billing/x402/config":{"x-stability":"stable","get":{"tags":["Discover"],"summary":"Fetch x402 pricing and payment configuration","description":"Returns the x402 payment configuration for this server, including canonical\n`paymentOptions`, the legacy Base-primary fields, facilitator URL, and per-call-type\nprice table. Base USDC is the primary option. Solana USDC-SPL is an additional option\nonly when it appears in `paymentOptions`; it does not replace the Base option.\n\nAI agents can use this endpoint to discover whether x402 anonymous pay-per-request is\nenabled, choose an advertised network, and obtain the exact USDC amounts required before\nsending the option's required payment header (`X-PAYMENT` for Base or\n`PAYMENT-SIGNATURE` for optional Solana) to a metered endpoint.\n\nThis endpoint requires no authentication and is always public.\n","operationId":"getX402Config","security":[],"responses":{"200":{"description":"x402 payment configuration","content":{"application/json":{"schema":{"type":"object","required":["enabled","network","asset","payTo","paymentOptions","facilitatorUrl","creditsPerUsdc","callPrices"],"properties":{"enabled":{"type":"boolean","description":"Whether x402 pay-per-request is active on this server.","example":true},"network":{"type":"string","description":"Blockchain network where payments are accepted.","example":"base"},"asset":{"type":"string","description":"USDC contract address on the configured network.","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"},"payTo":{"type":"string","nullable":true,"description":"EVM wallet address that receives payments (null when disabled).","example":"0xYourWalletAddress"},"paymentOptions":{"type":"array","description":"Canonical list of payment methods that can satisfy a live 402 challenge.\nBase is listed first when x402 is enabled. Solana is listed only when its\nrecipient wallet is configured, as an additional option rather than a\nreplacement for Base.\n","items":{"type":"object","required":["network","asset","payTo","header"],"properties":{"network":{"type":"string","description":"CAIP-2 network identifier, matching a 402 `accepts[].network` value.","example":"eip155:8453"},"asset":{"type":"string","description":"USDC token contract or mint address on `network`.","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"},"payTo":{"type":"string","description":"Recipient wallet address on `network`, matching a 402 `accepts[].payTo` value.","example":"0xYourWalletAddress"},"header":{"type":"string","enum":["X-PAYMENT","PAYMENT-SIGNATURE"],"description":"Required request header containing the signed x402 payment payload.","example":"X-PAYMENT"},"feePayer":{"type":"string","description":"Solana-only PayAI wallet that sponsors the transaction fee; absent for Base.","example":"2wKupLR9q6wXYppw8Gr2NvWxKBUqm4PPJKkQfoxHDBg4"}}},"example":[{"network":"eip155:8453","asset":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","payTo":"0xYourWalletAddress","header":"X-PAYMENT"},{"network":"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp","asset":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","payTo":"YourSolanaWalletAddress","header":"PAYMENT-SIGNATURE","feePayer":"2wKupLR9q6wXYppw8Gr2NvWxKBUqm4PPJKkQfoxHDBg4"}]},"solana":{"type":"object","nullable":true,"description":"Legacy optional Solana payment details. Prefer `paymentOptions` for new integrations.","required":["network","asset","payTo"],"properties":{"network":{"type":"string","example":"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"},"asset":{"type":"string","example":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"},"payTo":{"type":"string","example":"YourSolanaWalletAddress"}}},"facilitatorUrl":{"type":"string","description":"Base URL of the x402 facilitator used to verify payments.","example":"https://facilitator.payai.network"},"creditsPerUsdc":{"type":"integer","description":"How many platform credits equal 1 USDC (determines price per credit).","example":1000},"callPrices":{"type":"object","description":"Per-call-type price table.","additionalProperties":{"type":"object","required":["credits","usdcAtomicUnits","usdcDisplay"],"properties":{"credits":{"type":"integer","description":"Number of platform credits consumed per call."},"usdcAtomicUnits":{"type":"string","description":"Payment amount in USDC atomic units (6-decimal integer string)."},"usdcDisplay":{"type":"string","description":"Human-readable USDC cost string, e.g. \"$0.0050\"."}}},"example":{"chaos.run":{"credits":5,"usdcAtomicUnits":"5000","usdcDisplay":"$0.0050"},"rl.step":{"credits":3,"usdcAtomicUnits":"3000","usdcDisplay":"$0.0030"}}},"simulationRetentionDays":{"type":"integer","description":"Number of days an x402-wallet-owned simulation is retained after its\nlast activity (step, update). Every `/step-hybrid` call on a simulation\nresets this rolling window. After the window expires, the simulation and\nall its metrics are permanently deleted by the server's cleanup sweep.\n","example":90},"autoClaimOnFirstStep":{"type":"boolean","description":"When `true`, an unclaimed (demo) simulation is automatically assigned to\nthe paying wallet's ownership on its first x402-authenticated\n`/step-hybrid` call. No explicit claim endpoint is required.\n","example":true}}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations":{"x-stability":"stable","post":{"tags":["Create"],"summary":"Create a cloud simulation","description":"Creates a simulation with cloud resources (compute, network, database, storage).\nThe simulation serves as the environment for RL training.\n\n**Ownership:** If an `Authorization: Bearer <key>` header is provided, the\nnew simulation is immediately assigned to that API key and is fully write-accessible\nwithout any additional steps. Without authentication the simulation is created\nas a \"demo\" simulation (`apiKeyId: null`), subject to a 20-step limit; use\n`POST /simulations/{simulationId}/claim` to adopt it once you have a key.\n","operationId":"createSimulation","security":[],"x-x402-call-type":"simulation_create","x-x402-price-usdc":"$0.0030","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"oneOf":[{"required":["resources"],"not":{"anyOf":[{"required":["scenarioId"]},{"required":["connections"]}]}},{"required":["scenarioId"],"not":{"anyOf":[{"required":["resources"]},{"required":["connections"]}]}},{"required":["benchmarkCalibrationId"]},{"required":["sharedScenarioToken"]}],"properties":{"name":{"type":"string","description":"Human-readable name for the simulation","example":"Production Web App Autoscaling"},"appWeight":{"$ref":"#/components/schemas/AppWeight"},"costAssumptions":{"$ref":"#/components/schemas/SimulationCostAssumptions"},"scenarioId":{"type":"string","description":"Live scenario identifier from the scenario catalog; mutually exclusive with resources and connections","example":"web-autoscaling"},"benchmarkCalibrationId":{"type":"string","format":"uuid","description":"Owned benchmark calibration profile to apply to this simulation. Requires a write-scoped API key that owns the calibration."},"sharedScenarioToken":{"type":"string","description":"Public share token. Creates a separate variation simulation; the original shared scenario remains read-only."},"benchmarkVariation":{"type":"object","description":"Optional visitor-controlled variation settings; assumptions are never required to be reviewed before running.","properties":{"costAssumptions":{"$ref":"#/components/schemas/SimulationCostAssumptionsOverride"},"trafficRps":{"type":"number","minimum":0},"replicasByResource":{"type":"object","additionalProperties":{"type":"integer","minimum":1,"maximum":1000}},"podLossResourceIds":{"type":"array","items":{"type":"string"}},"autoscalingTargetCpuPercent":{"type":"number","minimum":1,"maximum":100},"cpuMultiplier":{"type":"number","minimum":0.1,"maximum":3},"memoryMultiplier":{"type":"number","minimum":0.1,"maximum":3}}},"scenarioOverrides":{"type":"object","description":"Scenario-only customization. Requires scenarioId and must not\nbe combined with resources or connections. Select exactly one\noverride supported by the selected catalog scenario.\n","additionalProperties":false,"oneOf":[{"required":["awsMultiRegionFailover"]},{"required":["sqsQueueBacklogSaturation"]},{"required":["eksSpotInterruption"]},{"required":["webAutoscaling"]},{"required":["dbFailover"]},{"required":["redisCacheCrashRecovery"]}],"properties":{"awsMultiRegionFailover":{"type":"object","additionalProperties":false,"description":"AWS Multi-Region Failover only. Choose before the first\nsimulation step. scenario.get returns route53Failover\nconfiguration: west fails at evaluation currentTime 5\nafter creation warm-up, stays down through 59 and returns\nat 60. Each step models 10 seconds; probes run every 10\nseconds with three failures/recoveries, independently\nfrom the 60-second DNS resolver TTL. Load ramps to 2600\nRPS at step 40 and holds until optional recovery.\n","properties":{"includeRecoveryRamp":{"type":"boolean","default":true,"description":"Defaults true when this override is supplied; without an override the catalog recovery ramp remains inactive."},"recoveryRampStartStep":{"type":"integer","minimum":40,"maximum":100000,"description":"Absolute simulation step, not seconds or post-warm-up offset. Omit for catalog step 60."},"dnsTtlSeconds":{"type":"number","exclusiveMinimum":0,"maximum":86400,"description":"Resolver cache TTL in simulated seconds; omit for catalog 60 seconds."}}},"sqsQueueBacklogSaturation":{"type":"object","additionalProperties":false,"properties":{"includeEventBurst":{"type":"boolean","default":true,"description":"Enable the catalog's 20-second 5,000 RPS SQS event burst, scheduled after creation warm-up.","example":true},"eventBurstStartStep":{"type":"integer","minimum":6,"maximum":100000,"description":"Absolute simulation step at which the optional burst starts, after the five-step creation warm-up. Omit to use catalog default step 6.","example":10}}},"eksSpotInterruption":{"type":"object","additionalProperties":false,"required":["startupSeconds"],"properties":{"startupSeconds":{"type":"integer","minimum":0,"maximum":3600,"description":"Catalog EKS Spot startup duration in seconds","example":60}}},"webAutoscaling":{"type":"object","additionalProperties":false,"required":["includeTrafficRecovery"],"properties":{"includeTrafficRecovery":{"type":"boolean","description":"Include the catalog Web App Autoscaling Traffic Recovery phase in the replay input before the first step.","example":true}}},"dbFailover":{"type":"object","additionalProperties":false,"description":"Database Failover scenario-only optional recovery ramp; choose before the first simulation step.","properties":{"includeTrafficRecovery":{"type":"boolean","description":"Activate the optional Traffic Recovery ramp after the five-step creation warm-up. Defaults to false.","default":false,"example":true}}},"redisCacheCrashRecovery":{"type":"object","additionalProperties":false,"required":["cacheableRequestShare","cacheHitRate"],"properties":{"cacheableRequestShare":{"type":"number","minimum":0,"maximum":1,"description":"Fraction of offered requests sent to Redis; per-simulation override (scenario default 0.8).","example":0.8},"cacheHitRate":{"type":"number","minimum":0,"maximum":1,"description":"Configured hit fraction among requests sent to Redis; per-simulation override (scenario default 0.8).","example":0.8}}}}},"description":{"type":"string","description":"Optional description of the simulation","example":"Multi-tier web application with load balancer and database"},"resources":{"type":"array","description":"Cloud resources in the simulation; mutually exclusive with scenarioId","items":{"$ref":"#/components/schemas/Resource"}},"connections":{"type":"array","description":"Network connections between resources","default":[],"items":{"$ref":"#/components/schemas/Connection"}},"traffic":{"type":"number","description":"Initial traffic load (requests per second). For the Redis\nCache Crash & Recovery scenario, omitted traffic starts at\n5,000 RPS; an explicit value is honored. That scenario has no\nactive traffic pattern, so use simulation.inject_traffic to\nchange load after stepping without changing finalized replay\nidentity. For SQS Queue Backlog Saturation, use\nscenarioOverrides.sqsQueueBacklogSaturation.includeEventBurst\nto enable the warmup-shifted finite burst at creation; an\nexplicit PATCH traffic update changes runtime load after the\nfirst step without rewriting the finalized replay inputs.\n","default":1000,"example":5000},"maxInstances":{"type":"integer","minimum":1,"description":"Optional ceiling on the autoscaled compute fleet size, stored as\n`autoscalingConfig.maxInstances` and enforced by the engine's\nautoscale cap logic. If omitted, the provider default applies\n(AWS 50, GCP 15, Azure/OCI/DigitalOcean 10) — which may be much\nlarger than your intended fleet size.\n","example":6},"minInstances":{"type":"integer","minimum":1,"description":"Optional floor on the autoscaled compute fleet size, stored as\n`autoscalingConfig.minInstances`.\n","example":2},"autoscalingTargetCpu":{"type":"number","minimum":0,"maximum":100,"description":"Canonical CPU HPA scale-out target. This targeted override wins over the provider default while CWM synthesizes all unrelated autoscaling defaults. If more than one supported target name is sent, every value must agree. Simulation-wide by default: sent at this top level, the engine applies one CPU threshold identically to every resource's scale decision. To override the threshold for ONE resource only, nest the same canonical name (or one of its three aliases) under that resource's `characteristics` as `scaleOutCpuThreshold` instead — every other resource keeps using the simulation-wide default. A misplaced or misnamed CPU-target field name (e.g. `autoscalingTargetCpuPercent`, `targetCPUUtilizationPercentage`) is rejected with 400 at either level, never silently dropped and defaulted.\n","example":70},"ecsCpuTargetTracking":{"type":"boolean","description":"Opt in to ECS Fargate CPU-only target tracking. Requires an AWS Fargate compute resource. Uses autoscalingTargetCpu as the target; latency and throughput do not trigger ECS scaling. The effective policy is persisted on resource.characteristics.applicationAutoscalingPolicy.\n"},"scaleOutCooldownSeconds":{"type":"integer","minimum":0,"maximum":86400,"description":"ECS CPU target-tracking scale-out cooldown in simulated seconds; requires ecsCpuTargetTracking."},"scaleInCooldownSeconds":{"type":"integer","minimum":0,"maximum":86400,"description":"ECS CPU target-tracking scale-in cooldown in simulated seconds; requires ecsCpuTargetTracking."},"simulationSecondsPerStep":{"type":"number","exclusiveMinimum":0,"maximum":60,"description":"Simulated seconds per step for ECS cooldowns (default 1); requires ecsCpuTargetTracking."},"scaleOutCpuThreshold":{"type":"number","minimum":0,"maximum":100,"description":"Equivalent alias for `autoscalingTargetCpu`.","example":70},"scaleOutCpuPercent":{"type":"number","minimum":0,"maximum":100,"description":"Grok-compatible alias for `autoscalingTargetCpu`.","example":70},"autoscaleTargetCpuPercent":{"type":"number","minimum":0,"maximum":100,"description":"Grok-compatible alias for `autoscalingTargetCpu`.","example":70},"resilienceConfig":{"allOf":[{"$ref":"#/components/schemas/ResilienceConfig"}],"description":"Optional retry/cascade resilience model. When present, the step engine\nmodels retry amplification, circuit-breaker state, rate limiting, and\ncascading-failure depth across declared `dependencies`. Set an edge's\ntimeout and retry count at\n`resilienceConfig.dependencies[].retryPolicy.timeoutMs` and\n`resilienceConfig.dependencies[].retryPolicy.maxRetries`. The same\npolicy accepts backoffMs, backoffMultiplier, jitterRatio,\nretryBudgetRatio, retryBudgetRps, and retryActorId. Omitted values\nretain defaults (timeoutMs 2000, maxRetries 2). Omit resilienceConfig\nto leave the resilience model disabled (byte-identical to existing behavior).\n","example":{"enabled":true,"dependencies":[{"id":"web-to-db","sourceId":"web-1","targetId":"db-1","retryPolicy":{"timeoutMs":10000,"maxRetries":0}}]}}}},"examples":{"awsWebApp":{"summary":"AWS multi-tier web application","value":{"name":"AWS Production Web App","description":"Multi-tier web application on AWS with load balancer and RDS","traffic":5000,"resources":[{"id":"r1","name":"ALB","type":"network","provider":"aws","serviceFamily":"elb","region":"us-east-1","config":{"tier":"standard","targetCapacity":10000}},{"id":"r2","name":"App Server","type":"compute","provider":"aws","serviceFamily":"ec2","region":"us-east-1","config":{"instanceType":"t3.large","instances":4,"autoScaling":true,"minInstances":2,"maxInstances":12}},{"id":"r3","name":"Primary DB","type":"database","provider":"aws","serviceFamily":"rds","region":"us-east-1","config":{"instanceType":"db.r5.large","multiAZ":true}},{"id":"r4","name":"Static Assets","type":"storage","provider":"aws","serviceFamily":"s3","region":"us-east-1","config":{"storageGB":500}}]}},"digitalOceanWebApp":{"summary":"DigitalOcean web application (Droplets + Managed PostgreSQL)","value":{"name":"DO Production Web App","description":"Multi-tier web application on DigitalOcean with Droplets, Managed PostgreSQL, Spaces, and Load Balancer","traffic":3000,"resources":[{"id":"r1","name":"DO Load Balancer","type":"network","provider":"digitalocean","serviceFamily":"load_balancer","region":"nyc3","config":{"tier":"standard","targetCapacity":5000}},{"id":"r2","name":"App Droplets","type":"compute","provider":"digitalocean","serviceFamily":"droplets","region":"nyc3","config":{"instanceType":"s-4vcpu-8gb","instances":3,"autoScaling":true,"minInstances":2,"maxInstances":8}},{"id":"r3","name":"Managed PostgreSQL","type":"database","provider":"digitalocean","serviceFamily":"managed_postgresql","region":"nyc3","config":{"instanceType":"db-s-2vcpu-4gb","multiAZ":true}},{"id":"r4","name":"Spaces Object Storage","type":"storage","provider":"digitalocean","serviceFamily":"spaces","region":"nyc3","config":{"storageGB":500}}]}},"digitalOceanTrafficSpike":{"summary":"DigitalOcean traffic spike — triggers DOKS HPA Droplet scale-out","value":{"name":"DO Traffic Spike Autoscaling","description":"Demonstrates DigitalOcean DOKS Horizontal Pod Autoscaler (HPA) scaling\nDroplets during a sudden traffic surge. Starting traffic (80 000 RPS) is\ndeliberately above the capacity of a single s-2vcpu-4gb Droplet to\nguarantee that CPU breaches the 75 % HPA threshold and triggers automatic\nDroplet provisioning within one simulation step (150 s cooldown window).\nRun the simulation and observe DOKS HPA scale-out events in the event log.\n","traffic":80000,"resources":[{"id":"r1","name":"DO Load Balancer","type":"network","provider":"digitalocean","serviceFamily":"load_balancer","region":"nyc3","config":{"tier":"standard","targetCapacity":100000}},{"id":"r2","name":"droplet-01","type":"compute","provider":"digitalocean","serviceFamily":"droplets","region":"nyc3","config":{"instanceType":"s-2vcpu-4gb","instances":1,"autoScaling":true,"minInstances":1,"maxInstances":10}},{"id":"r3","name":"Managed PostgreSQL","type":"database","provider":"digitalocean","serviceFamily":"managed_postgresql","region":"nyc3","config":{"instanceType":"db-s-1vcpu-1gb","multiAZ":false}}]}}}}}},"responses":{"201":{"description":"Simulation created successfully","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Simulation"},{"type":"object","properties":{"effectiveMaxInstances":{"type":"integer","description":"The autoscaled fleet-size ceiling the engine will enforce —\n`autoscalingConfig.maxInstances` when set, otherwise the\nprimary provider's default (AWS 50, GCP 15,\nAzure/OCI/DigitalOcean 10).\n"},"effectiveMinInstances":{"type":"integer","description":"The autoscaled fleet-size floor the engine will enforce —\n`autoscalingConfig.minInstances` when set, otherwise the\nprimary provider's default.\n"}}}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"500":{"$ref":"#/components/responses/InternalError"}}},"get":{"tags":["Simulations"],"summary":"List simulations for the authenticated caller","description":"Returns simulations owned by the authenticated caller.\n\n**API key callers:** Returns all simulations assigned to the API key.\nAdmin-scoped keys receive all simulations across all keys.\n\n**Wallet session callers:** Returns simulations claimed by the wallet\naddress (where `ownerWallet` matches), paginated via `limit` and `offset`\nquery parameters and sorted by `updatedAt` descending. The response shape\ndiffers — it is an object `{ simulations, total, limit, offset }` rather\nthan a plain array.\n\nRequires `read` scope or a valid wallet session JWT.\n","operationId":"listSimulations","security":[],"x-x402-call-type":"simulation_list","x-x402-price-usdc":"$0.0030","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Maximum number of simulations to return (wallet session only)"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0},"description":"Number of simulations to skip (wallet session only)"}],"responses":{"200":{"description":"Simulations list","content":{"application/json":{"schema":{"oneOf":[{"type":"array","description":"Array of simulations (API key callers)","items":{"$ref":"#/components/schemas/Simulation"}},{"type":"object","description":"Paginated response (wallet session callers)","required":["simulations","total","limit","offset"],"properties":{"simulations":{"type":"array","items":{"$ref":"#/components/schemas/Simulation"}},"total":{"type":"integer","description":"Total simulations matching the wallet address"},"limit":{"type":"integer"},"offset":{"type":"integer"}}}]}}}},"401":{"$ref":"#/components/responses/WalletAuthUnauthorized"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}":{"x-stability":"stable","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID","example":"550e8400-e29b-41d4-a716-446655440000"}],"get":{"tags":["Create"],"summary":"Fetch a simulation by ID","description":"Returns the full state of a simulation including its resources,\ncurrent time step, traffic load, and autoscaling history.\nRequires `read` scope and ownership.\n","operationId":"getSimulation","security":[],"x-x402-call-type":"simulation_get","x-x402-price-usdc":"$0.0030","responses":{"200":{"description":"Simulation state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Simulation"}}}},"401":{"$ref":"#/components/responses/WalletAuthUnauthorized"},"403":{"description":"Access denied — caller does not own this simulation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/cost-breakdown":{"x-stability":"stable","get":{"tags":["Simulations"],"summary":"Fetch the latest per-resource cost breakdown","description":"Returns the per-resource hourly cost array from the simulation's most\nrecent metrics record, joined with each resource's current status and\nserving state. `status` is raw health severity/cooldown; use\n`availabilityState`, `isRoutable`, and `routedRps` to determine whether\nthe resource is currently serving. A critical resource may therefore be\n`availabilityState: degraded` with `isRoutable: true` after it rejoins\nrouting. Use this after a failure or traffic surge to identify resources\nthat continue billing while genuinely unavailable (\"residual\" spend).\n\n`totalCostPerHour` is the exact sum of all `resources[].costPerHour`\nvalues. The response is always a flat array (no paging, no metrics\nhistory), making it cheap to call between scenario steps.\n\nAurora Serverless v2 rows identify writer/reader role, reader scaling\nmode, billable ACU and the per-ACU-hour rate. An unavailable quick-failure\nwriter retains its pre-failure allocated ACU in this model; this is a\nmodeling assumption, not a claim that AWS bills a failed instance like\na stopped cluster. AWS distinguishes stopped clusters from running\ninstances: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-cluster-stop-start.html\n(reviewed 2026-09-25). Aurora capacity/reader behavior:\nhttps://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.how-it-works.html\n(reviewed 2026-09-25).\n\nOpenShift rows retain their billing metadata. For ARO, compare like-for-like\nunits: `publishedUnitRatePerHour` is the official `$0.171/hour` rate per\n4-vCPU billing unit, while `derivedPlatformFeePerHour` and `costPerHour`\nare the topology-derived total. For example, three 2-vCPU workers produce\n`effectiveWorkerVcpu: 6` and a `$0.2565/hour` platform total\n(`publishedUnitRatePerHour × effectiveWorkerVcpu ÷ billingUnitVcpu`).\nPlatform, control-plane, and worker rows are separate additive entries;\nthe estimated control-plane charge must not be folded into the platform\nunit comparison or added a second time.\n\nReturns 404 when the simulation has not been stepped yet (no metrics\nrecord exists). Requires `read` scope and ownership, or an x402\nmicro-payment (`simulation_cost_breakdown`, $0.0030).\n","operationId":"getSimulationCostBreakdown","security":[],"x-x402-call-type":"simulation_cost_breakdown","x-x402-price-usdc":"$0.0030","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"responses":{"200":{"description":"Latest per-resource cost breakdown","content":{"application/json":{"schema":{"type":"object","required":["simulationId","stepIndex","totalCostPerHour","residualCostPerHour","residualCostDefinition","resources"],"properties":{"simulationId":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440000"},"stepIndex":{"type":"number","description":"Simulation step (metrics timestamp) the breakdown was computed at","example":42},"totalCostPerHour":{"type":"number","description":"Exact sum of all resources[].costPerHour values (USD/hr)","example":1.77},"costBasis":{"$ref":"#/components/schemas/CostBasis"},"residualCostPerHour":{"type":"number","description":"Exact subtotal of costPerHour for resources that are currently unavailable/parked or stopped. Healthy/degraded idle resources, including ALBs, are excluded.","example":0.045},"residualCostDefinition":{"type":"string","description":"Explains which billed resources are included in residualCostPerHour; totalCostPerHour includes all billed resources.","example":"Unavailable, parked, or stopped resource costs only. Healthy or degraded idle resources, including ALBs, are excluded; totalCostPerHour remains the complete billed total."},"resources":{"type":"array","items":{"type":"object","required":["resourceId","name","resourceType","provider","costPerHour","status"],"properties":{"resourceId":{"type":"string"},"name":{"type":"string","example":"app-nat"},"resourceType":{"type":"string","example":"network"},"provider":{"type":"string","example":"aws"},"costPerHour":{"type":"number","example":0.045},"instanceRole":{"type":"string","enum":["writer","reader"],"description":"Aurora Serverless v2 instance role."},"scalingMode":{"type":"string","enum":["writer-mirror","independent"],"description":"Reader promotion tier 0/1 mirror or tier 2–15 independent mode."},"billableAcu":{"type":"number","description":"Allocated ACU billed for this instance; frozen pre-failure ACU on modeled quick writer park."},"ratePerAcuHour":{"type":"number","description":"Aurora per-instance ACU-hour rate derived from the authoritative AWS rate."},"rateBasis":{"type":"string"},"billingReason":{"type":"string"},"rdsSqlServerBilling":{"$ref":"#/components/schemas/RdsSqlServerBillingResult"},"storageCostPerHour":{"type":"number","description":"RDS SQL Server allocated-storage component expressed as USD/hr."},"storageGb":{"type":"number","description":"Normalized allocated RDS SQL Server storage in GiB."},"status":{"type":"string","description":"Current resource status (\"stopped\" when billingState is stopped; \"billed\" for synthetic egress/data-transfer charges; \"removed\" when a real resource no longer exists)","enum":["healthy","warning","critical","stopped","billed","removed"]},"availabilityState":{"type":"string","enum":["available","degraded","unavailable","scaled_to_zero","cold_start"],"description":"Current serving classification for compute, Kubernetes, and database rows. `available` means healthy and serving; `degraded` means warning or critical severity while still serving; `unavailable` means a failed or parked resource is not serving. `scaled_to_zero` means an ECS Fargate service has no running tasks; `cold_start` means its tasks are starting and not yet routable. These Fargate lifecycle states are distinct from generic `unavailable`. Do not infer unavailability from status: critical alone.\n"},"isRoutable":{"type":"boolean","description":"Whether this resource is currently receiving routed traffic. True for available/degraded rows and false for unavailable, scaled_to_zero, or cold_start rows."},"routedRps":{"type":"number","description":"Current routed requests per second for tracked compute, Kubernetes, and database rows when available."},"offering":{"type":"string","description":"OpenShift offering associated with this row, when the resource is an OpenShift overlay."},"chargeType":{"type":"string","enum":["platform","control-plane","worker"],"description":"OpenShift accounting component; platform, control-plane, and worker rows are additive."},"pricingStatus":{"type":"string","enum":["official","estimated"],"description":"Provenance status for the pricing represented by this row."},"pricingSource":{"type":"string","description":"Pricing source associated with this row."},"effectiveDate":{"type":"string","description":"ISO date of the pricing evidence represented by this row."},"referenceRegion":{"type":"string","description":"Region represented by the published pricing evidence."},"pricingArithmetic":{"type":"string","description":"Published amount and conversion used for an OpenShift platform/license row."},"billingUnit":{"type":"string","enum":["4-vcpu-hour","cluster-hour"],"description":"Published OpenShift platform billing unit used before topology proration."},"billingUnitVcpu":{"type":"number","description":"Number of worker vCPUs in one published billing unit; 4 for ARO."},"publishedUnitRatePerHour":{"type":"number","description":"Published platform rate for one billing unit, before topology proration. For ARO this is $0.171/hour per 4 vCPU in the published East US reference."},"effectiveWorkerCount":{"type":"integer","description":"Total worker count represented by the derived OpenShift platform charge."},"effectiveWorkerVcpu":{"type":"number","description":"Total worker vCPU-equivalents used to prorate the published platform unit."},"derivedPlatformFeePerHour":{"type":"number","description":"Topology-derived platform charge; equals publishedUnitRatePerHour × effectiveWorkerVcpu ÷ billingUnitVcpu for ARO. For three 2-vCPU workers this is $0.2565/hour, not a second published rate."},"workerTopology":{"type":"object","description":"Per-pool worker quantities used to derive the OpenShift platform charge.","required":["workerCount","effectiveWorkerVcpu","nodePools"],"properties":{"workerCount":{"type":"integer"},"effectiveWorkerVcpu":{"type":"number"},"nodePools":{"type":"array","items":{"type":"object","required":["name","workerCount","workerVcpu","effectiveWorkerVcpu"],"properties":{"name":{"type":"string"},"workerCount":{"type":"integer"},"workerVcpu":{"type":"number"},"effectiveWorkerVcpu":{"type":"number"}}}}}}}}}}}}}},"401":{"$ref":"#/components/responses/WalletAuthUnauthorized"},"403":{"description":"Access denied — caller does not own this simulation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Simulation not found, or no metrics record exists yet (simulation not stepped)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/provider-api-limits":{"x-stability":"experimental","post":{"tags":["Simulations"],"summary":"Simulate provider quotas, throttling, retries, and concurrency","description":"Use this operation for provider quotas, API throttling, retries,\nbackoff, queueing, worker/concurrency limits, and other control-plane\noperations. It runs a bounded, synchronous workload through Cloud World\nModel's deterministic provider-limit scheduler and models throttling,\nqueueing, throughput, completion, retries, timeouts, failures,\ndependency blocking, and optional concurrency comparisons.\n\nThis is simulated provider-control-plane data, not a provider SDK or a\nlive quota lookup. It never calls AWS, Azure, GCP, DigitalOcean, OCI,\nor any other provider API, discovers negotiated quotas, creates\nresources, persists the workload, or changes the simulation's resource\ngraph or /step state. Provider throttling in the result is distinct\nfrom CWM's own HTTP 429/rate-limit middleware. Use resource `/step` for\ncloud workload behavior and use `/simulations/stateless` for the\nseparate stateless resource simulation; neither substitutes for this\nprovider-limit operation.\n\nSupply one service per request: the top-level `service` applies to\nevery operation group in the DAG. To compare services, submit separate\nrequests. Supported exact provider/service/operation/category tuples\nresolve with `result.policyResolutions[].policySource` set to\n`catalog_default`; unsupported tuples return `unsupported_policy`\nwithout a scoped caller override.\n\nExecution is protected by a 2-second wall-clock deadline and a\nprocess-local admission limit of two concurrent runs. Identical active\nor recently interrupted workloads, and workloads submitted while\nexecution capacity is full, are rejected with HTTP 409 and must not be\nautomatically retried. Deadline expiry is an HTTP 422 non-retryable\nfailure.\n\nCatalog defaults cover `aws.ec2.mutating-api` (`service: ec2`,\n`operation: mutating`, `category: compute`), AWS Elastic Load Balancing\nv1 and v2 (`service: elbv1` or `elbv2`, `category: network`), and Azure\nResource Manager read/write buckets. For each ELB service the exact\nsupported operations are `resource-intensive`, `registration`,\n`non-mutating`, and `mutating`, yielding `aws.elbv1.resource-intensive`,\n`aws.elbv1.registration`, `aws.elbv1.non-mutating`,\n`aws.elbv1.mutating`, `aws.elbv2.resource-intensive`,\n`aws.elbv2.registration`, `aws.elbv2.non-mutating`, and\n`aws.elbv2.mutating`. These are documented API action categories,\nnot arbitrary AWS action names. Do not infer an ELB category for an\nuncategorized action or interchange ELBv1 and ELBv2 buckets.\nAurora Serverless v1 Data API has two **independent** catalog models\n(`service: rds-data-api-aurora-serverless-v1`, `category: database`):\n`operation: requests-per-second` resolves\n`aws.rds-data-api-aurora-serverless-v1.requests-per-second` (1,000\nrequests/second per account in the current Region);\n`operation: concurrent-requests` resolves\n`aws.rds-data-api-aurora-serverless-v1.concurrent-requests` (500\nconcurrent requests to ONE cluster using the SAME secret, with excess\nrequests queued). Do not interpret them as joint enforcement of one\nworkload. A request mixing these two selectors is rejected; send\nseparate requests and do not treat results as simultaneous enforcement.\nBoth require `quotaScope.account`; concurrency additionally\nrequires `quotaScope.resource`, an opaque cluster-secret-pair label,\nnever an actual secret. The rate model has no documented burst; its\nfixed 1-second scheduler window approximates the AWS rate limit, not\nAWS's documented timing. Source:\nhttps://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Limits.html\nGeneric RDS control-plane, Aurora Serverless v2 Data API, and\nprovisioned Aurora Data API are not covered by these selectors.\nAWS IAM, generic RDS control-plane, EC2 Auto Scaling, Application Auto Scaling,\nand S3 control-plane requests have no numeric scheduler-compatible\npublic baseline in this catalog. S3 object per-prefix throughput is\ndata-plane guidance, not a control-plane quota. Such tuples, as well\nas GCP, DigitalOcean, and OCI tuples without a caller-supplied quota\noverride, return `unsupported_policy` rather than HTTP 429. Overrides must use a\nconcrete account, subscription, or project scope and include\ncaller-supplied provenance. The catalog policy source, documentation\nreference, confidence, and as-of date are returned in `provenance`\nand `result.policyResolutions`.\n\nEC2 Auto Scaling (`service: ec2-auto-scaling`, `category: compute`) initially\nmodels `DescribeAutoScalingGroups`, `DescribeScheduledActions`,\n`CreateAutoScalingGroup`, `UpdateAutoScalingGroup`, `SetDesiredCapacity`,\nand `DeleteAutoScalingGroup`. AWS documents one shared account API token\nbucket, `RateExceeded` on throttle, and no public numeric capacity/refill\npair. Use one account/Region-scoped caller `operation: all` token-bucket\noverride with caller-verified burst capacity and refill for these six\noperation groups. Put `operation: all` on the override policy and its\npolicy `scope`, not on the request's `quotaScope`; the latter can omit\n`operation` for a mixed workload. They contend for the same bucket.\nOther operations do not inherit this six-action override. Mark `throttled` retryable and\nconfigure exponential backoff to model client retry behavior. This is\nnot an Auto Scaling resource/configuration quota or an EC2 API bucket.\nAWS advises `IncludeInstances=false` for `DescribeAutoScalingGroups`\nto omit instance details; this advice does not change modeled token\nconsumption, latency, or quota. See\nhttps://docs.aws.amazon.com/autoscaling/ec2/APIReference/Welcome.html\nand docs/aws-api-limit-evidence.md for the evidence gap.\n\n`latency` is required because it is a Cloud World Model client-side\nmodel input: provide measured client values when available. If no\nmeasurements are available, use the canonical baseline\n`meanMs: 40`, `p95Ms: 80`, and `timeoutMs: 5000` shown below. These\nvalues are simulation guidance, not AWS service defaults and not a\nlive AWS latency claim. The same applies to every latency and retry\nvalue in this operation.\n\nFor mixed workloads, each `result.policyResolutions` entry is keyed by\noperation category, operation, and resource group (resource is omitted\nwhen absent). Multiple operation IDs with the same three values share\none resolution. Its `accounting` counts that group's planned units,\nattempts, retries, throttles, and completions; summing these fields\nacross resolutions gives the corresponding `result.accounting` totals.\nAttempts count initial and retry attempts, while retries and throttles\ncan count the same work, so do not sum different fields together.\nFor the `mixedAwsEc2Groups` request example below, the modeled result\ncontains this fragment (other result fields are omitted):\n\n```json\n{\n \"policyResolutions\": [\n {\"operationCategory\":\"compute\",\"operation\":\"mutating\",\"resource\":\"fleet-a\",\"policySource\":\"catalog_default\",\"accounting\":{\"plannedOperations\":3,\"attemptedOperations\":3,\"retryOperations\":0,\"throttledOperations\":0,\"completedOperations\":3,\"denominator\":\"planned_operations\"}},\n {\"operationCategory\":\"compute\",\"operation\":\"mutating\",\"resource\":\"fleet-b\",\"policySource\":\"catalog_default\",\"accounting\":{\"plannedOperations\":2,\"attemptedOperations\":2,\"retryOperations\":0,\"throttledOperations\":0,\"completedOperations\":2,\"denominator\":\"planned_operations\"}}\n ],\n \"accounting\": {\"plannedOperations\":5,\"attemptedOperations\":5,\"retryOperations\":0,\"throttledOperations\":0,\"completedOperations\":5,\"failedOperations\":0,\"timedOutOperations\":0,\"dependencyBlockedOperations\":0,\"unsupportedOperations\":0,\"pendingOperations\":0,\"terminalOperations\":5,\"denominator\":\"planned_operations\"}\n}\n```\n\nOmitting `retry` without selecting `awsSdkRetryProfile` selects the\nlegacy one-attempt, no-backoff baseline:\n`maxAttempts: 1`, `baseBackoffMs: 0`, `maxBackoffMs: 0`,\n`backoffMultiplier: 2`, and `jitterRatio: 0`; although generic retry\nconditions are allowed by the default schema, one attempt means no\nretry is made.\nAn explicitly selected `awsSdkRetryProfile` models the shared retry\nlayer of JavaScript v3 Standard and Adaptive. Adaptive's per-client\nrequest-rate limiter and cross-operation coupling are not modeled.\nExplicit `retry` settings override the selected profile. Provider\nquota policies remain a separate catalog concern.\n\nThe `aws-js-v3-standard-pre-2026` profile has transient and throttling\nbase delays of 100 ms, 10 transient / 5 throttling retry tokens, a\n500-token client budget, and 3 maximum attempts. The separately\nselectable `aws-js-v3-standard-2026` profile uses 50 ms transient and\n1,000 ms throttling bases, 14 transient / 5 throttling tokens, and a\n500-token budget. For DynamoDB and DynamoDB Streams, the 2026 profile\nuses a 25 ms transient base and 4 attempts; the pre-2026 profile stays\nat 100 ms and 3 attempts. Both profiles use seeded full jitter with a\n20,000 ms cap. AWS's `x-amz-retry-after` is clamped to the computed\njittered delay through 5,000 ms above it, up to 25,000 ms, without\nextra client jitter.\n\nAWS sources:\nhttps://docs.aws.amazon.com/sdkref/latest/guide/feature-retry-behavior.html\nhttps://github.com/aws/aws-sdk-js-v3/issues/8037\nhttps://aws.amazon.com/blogs/developer/announcing-updated-retry-behavior-for-aws-sdks-and-tools/\nAWS describes default rollout as no sooner than November 2026; the\nexact date is unconfirmed here. Profiles remain explicitly selectable\nand no saved or historical simulation is automatically rewritten.\n\nBounds: up to 256 operation groups, 100,000 aggregate planned units\nacross all distinct concurrency-sweep runs, 500,000 estimated attempts\n(aggregate planned units multiplied by the effective maximum attempts), 1,000\nworkers, 1,000 concurrency, 16 concurrency sweep values, 10 retry\nattempts, and 1,000,000 scheduler events. Aggregate work-budget checks\nrun before authentication billing or demo-quota consumption and return\nHTTP 413 when exceeded. Results with more than 1,000 planned operations\nomit per-unit detail: `result.operations` is empty and\n`result.outputDetail` is `summary_only`. This endpoint is a modeled\nsimulation, not a provider SDK or drop-in provider API.\n","operationId":"simulateProviderApiLimits","security":[],"x-x402-call-type":"provider_api_limits","x-x402-price-usdc":"$0.0030","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Owned simulation UUID used for access control and result context; workload state is not persisted."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["provider","region","service","operations","latency"],"properties":{"contractVersion":{"type":"integer","enum":[1],"default":1,"description":"Version of the provider API-limit simulation input contract."},"provider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"],"description":"Cloud provider whose modeled API quota policy will be applied."},"region":{"type":"string","minLength":1,"maxLength":128,"description":"Provider region used to resolve regional quota policies."},"service":{"type":"string","minLength":1,"maxLength":128,"description":"Provider service whose API operations will be simulated."},"operations":{"type":"array","minItems":1,"maxItems":256,"description":"DAG operation groups. Each item has id, operation, category,\nplannedCount (1..100,000), optional resource, and\ndependsOn IDs. The aggregate planned count is bounded at\n100,000.\n","items":{"type":"object","additionalProperties":false,"required":["id","operation","category","plannedCount"],"properties":{"id":{"type":"string","minLength":1,"maxLength":128,"description":"Unique identifier for this operation group and its dependencies."},"operation":{"type":"string","minLength":1,"maxLength":128,"description":"Provider operation class or operation name used to select applicable quota policies."},"category":{"type":"string","enum":["compute","database","storage","network","kubernetes","identity","monitoring","quota","other"],"description":"Service category used to resolve provider API-limit policies."},"plannedCount":{"type":"integer","minimum":1,"maximum":100000,"description":"Number of operations in this group to schedule."},"dependsOn":{"type":"array","maxItems":32,"items":{"type":"string","minLength":1,"maxLength":128,"description":"ID of a prerequisite operation group."}},"resource":{"type":"string","minLength":1,"maxLength":128,"description":"Optional opaque resource label used to match resource-scoped quota policies."},"sdkClientId":{"type":"string","minLength":1,"maxLength":128,"description":"Optional SDK client identity; retry-token budgets are independent for each identity. Defaults to `default`."},"longPolling":{"type":"boolean","description":"Marks a long-poll operation that waits before returning when the selected SDK retry budget is exhausted."},"retryAfterMs":{"type":"number","minimum":0,"maximum":120000,"description":"Simulated AWS `x-amz-retry-after` header delay in milliseconds; requires an explicit AWS SDK profile without a generic retry override and is clamped to AWS's effective delay bounds."}}}},"workerCount":{"type":"integer","minimum":1,"maximum":1000,"default":1,"description":"Number of simulated workers issuing the planned operations."},"maxConcurrency":{"type":"integer","minimum":1,"maximum":1000,"default":1,"description":"Maximum simultaneous in-flight operations scheduled across workers."},"concurrencySweep":{"type":"object","additionalProperties":false,"required":["values"],"properties":{"values":{"type":"array","minItems":1,"maxItems":16,"uniqueItems":true,"description":"Concurrency levels at which to repeat the modeled workload.","items":{"type":"integer","minimum":1,"maximum":1000,"description":"A distinct number of simultaneous in-flight operations to evaluate."}}}},"latency":{"type":"object","additionalProperties":false,"description":"Required caller-supplied latency assumptions used by the scheduler.","required":["meanMs","p95Ms","timeoutMs"],"properties":{"meanMs":{"type":"number","minimum":0,"maximum":120000,"description":"Caller-supplied mean operation latency in milliseconds."},"p95Ms":{"type":"number","minimum":0,"maximum":120000,"description":"Caller-supplied P95 operation latency in milliseconds."},"timeoutMs":{"type":"number","exclusiveMinimum":0,"maximum":120000,"description":"Caller-side request timeout in milliseconds."},"jitterMs":{"type":"number","minimum":0,"maximum":120000,"default":0,"description":"Maximum modeled latency variation in milliseconds."}}},"retry":{"type":"object","additionalProperties":false,"properties":{"maxAttempts":{"type":"integer","minimum":1,"maximum":10,"default":1},"baseBackoffMs":{"type":"number","minimum":0,"maximum":60000,"default":0},"maxBackoffMs":{"type":"number","minimum":0,"maximum":600000,"default":0},"backoffMultiplier":{"type":"number","minimum":1,"maximum":10,"default":2},"jitterRatio":{"type":"number","minimum":0,"maximum":1,"default":0},"retryable":{"type":"array","maxItems":3,"uniqueItems":true,"items":{"type":"string","enum":["throttled","timeout","server_error"]}}}},"awsSdkRetryProfile":{"$ref":"#/components/schemas/AwsSdkRetryProfile"},"quotaScope":{"type":"object","description":"Scope dimensions; region and service must match the request. Aurora Serverless v1 Data API requires account for both selectors; concurrent-requests additionally requires resource as an opaque cluster-secret-pair label, never an actual secret.","additionalProperties":{"type":"string","minLength":1,"maxLength":128}},"quotaOverrides":{"type":"array","maxItems":32,"description":"Caller-supplied policy overrides with concrete account, subscription, or project scope. Optional concurrencyOverflow queue/reject applies to concurrency ceilings; absent retains reject behavior.","items":{"type":"object","additionalProperties":true,"properties":{"concurrencyOverflow":{"type":"string","enum":["queue","reject"],"description":"Optional concurrency-ceiling overflow behavior; absent retains reject behavior."}}}},"seed":{"type":"integer","minimum":0,"maximum":4294967295,"default":0,"description":"Seed used to make randomized scheduler outcomes reproducible."},"concurrency":{"type":"integer","minimum":1,"maximum":1000,"description":"Fixed simulated concurrency when a concurrency sweep is not used."},"failureProbability":{"type":"number","minimum":0,"maximum":1,"default":0,"description":"Probability that an operation encounters a modeled transient failure."},"maxThrottleRate":{"type":"number","minimum":0,"maximum":1,"default":1,"description":"Upper bound on the fraction of planned operations the scheduler may throttle."},"maxEvents":{"type":"integer","minimum":1,"maximum":1000000,"default":1000000,"description":"Maximum scheduler events permitted for this request."}}},"examples":{"canonicalAwsEc2":{"summary":"Scenario-independent AWS EC2 mutating workload","value":{"provider":"aws","region":"us-east-1","service":"ec2","operations":[{"id":"launch","operation":"mutating","category":"compute","plannedCount":5}],"workerCount":2,"maxConcurrency":2,"concurrency":2,"latency":{"meanMs":40,"p95Ms":80,"timeoutMs":5000,"jitterMs":0},"retry":{"maxAttempts":2,"baseBackoffMs":100,"maxBackoffMs":100,"backoffMultiplier":2,"jitterRatio":0,"retryable":["throttled","timeout"]},"seed":4367}},"mixedAwsEc2Groups":{"summary":"AWS EC2 mutating operations in two resource groups","value":{"provider":"aws","region":"us-east-1","service":"ec2","operations":[{"id":"a-launch","operation":"mutating","category":"compute","resource":"fleet-a","plannedCount":2},{"id":"a-update","operation":"mutating","category":"compute","resource":"fleet-a","plannedCount":1},{"id":"b-launch","operation":"mutating","category":"compute","resource":"fleet-b","plannedCount":2}],"workerCount":2,"maxConcurrency":2,"concurrency":2,"latency":{"meanMs":40,"p95Ms":80,"timeoutMs":5000,"jitterMs":0},"retry":{"maxAttempts":1,"baseBackoffMs":0,"maxBackoffMs":0,"backoffMultiplier":2,"jitterRatio":0,"retryable":[]},"seed":4367}}}}}},"responses":{"200":{"description":"Deterministic provider-limit simulation result with catalog/override provenance.","content":{"application/json":{"schema":{"type":"object","required":["simulation","modeled","request","result","provenance"],"properties":{"simulation":{"type":"object","required":["id","name","currentTime"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"currentTime":{"type":"number"}}},"modeled":{"type":"object","required":["namespace","platformRateLimitNamespace","providerThrottlingIsModeled","platformHttpRateLimitsAreNotModeled"],"properties":{"namespace":{"type":"string","example":"simulated-provider-api-quota"},"platformRateLimitNamespace":{"type":"string","example":"cwm-api-rate-limit"},"providerThrottlingIsModeled":{"type":"boolean","enum":[true]},"platformHttpRateLimitsAreNotModeled":{"type":"boolean","enum":[true]}}},"request":{"$ref":"#/components/schemas/ProviderApiLimitSimulationRequest"},"result":{"$ref":"#/components/schemas/ProviderApiSchedulerResult"},"provenance":{"type":"object","required":["catalogVersion","policies"],"properties":{"catalogVersion":{"type":"integer","example":3},"policies":{"type":"array","description":"Effective policies with sourceKind, sourceReference, asOfDate, classification, and confidence.","items":{"$ref":"#/components/schemas/ProviderApiQuotaPolicy"}}}}}},"examples":{"canonicalAwsEc2Result":{"summary":"Completed modeled AWS EC2 workload","value":{"simulation":{"id":"00000000-0000-4000-8000-000000000001","name":"provider-limit-example","currentTime":0},"modeled":{"namespace":"simulated-provider-api-quota","platformRateLimitNamespace":"cwm-api-rate-limit","providerThrottlingIsModeled":true,"platformHttpRateLimitsAreNotModeled":true},"request":{"contractVersion":1,"provider":"aws","region":"us-east-1","service":"ec2","operations":[{"id":"launch","operation":"mutating","category":"compute","plannedCount":5,"dependsOn":[]}],"workerCount":2,"maxConcurrency":2,"concurrency":2,"latency":{"meanMs":40,"p95Ms":80,"timeoutMs":5000,"jitterMs":0},"retry":{"maxAttempts":2,"baseBackoffMs":100,"maxBackoffMs":100,"backoffMultiplier":2,"jitterRatio":0,"retryable":["throttled","timeout"]},"quotaOverrides":[],"seed":4367,"failureProbability":0,"maxThrottleRate":1,"maxEvents":1000000},"result":{"schedulerVersion":1,"contractVersion":1,"status":"supported","seed":4367,"concurrency":2,"policyResolutions":[{"contractVersion":1,"status":"supported","operationCategory":"compute","operation":"mutating","policySource":"catalog_default","policy":{"policyId":"aws.ec2.mutating-api","provider":"aws","service":"ec2","operationCategory":"compute","operation":"mutating","scope":{"account":"*","region":"*","service":"ec2","operation":"mutating"},"classification":"documented_public_default","provenance":{"sourceKind":"provider_documentation","sourceReference":"https://docs.aws.amazon.com/ec2/latest/devguide/ec2-api-throttling.html","asOfDate":"2026-09-17","customerSpecific":false,"observed":false,"confidence":"high"},"sustainedRate":{"limit":5,"unit":"requests_per_second","windowSeconds":1},"burstCapacity":{"capacity":100,"unit":"requests_per_second","windowSeconds":1,"refill":"token_bucket","refillRate":5},"enforcement":"token_bucket","notes":"AWS EC2 mutating API token-bucket baseline; not a negotiated account quota."},"accounting":{"plannedOperations":5,"attemptedOperations":5,"retryOperations":0,"throttledOperations":0,"completedOperations":5,"denominator":"planned_operations"}}],"unsupportedGroups":[],"accounting":{"plannedOperations":5,"attemptedOperations":5,"retryOperations":0,"throttledOperations":0,"completedOperations":5,"failedOperations":0,"timedOutOperations":0,"dependencyBlockedOperations":0,"unsupportedOperations":0,"pendingOperations":0,"terminalOperations":5,"denominator":"planned_operations"},"byCategory":[{"category":"compute","plannedOperations":5,"attemptedOperations":5,"retryOperations":0,"throttledOperations":0,"completedOperations":5,"failedOperations":0,"timedOutOperations":0,"dependencyBlockedOperations":0,"unsupportedOperations":0,"pendingOperations":0,"terminalOperations":5,"denominator":"planned_operations"}],"operations":[{"operationId":"launch","operation":"mutating","category":"compute","unitIndex":0,"outcome":"completed","attempts":1,"retryOperations":0,"throttledAttempts":0,"timedOutAttempts":0,"readyAtMs":0,"firstAttemptAtMs":0,"terminalAtMs":49.05134,"queueWaitMs":0,"throttleWaitMs":0,"retryBackoffMs":0,"serviceTimeMs":49.05134},{"operationId":"launch","operation":"mutating","category":"compute","unitIndex":1,"outcome":"completed","attempts":1,"retryOperations":0,"throttledAttempts":0,"timedOutAttempts":0,"readyAtMs":0,"firstAttemptAtMs":0,"terminalAtMs":69.771207,"queueWaitMs":0,"throttleWaitMs":0,"retryBackoffMs":0,"serviceTimeMs":69.771207},{"operationId":"launch","operation":"mutating","category":"compute","unitIndex":2,"outcome":"completed","attempts":1,"retryOperations":0,"throttledAttempts":0,"timedOutAttempts":0,"readyAtMs":0,"firstAttemptAtMs":49.05134,"terminalAtMs":110.367632,"queueWaitMs":49.05134,"throttleWaitMs":0,"retryBackoffMs":0,"serviceTimeMs":61.316292},{"operationId":"launch","operation":"mutating","category":"compute","unitIndex":3,"outcome":"completed","attempts":1,"retryOperations":0,"throttledAttempts":0,"timedOutAttempts":0,"readyAtMs":0,"firstAttemptAtMs":69.771207,"terminalAtMs":112.343654,"queueWaitMs":69.771207,"throttleWaitMs":0,"retryBackoffMs":0,"serviceTimeMs":42.572447},{"operationId":"launch","operation":"mutating","category":"compute","unitIndex":4,"outcome":"completed","attempts":1,"retryOperations":0,"throttledAttempts":0,"timedOutAttempts":0,"readyAtMs":0,"firstAttemptAtMs":110.367632,"terminalAtMs":163.758337,"queueWaitMs":110.367632,"throttleWaitMs":0,"retryBackoffMs":0,"serviceTimeMs":53.390705}],"queue":{"currentDepth":0,"peakDepth":5,"backpressureEvents":2},"timing":{"completionTimeMs":163.758337,"terminalCompletionTimeMs":163.758337,"successfulCompletionTimeMs":163.758337,"completionRatio":1,"effectiveThroughputPerSecond":30.532797,"totalQueueWaitMs":229.190179,"totalServiceTimeMs":276.101991,"totalRetryOverheadMs":0},"retryBehavior":{"source":"caller_generic","selectedProfile":null,"mode":null,"serviceDefault":null,"mechanics":"caller_generic","adaptiveRateLimiterModeled":false,"historicalRunsRewritten":false,"maxAttempts":2,"retryable":["throttled","timeout"],"generic":{"baseBackoffMs":100,"maxBackoffMs":100,"backoffMultiplier":2,"jitterRatio":0},"transientBaseDelayMs":null,"throttlingBaseDelayMs":null,"retryTokenCapacity":null,"transientRetryTokenCost":null,"throttlingRetryTokenCost":null,"maxBackoffCapMs":null,"retryAfterMaxExtraMs":null,"rolloutNote":null,"clientBudgets":[]},"optimization":{"objective":"successful_completion_time_ms_with_completion_ratio_and_throttle_constraint","maxThrottleRate":1,"acceptableConcurrency":[2],"recommendedConcurrency":2,"optimalConcurrencyRange":{"min":2,"max":2},"comparisons":[{"concurrency":2,"status":"supported","plannedOperations":5,"attemptedOperations":5,"completedOperations":5,"completionTimeMs":163.758337,"terminalCompletionTimeMs":163.758337,"successfulCompletionTimeMs":163.758337,"totalRetryOverheadMs":0,"completionRatio":1,"effectiveThroughputPerSecond":30.532797,"throttledOperations":0,"retryOperations":0,"failedOperations":0,"timedOutOperations":0,"dependencyBlockedOperations":0,"unsupportedOperations":0,"pendingOperations":0,"terminalOperations":5,"queuePeak":5,"objectiveValue":163.758337}]}},"provenance":{"catalogVersion":3,"policies":[{"policyId":"aws.ec2.mutating-api","provider":"aws","service":"ec2","operation":"mutating","classification":"documented_public_default","sourceKind":"provider_documentation","sourceReference":"https://docs.aws.amazon.com/ec2/latest/devguide/ec2-api-throttling.html","asOfDate":"2026-09-17","confidence":"high"}]}}}}}}},"400":{"description":"Request exceeds a documented bound, contains an invalid DAG/scope, or uses an invalid quota override.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/WalletAuthUnauthorized"},"402":{"description":"x402 payment required when no API key or wallet session is available."},"403":{"description":"Simulation not owned by the caller.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Simulation not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Non-retryable execution admission rejection. The response code is\n`PROVIDER_LIMIT_DUPLICATE` when the same workload is active or was\ninterrupted in the preceding five minutes, or\n`PROVIDER_LIMIT_BUSY` when this process already has two active runs\n(or its interrupted-run guard capacity is full). Do not\nautomatically retry.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderApiExecutionError"}}}},"413":{"description":"Non-retryable aggregate work-budget rejection performed before\nbilling. Returned when concurrency-sweep runs exceed 100,000\naggregate planned units or 500,000 estimated attempts.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderApiExecutionError"}}}},"422":{"description":"Non-retryable execution interruption. `PROVIDER_LIMIT_DEADLINE`\nmeans the 2-second execution deadline expired;\n`PROVIDER_LIMIT_DISCONNECTED` means execution stopped after the\nclient disconnected. Reduce the workload rather than automatically\nretrying.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderApiExecutionError"}}}},"429":{"description":"CWM platform HTTP rate limit. Modeled provider throttling is returned inside the 200 result and does not use this response."},"500":{"$ref":"#/components/responses/InternalError"}}}},"/rl/environments":{"x-stability":"stable","post":{"tags":["Create"],"summary":"Create an RL training environment","description":"Creates a reinforcement learning environment for training agents.\nLinks to an existing simulation and configures episode parameters.\n\nThis endpoint works with simulations built on **any supported provider**,\nincluding AWS, GCP, Azure, OCI, and **DigitalOcean**. Training an agent against\na DigitalOcean simulation lets you learn optimal autoscaling strategies for\nDroplet-based workloads, Managed Database failover handling, and\nmulti-datacenter traffic routing — all without incurring real cloud costs.\nThe observation space, action space, and reward function are identical\nregardless of provider.\n\n**Idle TTL:** Authenticated RL environments expire after **2 hours of inactivity**\n(no `step` or `reset` call received). Expired environments and their linked\nsimulations are removed automatically; subsequent requests return `404`. Reset the\nidle timer by calling `POST /rl/environments/{environmentId}/step` or\n`POST /rl/environments/{environmentId}/reset` at least once every 2 hours during\nlong training runs.\n\n**Note:** In-memory deployments (the default) do not persist RL environments\nacross server restarts. Reconnect or recreate the environment after a restart.\n\n**UI Status Viewer:** Once an environment is running you can monitor its episodes,\ncumulative rewards, and health without writing code — open the\n[RL Environment Status viewer](/admin/rl-environments) in the platform UI and enter\nyour API key to see all active environments at a glance.\n","operationId":"createRLEnvironment","x-x402-call-type":"rl_env_create","x-x402-price-usdc":"$0.0030","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/rl/environments \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"simulationId\": \"a638caad-7423-40a3-bb09-f91235d9392d\",\n \"episodeConfig\": {\n \"maxSteps\": 200,\n \"targetTrafficPattern\": \"wave\",\n \"initialTraffic\": 1500,\n \"targetSLA\": { \"maxLatencyP95\": 180, \"maxErrorRate\": 1.0 },\n \"costBudgetPerHour\": 3.50,\n \"enableFailures\": false\n }\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/rl/environments\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"simulationId\": \"a638caad-7423-40a3-bb09-f91235d9392d\",\n \"episodeConfig\": {\n \"maxSteps\": 200,\n \"targetTrafficPattern\": \"wave\",\n \"initialTraffic\": 1500,\n \"targetSLA\": {\"maxLatencyP95\": 180, \"maxErrorRate\": 1.0},\n \"costBudgetPerHour\": 3.50,\n \"enableFailures\": False,\n },\n },\n)\nresp.raise_for_status()\ndata = resp.json()\nenv_id = data[\"environment\"][\"id\"]\nprint(f\"Created RL environment: {env_id}\")\nprint(f\"Initial CPU: {data['observation']['metrics']['cpuUsage']}%\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/rl/environments`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n simulationId: \"a638caad-7423-40a3-bb09-f91235d9392d\",\n episodeConfig: {\n maxSteps: 200,\n targetTrafficPattern: \"wave\",\n initialTraffic: 1500,\n targetSLA: { maxLatencyP95: 180, maxErrorRate: 1.0 },\n costBudgetPerHour: 3.50,\n enableFailures: false,\n },\n }),\n});\nconst data = await resp.json();\nconst envId = data.environment.id;\nconsole.log(`Created RL environment: ${envId}`);\nconsole.log(`Initial CPU: ${data.observation.metrics.cpuUsage}%`);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["simulationId","episodeConfig"],"properties":{"simulationId":{"type":"string","format":"uuid","description":"ID of the simulation to train on","example":"a638caad-7423-40a3-bb09-f91235d9392d"},"episodeConfig":{"$ref":"#/components/schemas/EpisodeConfig","description":"Episode limits, target service levels, and optional traffic and failure settings."},"rewardWeights":{"$ref":"#/components/schemas/RewardWeights"},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when episode completes","example":"https://your-app.com/webhooks/rl-episode"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"your-secret-key-here"}}},"examples":{"digitalOceanWaveTraffic":{"summary":"DigitalOcean Droplet cluster — wave traffic pattern","description":"Train an agent on a DigitalOcean simulation using a wave traffic pattern.\nThe simulationId must reference an existing simulation that contains\nDigitalOcean resources (Droplets, Managed Database, Load Balancer).\nThe cost budget of $3.50/hr reflects typical pricing for two\ns-2vcpu-4gb Droplets plus a single Managed PostgreSQL basic node in nyc3.\n","value":{"simulationId":"b7e1c2d4-3f8a-4b5e-9c0d-1e2f3a4b5c6d","episodeConfig":{"maxSteps":200,"targetTrafficPattern":"wave","initialTraffic":1500,"targetSLA":{"maxLatencyP95":180,"maxErrorRate":1},"costBudgetPerHour":3.5,"enableFailures":false}}},"digitalOceanBurstTraffic":{"summary":"DigitalOcean Droplet cluster — burst traffic with failure injection","description":"More challenging episode: burst traffic spikes combined with random failure\ninjection (e.g., Managed Database failover). Useful for training a robust\nagent that can handle both scaling pressure and partial outages.\n","value":{"simulationId":"b7e1c2d4-3f8a-4b5e-9c0d-1e2f3a4b5c6d","episodeConfig":{"maxSteps":150,"targetTrafficPattern":"burst","initialTraffic":800,"targetSLA":{"maxLatencyP95":200,"maxErrorRate":2},"costBudgetPerHour":5,"enableFailures":true}}},"digitalOceanAMDNVMe":{"summary":"DigitalOcean AMD NVMe Droplet cluster — sustained ramp traffic","description":"Train an agent on a DigitalOcean simulation using AMD NVMe Droplets\n(s-2vcpu-4gb-amd). The AMD variant uses NVMe-backed local storage and\nAMD EPYC processors, offering lower per-hour cost than equivalent\nIntel Droplets while delivering comparable CPU performance for\ncompute-bound workloads.\n\nThe cost budget of $2.80/hr reflects typical pricing for two\ns-2vcpu-4gb-amd Droplets plus a single Managed PostgreSQL basic node\nin nyc3. Use this example to benchmark agent policies across Intel vs.\nAMD Droplet fleets under a sustained ramp traffic pattern.\n","value":{"simulationId":"c9f2d3e5-4a7b-4c6f-8d1e-2f3a4b5c6d7e","episodeConfig":{"maxSteps":200,"targetTrafficPattern":"ramp","initialTraffic":1000,"targetSLA":{"maxLatencyP95":175,"maxErrorRate":1},"costBudgetPerHour":2.8,"enableFailures":false,"computeType":"s-2vcpu-4gb-amd"}}},"awsSpot":{"summary":"AWS EC2 Spot — cost-optimized batch RL training using Spot Instances","description":"Train an agent on a cost-sensitive AWS workload backed by EC2 Spot Instances.\nThe simulationId must reference an existing simulation containing Spot-eligible\nresources (e.g. t3.xlarge or c5.2xlarge instances). The cost budget of $1.80/hr\nreflects typical Spot pricing for a small fleet of t3.large instances in us-east-1\n(roughly 70% below on-demand list price). Latency SLA is relaxed to 500 ms p95\nto accommodate the occasional Spot interruption and re-scheduling delay. Use this\nexample to benchmark agent policies that prioritise cost efficiency over strict\nlatency guarantees — a common requirement for ML training, data pipelines, and\nother fault-tolerant batch workloads.\n","value":{"simulationId":"d4e5f6a7-1b2c-4d3e-8f9a-0b1c2d3e4f5a","episodeConfig":{"maxSteps":200,"targetTrafficPattern":"ramp","initialTraffic":1000,"targetSLA":{"maxLatencyP95":500,"maxErrorRate":2},"costBudgetPerHour":1.8,"enableFailures":true},"webhookUrl":"https://your-app.example.com/webhooks/rl-episode","webhookSecret":"aws-spot-rl-secret"}},"gcpSpot":{"summary":"GCP Spot VM — cost-optimized batch RL training using preemptible compute","description":"Train an agent on a cost-sensitive GCP workload backed by Spot VMs (formerly\npreemptible). The simulationId must reference an existing simulation containing\nSpot-eligible resources (e.g. n2-standard-4 or c2-standard-8 Spot instances in\nus-central1). The cost budget of $1.60/hr reflects typical Spot pricing for a\nsmall fleet of n2-standard-4 instances (roughly 70% below on-demand list price).\nLatency SLA is relaxed to 500 ms p95 to accommodate the occasional preemption\nand re-scheduling delay. Use this example to benchmark agent policies that\nprioritise cost efficiency over strict latency guarantees — ideal for ML\ntraining, data processing, and other fault-tolerant batch workloads on GCP.\n","value":{"simulationId":"e5f6a7b8-2c3d-4e5f-9a0b-1c2d3e4f5a6b","episodeConfig":{"maxSteps":200,"targetTrafficPattern":"ramp","initialTraffic":1000,"targetSLA":{"maxLatencyP95":500,"maxErrorRate":2},"costBudgetPerHour":1.6,"enableFailures":true},"webhookUrl":"https://your-app.example.com/webhooks/rl-episode","webhookSecret":"gcp-spot-rl-secret"}},"azureSpot":{"summary":"Azure Spot VM — cost-optimized batch RL training using Azure Spot instances","description":"Train an agent on a cost-sensitive Azure workload backed by Azure Spot VMs.\nThe simulationId must reference an existing simulation containing Spot-eligible\nresources (e.g. Standard_D4s_v3 or Standard_F8s_v2 Spot instances in eastus).\nThe cost budget of $1.50/hr reflects typical Spot pricing for a small fleet of\nStandard_D4s_v3 instances (roughly 70% below pay-as-you-go list price). Latency\nSLA is relaxed to 400 ms p95 to accommodate Azure Spot eviction and re-deployment\ndelays. Use this example to benchmark agent policies that prioritise cost\nefficiency over strict latency guarantees — well-suited for batch inference,\ndata pipelines, and other interruption-tolerant workloads on Azure.\n","value":{"simulationId":"f6a7b8c9-3d4e-4f5a-0b1c-2d3e4f5a6b7c","episodeConfig":{"maxSteps":200,"targetTrafficPattern":"ramp","initialTraffic":1000,"targetSLA":{"maxLatencyP95":400,"maxErrorRate":2},"costBudgetPerHour":1.5,"enableFailures":true},"webhookUrl":"https://your-app.example.com/webhooks/rl-episode","webhookSecret":"azure-spot-rl-secret"}},"ociPreemptible":{"summary":"OCI Ampere A1 Preemptible — cost-optimized batch RL training using OCI preemptible compute","description":"Train an agent on a cost-sensitive OCI workload backed by preemptible Ampere A1\ninstances. The simulationId must reference an existing simulation containing\npreemptible-eligible resources (e.g. VM.Standard.A1.Flex instances in\nus-ashburn-1). OCI preemptible instances are priced at roughly 50% below\nstandard on-demand rates, making them the most cost-effective option in OCI for\nfault-tolerant batch workloads. The cost budget of $1.20/hr reflects typical\npreemptible pricing for a small Ampere A1 fleet. Latency SLA is relaxed to\n600 ms p95 to accommodate the occasional preemption and re-scheduling delay.\nUse this example to benchmark agent policies that prioritise cost efficiency\nover strict latency guarantees — ideal for ML training, data pipelines,\ngenomic workloads, and other interruption-tolerant tasks on OCI.\n","value":{"simulationId":"a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d","episodeConfig":{"maxSteps":200,"targetTrafficPattern":"ramp","initialTraffic":1000,"targetSLA":{"maxLatencyP95":600,"maxErrorRate":2},"costBudgetPerHour":1.2,"enableFailures":true},"webhookUrl":"https://your-app.example.com/webhooks/rl-episode","webhookSecret":"oci-preemptible-rl-secret"}}}}}},"responses":{"201":{"description":"RL environment created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"environment":{"$ref":"#/components/schemas/RLEnvironment"},"observation":{"$ref":"#/components/schemas/Observation"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"description":"Simulation not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/rl/environments/{environmentId}":{"x-stability":"stable","get":{"tags":["Create"],"summary":"Fetch an RL training environment","description":"Retrieve the current state and configuration of an RL environment","operationId":"getRLEnvironment","x-x402-call-type":"rl_env_get","x-x402-price-usdc":"$0.0030","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl https://www.cloudworldmodel.ai/api/rl/environments/env-aws-001 \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nresp = requests.get(\n f\"{BASE_URL}/rl/environments/{ENV_ID}\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\nenv = resp.json()\nprint(f\"Environment {env['id']} isActive={env['isActive']} \"\n f\"step={env['currentStep']}/{env['maxSteps']}\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\nconst ENV_ID = \"env-aws-001\";\n\nconst resp = await fetch(`${BASE_URL}/rl/environments/${ENV_ID}`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n});\nconst env = await resp.json();\nconsole.log(`Environment ${env.id} isActive=${env.isActive} step=${env.currentStep}/${env.maxSteps}`);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Environment details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RLEnvironment"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Create"],"summary":"Delete an RL training environment","description":"Cancel a running RL training episode. This endpoint is idempotent - calling it multiple times\non the same episode will return success without error.\n\n**Cancellation Rules:**\n- Episodes with isActive=true will be cancelled\n- Episodes already cancelled (isActive=false) will return success (idempotent behavior)\n- Cancelled episodes will have isActive set to false and a cancelledAt timestamp\n","operationId":"cancelRLEnvironment","x-x402-call-type":"rl_env_delete","x-x402-price-usdc":"$0.0030","security":[],"parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Episode cancelled successfully or was already cancelled.\nReturns the same response whether cancelling for the first time or if already cancelled\n(idempotent operation).\n","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"isActive":{"type":"boolean","enum":[false]},"cancelledAt":{"type":"string","format":"date-time"},"message":{"type":"string","description":"Message indicating if episode was just cancelled or already cancelled"}}},"examples":{"newlyCancelled":{"value":{"id":"env_abc123","isActive":false,"cancelledAt":"2024-01-15T10:30:00Z","message":"RL training episode cancelled successfully"}},"alreadyCancelled":{"value":{"id":"env_abc123","isActive":false,"cancelledAt":"2024-01-15T09:45:00Z","message":"Episode already cancelled"}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/simulations/stateless":{"x-stability":"stable","post":{"tags":["Execute"],"summary":"Run a stateless synchronous cloud simulation","description":"Executes a tightly bounded cloud scenario entirely in memory through the\nproduction hybrid, pricing, failure, and autoscaling engine paths. No\nsimulation, metric, event, result, ownership, or job record is created.\nThe same seed and input produce the same result. This REST-only operation\nrequires write scope via a bearer key or one x402 payment; MCP transport\nis intentionally omitted because it cannot preserve payment challenge,\nsettlement, and replay semantics end to end.\n","operationId":"runStatelessSimulation","security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.003"}},"x-x402-call-type":"simulation.stateless","x-x402-price-usdc":"$0.0030","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["resources","traffic"],"properties":{"seed":{"type":"integer","minimum":0,"maximum":4294967295,"default":1,"description":"Deterministic seed for reproducible execution with identical input."},"steps":{"type":"integer","minimum":1,"maximum":10,"default":3,"description":"Number of simulation steps to execute synchronously."},"resources":{"type":"array","minItems":1,"maxItems":8,"description":"Required resource graph to model; each resource needs an ID, name, type, and provider.","items":{"type":"object","additionalProperties":false,"required":["id","name","type","provider"],"properties":{"id":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$","description":"Unique caller-supplied identifier used to connect and target this resource."},"name":{"type":"string","minLength":1,"maxLength":80,"description":"Human-readable resource name shown in simulation results."},"type":{"type":"string","enum":["compute","database","storage","network","cache","queue","kubernetes"],"description":"Modeled cloud resource category."},"provider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"],"description":"Cloud provider whose resource and pricing model to use."},"characteristics":{"type":"object","additionalProperties":false,"description":"Optional provider-specific sizing and behavior settings for this resource.","properties":{"instanceType":{"type":"string","minLength":1,"maxLength":64,"description":"Compatibility alias for size. If size is omitted, this value is canonicalized into size; if both are supplied they must match, with size remaining canonical."},"size":{"type":"string","minLength":1,"maxLength":64,"description":"Provider size or SKU used to resolve modeled capacity and hourly price."},"serviceFamily":{"type":"string","minLength":1,"maxLength":64,"description":"Provider service family used to select resource-specific behavior and rates."},"maxThroughput":{"type":"number","exclusiveMinimum":0,"maximum":1000000,"description":"Optional modeled maximum request throughput for the resource."},"baseLatency":{"type":"number","minimum":0,"maximum":60000,"description":"Optional baseline request latency in milliseconds."},"costMultiplier":{"type":"number","minimum":0,"maximum":100,"description":"Optional multiplier applied to the resource's modeled hourly cost."},"maxConnections":{"type":"integer","minimum":1,"maximum":1000000,"description":"Maximum concurrent database connections used for pool-capacity modeling."},"connectionDemand":{"type":"object","additionalProperties":false,"description":"Optional database connection-demand model. Declared counts are ASSUMPTION / plan-time budgets, not live connection observations. Omission preserves the load-derived behavior. Over-budget declared demand adds a bounded rule-based pool-saturation error signal; it is not a provider-calibrated rate. The optional saturationErrorThreshold suppresses the load-derived pool error below the configured utilization without changing connection capacity.","required":["mode"],"properties":{"mode":{"type":"string","enum":["load-derived","declared","max"],"description":"load-derived uses the existing RPS-derived estimate; declared ignores instantaneous RPS; max uses the greatest of load-derived and declared/floor demand."},"saturationErrorThreshold":{"type":"number","minimum":0,"maximum":0.99,"description":"Optional threshold for the load-derived pool error curve. Below it, connection-pool saturation adds no errors; above it, the curve rises with utilization beyond the threshold. When multiple databases exist, set the same value on all of them for this override to apply. Does not change connection capacity."},"declaredConnections":{"type":"integer","minimum":0,"maximum":1000000,"description":"ASSUMPTION / plan-time peak pool budget, e.g. replicas × per-pod pool size; not observed live DB connections."},"idlePoolFloor":{"type":"integer","minimum":0,"maximum":1000000,"description":"ASSUMPTION / plan-time minimum idle pool footprint; not observed live DB connections."}},"allOf":[{"if":{"properties":{"mode":{"const":"load-derived"}},"required":["mode"]},"then":{"not":{"anyOf":[{"required":["declaredConnections"]},{"required":["idlePoolFloor"]}]}}},{"if":{"properties":{"mode":{"enum":["declared","max"]}},"required":["mode"]},"then":{"anyOf":[{"required":["declaredConnections"]},{"required":["idlePoolFloor"]}]}}]},"nodeCount":{"type":"integer","minimum":1,"maximum":20,"description":"Initial Kubernetes worker-node count."},"minNodes":{"type":"integer","minimum":1,"maximum":20,"description":"Minimum Kubernetes worker-node count when autoscaling is enabled."},"maxNodes":{"type":"integer","minimum":1,"maximum":20,"description":"Maximum Kubernetes worker-node count when autoscaling is enabled."},"autoscaling":{"type":"boolean","description":"Whether the resource may scale within its configured bounds."},"pricingModel":{"type":"string","enum":["on-demand","spot"],"description":"Modeled Kubernetes node pricing class."},"eksSpotInterruption":{"type":"object","additionalProperties":false,"properties":{"enabled":{"type":"boolean","const":true,"default":true},"triggerAtStep":{"type":"integer","minimum":0,"maximum":100000,"default":0},"noticeWindowSeconds":{"type":"integer","const":120,"default":120},"simulationSecondsPerStep":{"type":"number","exclusiveMinimum":0,"maximum":60,"default":1},"workloadReplicas":{"type":"integer","minimum":1,"maximum":10000,"default":1},"imageSizeMiB":{"type":"number","minimum":0,"maximum":102400,"default":0},"imageCacheState":{"type":"string","enum":["warm","cold"],"default":"cold"},"imageCacheHit":{"type":"boolean"},"pullBandwidthMiBPerSecond":{"type":"number","exclusiveMinimum":0,"maximum":100000,"default":100},"schedulingCapacity":{"type":"integer","minimum":0,"maximum":10000,"default":1},"startupSeconds":{"type":"number","minimum":0,"maximum":3600,"default":10},"interruptionHandling":{"type":"string","enum":["reschedule","drain-only","fail-fast"],"default":"reschedule"}}}}}}}},"connections":{"type":"array","maxItems":16,"default":[],"description":"Optional directed resource links; each connection identifies a source and target resource.","items":{"type":"object","additionalProperties":false,"required":["sourceId","targetId"],"properties":{"sourceId":{"type":"string","minLength":1,"maxLength":64,"description":"ID of the resource sending traffic or requests along this connection."},"targetId":{"type":"string","minLength":1,"maxLength":64,"description":"ID of the resource receiving traffic or requests along this connection."}}}},"traffic":{"type":"object","additionalProperties":false,"required":["initialRps"],"description":"Required starting traffic and optional one-time traffic change during the run.","properties":{"initialRps":{"type":"integer","minimum":1,"maximum":100000,"description":"Starting offered request rate in requests per second."},"change":{"type":"object","additionalProperties":false,"required":["atStep","targetRps"],"properties":{"atStep":{"type":"integer","minimum":1,"maximum":10,"description":"One-based simulation step at which to change the offered request rate."},"targetRps":{"type":"integer","minimum":1,"maximum":100000,"description":"Offered request rate in requests per second from atStep onward."}}}}},"autoscaling":{"type":"object","additionalProperties":false,"description":"Optional compute fleet autoscaling bounds and CPU target.","properties":{"minInstances":{"type":"integer","minimum":1,"maximum":8,"default":1,"description":"Minimum number of compute instances retained by autoscaling."},"maxInstances":{"type":"integer","minimum":1,"maximum":8,"default":4,"description":"Maximum number of compute instances autoscaling may create."},"targetCpuPercent":{"type":"number","minimum":20,"maximum":90,"default":70,"description":"CPU utilization target used to make autoscaling decisions."}}},"failure":{"type":"object","additionalProperties":false,"required":["type","targetResourceId","atStep"],"description":"Optional single failure to inject against one resource during the run.","properties":{"type":{"type":"string","enum":["instance_kill","instance_down","az_outage","database_overload","network_latency","spot_interruption"],"description":"Failure mode to inject during the synchronous run."},"targetResourceId":{"type":"string","minLength":1,"maxLength":64,"description":"ID of the resource affected by the failure."},"atStep":{"type":"integer","minimum":1,"maximum":10,"description":"One-based simulation step when the failure starts."},"durationSteps":{"type":"integer","minimum":1,"maximum":10,"default":1,"description":"Number of simulation steps for which the failure remains active."},"spotInterruption":{"type":"object","additionalProperties":false,"properties":{"triggerAtStep":{"type":"integer","minimum":0,"maximum":100000},"noticeWindowSeconds":{"type":"integer","const":120},"simulationSecondsPerStep":{"type":"number","exclusiveMinimum":0,"maximum":60},"workloadReplicas":{"type":"integer","minimum":1,"maximum":10000},"imageSizeMiB":{"type":"number","minimum":0,"maximum":102400},"imageCacheState":{"type":"string","enum":["warm","cold"]},"imageCacheHit":{"type":"boolean"},"pullBandwidthMiBPerSecond":{"type":"number","exclusiveMinimum":0,"maximum":100000},"schedulingCapacity":{"type":"integer","minimum":0,"maximum":10000},"startupSeconds":{"type":"number","minimum":0,"maximum":3600},"interruptionHandling":{"type":"string","enum":["reschedule","drain-only","fail-fast"]}}}}}}},"example":{"seed":42,"steps":3,"resources":[{"id":"web-1","name":"Web server","type":"compute","provider":"aws","characteristics":{"instanceType":"t3.medium","autoscaling":true}}],"traffic":{"initialRps":500,"change":{"atStep":2,"targetRps":2000}},"autoscaling":{"minInstances":1,"maxInstances":4,"targetCpuPercent":70}}}}},"responses":{"200":{"description":"Synchronous stateless simulation result","content":{"application/json":{"schema":{"type":"object","required":["execution","cost","performance","autoscaling","utilization","error","reliability","eksSpotInterruptions","coverage","decisions"],"properties":{"execution":{"type":"object","required":["stateless","synchronous","steps","seed","engineVersion"],"properties":{"stateless":{"type":"boolean"},"synchronous":{"type":"boolean"},"steps":{"type":"integer"},"seed":{"type":"integer"},"engineVersion":{"type":"string"}}},"cost":{"type":"object","required":["baselinePerHourUsd","finalPerHourUsd","deltaPerHourUsd","deltaPercent"],"properties":{"baselinePerHourUsd":{"type":"number"},"finalPerHourUsd":{"type":"number"},"deltaPerHourUsd":{"type":"number"},"deltaPercent":{"type":"number"}}},"performance":{"type":"object","required":["latencyPercentileStatus","latencyP50Ms","latencyP95Ms","latencyP99Ms","throughputRps"],"properties":{"latencyPercentileStatus":{"type":"string","enum":["modeled","unavailable"]},"latencyUnavailableReason":{"type":"string"},"latencyP50Ms":{"type":"number","nullable":true},"latencyP95Ms":{"type":"number","nullable":true},"latencyP99Ms":{"type":"number","nullable":true},"throughputRps":{"type":"number"}}},"autoscaling":{"type":"object","description":"Normalized autoscaling settings and the fleet size materialized before the first step.","required":["enabled","requestedMinInstances","requestedMaxInstances","effectiveMinInstances","effectiveMaxInstances","targetCpuPercent","initialFleetSize"],"properties":{"enabled":{"type":"boolean"},"requestedMinInstances":{"type":"integer","nullable":true},"requestedMaxInstances":{"type":"integer","nullable":true},"effectiveMinInstances":{"type":"integer","nullable":true},"effectiveMaxInstances":{"type":"integer","nullable":true},"targetCpuPercent":{"type":"number","nullable":true},"initialFleetSize":{"type":"integer","minimum":0}}},"utilization":{"type":"object","required":["cpuPercent","memoryPercent","resourceCount"],"properties":{"cpuPercent":{"type":"number"},"memoryPercent":{"type":"number"},"kubernetesNodePercent":{"type":"number"},"resourceCount":{"type":"integer"}}},"error":{"type":"object","required":["ratePercent","successfulThroughputRps","breakdown"],"properties":{"ratePercent":{"type":"number"},"successfulThroughputRps":{"type":"number"},"breakdown":{"nullable":true,"type":"object","required":["poolSaturation","dbFailure","computeFailure","capacityOverload","cpuOverload","ociStorage","queueAbsorption"],"properties":{"staleDNS":{"type":"number","minimum":0,"maximum":100,"description":"Modeled stale-DNS failed-request contribution in percentage points, included in ratePercent."},"poolSaturation":{"type":"number"},"databasePressure":{"type":"number","description":"Provider-fit database connection pressure below the usable pool limit; shares a 5-point cap with poolSaturation."},"dbFailure":{"type":"number"},"computeFailure":{"type":"number"},"capacityOverload":{"type":"number"},"cpuOverload":{"type":"number"},"ociStorage":{"type":"number"},"queueAbsorption":{"type":"number"},"dependencyFailure":{"type":"number"},"runtimeMemory":{"type":"number"},"endpointPropagation":{"type":"number","minimum":0,"maximum":100},"startupBackpressure":{"type":"number"}}}}},"reliability":{"type":"object","required":["availabilityPercent","unavailableResourceIds","failureApplied"],"properties":{"availabilityPercent":{"type":"number"},"unavailableResourceIds":{"type":"array","items":{"type":"string"}},"failureApplied":{"type":"boolean"}}},"eksSpotInterruptions":{"type":"array","description":"Final deterministic EKS Spot interruption telemetry for each configured lifecycle, including optional authoritative seeded migrationEvaluation and field-level provenance.","items":{"$ref":"#/components/schemas/EksSpotInterruptionTelemetry"}},"coverage":{"type":"object","required":["level","modeledCount","estimatedCount","knownGapCount","notObservableCategories","structuralLimitations","modeledCostShareOfTotal","billingPolicyCoverage","resources"],"properties":{"level":{"type":"string","enum":["full","partial","limited"]},"modeledCount":{"type":"number"},"estimatedCount":{"type":"number"},"knownGapCount":{"type":"number"},"notObservableCategories":{"type":"array","items":{"type":"string"}},"structuralLimitations":{"type":"array","items":{"type":"string"}},"modeledCostShareOfTotal":{"type":"number"},"billingPolicyCoverage":{"type":"object","required":["complete","status","exclusions"],"properties":{"complete":{"type":"boolean"},"status":{"type":"string","enum":["complete","excluded_policies"]},"exclusions":{"type":"array","items":{"type":"object"}}}},"resources":{"type":"array","items":{"type":"object","required":["name","resourceType","costFidelity","behaviourFidelity","shareOfSimulatedCost","reason","coverageBasis"],"properties":{"name":{"type":"string"},"resourceType":{"type":"string"},"costFidelity":{"type":"string"},"behaviourFidelity":{"type":"string"},"shareOfSimulatedCost":{"type":"number"},"shareExcluded":{"type":"boolean"},"reason":{"type":"string"},"coverageBasis":{"type":"string","enum":["deterministic","ml","estimated","unsupported"]},"note":{"type":"string"}}}}}},"decisions":{"type":"object","required":["blendedSteps","fallbackSteps","events"],"properties":{"blendedSteps":{"type":"integer"},"fallbackSteps":{"type":"integer"},"events":{"type":"array","items":{"type":"object","required":["step","severity","message"],"properties":{"step":{"type":"integer"},"severity":{"type":"string"},"message":{"type":"string"}}}}}},"settlement":{"description":"Added by successful x402 settlement; absent for API-key calls.","type":"object","properties":{"transactionHash":{"type":"string"},"network":{"type":"string"},"payer":{"type":"string"}}},"walletSession":{"description":"Short-lived wallet session added by successful x402 settlement; absent for API-key calls.","type":"object","properties":{"token":{"type":"string"},"expiresAt":{"type":"string"}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"x402 payment required or insufficient credits"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/step-hybrid":{"x-stability":"stable","post":{"tags":["Execute"],"summary":"Run one hybrid ML and rules simulation step","description":"Steps the simulation using the Hybrid Prediction Engine, which blends a\ndeterministic rule-based simulation with a simulated ML-based prediction.\nThe engine uses a confidence threshold to decide how much weight to give\neach path, and falls back to pure rules when ML confidence is low.\n\nReturns the updated simulation state, step metrics, events, the hybrid\ndecision record (including ML confidence and blending rationale), and the\nupdated cumulative hybrid result object.\n\nRequires `write` scope and ownership.\n","operationId":"stepSimulationHybrid","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/simulations/sim-abc123/step-hybrid \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"config\": {\"mlWeight\": 0.6, \"confidenceThreshold\": 0.5}}'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/simulations/sim-abc123/step-hybrid\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\"config\": {\"mlWeight\": 0.6, \"confidenceThreshold\": 0.5}},\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(\"ML confidence:\", data[\"hybridDecision\"][\"mlPrediction\"][\"overallConfidence\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/simulations/sim-abc123/step-hybrid`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ config: { mlWeight: 0.6, confidenceThreshold: 0.5 } }),\n});\nconst data = await resp.json();\nconsole.log(\"ML confidence:\", data.hybridDecision.mlPrediction.overallConfidence);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.003"}},"x-x402-call-type":"simulation_step_hybrid","x-x402-price-usdc":"$0.0030","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID","example":"sim-abc123"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"steps":{"type":"integer","description":"Number of time steps to advance in a single call (1–500, default 1).\nEach sub-step evaluates traffic patterns at the correct elapsed time,\nso a ramp or wave pattern reaches the same final value whether driven\nas N×1 individual calls or a single batch call of N.\n","minimum":1,"maximum":500,"default":1,"example":5},"config":{"type":"object","description":"Optional hybrid engine configuration overrides","properties":{"mlWeight":{"type":"number","minimum":0,"maximum":1,"description":"Weight given to ML prediction vs rule-based (0 = pure rules, 1 = pure ML)","example":0.6},"confidenceThreshold":{"type":"number","minimum":0,"maximum":1,"description":"Minimum ML confidence to apply blending; below this threshold pure rules are used","example":0.5}}}},"example":{"config":{"mlWeight":0.6,"confidenceThreshold":0.5}}},"examples":{"defaultBlend":{"summary":"Default hybrid blend (60% ML, 40% rules)","value":{"config":{"mlWeight":0.6,"confidenceThreshold":0.5}}},"rulesHeavy":{"summary":"Rules-heavy blend — prefer deterministic path","value":{"config":{"mlWeight":0.2,"confidenceThreshold":0.7}}}}}}},"responses":{"200":{"description":"Hybrid step result","content":{"application/json":{"schema":{"type":"object","properties":{"simulation":{"$ref":"#/components/schemas/Simulation"},"scenarioHash":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Canonical SHA-256 of the persisted scenario graph and attached traffic-pattern order."},"effectiveConfigHash":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Versioned prediction SHA-256 over the replay startup-input hash, engine version, and calibration identity. The original replay-only hash remains at replayIdentity.effectiveConfigHash."},"predictionEffectiveConfigHash":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Engine-versioned prediction identity, separate from the replay-input identity."},"engineVersion":{"type":"string","description":"Simulation engine version used to produce the prediction."},"calibrationEvidence":{"$ref":"#/components/schemas/CalibrationEvidence"},"predictionEvidence":{"$ref":"#/components/schemas/PredictionEvidence"},"appWeight":{"$ref":"#/components/schemas/AppWeight"},"appWeightDefaulted":{"type":"boolean"},"replayIdentity":{"$ref":"#/components/schemas/ReplayIdentity"},"metrics":{"type":"object","description":"Metrics snapshot for this step. `throughput` is successful throughput (goodput). The normal simulation engine emits `offeredRps` on the final top-level step result as the aggregate original-client offered load for the same step/window. Legacy persisted histories may omit it; it is never derived from retry/path/per-resource figures.\n","properties":{"costBasis":{"$ref":"#/components/schemas/CostBasis"},"simulationId":{"type":"string"},"timestamp":{"type":"number","description":"Simulation timestamp for this metric snapshot (seconds)"},"latencyAvailability":{"$ref":"#/components/schemas/PredictionLatencyAvailability"},"latencyP50":{"type":"number","nullable":true,"description":"Median latency (ms), or null when no serving-tier latency distribution is modeled."},"latencyP95":{"type":"number","nullable":true,"description":"P95 latency (ms), or null when no serving-tier latency distribution is modeled."},"latencyP99":{"nullable":true,"type":"number","description":"Modeled 99th-percentile latency in milliseconds, or null when no serving-tier latency distribution is modeled. Interpret with latencyP99Basis and predictionEvidence.latencyP99 for the P99-specific basis and evidence level / assumption range.\n"},"latencyPercentileStatus":{"type":"string","enum":["modeled","unavailable"],"description":"Whether P50/P95/P99 are supported by a modeled serving-tier latency distribution."},"latencyP99Basis":{"type":"string","description":"Percentile-specific P99 basis: owned in-VPC internal-ALB fit, scaled from that fit (not directly measured), or uncalibrated generic model. Historical metric rows may omit this field.\n"},"latencyBasis":{"type":"string","description":"General modeled latency path/boundary (for example, in-VPC load-balancer or end-to-end model). Use latencyP99Basis for percentile-specific P99 provenance.\n"},"calibrationEvidence":{"$ref":"#/components/schemas/CalibrationEvidence"},"predictionEvidence":{"$ref":"#/components/schemas/PredictionEvidence"},"engineVersion":{"type":"string","description":"Simulation engine version used to produce this metric."},"predictionEffectiveConfigHash":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Engine-versioned prediction identity, separate from replay identity."},"cpuUsage":{"type":"number","description":"CPU utilization (%)"},"memoryUsage":{"type":"number","description":"Memory utilization (%)"},"throughput":{"type":"number","description":"Completed request rate in RPS; may exceed offeredRps during backlog drain, with queued completions identified in applicationQueue."},"backlogDrainRps":{"type":"number","minimum":0,"description":"Completed request rate above this interval's offeredRps, explicitly attributed to backlog drain."},"offeredRps":{"type":"number","minimum":0,"description":"Aggregate original-client request rate offered to the simulation for this step/window (RPS). The normal simulation engine emits this on the final top-level step result and persists it with current metric history; legacy histories may omit it. Never infer from retry/path/per-resource figures.\n"},"modeledShedRps":{"type":"number","minimum":0,"description":"Aggregate request rate shed by modeled capacity limits for this step/window (RPS). Optional: emitted only when complete aggregate capacity evidence exists, never fabricated from goodput or error rate, and distinct from application errors.\n"},"errorRate":{"type":"number","description":"Error rate (%)"},"costPerHour":{"type":"number","description":"Estimated infrastructure cost per hour (USD)"}}},"events":{"type":"array","items":{"type":"object","required":["type"],"properties":{"type":{"type":"string","description":"Stable event discriminator for this step event."}}}},"hybridDecision":{"$ref":"#/components/schemas/HybridDecision"},"coverageSummary":{"type":"object","description":"Resource/behavior and base-rate coverage summary for this hybrid step —\nwhat CWM could model deterministically, what it estimated, and what it\ncould not see at all. `level` never means complete economic or invoice\ncoverage; inspect `billingPolicyCoverage` separately. Shares are of\nSIMULATED cost, never a real bill; known\ngaps are listed by name with a zero share. Also persisted on each\ndecision in the cumulative hybrid result.\n\n**Share distribution:** `shareOfSimulatedCost` values do NOT necessarily\nsum to 1.0. When an unrecognized SKU is present its share is deliberately\nforced to 0. Use `modeledCostShareOfTotal` as the authoritative denominator.\n","properties":{"level":{"type":"string","enum":["full","partial","limited"],"description":"Resource/behavior fidelity only. Deterministic rules — \"full\": every resource is modeled and no\nnot-observable category applies; \"limited\": at least one\nnot-observable category applies to this resource mix; \"partial\":\nanything estimated or a known gap, but nothing not-observable.\n","example":"partial"},"modeledCount":{"type":"integer","example":12},"estimatedCount":{"type":"integer","example":2},"knownGapCount":{"type":"integer","example":3},"modeledCostShareOfTotal":{"type":"number","description":"Sum of shareOfSimulatedCost ONLY for rows with costFidelity \"modeled\" (deterministically simulated resources). Rows with estimated or known_gap fidelity are excluded because their cost is an approximation. Always present (0 for zero-resource or all-estimated topologies). Authoritative Trust Layer denominator: values in (0, 1] indicate the fraction of simulated cost backed by fully-modeled resources. A value well below 1.0 (e.g. 0.0166) signals that most simulated cost is in unsupported or estimated resources. Consistent across all decision objects in a session for topologies with the same resource mix. Measures rule-coverage completeness, not billing accuracy. A value near 1.0 means nearly all simulated cost is backed by deterministic rules; it is not evidence that the cost figures match a real billing statement or that discounts, support charges, and marketplace fees are included.\n","example":0.9834},"notObservableCategories":{"type":"array","description":"Topology-specific dimensions CWM cannot observe about THIS simulation's environment. Distinct from structuralLimitations. An empty array is valid and intentional — it means no not-observable predicates fired for this topology (not a defect).\n","items":{"type":"string"},"example":["Support plan costs"]},"structuralLimitations":{"type":"array","description":"Permanent model-level exclusions for the providers present in the resource mix (e.g. IAM, CloudWatch metering, AWS Support). Semantically separate from notObservableCategories: these describe limits of the simulation model, not of this particular topology. Consistent across all decision objects within a session (same providers → same structural exclusions).\n","items":{"type":"string"},"example":["IAM","CloudWatch","AWS Support"]},"billingPolicyCoverage":{"type":"object","required":["complete","status","exclusions"],"description":"Economic billing-policy dimension, separate from resource behavior and base-rate fidelity. An RDS resource always lists RDS Extended Support as an unpriced exclusion; CWM does not estimate that surcharge.\n","properties":{"complete":{"type":"boolean"},"status":{"type":"string","enum":["complete","excluded_policies"]},"exclusions":{"type":"array","items":{"type":"object","required":["resourceId","resourceName","policy","priced","reason"],"properties":{"resourceId":{"type":"string"},"resourceName":{"type":"string"},"policy":{"type":"string","example":"RDS Extended Support"},"priced":{"type":"boolean","enum":[false]},"reason":{"type":"string"}}}}}},"resources":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","example":"Web Server"},"resourceType":{"type":"string","description":"Resource type, or \"cost_category\" for category rows.","example":"compute"},"costFidelity":{"type":"string","enum":["modeled","estimated","known_gap","not_observable"],"description":"Coverage fidelity for the cost dimension of this resource. \"modeled\" = Backed by deterministic CWM pricing rules using public list rates. Modeled does not mean billing-validated — real invoices may differ due to committed-use discounts, support surcharges, marketplace fees, and on-demand price changes since the last benchmark calibration. \"estimated\" = approximated via ML-blend or extrapolation. \"known_gap\" = resource exists in the topology but cost is not modeled. \"not_observable\" = CWM has no visibility into this charge category.\n"},"behaviourFidelity":{"type":"string","enum":["modeled","estimated","known_gap","not_observable"]},"shareOfSimulatedCost":{"type":"number","description":"Share (0–1) of total simulated cost. May be deliberately 0 when the SKU is unrecognized (see shareExcluded). Does NOT necessarily sum to 1.0 — use modeledCostShareOfTotal as the authoritative denominator.\n","example":0.42},"shareExcluded":{"type":"boolean","description":"Present and true when shareOfSimulatedCost is deliberately zeroed because the resource's declared SKU/basis is unrecognized or unsupported. Absent (or false) for resources whose zero share is legitimate (e.g. free-tier or zero-cost resources).\n","example":true},"reason":{"type":"string","example":"CPU, latency, autoscaling, warm-up, and hourly cost follow deterministic provider-calibrated rules."},"coverageBasis":{"type":"string","enum":["deterministic","ml","estimated","unsupported"]}}}}}},"hybridResult":{"$ref":"#/components/schemas/HybridSimulationResult"},"expiresAt":{"type":"string","format":"date-time","description":"Present only on x402 (wallet-paying) calls. ISO-8601 timestamp when\nownership (and the simulation itself) expires. Reset to now+90 days on\nevery successful step-hybrid call.\n","example":"2026-10-17T22:00:00.000Z"},"ownership":{"type":"object","description":"Present only on x402 (wallet-paying) calls. Describes the wallet\nthat owns this simulation and when that ownership expires.\n","properties":{"type":{"type":"string","description":"Ownership type — always \"wallet\" for x402 callers.","example":"wallet"},"wallet":{"type":"string","description":"Normalized Ethereum wallet address that owns this simulation.","example":"0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"},"expiresAt":{"type":"string","format":"date-time","description":"ISO-8601 timestamp when ownership (and the simulation itself)\nexpires. Reset to now+90 days on every successful step-hybrid call.\n","example":"2026-10-17T22:00:00.000Z"},"retentionDays":{"type":"integer","description":"Rolling retention window in days (always 90).","example":90}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Simulation already owned by another wallet or API key","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","example":"simulation_owned_by_another_wallet"},"message":{"type":"string","example":"This simulation is owned by a different wallet."}}}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/inject-traffic":{"x-stability":"stable","post":{"tags":["Simulations"],"summary":"Submit a deterministic traffic injection for a simulation","description":"Changes the simulation's traffic level in a single call. Supply\n`targetRps` or `deltaPercent` for deterministic, reproducible results\n(recommended for agents and controlled scenarios):\n\n- **`targetRps`** (absolute, recommended) — sets traffic to exactly this\n value (clamped 1–500 000 RPS). Takes precedence over `deltaPercent`.\n Response `mode: \"target_rps\"`.\n- **`deltaPercent`** (relative, recommended) — new traffic is\n `currentTraffic × (1 + deltaPercent/100)`, rounded (e.g. `75` turns\n 400 RPS into 700 RPS). Response `mode: \"delta_percent\"`.\n- **`random: true`** (explicit random spike) — injects a large random\n spike of +30 000–50 000 RPS on top of current traffic\n (`mode: \"random_spike\"`; if a ramp pattern is active the call instead\n advances it by one increment, `mode: \"ramp_increment\"`). Not suitable\n for controlled scenarios or reproducible runs — use only when an\n uncontrolled spike is intentional.\n\nWhen `targetRps` or `deltaPercent` is supplied before replay identity is\nfinalized, active traffic patterns are **deactivated** (their names are\nechoed in `deactivatedPatterns`) so the injected level persists across\nsubsequent `step` calls. For the SQS Queue Backlog Saturation scenario,\na finalized run with an active pattern instead uses the server-managed\n`runtimeTrafficOverrideActive` control: the persisted pattern and replay\nsnapshot stay unchanged, while subsequent steps honor the explicit\ntraffic level. Other finalized simulations with active patterns return\n`409 Conflict`; replay identity finalization cannot be bypassed.\n\nThe Redis Cache Crash & Recovery scenario starts at 5,000 RPS by default\nand has no active traffic pattern. Its create-time `traffic` value is\nhonored, and this endpoint can change load after stepping without\nchanging finalized replay identity.\n\nEvery response echoes the applied outcome:\n`{ previousRps, appliedRps, mode, requestedTargetRps?, requestedDeltaPercent? }`.\n\n**Unknown fields are rejected before payment settlement** — if the request body\ncontains any field other than `targetRps`, `deltaPercent`, or `random` (for\nexample the common agent mistakes `multiplier`, `factor`, or `spike`), the\nendpoint returns a structured 400 with a hint naming the likely intended field\nand, when the value is numeric, the equivalent `deltaPercent` expression. An\nempty body `{}` also returns 400 with a hint naming all three valid patterns.\nNo USDC charge is settled on a 400 response.\n\nRequires `write` scope and ownership.\n","operationId":"injectTraffic","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/simulations/sim-abc123/inject-traffic \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{ \"targetRps\": 700 }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/simulations/sim-abc123/inject-traffic\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\"targetRps\": 700},\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(\"Traffic event:\", data[\"event\"][\"message\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/simulations/sim-abc123/inject-traffic`, {\n method: \"POST\",\n headers: { \"Authorization\": `Bearer ${API_KEY}`, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ targetRps: 700 }),\n});\nconst data = await resp.json();\nconsole.log(\"Traffic event:\", data.event.message);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.003"}},"x-x402-call-type":"simulation.inject_traffic","x-x402-price-usdc":"$0.0030","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"targetRps":{"type":"integer","minimum":1,"maximum":500000,"description":"Absolute traffic target in RPS. Takes precedence over deltaPercent. Deactivates any active traffic patterns so the level persists across steps."},"deltaPercent":{"type":"number","exclusiveMinimum":-100,"description":"Relative traffic change in percent (e.g. 75 = +75% of current traffic). Ignored when targetRps is supplied."},"random":{"type":"boolean","enum":[true],"description":"Set to `true` to explicitly opt into random spike selection. Mutually exclusive with targetRps and deltaPercent."}},"additionalProperties":false},"examples":{"deterministic":{"summary":"Deterministic — set exact target RPS","value":{"targetRps":700}},"random":{"summary":"Explicit random spike","value":{"random":true}}}}}},"responses":{"200":{"description":"Traffic change applied","content":{"application/json":{"schema":{"type":"object","properties":{"simulation":{"$ref":"#/components/schemas/Simulation"},"event":{"type":"object","description":"Event recording the traffic change","properties":{"id":{"type":"string"},"simulationId":{"type":"string"},"severity":{"type":"string","example":"info"},"message":{"type":"string","example":"Traffic set (target_rps): 400 → 700 RPS (+300 RPS)"}}},"previousRps":{"type":"number","description":"Traffic level (RPS) before this call","example":400},"appliedRps":{"type":"number","description":"Traffic level (RPS) after this call","example":700},"mode":{"type":"string","enum":["target_rps","delta_percent","random_spike","ramp_increment"],"description":"Which injection mode was applied","example":"target_rps"},"requestedTargetRps":{"type":"integer","description":"Echo of the targetRps request field (present only when supplied)"},"requestedDeltaPercent":{"type":"number","description":"Echo of the deltaPercent request field (present only when supplied)"},"deactivatedPatterns":{"type":"array","items":{"type":"string"},"description":"Names of traffic patterns deactivated by a controlled inject (present only when patterns were active)"}}}}}},"400":{"description":"Invalid request body — e.g. `targetRps` out of range (1–500 000), `deltaPercent` ≤ −100, an unknown field, or an empty body `{}` (must supply at least one of `targetRps`, `deltaPercent`, or `random: true`). The response body contains a structured `FieldError` (or array of `FieldError` objects) so agents can programmatically identify the rejected field and self-correct.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Replay identity is finalized and an active traffic pattern would\nneed to be deactivated for this controlled override. The traffic\noverride is rejected; replay identity finalization cannot be bypassed.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/deploy":{"x-stability":"experimental","post":{"tags":["Simulations"],"summary":"Start a rolling ECS Fargate deployment","description":"Starts a rolling replacement of all running tasks in an `ecsFargate`\nresource. Tasks are replaced in batches of `maxSurge`: each new batch\nwarms through `taskStartupSeconds` (the resource's configured startup\ndelay) before the corresponding old tasks begin draining through\n`taskScaleDownSeconds`. During the drain window the ready new tasks\nserve new connections while the old tasks finish existing ones, so\neffective serving capacity is preserved throughout the deploy.\n\nProgress is visible in subsequent `GET /simulations/{simulationId}`\nresponses under each resource's `metadata.fargateDeployReplacingCount`\nand in `normalizedConfig.requestServing[].rollingDeploy` (present only\nwhile a deploy is in flight). A `\"success\"` event is emitted when the\nfinal batch completes.\n\nOnly `ecsFargate` resources support this action. Provide `resourceId`\nor `resourceName` to target a specific resource; omit both to\nauto-detect the first ecsFargate resource in the simulation.\n\nRequires `write` scope and ownership.\n","operationId":"deploySimulationResource","x-x402-call-type":"simulation.deploy","x-x402-price-usdc":"$0.0030","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/simulations/sim-abc123/deploy \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"resourceName\": \"fargate-service-01\", \"maxSurge\": 2}'\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.003"}},"parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","description":"Target a specific ecsFargate resource by `resourceId` or `resourceName`. Omit both to auto-detect the first ecsFargate resource. `maxSurge` controls how many new tasks are launched per batch before the corresponding old tasks begin draining (default 1).","properties":{"resourceId":{"type":"string","description":"ID of the ecsFargate resource to deploy. Takes precedence over resourceName."},"resourceName":{"type":"string","description":"Human-readable name of the ecsFargate resource to deploy."},"maxSurge":{"type":"integer","minimum":1,"default":1,"description":"Number of new tasks to launch per batch before the corresponding old tasks drain (default 1). Larger values speed up the deploy at the cost of temporarily running more tasks."}},"additionalProperties":false},"examples":{"autoDetect":{"summary":"Deploy the only ecsFargate resource (auto-detected)","value":{}},"byNameSurge2":{"summary":"Deploy a named service with maxSurge 2","value":{"resourceName":"fargate-service-01","maxSurge":2}}}}}},"responses":{"200":{"description":"Rolling deployment initiated","content":{"application/json":{"schema":{"type":"object","properties":{"simulation":{"type":"object","description":"Updated simulation state (resources with deploy metadata set)"},"event":{"type":"object","description":"Deployment-started event emitted to the simulation event log"},"deployedResourceId":{"type":"string","description":"ID of the ecsFargate resource being deployed"},"deployedResourceName":{"type":"string","description":"Name of the ecsFargate resource being deployed"}}},"example":{"simulation":{"id":"sim-abc123","resources":[]},"event":{"id":"evt-id","simulationId":"sim-abc123","severity":"info","message":"fargate-service-01: rolling deployment started — replacing 4 tasks in batches of 1"},"deployedResourceId":"res-1","deployedResourceName":"fargate-service-01"}}}},"400":{"description":"No ecsFargate resource found, or resource already deploying"},"402":{"description":"Payment required (x402)"},"403":{"description":"Access denied (not the owner)"},"404":{"description":"Simulation or resource not found"}}}},"/simulations/{simulationId}/inject-failure":{"x-stability":"stable","post":{"tags":["Simulations"],"summary":"Submit a targeted node failure injection","description":"Fails one resource in the simulation (marks it critical) and records an\nerror event.\n\nQuick `inject-failure` uses the resource's bounded `failureParkSteps`\nwindow (or `criticalSteps` when that field is omitted): the targeted\ncompute/Kubernetes node is unavailable and receives no routed traffic\nduring that window, then rejoins the routing pool automatically when\norganic cooldown can resume. A targeted database is also unavailable\nduring its bounded park window. With no serving database and positive\noffered traffic, the next step reports 100% errors, zero throughput,\nand `errorBreakdown.dbFailure: 100`. If an AWS Aurora writer explicitly\ndeclares a healthy related replica (`replicaOf` the writer) via\n`characteristics.auroraStandbyResourceId`, the modeled first step\nremains interrupted; on the second step the standby takes over and\n`metrics.databases[].auroraFailover` reports the failed and standby\nresource IDs, phase, and success. Promotion removes the writer-outage\npenalty while the writer remains parked. `multiAz` or a second,\nunrelated database alone does not prove a write-capable standby.\nEach quick step is one modeled simulation second: the second-step\npromotion is one second after injection, not 30 seconds. Chaos\n`database_crash` instead samples at 10-second intervals with a default\n30-second promotion and a 1800-second sole-writer restart after the\ninjection duration. Compare equivalent outage/serving phases, not\nequal step indices or wall-clock times. These are deterministic\nassumptions, not observed AWS failover timing.\nIts original connection configuration is\nrestored after the window, followed by organic cooldown, or earlier\nvia `recover-resource`. Random selection remains compute/Kubernetes-only.\nNetwork and other non-node resources cannot be quick-failed.\nAuthenticated callers can instead use `failure.create` with\n`database_overload` for a typed database failure; this typed tool is\nnot available anonymously. This is different from the persistent\nreversible `instance_down` failure created through `/failures`, which\nremains unavailable until deactivated or recovered with\n`recover-resource`; `instance_kill` permanently removes the resource.\nPoll the step response or `GET /simulations/{simulationId}` for\n`availabilityState`, `isRoutable`, `routedRps`, and `recoveryProgress`.\n`failureParkSteps` must be a positive integer; an explicit zero is\nrejected rather than treated as an ambiguous no-park mode.\n\n**Targeting (recommended)** — pass `resourceId` or `resourceName`\n(human-readable name; exact match preferred, an unambiguous prefix is\naccepted) in the request body to fail a specific, named resource (e.g.\n`{ \"resourceName\": \"web-server-01\" }`). This path is deterministic and\nreproducible — use it for controlled scenarios, CI fault-tree runs, and\nany case where a predictable outcome matters. If `resourceName` matches\nmore than one resource, a 400 is returned with a `candidates` list\nnaming every match so the caller can retry unambiguously. If the\nresolved resource is not in a faileable state (already critical/warning),\na 400 describing its current status is returned.\n\n**Random mode (opt-in)** — pass `{\"random\": true}` to explicitly select\na random healthy node via the simulation's RNG. This path is\n**non-deterministic** and not suitable for controlled scenarios or CI\nreplay. Use it only when intentional random selection is explicitly\ndesired. An empty body `{}` returns 400 with a hint naming all three\nvalid patterns.\n\nThe response always echoes the applied outcome via\n`resolvedResourceId`, `resolvedResourceName`, and `previousHealth` —\non the random path these are populated from whichever resource was\nselected.\n\nReturns 400 if no healthy nodes are available to fail.\nFor fine-grained control over failure type and duration, use\n`POST /simulations/{simulationId}/failures` instead.\n\n**Resource name discovery (agents: do this first)** — before calling this\nendpoint, call `GET /simulations/{simulationId}` and read the top-level\n`resources` array. Each element has an `id` and a `name` field. Use one of\nthose values as `resourceId` or `resourceName` in your request body. The\nresponse also returns a `healthyNodes` array `[{id, name}, ...]` listing\nevery currently healthy resource, so you can update your inventory on each\ncall without a separate lookup.\n\n**Unknown fields are rejected before payment settlement** — if the request body\ncontains any field other than `resourceId`, `resourceName`, or `random` (for\nexample the common agent mistake `targetResourceId`, `target`, or `resource`),\nthe endpoint returns a structured 400 with a \"Did you mean\" hint before any\nUSDC charge is settled. An empty body `{}` also returns 400 with a hint naming\nall three valid patterns.\n\nRequires `write` scope and ownership.\n","operationId":"injectFailure","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/simulations/sim-abc123/inject-failure \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{ \"resourceName\": \"web-server-01\" }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/simulations/sim-abc123/inject-failure\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\"resourceName\": \"web-server-01\"},\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(\"Failure event:\", data[\"event\"][\"message\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/simulations/sim-abc123/inject-failure`, {\n method: \"POST\",\n headers: { \"Authorization\": `Bearer ${API_KEY}`, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ resourceName: \"web-server-01\" }),\n});\nconst data = await resp.json();\nconsole.log(\"Failure event:\", data.event.message);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.003"}},"x-x402-call-type":"simulation.inject_failure","x-x402-price-usdc":"$0.0030","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Specify `resourceId` or `resourceName` to target a specific node (deterministic, reproducible). Pass `random: true` to explicitly opt into random node selection (non-deterministic).","properties":{"resourceId":{"type":"string","description":"**Recommended** — ID of the resource to fail. Takes precedence over resourceName. Use this for reproducible, deterministic fault injection."},"resourceName":{"type":"string","description":"**Recommended** — Human-readable name of the resource to fail (exact match preferred; unambiguous prefix accepted). Ambiguous names return 400 with a candidates list."},"random":{"type":"boolean","enum":[true],"description":"Set to `true` to explicitly opt into random node selection. Mutually exclusive with resourceId and resourceName."}},"additionalProperties":false},"examples":{"deterministic":{"summary":"Deterministic — target a specific resource","value":{"resourceName":"web-server-01"}},"random":{"summary":"Explicit random node selection","value":{"random":true}}}}}},"responses":{"200":{"description":"Node failure injected","content":{"application/json":{"schema":{"type":"object","properties":{"simulation":{"$ref":"#/components/schemas/Simulation"},"event":{"type":"object","description":"Event recording the node failure"},"resolvedResourceId":{"type":"string","description":"ID of the resource that was failed (targeted or randomly selected)"},"resolvedResourceName":{"type":"string","description":"Name of the resource that was failed"},"previousHealth":{"type":"string","description":"The resource's health status immediately before the failure was applied (e.g. healthy)"},"healthyNodes":{"type":"array","description":"Every resource still in healthy status after this failure was applied, as `{id, name}` pairs. Agents can use this list to pick valid targets for subsequent inject-failure calls without an extra `GET /simulations/{simulationId}` round-trip.","items":{"type":"object","properties":{"id":{"type":"string","description":"Resource ID"},"name":{"type":"string","description":"Resource name"}}}}}}}}},"400":{"description":"No healthy nodes available to fail, ambiguous resourceName (body includes a candidates array listing every matching resource), the targeted resource is not in a faileable state (body describes its current status), or an empty body `{}` (must supply at least one of `resourceName`, `resourceId`, or `random: true`). The response body contains a structured `FieldError` (or array of `FieldError` objects) so agents can programmatically identify the rejected field and self-correct.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/rl/environments/{environmentId}/step":{"x-stability":"stable","post":{"tags":["Execute"],"summary":"Run an RL action and advance one step","description":"Executes an agent action, simulates one time step, and returns the next observation,\nreward, and episode completion status. This is the core training loop interaction.\n\n**Idle TTL:** Each successful step call resets the environment's 2-hour idle timer.\nEnvironments that receive no `step` or `reset` calls for 2 hours are automatically\ndeactivated and their linked simulation artifacts removed. Subsequent requests to a\ndeactivated environment return `404`. Call `step` or `reset` at least once every\n2 hours during long training runs to keep the environment alive.\n","operationId":"stepRLEnvironment","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/rl/environments/env-aws-001/step \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"action\": {\"type\": \"scale_out\", \"parameters\": {\"instanceCount\": 1}}}'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nresp = requests.post(\n f\"{BASE_URL}/rl/environments/{ENV_ID}/step\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\"action\": {\"type\": \"scale_out\", \"parameters\": {\"instanceCount\": 1}}},\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(f\"t={data['t']} reward={data['reward']:.3f} done={data['done']}\")\nprint(f\"CPU: {data['obs']['cpu_util']:.1%} P95: {data['metrics']['latency_p95']} ms \"\n f\"cost: ${data['metrics']['cost_usd_hr']:.2f}/hr\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\nconst ENV_ID = \"env-aws-001\";\n\nconst resp = await fetch(`${BASE_URL}/rl/environments/${ENV_ID}/step`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ action: { type: \"scale_out\", parameters: { instanceCount: 1 } } }),\n});\nconst data = await resp.json();\nconsole.log(`t=${data.t} reward=${data.reward.toFixed(3)} done=${data.done}`);\nconsole.log(`CPU: ${(data.obs.cpu_util * 100).toFixed(1)}% P95: ${data.metrics.latency_p95} ms cost: $${data.metrics.cost_usd_hr.toFixed(2)}/hr`);\n"},{"lang":"Python","label":"Python – warm-up/training","source":"\"\"\"\nTwo-phase training loop: fast warm-up followed by fine-grained training.\n\nPhase 1 – Warm-up (300 s ticks)\n Use large tick_seconds to fast-forward through startup noise before the\n agent starts making meaningful autoscaling decisions. Each step advances\n the simulation clock by 5 minutes, so 20 warm-up steps cover ~1.7 hours\n of simulated time in seconds of wall time.\n\nPhase 2 – Training (60 s ticks)\n Switch to 1-minute ticks for precise autoscaling control. The agent now\n observes and reacts to traffic on a per-minute basis, matching the\n granularity of real autoscaling cooldown windows (e.g. AWS default: 300 s).\n\"\"\"\nimport requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nsession = requests.Session()\nsession.headers.update({\"Authorization\": f\"Bearer {API_KEY}\"})\n\ndef step(action: dict, tick_seconds: int) -> dict:\n resp = session.post(\n f\"{BASE_URL}/rl/environments/{ENV_ID}/step\",\n json={\"action\": action, \"tick_seconds\": tick_seconds},\n )\n resp.raise_for_status()\n return resp.json()\n\nNO_OP = {\"type\": \"no_op\", \"parameters\": {}}\n\n# --- Phase 1: Warm-up (300 s / 5-minute ticks) ---\nWARMUP_STEPS = 20\nprint(\"=== Warm-up phase (300 s ticks) ===\")\nfor i in range(WARMUP_STEPS):\n data = step(NO_OP, tick_seconds=300)\n print(\n f\" warmup {i+1:2d}/{WARMUP_STEPS} \"\n f\"sim={data['sim_time_human']:>8s} \"\n f\"cpu={data['obs']['cpu_util']:.1%} \"\n f\"instances={data['obs']['instances']}\"\n )\n if data[\"done\"]:\n print(\" Episode ended during warm-up — reset and retry.\")\n break\n\n# --- Phase 2: Training (60 s / 1-minute ticks) ---\nprint(\"\\n=== Training phase (60 s ticks) ===\")\ndone = False\nwhile not done:\n cpu = data[\"obs\"][\"cpu_util\"]\n if cpu > 0.75:\n action = {\"type\": \"scale_out\", \"parameters\": {\"instanceCount\": 1}}\n elif cpu < 0.30 and data[\"obs\"][\"instances\"] > 1:\n action = {\"type\": \"scale_in\", \"parameters\": {\"instanceCount\": 1}}\n else:\n action = NO_OP\n\n data = step(action, tick_seconds=60)\n done = data[\"done\"]\n obs = data[\"obs\"]\n # Per-node routed load — optional field, so guard against its absence\n # (absent on reset observations and compute-free simulations):\n rps_per_node = obs.get(\"routedRps_per_node\", obs[\"rps\"] / max(obs[\"instances\"], 1))\n print(\n f\" t={data['t']:4d} sim={data['sim_time_human']:>8s} \"\n f\"reward={data['reward']:+.3f} \"\n f\"cpu={data['obs']['cpu_util']:.1%} \"\n f\"rps/node={rps_per_node:.1f} \"\n f\"p95={data['metrics']['latency_p95']} ms \"\n f\"cost=${data['metrics']['cost_usd_hr']:.2f}/hr \"\n f\"done={done}\"\n )\n"},{"lang":"Node.js","label":"Node.js – warm-up/training","source":"/**\n * Two-phase training loop: fast warm-up followed by fine-grained training.\n *\n * Phase 1 – Warm-up (300 s ticks)\n * Use large tick_seconds to fast-forward through startup noise before the\n * agent starts making meaningful autoscaling decisions. Each step advances\n * the simulation clock by 5 minutes, so 20 warm-up steps cover ~1.7 hours\n * of simulated time in seconds of wall time.\n *\n * Phase 2 – Training (60 s ticks)\n * Switch to 1-minute ticks for precise autoscaling control. The agent now\n * observes and reacts to traffic on a per-minute basis, matching the\n * granularity of real autoscaling cooldown windows (e.g. AWS default: 300 s).\n */\nconst BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\nconst ENV_ID = \"env-aws-001\";\n\nasync function step(action, tickSeconds) {\n const resp = await fetch(`${BASE_URL}/rl/environments/${ENV_ID}/step`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ action, tick_seconds: tickSeconds }),\n });\n if (!resp.ok) throw new Error(`step failed: ${resp.status}`);\n return resp.json();\n}\n\nconst NO_OP = { type: \"no_op\", parameters: {} };\n\n// --- Phase 1: Warm-up (300 s / 5-minute ticks) ---\nconst WARMUP_STEPS = 20;\nconsole.log(\"=== Warm-up phase (300 s ticks) ===\");\nlet data;\nfor (let i = 0; i < WARMUP_STEPS; i++) {\n data = await step(NO_OP, 300);\n console.log(\n ` warmup ${String(i + 1).padStart(2)}/${WARMUP_STEPS}` +\n ` sim=${data.sim_time_human.padStart(8)}` +\n ` cpu=${(data.obs.cpu_util * 100).toFixed(1)}%` +\n ` instances=${data.obs.instances}`\n );\n if (data.done) {\n console.log(\" Episode ended during warm-up — reset and retry.\");\n break;\n }\n}\n\n// --- Phase 2: Training (60 s / 1-minute ticks) ---\nconsole.log(\"\\n=== Training phase (60 s ticks) ===\");\nlet done = false;\nwhile (!done) {\n const cpu = data.obs.cpu_util;\n let action;\n if (cpu > 0.75) {\n action = { type: \"scale_out\", parameters: { instanceCount: 1 } };\n } else if (cpu < 0.30 && data.obs.instances > 1) {\n action = { type: \"scale_in\", parameters: { instanceCount: 1 } };\n } else {\n action = NO_OP;\n }\n\n data = await step(action, 60);\n done = data.done;\n console.log(\n ` t=${String(data.t).padStart(4)} sim=${data.sim_time_human.padStart(8)}` +\n ` reward=${data.reward >= 0 ? \"+\" : \"\"}${data.reward.toFixed(3)}` +\n ` cpu=${(data.obs.cpu_util * 100).toFixed(1)}%` +\n ` p95=${data.metrics.latency_p95} ms` +\n ` cost=$${data.metrics.cost_usd_hr.toFixed(2)}/hr` +\n ` done=${done}`\n );\n}\n"},{"lang":"Python","label":"Python – mid-episode cold instance (warmup_factor)","source":"\"\"\"\nMid-episode warmup_factor safe-access pattern.\n\nobs.warmup_factor is an optional field — it is ABSENT from the observation\nvector until the first cold compute instance exists in the episode. This happens\nin two cases:\n 1. An `add_resource` action provisions a new compute resource mid-episode.\n 2. Canvas autoscaling adds a compute replica in response to high CPU/traffic.\n\nBecause the field appears dynamically, agents building a fixed-width\nobservation vector must default to 1.0 (fully warmed) when the field is absent:\n\n wf = obs.get(\"warmup_factor\", 1.0)\n\nA value < 1.0 means at least one compute instance is still warming up and is\nnot yet at full throughput. Use wf directly as a feature — do not re-derive it\nfrom step counters or metrics.compute[].warmup_steps_remaining.\n\nThe pattern below shows a typical episode loop that handles both the pre-warmup\nphase (field absent) and the post-add_resource warm-up window (field in (0, 1]).\n\"\"\"\nimport requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nsession = requests.Session()\nsession.headers.update({\"Authorization\": f\"Bearer {API_KEY}\"})\n\ndef step(action: dict) -> dict:\n resp = session.post(\n f\"{BASE_URL}/rl/environments/{ENV_ID}/step\",\n json={\"action\": action},\n )\n resp.raise_for_status()\n return resp.json()\n\ndone = False\nwhile not done:\n # --- build observation vector (always safe to call even if field absent) ---\n obs = step({\"type\": \"no_op\", \"parameters\": {}})[\"obs\"]\n wf = obs.get(\"warmup_factor\", 1.0) # 1.0 = fully warmed / no cold instance\n\n cpu = obs[\"cpu_util\"]\n\n # penalise decisions made while a cold instance is still warming up\n effective_cpu = cpu / wf if wf > 0 else cpu\n\n if effective_cpu > 0.75:\n action = {\"type\": \"scale_out\", \"parameters\": {\"instanceCount\": 1}}\n elif effective_cpu < 0.30 and obs[\"instances\"] > 1:\n action = {\"type\": \"scale_in\", \"parameters\": {\"instanceCount\": 1}}\n else:\n action = {\"type\": \"no_op\", \"parameters\": {}}\n\n data = step(action)\n done = data[\"done\"]\n print(\n f\" t={data['t']:4d} wf={wf:.2f} \"\n f\"cpu={cpu:.1%} effective_cpu={effective_cpu:.1%} \"\n f\"reward={data['reward']:+.3f} done={done}\"\n )\n"},{"lang":"Node.js","label":"Node.js – mid-episode cold instance (warmup_factor)","source":"/**\n * Mid-episode warmup_factor safe-access pattern.\n *\n * obs.warmup_factor is an optional field — it is ABSENT from the observation\n * vector until the first cold compute instance exists in the episode. This\n * happens in two cases:\n * 1. An `add_resource` action provisions a new compute resource mid-episode.\n * 2. Canvas autoscaling adds a compute replica in response to high CPU/traffic.\n *\n * Because the field appears dynamically, agents building a fixed-width\n * observation vector must default to 1.0 (fully warmed) when the field is absent:\n *\n * const wf = data.obs.warmup_factor ?? 1.0;\n *\n * A value < 1.0 means at least one compute instance is still warming up.\n * Use wf directly as a feature — do not re-derive it from\n * metrics.compute[].warmup_steps_remaining.\n */\nconst BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\nconst ENV_ID = \"env-aws-001\";\n\nasync function step(action) {\n const resp = await fetch(`${BASE_URL}/rl/environments/${ENV_ID}/step`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ action }),\n });\n if (!resp.ok) throw new Error(`step failed: ${resp.status}`);\n return resp.json();\n}\n\nlet done = false;\nlet data = await step({ type: \"no_op\", parameters: {} });\nwhile (!done) {\n // --- build observation vector (always safe even if field absent) ---\n const obs = data.obs;\n const wf = obs.warmup_factor ?? 1.0; // 1.0 = fully warmed / no cold instance\n\n const cpu = obs.cpu_util;\n // penalise decisions made while a cold instance is still warming up\n const effectiveCpu = wf > 0 ? cpu / wf : cpu;\n\n let action;\n if (effectiveCpu > 0.75) {\n action = { type: \"scale_out\", parameters: { instanceCount: 1 } };\n } else if (effectiveCpu < 0.30 && obs.instances > 1) {\n action = { type: \"scale_in\", parameters: { instanceCount: 1 } };\n } else {\n action = { type: \"no_op\", parameters: {} };\n }\n\n data = await step(action);\n done = data.done;\n console.log(\n ` t=${String(data.t).padStart(4)} wf=${wf.toFixed(2)}` +\n ` cpu=${(cpu * 100).toFixed(1)}% effectiveCpu=${(effectiveCpu * 100).toFixed(1)}%` +\n ` reward=${data.reward >= 0 ? \"+\" : \"\"}${data.reward.toFixed(3)} done=${done}`\n );\n}\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.003"}},"x-x402-call-type":"rl.step","x-x402-price-usdc":"$0.0030","parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the RL environment","example":"env-aws-001"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["action"],"properties":{"action":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["adjust_threshold","scale_out","scale_in","add_resource","remove_resource","no_op","set_recovery_policy"],"description":"Type of action to execute"},"parameters":{"type":"object","additionalProperties":true,"description":"Action-specific parameters (cpuThreshold, instanceCount, resourceId, etc.)"}},"example":{"type":"scale_out","parameters":{"instanceCount":1}}},"tick_seconds":{"type":"integer","minimum":1,"maximum":3600,"description":"Per-step override for the simulation clock advancement. When provided,\nthis value overrides the episode-level `tick_seconds` set in `episodeConfig`\nfor this step only. Useful for agents that want to fast-forward through\nwarm-up phases (e.g. 300 s ticks) and then switch to finer-grained steps\n(e.g. 60 s ticks) for precise autoscaling decisions. The actual value\nused is reflected in `observation.tick_seconds` in the response.\n","example":300}},"example":{"action":{"type":"scale_out","parameters":{"instanceCount":1}}}},"examples":{"doAdjustThreshold":{"summary":"DO: tune Droplet autoscaling thresholds","description":"Lower the CPU scale-out trigger from the default 70 % to 65 % so the\nDigitalOcean autoscaler scales out earlier, reducing latency during wave\ntraffic peaks. Also tightens the throughput threshold to 70 %.\n","value":{"action":{"type":"adjust_threshold","parameters":{"cpuThreshold":65,"throughputThreshold":70,"latencyThreshold":160}}}},"doScaleOut":{"summary":"DO: proactively add a Droplet replica","description":"Force-provision one additional s-2vcpu-4gb Droplet replica ahead of a\npredicted traffic spike. The first observation after this action will\nreflect the ~30 s cold-start latency overhead modelled for DigitalOcean.\n","value":{"action":{"type":"scale_out","parameters":{"instanceCount":1}}}},"doScaleIn":{"summary":"DO: remove an idle Droplet replica","description":"Remove one Droplet replica when traffic has subsided. The 180 s cooldown\nin the DigitalOcean autoscaling profile prevents thrashing between\nscale-in and scale-out actions.\n","value":{"action":{"type":"scale_in","parameters":{"instanceCount":1}}}},"digitaloceanAMDNVMeScaleOut":{"summary":"DO AMD NVMe: add an s-2vcpu-4gb-amd Droplet replica","description":"Force-provision one additional AMD NVMe Droplet (s-2vcpu-4gb-amd,\n$0.038/hr per instance) ahead of a predicted traffic spike. The AMD\nEPYC variant's NVMe-backed local storage delivers higher I/O throughput\nthan the standard Intel Droplet at the same price point. After the ~30 s\ncold-start overhead the cluster moves from 2 to 3 instances, bringing\nCPU utilisation down from ~71 % to ~56 % and P95 latency from ~148 ms\nto ~118 ms. This action triggers the `digitaloceanAMDNVMeStep` response\nshape, where resources are named \"Droplet s-2vcpu-4gb-amd\" to\ndistinguish AMD NVMe Droplets from standard Intel Droplets.\n","value":{"action":{"type":"scale_out","parameters":{"instanceCount":1}}}}}}}},"responses":{"200":{"description":"Step executed successfully","content":{"application/json":{"schema":{"type":"object","required":["t","obs","metrics","reward","reward_components","done","sim_time_human","info"],"properties":{"t":{"type":"integer","description":"Current simulation time step (incremented after each action)","example":15},"obs":{"$ref":"#/components/schemas/RLObs"},"metrics":{"$ref":"#/components/schemas/RLMetrics"},"resources":{"type":"array","description":"Full resource list with per-resource `recoveryPolicy`. Resources that have never had `set_recovery_policy` applied carry the global defaults (criticalCpuThreshold: 80, criticalSteps: 4, warningCpuThreshold: 70, warningSteps: 3). Use this to confirm a `set_recovery_policy` action took effect or to compare healing configurations across resources.\n","items":{"$ref":"#/components/schemas/Resource"}},"reward":{"type":"number","description":"Scalar total reward for this step (weighted sum of reward_components)","example":0.481},"reward_components":{"type":"object","description":"Individual reward sub-scores before weighting","properties":{"performance":{"type":"number","description":"Performance score (0–1, based on latency and errors)","example":0.812},"cost":{"type":"number","description":"Cost efficiency score (0–1, based on budget)","example":0.924},"stability":{"type":"number","description":"Stability score (−1 to 1, penalizes excessive changes)","example":-0.1},"sla":{"type":"number","description":"SLA compliance score (−1 to 0, penalizes violations)","example":0},"connection_pressure":{"type":"number","description":"DB connection-pool saturation penalty. Only present when the simulation contains database resources. 0 when pool pressure ≤ 1.0 (healthy); decreases with slope −1 per unit of pressure from 1.0 to 1.5 (reaching −0.5), then drops with slope −2 per unit above 1.5 — twice as steep — flooring at −1.0 at pressure ≥ 1.75. Added directly to the weighted sum of the other four components so agents are penalised for driving pools into exhaustion even before latency rises. NOTE: This component is absent until the episode first contains a database resource. When a DB is added mid-episode (via add_resource), the penalty is ramped in linearly over 5 steps (starting at 1/5 of its full magnitude on the first step it appears, reaching full strength after 5 steps) so its introduction does not cause a sudden step-to-step reward discontinuity. Agents may still treat the first appearance as a near-zero baseline.\n","example":-0.3},"unmodeled_cost":{"type":"number","description":"Penalty applied when one or more cost dimensions are active but not modeled in the reward function (e.g. egress charges, cross-AZ traffic). Absent when no unmodeled dimensions are detected. Magnitude equals -(count × unmodeled_cost_penalty_per_dimension). Agents should treat this as a persistent blind-spot signal rather than a transient cost spike.\n","example":-0.2}}},"done":{"type":"boolean","description":"Whether the episode is complete","example":false},"sim_time_human":{"type":"string","description":"Human-readable representation of the elapsed simulated time.\nFormat is `Xh Ym` when at least one hour has elapsed,\n`Xm Ys` when at least one minute (but less than one hour)\nhas elapsed, and `Xs` for less than one minute.\nMirrors the value inside `info.sim_time_human` for convenient\ntop-level access without unpacking the info object.\n","example":"15s"},"info":{"type":"object","description":"Additional diagnostic information","additionalProperties":true,"properties":{"stepMetrics":{"type":"object","description":"Raw metrics from this step"},"eventsGenerated":{"type":"integer","description":"Number of events generated this step"},"currentCost":{"type":"number","description":"Current cost per hour"},"sim_time_human":{"type":"string","description":"Human-readable representation of the elapsed simulated time.\nFormat is `Xh Ym` when at least one hour has elapsed,\n`Xm Ys` when at least one minute (but less than one hour)\nhas elapsed, and `Xs` for less than one minute.\n","example":"1h 0m"},"scale_clamped":{"type":"boolean","description":"Present and `true` when a `scale_out` or `scale_in` action was trimmed to honour the effective per-resource instance bounds (`characteristics.maxInstances` / `characteristics.minInstances` vs `autoscalingConfig.maxInstances` / `autoscalingConfig.minInstances`, whichever is tighter). Absent when no clamping occurred.\n","example":true},"requested":{"type":"integer","description":"Present when `scale_clamped` is `true`. The number of instances the action *requested* to add (scale_out) or remove (scale_in) — i.e. the `instanceCount` action parameter.\n","example":10},"actual":{"type":"integer","description":"Present when `scale_clamped` is `true`. The number of instances that were *actually* added (scale_out) or removed (scale_in) after applying the effective bound. `0` when the action was fully blocked (fleet is already at the hard limit).\n","example":2},"limit":{"type":"integer","description":"Present when `scale_clamped` is `true`. The effective bound that triggered clamping (`effectiveMax` for scale_out or `effectiveMin` for scale_in).\n","example":3}}},"tradeoffSummary":{"$ref":"#/components/schemas/RLTradeoffSummary"},"unmodeled_cost_warning":{"type":"array","items":{"type":"string"},"description":"List of cost dimension identifiers that are active this step but not modeled in the reward function. Present (and non-empty) only when at least one unmodeled dimension is detected. Omitted when all detected dimensions are modeled. Known values: `egress` (network egress charges are incurred whenever traffic > 0), `cross_az_traffic` (cross-AZ data transfer charges apply when resources with different availability zones are connected).\n","example":["egress","cross_az_traffic"]},"unmodeled_cost_penalty":{"type":"number","description":"Total reward penalty applied this step due to unmodeled cost dimensions. Equals -(count of active unmodeled dimensions × episodeConfig.unmodeled_cost_penalty_per_dimension). Present only when `unmodeled_cost_warning` is non-empty.\n","example":-0.2}}},"examples":{"awsStepWithDb":{"summary":"AWS — step response after scale-out (EC2 m5.large + RDS, us-east-1)","description":"The agent scaled out by 1 EC2 instance (now 3 × m5.large). CPU dropped\nfrom 69 % to 53 %, P95 latency is well within the 200 ms SLA, and cost\nrose to $0.57/hr. connection_pressure reflects the RDS Multi-AZ\nconnection-pool ratio; 0.42 is healthy (well below pool exhaustion).\n","value":{"t":42,"obs":{"rps":4750,"cpu_util":0.534,"instances":3,"routedRps_per_node":1583.3,"traffic":4750,"currentTime":42},"metrics":{"cost_usd_hr":0.57,"latency_p95":98,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.42},"reward":0.531,"reward_components":{"performance":0.841,"cost":0.91,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"42s","info":{"stepMetrics":{"cpuUsage":53.4,"throughput":4750},"eventsGenerated":1,"currentCost":0.57,"sim_time_human":"42s"}}},"gcpStepWithDb":{"summary":"GCP — step response after no-op (GCE e2-standard-4 + Cloud SQL, us-central1)","description":"The agent issued a no-op while the cluster ran at 2 × e2-standard-4 GCE\ninstances with Cloud Load Balancing and Cloud SQL. CPU is stable at 48 %,\nP95 latency is 91 ms, and cost is $0.44/hr. connection_pressure reflects\nthe Cloud SQL connection-pool ratio; 0.38 indicates plenty of headroom.\n","value":{"t":30,"obs":{"rps":3820,"cpu_util":0.478,"instances":2,"routedRps_per_node":1910,"traffic":3820,"currentTime":30},"metrics":{"cost_usd_hr":0.44,"latency_p95":91,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.38},"reward":0.612,"reward_components":{"performance":0.873,"cost":0.951,"stability":0,"sla":0},"done":false,"sim_time_human":"30s","info":{"stepMetrics":{"cpuUsage":47.8,"throughput":3820},"eventsGenerated":0,"currentCost":0.44,"sim_time_human":"30s"}}},"azureStepWithDb":{"summary":"Azure — step response after no-op (Standard_D4s_v3 + Azure SQL, East US)","description":"The agent issued a no-op while the cluster ran at 2 × Standard_D4s_v3\nVMs behind Azure Load Balancer with Azure SQL. CPU is at 55 %, P95\nlatency is 104 ms, and cost is $0.52/hr. connection_pressure reflects\nthe Azure SQL connection-pool ratio; 0.51 is moderate but healthy.\n","value":{"t":28,"obs":{"rps":4300,"cpu_util":0.551,"instances":2,"traffic":4300,"currentTime":28},"metrics":{"cost_usd_hr":0.52,"latency_p95":104,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.51},"reward":0.487,"reward_components":{"performance":0.796,"cost":0.938,"stability":0,"sla":0},"done":false,"sim_time_human":"28s","info":{"stepMetrics":{"cpuUsage":55.1,"throughput":4300},"eventsGenerated":0,"currentCost":0.52,"sim_time_human":"28s"}}},"ociStepWithDb":{"summary":"OCI — step response after scale-in (VM.Standard3.Flex + Autonomous DB, us-ashburn-1)","description":"The agent scaled in by 1 instance (now 2 × VM.Standard3.Flex) after\ntraffic subsided. CPU is low at 34 %, P95 latency is 72 ms, and cost\nis $0.31/hr. connection_pressure reflects the Autonomous Database\nconnection-pool ratio; 0.29 is well within healthy bounds.\n","value":{"t":25,"obs":{"rps":4820,"cpu_util":0.342,"instances":2,"traffic":4820,"currentTime":25},"metrics":{"cost_usd_hr":0.31,"latency_p95":72,"error_rate":0.001,"uptime":0.999,"sla_violations":0,"connection_pressure":0.29},"reward":0.703,"reward_components":{"performance":0.921,"cost":0.985,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"25s","info":{"stepMetrics":{"cpuUsage":34.2,"throughput":4820},"eventsGenerated":1,"currentCost":0.31,"sim_time_human":"25s"}}},"doStepAfterScaleOut":{"summary":"DO: step response after scaling out to 3 Droplet replicas (with Managed PostgreSQL)","description":"The agent scaled out by 1 Droplet (now 3 × s-2vcpu-4gb). CPU dropped\nfrom 72 % to 58 %, P95 latency improved to 104 ms, and cost rose to\n$1.08/hr (within the $3.50/hr budget). Reward is positive because the\nSLA is satisfied and cost efficiency is high. connection_pressure\nreflects the Managed PostgreSQL connection-pool ratio; 0.44 is healthy.\n","value":{"t":15,"obs":{"rps":1620,"cpu_util":0.582,"instances":3,"traffic":1620,"currentTime":15},"metrics":{"cost_usd_hr":1.08,"latency_p95":104,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.44},"reward":0.481,"reward_components":{"performance":0.812,"cost":0.924,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"15s","info":{"stepMetrics":{"cpuUsage":58.2,"throughput":1620},"eventsGenerated":1,"currentCost":1.08,"sim_time_human":"15s"}}},"doStepThresholdTune":{"summary":"DO: step response after tuning CPU/throughput thresholds","description":"The agent lowered the CPU threshold to 65 % without changing instance\ncount. The cluster runs at 2 Droplets. Metrics are stable; the small\nstability penalty (-0.05) reflects the configuration change itself.\nNo database resource in this scenario — connection_pressure is absent.\n","value":{"t":8,"obs":{"rps":1480,"cpu_util":0.614,"instances":2,"traffic":1480,"currentTime":8},"metrics":{"cost_usd_hr":0.72,"latency_p95":118,"error_rate":0.004,"uptime":0.996,"sla_violations":0},"reward":0.392,"reward_components":{"performance":0.743,"cost":0.96,"stability":-0.05,"sla":0},"done":false,"sim_time_human":"15s","info":{"stepMetrics":{"cpuUsage":61.4,"throughput":1480},"eventsGenerated":0,"currentCost":0.72,"sim_time_human":"15s"}}},"digitaloceanAMDNVMeStep":{"summary":"DigitalOcean — step response after scale-out (s-2vcpu-4gb-amd AMD NVMe Droplets, nyc3)","description":"The agent scaled out by 1 AMD NVMe Droplet (now 3 × s-2vcpu-4gb-amd,\n$0.038/hr per instance). CPU dropped from 71 % to 56 %, P95 latency\nimproved to 98 ms, and cost rose to $0.93/hr (within the $3.50/hr budget).\nThe per-instance compute cost is $0.038/hr, so 3 instances total $0.114/hr\nfor compute alone; `metrics.cost_usd_hr` (0.93) is the full simulated stack\ncost including the DO Load Balancer, Managed PostgreSQL, and modelled\noverhead. The AMD EPYC variant's NVMe-backed local storage yields slightly\nlower P95 latency than the Intel s-2vcpu-4gb equivalent at the same traffic\nlevel — 98 ms vs ~104 ms after scale-out. connection_pressure reflects the\nManaged PostgreSQL db-s-2vcpu-4gb connection-pool ratio; 0.41 is healthy.\nResources are named \"Droplet s-2vcpu-4gb-amd\" to distinguish AMD NVMe\nDroplets from standard Intel Droplets.\n","value":{"t":20,"obs":{"rps":1620,"cpu_util":0.561,"instances":3,"traffic":1620,"currentTime":20},"metrics":{"cost_usd_hr":0.93,"latency_p95":98,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.41},"reward":0.514,"reward_components":{"performance":0.834,"cost":0.918,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"20s","info":{"stepMetrics":{"cpuUsage":56.1,"throughput":1620},"eventsGenerated":1,"currentCost":0.93,"sim_time_human":"20s"}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/rl/environments/{environmentId}/batch-step":{"x-stability":"stable","post":{"tags":["RL Environments"],"summary":"Run multiple RL actions in a single request","description":"Executes up to 30 actions sequentially in a single HTTP round-trip, advancing the\nsimulation by one step per action. Stops early and returns partial results if the\nepisode ends (`done: true`) before all actions are processed.\n\n**Rate limiting:** Each action in the batch counts as one call against the\n5 000 req/hr RL training quota. A batch of 30 actions consumes 30 quota units.\nIf the quota is exhausted mid-batch, the endpoint returns 429 with a `Retry-After`\nheader indicating how many seconds remain until the window resets.\n\n**Idle TTL:** The last successful batch-step call resets the environment's 2-hour\nidle timer, the same as a single `step` call.\n","operationId":"batchStepRLEnvironment","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/rl/environments/env-aws-001/batch-step \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"steps\": [\n {\"action\": {\"type\": \"scale_out\", \"parameters\": {\"instanceCount\": 1}}},\n {\"action\": {\"type\": \"no_op\", \"parameters\": {}}},\n {\"action\": {\"type\": \"no_op\", \"parameters\": {}}}\n ]\n }'\n"},{"lang":"Python","label":"Python","source":"import time, requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nsession = requests.Session()\nsession.headers.update({\"Authorization\": f\"Bearer {API_KEY}\"})\n\ndef batch_step(steps: list[dict], max_retries: int = 5) -> dict:\n for attempt in range(max_retries):\n resp = session.post(\n f\"{BASE_URL}/rl/environments/{ENV_ID}/batch-step\",\n json={\"steps\": steps},\n )\n if resp.status_code == 429:\n retry_after = int(resp.headers.get(\"Retry-After\", 60))\n print(f\"Rate limited — waiting {retry_after}s\")\n time.sleep(retry_after)\n continue\n resp.raise_for_status()\n return resp.json()\n raise RuntimeError(\"Exceeded max retries\")\n\nNO_OP = {\"action\": {\"type\": \"no_op\", \"parameters\": {}}}\nSCALE_OUT = {\"action\": {\"type\": \"scale_out\", \"parameters\": {\"instanceCount\": 1}}}\n\n# Warm-up: 30 no-ops at 300 s each (covers ~2.5 hours sim time)\nwarmup = batch_step([{**NO_OP, \"tick_seconds\": 300}] * 30)\nprint(f\"Warm-up complete: {len(warmup['results'])} steps\")\n\n# Training loop\ntotal_reward = 0.0\nwhile True:\n result = batch_step([SCALE_OUT] + [NO_OP] * 9)\n for step_result in result[\"results\"]:\n total_reward += step_result[\"reward\"]\n if step_result[\"done\"]:\n print(f\"Episode complete — total reward: {total_reward:.2f}\")\n break\n else:\n continue\n break\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\nconst ENV_ID = \"env-aws-001\";\n\nasync function batchStep(steps, maxRetries = 5) {\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n const resp = await fetch(`${BASE_URL}/rl/environments/${ENV_ID}/batch-step`, {\n method: \"POST\",\n headers: { \"Authorization\": `Bearer ${API_KEY}`, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ steps }),\n });\n if (resp.status === 429) {\n const retryAfter = parseInt(resp.headers.get(\"Retry-After\") ?? \"60\", 10);\n console.log(`Rate limited — waiting ${retryAfter}s`);\n await new Promise(r => setTimeout(r, retryAfter * 1000));\n continue;\n }\n if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n return resp.json();\n }\n throw new Error(\"Exceeded max retries\");\n}\n\nconst NO_OP = { action: { type: \"no_op\", parameters: {} } };\nconst SCALE_OUT = { action: { type: \"scale_out\", parameters: { instanceCount: 1 } } };\n\n// 30-step warm-up at 300 s ticks\nconst warmup = await batchStep(Array(30).fill({ ...NO_OP, tick_seconds: 300 }));\nconsole.log(`Warm-up complete: ${warmup.results.length} steps`);\n\n// Training loop using 10-step batches\nlet totalReward = 0;\nlet done = false;\nwhile (!done) {\n const { results } = await batchStep([SCALE_OUT, ...Array(9).fill(NO_OP)]);\n for (const r of results) {\n totalReward += r.reward;\n if (r.done) { done = true; break; }\n }\n}\nconsole.log(`Episode complete — total reward: ${totalReward.toFixed(2)}`);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-x402-call-type":"rl.batch_step","x-x402-price-usdc":"$0.0030","parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the RL environment"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["steps"],"properties":{"steps":{"type":"array","minItems":1,"maxItems":30,"description":"Ordered list of step actions to execute (max 30)","items":{"type":"object","required":["action"],"properties":{"action":{"$ref":"#/components/schemas/Action","description":"RL action to apply at this step."},"tick_seconds":{"type":"integer","minimum":1,"maximum":3600,"description":"Simulated seconds to advance per step (overrides environment default)"}}}}}},"examples":{"threeStepBatch":{"summary":"Scale out then observe","value":{"steps":[{"action":{"type":"scale_out","parameters":{"instanceCount":1}}},{"action":{"type":"no_op","parameters":{}}},{"action":{"type":"no_op","parameters":{}}}]}},"warmupBatch":{"summary":"30-step warm-up at 300 s ticks","value":{"steps":[{"action":{"type":"no_op","parameters":{}},"tick_seconds":300}]}}}}}},"responses":{"200":{"description":"Batch steps executed successfully","content":{"application/json":{"schema":{"type":"object","required":["results"],"properties":{"results":{"type":"array","description":"Step results in the same order as the request `steps` array.\nMay be shorter than the request array if the episode ended early\n(`done: true` in the last result).\n","items":{"type":"object","required":["t","obs","metrics","reward","reward_components","done","sim_time_human","info"],"properties":{"t":{"type":"integer","description":"Current step index"},"obs":{"$ref":"#/components/schemas/RLObs"},"metrics":{"$ref":"#/components/schemas/RLMetrics"},"resources":{"type":"array","description":"Full resource list with per-resource recoveryPolicy","items":{"$ref":"#/components/schemas/Resource"}},"reward":{"type":"number","description":"Scalar total reward for this step"},"reward_components":{"type":"object","properties":{"performance":{"type":"number"},"cost":{"type":"number"},"stability":{"type":"number"},"sla":{"type":"number"},"connection_pressure":{"type":"number","description":"DB connection-pool saturation penalty. Only present when the simulation contains database resources. 0 when healthy; negative (floor −1.0) when pool is exhausted. When a DB is added mid-episode the penalty is ramped in linearly over 5 steps so it does not cause a sudden reward jump. See step response for full formula.\n"}}},"done":{"type":"boolean","description":"True when the episode has ended"},"sim_time_human":{"type":"string","description":"Human-readable simulation time"},"info":{"type":"object","additionalProperties":true,"description":"Additional episode metadata. When a `scale_out` or `scale_in` action is trimmed to honour per-resource bounds (`characteristics.maxInstances` / `characteristics.minInstances`), the following flat keys are added: `scale_clamped` (boolean, always `true`), `requested` (integer — instances requested), `actual` (integer — instances applied, `0` if fully blocked), `limit` (integer — effective bound). Absent when no clamping occurred.\n"}}}}}},"examples":{"threeStepResult":{"summary":"Three-step batch — scale out then two no-ops","value":{"results":[{"t":1,"obs":{"rps":4900,"cpu_util":0.58,"instances":3,"traffic":4900,"currentTime":60,"tick_seconds":60},"metrics":{"cost_usd_hr":0.41,"latency_p95":112,"error_rate":0.004,"uptime":0.996,"sla_violations":0},"reward":0.734,"reward_components":{"performance":0.812,"cost":0.901,"stability":0,"sla":0},"done":false,"sim_time_human":"1m 0s","info":{"sim_time_human":"1m 0s"},"resources":[]},{"t":2,"obs":{"rps":5100,"cpu_util":0.44,"instances":3,"traffic":5100,"currentTime":120,"tick_seconds":60},"metrics":{"cost_usd_hr":0.41,"latency_p95":89,"error_rate":0.001,"uptime":0.999,"sla_violations":0},"reward":0.819,"reward_components":{"performance":0.889,"cost":0.901,"stability":0,"sla":0},"done":false,"sim_time_human":"2m 0s","info":{"sim_time_human":"2m 0s"},"resources":[]}]}},"awsBatchWithDb":{"summary":"AWS — batch-step (EC2 m5.large + RDS Multi-AZ, us-east-1)","description":"Two-step batch: scale out by 1 EC2 instance, then observe with a\nno-op. Each step's metrics include connection_pressure, which\nreflects the RDS Multi-AZ connection-pool ratio; values near 0.4\nare healthy (well below pool exhaustion).\n","value":{"results":[{"t":1,"obs":{"rps":4750,"cpu_util":0.61,"instances":3,"traffic":4750,"currentTime":60,"tick_seconds":60},"metrics":{"cost_usd_hr":0.57,"latency_p95":108,"error_rate":0.004,"uptime":0.996,"sla_violations":0,"connection_pressure":0.45},"reward":0.531,"reward_components":{"performance":0.812,"cost":0.901,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"1m 0s","info":{"sim_time_human":"1m 0s"},"resources":[]},{"t":2,"obs":{"rps":4820,"cpu_util":0.53,"instances":3,"traffic":4820,"currentTime":120,"tick_seconds":60},"metrics":{"cost_usd_hr":0.57,"latency_p95":98,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.42},"reward":0.612,"reward_components":{"performance":0.841,"cost":0.91,"stability":0,"sla":0},"done":false,"sim_time_human":"2m 0s","info":{"sim_time_human":"2m 0s"},"resources":[]}]}},"gcpBatchWithDb":{"summary":"GCP — batch-step (GCE e2-standard-4 + Cloud SQL, us-central1)","description":"Two-step batch of no-ops while the cluster runs at 2 × e2-standard-4\nwith Cloud Load Balancing and Cloud SQL. Each step's metrics include\nconnection_pressure, which reflects the Cloud SQL connection-pool\nratio; 0.38 indicates plenty of headroom.\n","value":{"results":[{"t":1,"obs":{"rps":3820,"cpu_util":0.48,"instances":2,"traffic":3820,"currentTime":60,"tick_seconds":60},"metrics":{"cost_usd_hr":0.44,"latency_p95":91,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.38},"reward":0.612,"reward_components":{"performance":0.873,"cost":0.951,"stability":0,"sla":0},"done":false,"sim_time_human":"1m 0s","info":{"sim_time_human":"1m 0s"},"resources":[]},{"t":2,"obs":{"rps":3760,"cpu_util":0.46,"instances":2,"traffic":3760,"currentTime":120,"tick_seconds":60},"metrics":{"cost_usd_hr":0.44,"latency_p95":88,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.36},"reward":0.628,"reward_components":{"performance":0.884,"cost":0.951,"stability":0,"sla":0},"done":false,"sim_time_human":"2m 0s","info":{"sim_time_human":"2m 0s"},"resources":[]}]}},"azureBatchWithDb":{"summary":"Azure — batch-step (Standard_D4s_v3 + Azure SQL, East US)","description":"Two-step batch of no-ops while the cluster runs at 2 × Standard_D4s_v3\nVMs behind Azure Load Balancer with Azure SQL. Each step's metrics\ninclude connection_pressure, which reflects the Azure SQL\nconnection-pool ratio; 0.51 is moderate but healthy.\n","value":{"results":[{"t":1,"obs":{"rps":4300,"cpu_util":0.55,"instances":2,"traffic":4300,"currentTime":60,"tick_seconds":60},"metrics":{"cost_usd_hr":0.52,"latency_p95":104,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.51},"reward":0.487,"reward_components":{"performance":0.796,"cost":0.938,"stability":0,"sla":0},"done":false,"sim_time_human":"1m 0s","info":{"sim_time_human":"1m 0s"},"resources":[]},{"t":2,"obs":{"rps":4360,"cpu_util":0.56,"instances":2,"traffic":4360,"currentTime":120,"tick_seconds":60},"metrics":{"cost_usd_hr":0.52,"latency_p95":106,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.49},"reward":0.482,"reward_components":{"performance":0.79,"cost":0.938,"stability":0,"sla":0},"done":false,"sim_time_human":"2m 0s","info":{"sim_time_human":"2m 0s"},"resources":[]}]}},"ociBatchWithDb":{"summary":"OCI — batch-step (VM.Standard3.Flex + Autonomous DB, us-ashburn-1)","description":"Two-step batch: scale in by 1 instance, then observe with a no-op\n(now 2 × VM.Standard3.Flex). Each step's metrics include\nconnection_pressure, which reflects the Autonomous Database\nconnection-pool ratio; 0.29 is well within healthy bounds.\n","value":{"results":[{"t":1,"obs":{"rps":4820,"cpu_util":0.34,"instances":2,"traffic":4820,"currentTime":60,"tick_seconds":60},"metrics":{"cost_usd_hr":0.31,"latency_p95":72,"error_rate":0.001,"uptime":0.999,"sla_violations":0,"connection_pressure":0.31},"reward":0.703,"reward_components":{"performance":0.921,"cost":0.985,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"1m 0s","info":{"sim_time_human":"1m 0s"},"resources":[]},{"t":2,"obs":{"rps":4780,"cpu_util":0.33,"instances":2,"traffic":4780,"currentTime":120,"tick_seconds":60},"metrics":{"cost_usd_hr":0.31,"latency_p95":70,"error_rate":0.001,"uptime":0.999,"sla_violations":0,"connection_pressure":0.29},"reward":0.812,"reward_components":{"performance":0.934,"cost":0.985,"stability":0,"sla":0},"done":false,"sim_time_human":"2m 0s","info":{"sim_time_human":"2m 0s"},"resources":[]}]}},"doBatchWithDb":{"summary":"DO — batch-step (Droplet s-2vcpu-4gb + Managed PostgreSQL)","description":"Two-step batch: scale out by 1 Droplet, then observe with a no-op\n(now 3 × s-2vcpu-4gb). Each step's metrics include\nconnection_pressure, which reflects the Managed PostgreSQL\nconnection-pool ratio; 0.44 is healthy.\n","value":{"results":[{"t":1,"obs":{"rps":1620,"cpu_util":0.58,"instances":3,"traffic":1620,"currentTime":60,"tick_seconds":60},"metrics":{"cost_usd_hr":1.08,"latency_p95":104,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.46},"reward":0.481,"reward_components":{"performance":0.812,"cost":0.924,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"1m 0s","info":{"sim_time_human":"1m 0s"},"resources":[]},{"t":2,"obs":{"rps":1580,"cpu_util":0.55,"instances":3,"traffic":1580,"currentTime":120,"tick_seconds":60},"metrics":{"cost_usd_hr":1.08,"latency_p95":99,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.44},"reward":0.503,"reward_components":{"performance":0.831,"cost":0.924,"stability":0,"sla":0},"done":false,"sim_time_human":"2m 0s","info":{"sim_time_human":"2m 0s"},"resources":[]}]}},"digitaloceanAMDNVMeBatchStep":{"summary":"DO AMD NVMe — batch-step (s-2vcpu-4gb-amd Droplets + Managed PostgreSQL, nyc3)","description":"Two-step batch on a DigitalOcean simulation backed by AMD NVMe Droplets\n(s-2vcpu-4gb-amd, $0.038/hr per instance). Step 1 scales out by 1 Droplet\n(now 3 × s-2vcpu-4gb-amd), bringing CPU down from 71 % to 56 %. The\nper-instance compute cost is $0.038/hr, so 3 instances total $0.114/hr for\ncompute alone; `metrics.cost_usd_hr` (0.93) is the full simulated stack\ncost including the DO Load Balancer, Managed PostgreSQL, and modelled\noverhead. Step 2 is a no-op that confirms the cluster has stabilised. The\nAMD EPYC variant's NVMe-backed local storage yields slightly lower P95\nlatency than the Intel s-2vcpu-4gb equivalent at the same traffic level —\n98 ms vs ~104 ms after scale-out. connection_pressure reflects the Managed\nPostgreSQL db-s-2vcpu-4gb connection-pool ratio; 0.41 is healthy. Resources\nare named \"Droplet s-2vcpu-4gb-amd\" to distinguish AMD NVMe Droplets from\nstandard Intel Droplets.\n","value":{"results":[{"t":1,"obs":{"rps":1620,"cpu_util":0.561,"instances":3,"traffic":1620,"currentTime":60,"tick_seconds":60},"metrics":{"cost_usd_hr":0.93,"latency_p95":98,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.41},"reward":0.514,"reward_components":{"performance":0.834,"cost":0.918,"stability":-0.1,"sla":0},"done":false,"sim_time_human":"1m 0s","info":{"sim_time_human":"1m 0s"},"resources":[{"id":"res-lb-amd-001","name":"DO Load Balancer","type":"network","provider":"digitalocean","instances":1},{"id":"res-droplet-amd-001","name":"Droplet s-2vcpu-4gb-amd","type":"compute","provider":"digitalocean","instances":3},{"id":"res-pg-amd-001","name":"Managed PostgreSQL","type":"database","provider":"digitalocean","instances":1}]},{"t":2,"obs":{"rps":1580,"cpu_util":0.541,"instances":3,"traffic":1580,"currentTime":120,"tick_seconds":60},"metrics":{"cost_usd_hr":0.93,"latency_p95":94,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.4},"reward":0.531,"reward_components":{"performance":0.848,"cost":0.918,"stability":0,"sla":0},"done":false,"sim_time_human":"2m 0s","info":{"sim_time_human":"2m 0s"},"resources":[{"id":"res-lb-amd-001","name":"DO Load Balancer","type":"network","provider":"digitalocean","instances":1},{"id":"res-droplet-amd-001","name":"Droplet s-2vcpu-4gb-amd","type":"compute","provider":"digitalocean","instances":3},{"id":"res-pg-amd-001","name":"Managed PostgreSQL","type":"database","provider":"digitalocean","instances":1}]}]}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"description":"RL rate limit exceeded (5 000 req/hr)","headers":{"Retry-After":{"description":"Seconds until the rate-limit window resets","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/rl/environments/{environmentId}/eval-episodes":{"x-stability":"stable","post":{"tags":["RL Environments"],"summary":"Submit an eval-episode job for asynchronous execution","description":"Replays one or more ordered action sequences through a fresh episode reset and returns\nper-episode cumulative rewards computed with `evalCostOverrides` applied. Use this to\ndetect **reward blind spots**: policies trained without egress or cross-AZ cost pricing\nwill show a significant reward drop (reward_collapse) when those dimensions are priced.\n\nThe endpoint does **not** mutate any stored environment or simulation state — all\nepisode rollouts are in-memory and ephemeral.\n\n**Async mode (default):** Returns `202 Accepted` immediately with a `jobId`. Poll\n`GET /rl/environments/{environmentId}/eval-episodes/{jobId}` until `status` is\n`completed` or `failed`.\n\n**Sync mode (`?sync=true`):** Runs the rollout in-process and returns the full result\ninline with `200 OK`. Suitable for small workloads (≤ 3 episodes, ≤ 20 steps each).\n\n**reward_collapse logic:** `reward_collapse` is `true` when\n`meanEvalReward < trainingTotalReward − collapseThreshold × |trainingTotalReward|`.\nWith the default threshold of 0.20, any policy whose eval reward is more than 20 %\nbelow its training reward is flagged as collapsing.\n","operationId":"evalRLEpisodes","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/rl/environments/env-aws-001/eval-episodes \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"actions\": [\n [\n {\"type\": \"no_op\", \"parameters\": {}},\n {\"type\": \"scale_out\", \"parameters\": {\"instanceCount\": 1}},\n {\"type\": \"no_op\", \"parameters\": {}}\n ]\n ],\n \"evalCostOverrides\": {\"egress\": 0.5, \"cross_az_traffic\": 0.3},\n \"collapseThreshold\": 0.20\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nresp = requests.post(\n f\"{BASE_URL}/rl/environments/{ENV_ID}/eval-episodes\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"actions\": [\n [{\"type\": \"no_op\", \"parameters\": {}}] * 10\n ],\n \"evalCostOverrides\": {\"egress\": 0.5, \"cross_az_traffic\": 0.3},\n \"collapseThreshold\": 0.20,\n },\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(f\"reward_collapse={data['reward_collapse']} mean_eval={data['meanEvalReward']:.3f} training={data['trainingTotalReward']:.3f}\")\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-x402-call-type":"rl.eval","x-x402-price-usdc":"$0.0030","parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the RL environment whose training reward is used as the baseline"},{"name":"sync","in":"query","required":false,"schema":{"type":"boolean","default":false},"description":"When `true`, run the eval synchronously and return the full result inline with `200 OK`. When omitted or `false` (default), create a background job and return `202 Accepted` with a `jobId`.\n"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["actions"],"properties":{"actions":{"type":"array","minItems":1,"maxItems":10,"description":"Array of episodes. Each episode is an ordered array of actions to replay from a fresh reset. All episodes use the same `evalCostOverrides`. Maximum 10 episodes.\n","items":{"type":"array","description":"Ordered sequence of actions for one eval episode","items":{"$ref":"#/components/schemas/Action"}}},"collapseThreshold":{"type":"number","minimum":0,"maximum":1,"default":0.2,"description":"Fractional drop in reward that triggers `reward_collapse`. Default is 0.20 (collapse when mean eval reward is more than 20 % below training total reward).\n"},"evalCostOverrides":{"type":"object","additionalProperties":{"type":"number","minimum":0},"description":"Per-dimension cost override rates in USD per 1 000 RPS per hour. Supported keys: `egress` (any simulation with traffic > 0) and `cross_az_traffic` (simulations with resources in distinct Availability Zones). These rates inflate `costPerHour` before the reward cost score is computed, revealing policies that exploit unpriced cost blind spots.\n","example":{"egress":0.5,"cross_az_traffic":0.3}}}},"examples":{"basicEval":{"summary":"Single eval episode with egress and cross-AZ costs priced","value":{"actions":[[{"type":"no_op","parameters":{}},{"type":"scale_out","parameters":{"instanceCount":1}},{"type":"no_op","parameters":{}}]],"evalCostOverrides":{"egress":0.5,"cross_az_traffic":0.3},"collapseThreshold":0.2}}}}}},"responses":{"200":{"description":"Eval episodes completed synchronously (only returned when `?sync=true` is set).\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RLEvalJobResult"},"examples":{"collapseDetected":{"summary":"Reward collapse detected — policy exploits egress blind spot","value":{"episodes":[{"episodeIndex":0,"stepsExecuted":3,"totalReward":1.82}],"meanEvalReward":1.82,"trainingTotalReward":4.5,"collapseThreshold":0.2,"reward_collapse":true}},"noCollapse":{"summary":"No collapse — policy is robust to unpriced cost dimensions","value":{"episodes":[{"episodeIndex":0,"stepsExecuted":3,"totalReward":4.21}],"meanEvalReward":4.21,"trainingTotalReward":4.5,"collapseThreshold":0.2,"reward_collapse":false}}}}}},"202":{"description":"Eval job accepted. The job is running asynchronously. Poll `GET /rl/environments/{environmentId}/eval-episodes/{jobId}` until `status` is `completed` or `failed`.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RLEvalJobAccepted"},"example":{"jobId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","status":"pending","createdAt":"2026-01-01T00:00:00.000Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied — caller does not own this RL environment","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"description":"RL rate limit exceeded (5 000 req/hr)","headers":{"Retry-After":{"description":"Seconds until the rate-limit window resets","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/rl/environments/{environmentId}/eval-episodes/{jobId}":{"x-stability":"stable","get":{"tags":["RL Environments"],"summary":"Fetch an eval-episode job result","description":"Polls the status of an async eval job created by\n`POST /rl/environments/{environmentId}/eval-episodes`.\n\n- While the job is `pending` or `running` the endpoint returns `202` with only `{jobId, status, createdAt}`.\n- When the job is `completed` the endpoint returns `200` with the full eval result merged with job metadata.\n- When the job is `failed` the endpoint returns `200` with `{jobId, status, error, createdAt, completedAt}`.\n","operationId":"getRLEvalJob","security":[],"x-x402-call-type":"rl_eval_status","x-x402-price-usdc":"$0.0030","parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the RL environment that owns this eval job"},{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the eval job returned by the POST endpoint"}],"responses":{"200":{"description":"Job completed (or failed) — inspect `status` to distinguish","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/RLEvalJobCompleted"},{"$ref":"#/components/schemas/RLEvalJobFailed"}]},"examples":{"completed":{"summary":"Job completed successfully","value":{"jobId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","status":"completed","createdAt":"2026-01-01T00:00:00.000Z","completedAt":"2026-01-01T00:00:01.234Z","episodes":[{"episodeIndex":0,"stepsExecuted":3,"totalReward":1.82}],"meanEvalReward":1.82,"trainingTotalReward":4.5,"collapseThreshold":0.2,"reward_collapse":true}},"failed":{"summary":"Job failed","value":{"jobId":"3fa85f64-5717-4562-b3fc-2c963f66afa6","status":"failed","error":"Environment or simulation no longer exists","createdAt":"2026-01-01T00:00:00.000Z","completedAt":"2026-01-01T00:00:00.100Z"}}}}}},"202":{"description":"Job is still pending or running — continue polling","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RLEvalJobAccepted"}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied — caller does not own this RL environment or job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/rl/environments/{environmentId}/reset":{"x-stability":"stable","post":{"tags":["RL Environments"],"summary":"Start a new RL episode by resetting the environment","description":"Resets the environment to its initial state, clearing all scaling history and events.\nUse this to start a new training episode after the previous one completes.\n","operationId":"resetRLEnvironment","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/rl/environments/env-aws-001/reset \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nresp = requests.post(\n f\"{BASE_URL}/rl/environments/{ENV_ID}/reset\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(f\"Environment reset — step: {data['environment']['currentStep']}\")\nprint(f\"Initial CPU: {data['observation']['metrics']['cpuUsage']}%\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\nconst ENV_ID = \"env-aws-001\";\n\nconst resp = await fetch(`${BASE_URL}/rl/environments/${ENV_ID}/reset`, {\n method: \"POST\",\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n});\nconst data = await resp.json();\nconsole.log(`Environment reset — step: ${data.environment.currentStep}`);\nconsole.log(`Initial CPU: ${data.observation.metrics.cpuUsage}%`);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-x402-call-type":"rl_env_reset","x-x402-price-usdc":"$0.0030","parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the RL environment to reset"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"rewardWeights":{"$ref":"#/components/schemas/RewardWeights"}}}}}},"responses":{"200":{"description":"Environment reset successfully","content":{"application/json":{"schema":{"type":"object","properties":{"environment":{"$ref":"#/components/schemas/RLEnvironment"},"observation":{"$ref":"#/components/schemas/Observation"},"info":{"type":"object","description":"Metadata about the initial environment state","properties":{"sim_time_human":{"type":"string","description":"Human-readable simulation time at episode start (always \"0s\")","example":"0s"}}}}},"examples":{"awsReset":{"summary":"AWS — environment reset (EC2 m5.large cluster, us-east-1)","description":"Episode reset on an AWS simulation. Resources return to their initial\nstate: 2 × m5.large EC2 instances behind an ALB with RDS Multi-AZ.\nScaling history and events are cleared. connectionPressure reflects\nthe RDS connection-pool ratio.\n","value":{"environment":{"id":"env-aws-001","simulationId":"sim-aws-001","isActive":true,"currentStep":0,"maxSteps":100},"observation":{"metrics":{"cpuUsage":41,"latencyP50":38,"latencyP95":82,"errorRate":0.2,"throughput":4800,"costPerHour":0.38,"connectionPressure":0.3},"resources":[{"id":"res-alb-001","name":"ALB","type":"network","provider":"aws","instances":1},{"id":"res-ec2-001","name":"EC2 m5.large","type":"compute","provider":"aws","instances":2},{"id":"res-rds-001","name":"RDS db.r5.large","type":"database","provider":"aws","instances":1}],"traffic":4800,"currentTime":0,"autoscalingConfig":{"scaleOutCpuThreshold":70,"scaleInCpuThreshold":30,"maxInstances":12,"minInstances":2},"scalingHistory":[],"recentEvents":[]},"info":{"sim_time_human":"0s"}}},"gcpReset":{"summary":"GCP — environment reset (GCE e2-standard-4 cluster, us-central1)","description":"Episode reset on a GCP simulation. Resources return to their initial\nstate: 2 × e2-standard-4 GCE instances behind Cloud Load Balancing\nwith Cloud SQL db-standard-4. Scaling history and events are cleared.\nconnectionPressure reflects the Cloud SQL connection-pool ratio.\n","value":{"environment":{"id":"env-gcp-001","simulationId":"sim-gcp-001","isActive":true,"currentStep":0,"maxSteps":150},"observation":{"metrics":{"cpuUsage":42,"latencyP50":40,"latencyP95":88,"errorRate":0.2,"throughput":3900,"costPerHour":0.44,"connectionPressure":0.28},"resources":[{"id":"res-lb-001","name":"Cloud Load Balancing","type":"network","provider":"gcp","instances":1},{"id":"res-gce-001","name":"GCE e2-standard-4","type":"compute","provider":"gcp","instances":2},{"id":"res-csql-001","name":"Cloud SQL db-standard-4","type":"database","provider":"gcp","instances":1}],"traffic":3900,"currentTime":0,"autoscalingConfig":{"scaleOutCpuThreshold":68,"scaleInCpuThreshold":30,"maxInstances":10,"minInstances":2},"scalingHistory":[],"recentEvents":[]},"info":{"sim_time_human":"0s"}}},"azureReset":{"summary":"Azure — environment reset (Standard_D4s_v3 VM Scale Set, East US)","description":"Episode reset on an Azure simulation. Resources return to their initial\nstate: 2 × Standard_D4s_v3 VMs behind an Azure Load Balancer\nwith Azure SQL General Purpose 4 vCores. Scaling history is cleared.\nconnectionPressure reflects the Azure SQL connection-pool ratio.\n","value":{"environment":{"id":"env-azure-001","simulationId":"sim-azure-001","isActive":true,"currentStep":0,"maxSteps":150},"observation":{"metrics":{"cpuUsage":45,"latencyP50":44,"latencyP95":96,"errorRate":0.3,"throughput":4400,"costPerHour":0.52,"connectionPressure":0.33},"resources":[{"id":"res-alb-001","name":"Azure Load Balancer","type":"network","provider":"azure","instances":1},{"id":"res-vm-001","name":"Standard_D4s_v3","type":"compute","provider":"azure","instances":2},{"id":"res-sql-001","name":"Azure SQL General Purpose","type":"database","provider":"azure","instances":1}],"traffic":4400,"currentTime":0,"autoscalingConfig":{"scaleOutCpuThreshold":70,"scaleInCpuThreshold":30,"maxInstances":10,"minInstances":2},"scalingHistory":[],"recentEvents":[]},"info":{"sim_time_human":"0s"}}},"ociReset":{"summary":"OCI — environment reset (VM.Standard3.Flex + Autonomous DB, us-ashburn-1)","description":"Episode reset on an OCI simulation. Resources return to their initial\nstate: 2 × VM.Standard3.Flex instances behind OCI Load Balancer\nwith Autonomous Database (2 OCPU). Scaling history is cleared.\nconnectionPressure reflects the Autonomous DB connection-pool ratio.\n","value":{"environment":{"id":"env-oci-001","simulationId":"sim-oci-001","isActive":true,"currentStep":0,"maxSteps":150},"observation":{"metrics":{"cpuUsage":40,"latencyP50":36,"latencyP95":76,"errorRate":0.1,"throughput":4900,"costPerHour":0.31,"connectionPressure":0.22},"resources":[{"id":"res-lb-001","name":"OCI Load Balancer","type":"network","provider":"oci","instances":1},{"id":"res-vm-001","name":"VM.Standard3.Flex","type":"compute","provider":"oci","instances":2},{"id":"res-adb-001","name":"Autonomous Database 2 OCPU","type":"database","provider":"oci","instances":1}],"traffic":4900,"currentTime":0,"autoscalingConfig":{"scaleOutCpuThreshold":65,"scaleInCpuThreshold":28,"maxInstances":10,"minInstances":2},"scalingHistory":[],"recentEvents":[]},"info":{"sim_time_human":"0s"}}},"digitaloceanReset":{"summary":"DigitalOcean — environment reset (s-2vcpu-4gb Droplets, nyc3)","description":"Episode reset on a DigitalOcean simulation. Resources return to their\ninitial state: 2 × s-2vcpu-4gb Droplets behind a DO Load Balancer\nwith Managed PostgreSQL db-s-2vcpu-4gb. Scaling history is cleared.\nconnectionPressure reflects the Managed PostgreSQL connection-pool ratio.\n","value":{"environment":{"id":"env-do-001","simulationId":"sim-do-001","isActive":true,"currentStep":0,"maxSteps":200},"observation":{"metrics":{"cpuUsage":38,"latencyP50":48,"latencyP95":98,"errorRate":0.3,"throughput":1480,"costPerHour":0.72,"connectionPressure":0.4},"resources":[{"id":"res-lb-001","name":"DO Load Balancer","type":"network","provider":"digitalocean","instances":1},{"id":"res-droplet-001","name":"Droplet s-2vcpu-4gb","type":"compute","provider":"digitalocean","instances":2},{"id":"res-pg-001","name":"Managed PostgreSQL","type":"database","provider":"digitalocean","instances":1}],"traffic":1480,"currentTime":0,"autoscalingConfig":{"scaleOutCpuThreshold":65,"scaleInCpuThreshold":35,"maxInstances":8,"minInstances":1},"scalingHistory":[],"recentEvents":[]},"info":{"sim_time_human":"0s"}}},"digitaloceanAMDNVMeReset":{"summary":"DigitalOcean — environment reset (s-2vcpu-4gb-amd AMD NVMe Droplets, nyc3)","description":"Episode reset on a DigitalOcean simulation backed by AMD NVMe Droplets\n(s-2vcpu-4gb-amd) at $0.038/hr per instance. The AMD EPYC variant\nprovides NVMe-backed local storage and lower P95 latency than the\nIntel equivalent under the same traffic load. Resources return to their\ninitial state: 2 × s-2vcpu-4gb-amd Droplets behind a DO Load Balancer\nwith Managed PostgreSQL db-s-2vcpu-4gb. Scaling history is cleared.\nconnectionPressure reflects the Managed PostgreSQL connection-pool ratio.\nUse this alongside the Intel example to compare agent policy performance\nacross Droplet variants with identical topology.\n","value":{"environment":{"id":"env-do-amd-001","simulationId":"c9f2d3e5-4a7b-4c6f-8d1e-2f3a4b5c6d7e","isActive":true,"currentStep":0,"maxSteps":200},"observation":{"metrics":{"cpuUsage":35.5,"latencyP50":44,"latencyP95":92,"errorRate":0.2,"throughput":995,"costPerHour":0.62,"connectionPressure":0.37},"resources":[{"id":"res-lb-amd-001","name":"DO Load Balancer","type":"network","provider":"digitalocean","instances":1},{"id":"res-droplet-amd-001","name":"Droplet s-2vcpu-4gb-amd","type":"compute","provider":"digitalocean","instances":2},{"id":"res-pg-amd-001","name":"Managed PostgreSQL","type":"database","provider":"digitalocean","instances":1}],"traffic":995,"currentTime":0,"autoscalingConfig":{"scaleOutCpuThreshold":65,"scaleInCpuThreshold":35,"maxInstances":8,"minInstances":1},"scalingHistory":[],"recentEvents":[]},"info":{"sim_time_human":"0s"}}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/rl/environments/{environmentId}/observation":{"x-stability":"stable","get":{"tags":["RL Environments"],"summary":"Fetch the current observation without executing an action","description":"Returns the current state observation without advancing the simulation.\nUseful for initial state inspection or debugging.\n","operationId":"getRLObservation","x-x402-call-type":"rl_env_observation","x-x402-price-usdc":"$0.0030","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl https://www.cloudworldmodel.ai/api/rl/environments/env-aws-001/observation \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\nENV_ID = \"env-aws-001\"\n\nresp = requests.get(\n f\"{BASE_URL}/rl/environments/{ENV_ID}/observation\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\ndata = resp.json()\nobs, metrics = data[\"obs\"], data[\"metrics\"]\nprint(f\"Step {obs['currentTime']} CPU: {obs['cpu_util']:.1%} \"\n f\"P95: {metrics['latency_p95']} ms cost: ${metrics['cost_usd_hr']:.2f}/hr\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\nconst ENV_ID = \"env-aws-001\";\n\nconst resp = await fetch(`${BASE_URL}/rl/environments/${ENV_ID}/observation`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n});\nconst { obs, metrics } = await resp.json();\nconsole.log(`Step ${obs.currentTime} CPU: ${(obs.cpu_util * 100).toFixed(1)}% ` +\n `P95: ${metrics.latency_p95} ms cost: $${metrics.cost_usd_hr.toFixed(2)}/hr`);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"parameters":[{"name":"environmentId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the RL environment"}],"responses":{"200":{"description":"Current observation","content":{"application/json":{"schema":{"type":"object","required":["obs","metrics","resources"],"properties":{"obs":{"$ref":"#/components/schemas/RLObs"},"metrics":{"$ref":"#/components/schemas/RLMetrics"},"resources":{"type":"array","description":"Full resource list with per-resource `recoveryPolicy`. Resources that have never had `set_recovery_policy` applied carry the global defaults (criticalCpuThreshold: 80, criticalSteps: 4, warningCpuThreshold: 70, warningSteps: 3). Use this to confirm a `set_recovery_policy` action took effect or to compare healing configurations across resources.\n","items":{"$ref":"#/components/schemas/Resource"}},"unmodeled_cost_warning":{"type":"array","description":"List of active unmodeled cost dimension keys (e.g. `[\"egress\", \"cross_az_traffic\"]`). Absent when no unmodeled dimensions are detected. Mirrors the same field in the step response so agents can poll current topology exposure without executing an action.\n","items":{"type":"string"},"example":["egress"]},"reward_components":{"type":"object","description":"Partial reward breakdown reflecting the current simulation state without a step. Only `unmodeled_cost` is present here (the other components — performance, cost, stability, sla — require a step action to compute). Absent when no unmodeled dimensions are active.\n","properties":{"unmodeled_cost":{"type":"number","description":"Penalty for active unmodeled cost dimensions at the current topology. Equals -(count × episodeConfig.unmodeled_cost_penalty_per_dimension). Agents should watch this field across observation polls to detect when a topology change (add_resource / remove_resource) introduced or eliminated a cross-AZ or egress charge mid-episode.\n","example":-0.1}}}}},"examples":{"awsObservation":{"summary":"AWS — observation at step 42 (EC2 m5.large, us-east-1)","description":"Mid-episode observation for an AWS simulation. The cluster is running\n3 × m5.large EC2 instances behind an ALB with RDS Multi-AZ. CPU is\nmoderate, P95 latency is within the 200 ms SLA, and cost is on-budget.\nconnection_pressure reflects the RDS connection-pool ratio.\n","value":{"obs":{"rps":4750,"cpu_util":0.534,"instances":3,"traffic":4750,"currentTime":42},"metrics":{"cost_usd_hr":0.57,"latency_p95":98,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.42}}},"gcpObservation":{"summary":"GCP — observation at step 30 (GCE e2-standard-4, us-central1)","description":"Mid-episode observation for a GCP simulation. The cluster is running\n2 × e2-standard-4 GCE instances behind Cloud Load Balancing with\nCloud SQL. CPU is stable and well within the SLA.\nconnection_pressure reflects the Cloud SQL connection-pool ratio.\n","value":{"obs":{"rps":3820,"cpu_util":0.478,"instances":2,"traffic":3820,"currentTime":30},"metrics":{"cost_usd_hr":0.44,"latency_p95":91,"error_rate":0.002,"uptime":0.998,"sla_violations":0,"connection_pressure":0.38}}},"azureObservation":{"summary":"Azure — observation at step 28 (Standard_D4s_v3, East US)","description":"Mid-episode observation for an Azure simulation. The cluster is running\n2 × Standard_D4s_v3 VMs behind Azure Load Balancer with Azure SQL.\nCPU is moderate; the agent has not yet triggered a scale-out.\nconnection_pressure reflects the Azure SQL connection-pool ratio.\n","value":{"obs":{"rps":4300,"cpu_util":0.551,"instances":2,"traffic":4300,"currentTime":28},"metrics":{"cost_usd_hr":0.52,"latency_p95":104,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.51}}},"ociObservation":{"summary":"OCI — observation at step 25 (VM.Standard3.Flex, us-ashburn-1)","description":"Mid-episode observation for an OCI simulation. The cluster is running\n2 × VM.Standard3.Flex instances behind OCI Load Balancer with\nAutonomous Database. CPU is low; the agent may consider scaling in.\nconnection_pressure reflects the Autonomous DB connection-pool ratio.\n","value":{"obs":{"rps":4820,"cpu_util":0.342,"instances":2,"traffic":4820,"currentTime":25},"metrics":{"cost_usd_hr":0.31,"latency_p95":72,"error_rate":0.001,"uptime":0.999,"sla_violations":0,"connection_pressure":0.29}}},"digitaloceanObservation":{"summary":"DigitalOcean — observation at step 15 (s-2vcpu-4gb Droplets, nyc3)","description":"Mid-episode observation for a DigitalOcean simulation. The cluster is\nrunning 2 × s-2vcpu-4gb Droplets behind a DO Load Balancer with\nManaged PostgreSQL. CPU is stable within the target range.\nconnection_pressure reflects the Managed PostgreSQL connection-pool ratio.\n","value":{"obs":{"rps":1480,"cpu_util":0.614,"instances":2,"traffic":1480,"currentTime":15},"metrics":{"cost_usd_hr":0.72,"latency_p95":118,"error_rate":0.004,"uptime":0.996,"sla_violations":0,"connection_pressure":0.63}}},"digitaloceanAMDNVMeObservation":{"summary":"DigitalOcean — observation at step 20 (s-2vcpu-4gb-amd AMD NVMe Droplets, nyc3)","description":"Mid-episode observation for a DigitalOcean simulation backed by AMD NVMe\nDroplets (s-2vcpu-4gb-amd) at $0.038/hr per instance. The cluster is\nrunning 2 × s-2vcpu-4gb-amd Droplets behind a DO Load Balancer with\nManaged PostgreSQL db-s-2vcpu-4gb. CPU is moderate at 58 %; the agent\nmay consider scaling out before the 65 % threshold is crossed. The AMD\nEPYC variant's NVMe-backed storage contributes to slightly lower P95\nlatency than the Intel equivalent under the same traffic load.\nconnection_pressure reflects the Managed PostgreSQL connection-pool\nratio; 0.48 is healthy with headroom remaining.\n","value":{"obs":{"rps":1580,"cpu_util":0.578,"instances":2,"traffic":1580,"currentTime":20},"metrics":{"cost_usd_hr":0.62,"latency_p95":108,"error_rate":0.003,"uptime":0.997,"sla_violations":0,"connection_pressure":0.48}}}}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/chaos/run":{"x-stability":"stable","post":{"tags":["Execute"],"summary":"Run a chaos engineering test","description":"Execute a chaos engineering test by injecting failures into a simulation.\nCan use pre-built scenarios or custom failure injections.\n\nThis endpoint works with simulations built on **any supported provider**,\nincluding AWS, GCP, Azure, OCI, and **DigitalOcean**. When targeting a\nDigitalOcean-based simulation, failure types such as `kill_instance`\naffect Droplets, `zone_failure` targets DigitalOcean datacenter regions,\nand `database_crash` targets Managed Database clusters.\n\n**Failure naming surfaces** — three zone-related names, three different contexts:\n- `zone_outage` — custom injection type in `customInjections[].failureType` (this endpoint)\n- `az_outage` — failure type in the synchronous `POST /simulations/{id}/failures` API\n- `zone_failure` — pre-built scenario ID used in `scenarioId`\n\n**Multi-fault cascade example** — inject a zone outage and a database slowdown simultaneously\nvia `customInjections` to test correlated failure handling:\n\n```json\n{\n \"simulationId\": \"sim-abc123\",\n \"customInjections\": [\n { \"failureType\": \"zone_outage\", \"targetZone\": \"us-east-1a\", \"duration\": 120 },\n { \"failureType\": \"database_slowdown\", \"targetResourceId\": \"db-primary\", \"intensity\": 80, \"duration\": 120 }\n ]\n}\n```\n\nFor `database_crash` custom injections, `promotionDelaySeconds` (default 30)\nand `restartDelaySeconds` (default 1800) are bounded integer simulation-clock\nassumptions, not measured provider behavior or SLAs. A healthy database\nexplicitly linked by `replicaOf` promotes after the first delay; without\none, the writer restarts after the injection duration plus the second delay.\nCrashes scheduled for the same simulation instant are considered together:\na simultaneously crashed replica is not eligible. In that case the writer\nuses sole_writer_restart, with promotionUnavailableReason set to\nrelated_replica_unavailable_at_injection; a replica lost after promotion\nwas announced instead produces an Explicit replica promotion failed event.\n`multiAz` and `instanceCount` do not imply an eligible replica. The chosen\neffective values are returned per target in\n`resilienceScore.metrics.databaseCrashAssumptions`.\nWhile no database serves, positive offered work has 100% errors and zero\ngoodput; promotion restores service without a residual writer-outage\npenalty. Chaos samples every 10 simulation seconds (default promotion\nat 30 seconds). Quick `inject-failure` instead advances one simulation\nsecond per step, promotes on the second step (one second after injection),\nand rejoins after its bounded park window. Compare equivalent\nunavailable/serving phases, not equal step indices or wall-clock times.\n\nThe prebuilt `scenarioId: \"database_crash\"` also accepts these two fields\nat the top level of this request (not inside `customInjections`). They\noverride only this job, not the saved scenario. Other scenario IDs and\ncustom injections cannot use the top-level fields.\n\n **Resilience grade** — the `grade` in the result is derived from the\n live weighted `overall` score, including recovery, availability, data\n integrity, and graceful degradation. Vulnerability findings remain\n architectural evidence and may be similar across fault combinations,\n but the grade is not a static SPOF label. To inspect both, pair\n `POST /chaos/run` (vulnerability report) with the synchronous\n `POST /simulations/{id}/failures` + step-loop pattern (live metrics).\n","operationId":"runChaosTest","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/chaos/run \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"simulationId\": \"a638caad-7423-40a3-bb09-f91235d9392d\",\n \"scenarioId\": \"zone_failure\",\n \"duration\": 300,\n \"webhookUrl\": \"https://your-app.com/webhooks/chaos\",\n \"webhookSecret\": \"your-secret-key-here\"\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/chaos/run\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"simulationId\": \"a638caad-7423-40a3-bb09-f91235d9392d\",\n \"scenarioId\": \"zone_failure\",\n \"duration\": 300,\n \"webhookUrl\": \"https://your-app.com/webhooks/chaos\",\n \"webhookSecret\": \"your-secret-key-here\",\n },\n)\nresp.raise_for_status()\njob = resp.json()[\"job\"]\nprint(f\"Chaos job started: {job['id']} status={job['status']}\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/chaos/run`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n simulationId: \"a638caad-7423-40a3-bb09-f91235d9392d\",\n scenarioId: \"zone_failure\",\n duration: 300,\n webhookUrl: \"https://your-app.com/webhooks/chaos\",\n webhookSecret: \"your-secret-key-here\",\n }),\n});\nconst { job } = await resp.json();\nconsole.log(`Chaos job started: ${job.id} status=${job.status}`);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.005"}},"x-x402-call-type":"chaos.run","x-x402-price-usdc":"$0.0050","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["simulationId","duration"],"properties":{"simulationId":{"type":"string","format":"uuid","description":"ID of the base simulation to test","example":"a638caad-7423-40a3-bb09-f91235d9392d"},"scenarioId":{"type":"string","description":"Pre-built scenario ID (optional)","example":"zone_failure","enum":["zone_failure","database_crash","network_partition","cascading_failure","random_instance_failure","database_slowdown","database_overload"]},"promotionDelaySeconds":{"type":"integer","minimum":0,"maximum":86400,"description":"Prebuilt database_crash scenario only: simulated explicit-replica promotion delay (default 30 seconds); not a provider SLA. Cannot be combined with customInjections."},"restartDelaySeconds":{"type":"integer","minimum":0,"maximum":86400,"description":"Prebuilt database_crash scenario only: simulated sole-writer restart delay (default 1800 seconds); not a provider SLA. Cannot be combined with customInjections."},"customInjections":{"type":"array","description":"Custom failure injections (optional)","items":{"type":"object","required":["failureType"],"properties":{"failureType":{"type":"string","enum":["database_crash","database_slowdown","database_overload","zone_outage","region_outage","permanent_data_loss","instance_failure","eks_spot_interruption","network_latency","network_partition","cascading_failure","cpu_stress"],"description":"Type of failure to inject"},"targetResourceId":{"type":"string","description":"Internal ID of the specific resource to target (optional)"},"targetResourceName":{"type":"string","description":"Resource display name to target instead of an internal ID. Resolved case-insensitively — exact name match first, then unique-prefix match (e.g. \"aws-server\" finds \"aws-server-01\"). If the name matches multiple resources the request fails with 400 and a `candidates` list of matching resources."},"targetZone":{"type":"string","description":"Availability zone to target (optional, e.g. for zone_outage)"},"targetRegion":{"type":"string","description":"Canonical region key for region_outage or permanent_data_loss."},"targetProvider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"],"description":"Cloud provider to scope the injection to (optional)"},"duration":{"type":"number","description":"How long the failure lasts in simulation seconds (optional)"},"promotionDelaySeconds":{"type":"integer","minimum":0,"maximum":86400,"description":"database_crash only: explicit replica promotion delay in simulation seconds (default 30); not a provider SLA."},"restartDelaySeconds":{"type":"integer","minimum":0,"maximum":86400,"description":"database_crash only: sole-writer restart delay after injection ends in simulation seconds (default 1800); not a provider SLA."},"intensity":{"type":"number","minimum":0,"maximum":100,"description":"Failure severity from 0 (minimal) to 100 (maximum, optional)"},"startTime":{"type":"number","description":"Simulation time at which the failure begins (optional)"},"affectedResources":{"type":"array","items":{"type":"string"},"description":"Additional resource IDs affected by the injection (optional)"}}}},"duration":{"type":"integer","description":"Test duration in simulation steps","default":300,"example":300},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when job completes","example":"https://your-app.com/webhooks/chaos"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"your-secret-key-here"}},"example":{"simulationId":"a638caad-7423-40a3-bb09-f91235d9392d","scenarioId":"zone_failure","duration":300}},"examples":{"prebuiltScenario":{"summary":"Pre-built zone failure scenario (any provider)","value":{"simulationId":"a638caad-7423-40a3-bb09-f91235d9392d","scenarioId":"zone_failure","duration":300,"webhookUrl":"https://your-app.com/webhooks/chaos","webhookSecret":"your-secret-key-here"}},"digitalOceanDropletCrash":{"summary":"DigitalOcean — crash a Droplet with custom injection","value":{"simulationId":"d1234abc-0000-40a3-bb09-d091235d9392","duration":180,"customInjections":[{"failureType":"instance_failure","targetResourceName":"droplet-web-1","startTime":30,"duration":90,"intensity":80}],"webhookUrl":"https://your-app.com/webhooks/chaos","webhookSecret":"your-secret-key-here"}},"digitalOceanDatabaseCrash":{"summary":"DigitalOcean — crash a Managed Database cluster","value":{"simulationId":"d1234abc-0000-40a3-bb09-d091235d9392","scenarioId":"database_crash","promotionDelaySeconds":45,"restartDelaySeconds":60,"duration":240,"webhookUrl":"https://your-app.com/webhooks/chaos","webhookSecret":"your-secret-key-here"}}}}}},"responses":{"202":{"description":"Chaos test job accepted and started","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string","format":"uuid","description":"Top-level chaos job ID (mirrors `job.id`) for convenient access.","example":"job-abc123"},"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"job-abc123"},"type":{"type":"string","enum":["chaos_test"],"example":"chaos_test"},"status":{"type":"string","enum":["pending","running"],"example":"running"},"simulationId":{"type":"string","format":"uuid"},"createdAt":{"type":"string","format":"date-time"}}},"message":{"type":"string","example":"Chaos test job started. Use GET /chaos/jobs/{id} to check status."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"description":"Simulation not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/chaos/batch":{"x-stability":"stable","post":{"tags":["Execute"],"summary":"Run a batch of chaos tests in parallel","description":"Execute multiple chaos engineering tests in parallel. Each scenario can use either\na pre-built scenario or custom failure injections. Results are aggregated across all tests.\n\n**DigitalOcean compatibility:** All pre-built scenarios (`zone_failure`, `database_crash`,\n`network_partition`, etc.) and custom injection types (`kill_instance`, `zone_outage`,\n`database_slowdown`, etc.) work identically on DigitalOcean simulations.\nUse Droplet-specific resource IDs (e.g. `droplet-web-1`) and DO datacenter region names\n(e.g. `nyc3`, `sfo3`) when targeting a DigitalOcean simulation.\n","operationId":"createBatchChaosTest","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/chaos/batch \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"simulationId\": \"a638caad-7423-40a3-bb09-f91235d9392d\",\n \"scenarios\": [\n {\"scenarioId\": \"zone_failure\", \"duration\": 120},\n {\"scenarioId\": \"database_crash\", \"duration\": 90}\n ],\n \"webhookUrl\": \"https://your-app.com/webhooks/chaos\",\n \"webhookSecret\": \"your-secret-key-here\"\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/chaos/batch\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"simulationId\": \"a638caad-7423-40a3-bb09-f91235d9392d\",\n \"scenarios\": [\n {\"scenarioId\": \"zone_failure\", \"duration\": 120},\n {\"scenarioId\": \"database_crash\", \"duration\": 90},\n ],\n \"webhookUrl\": \"https://your-app.com/webhooks/chaos\",\n \"webhookSecret\": \"your-secret-key-here\",\n },\n)\nresp.raise_for_status()\njob = resp.json()[\"job\"]\nprint(f\"Batch job started: {job['id']} totalJobs={job['totalJobs']}\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/chaos/batch`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n simulationId: \"a638caad-7423-40a3-bb09-f91235d9392d\",\n scenarios: [\n { scenarioId: \"zone_failure\", duration: 120 },\n { scenarioId: \"database_crash\", duration: 90 },\n ],\n webhookUrl: \"https://your-app.com/webhooks/chaos\",\n webhookSecret: \"your-secret-key-here\",\n }),\n});\nconst { job } = await resp.json();\nconsole.log(`Batch job started: ${job.id} totalJobs=${job.totalJobs}`);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.005"}},"x-x402-call-type":"chaos_batch","x-x402-price-usdc":"$0.0050","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["simulationId","scenarios"],"properties":{"simulationId":{"type":"string","format":"uuid","description":"ID of the base simulation to test","example":"a638caad-7423-40a3-bb09-f91235d9392d"},"scenarios":{"type":"array","description":"Array of chaos test scenarios to execute in parallel","minItems":1,"maxItems":10,"items":{"type":"object","properties":{"scenarioId":{"type":"string","description":"Pre-built scenario ID (optional, mutually exclusive with customInjections)","example":"zone_failure","enum":["zone_failure","database_crash","network_partition","cascading_failure","random_instance_failure","database_slowdown"]},"promotionDelaySeconds":{"type":"integer","minimum":0,"maximum":86400,"description":"Prebuilt database_crash scenario only: simulated replica promotion delay (default 30 seconds); not a provider SLA. Cannot be combined with customInjections."},"restartDelaySeconds":{"type":"integer","minimum":0,"maximum":86400,"description":"Prebuilt database_crash scenario only: simulated sole-writer restart delay (default 1800 seconds); not a provider SLA. Cannot be combined with customInjections."},"customInjections":{"type":"array","description":"Custom failure injections (optional, mutually exclusive with scenarioId)","items":{"type":"object","required":["failureType"],"properties":{"failureType":{"type":"string","enum":["database_crash","database_slowdown","database_overload","zone_outage","region_outage","permanent_data_loss","instance_failure","eks_spot_interruption","network_latency","network_partition","cascading_failure","cpu_stress"],"description":"Type of failure to inject"},"targetResourceId":{"type":"string","description":"Internal ID of the specific resource to target (optional)"},"targetResourceName":{"type":"string","description":"Resource display name to target instead of an internal ID. Resolved case-insensitively — exact name match first, then unique-prefix match (e.g. \"aws-server\" finds \"aws-server-01\"). If the name matches multiple resources the request fails with 400 and a `candidates` list of matching resources."},"targetZone":{"type":"string","description":"Availability zone to target (optional, e.g. for zone_outage)"},"targetRegion":{"type":"string","description":"Canonical region key for region_outage or permanent_data_loss."},"targetProvider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"],"description":"Cloud provider to scope the injection to (optional)"},"duration":{"type":"number","description":"How long the failure lasts in simulation seconds (optional)"},"promotionDelaySeconds":{"type":"integer","minimum":0,"maximum":86400,"description":"database_crash only: explicit replica promotion delay in simulation seconds (default 30); not a provider SLA."},"restartDelaySeconds":{"type":"integer","minimum":0,"maximum":86400,"description":"database_crash only: sole-writer restart delay after injection ends in simulation seconds (default 1800); not a provider SLA."},"intensity":{"type":"number","minimum":0,"maximum":100,"description":"Failure severity from 0 (minimal) to 100 (maximum, optional)"},"startTime":{"type":"number","description":"Simulation time at which the failure begins (optional)"}}}},"duration":{"type":"integer","description":"Test duration in simulation steps","minimum":10,"maximum":300,"default":300,"example":120}}}},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when batch completes","example":"https://example.com/webhook"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"secret123"}},"example":{"simulationId":"a638caad-7423-40a3-bb09-f91235d9392d","scenarios":[{"scenarioId":"zone_failure","duration":120},{"scenarioId":"database_crash","duration":90}]}},"examples":{"generic":{"summary":"Generic batch — zone failure + DB crash + custom kill","value":{"simulationId":"sim_abc123","scenarios":[{"scenarioId":"zone_failure","duration":120},{"scenarioId":"database_crash","duration":90},{"customInjections":[{"failureType":"instance_failure","targetResourceName":"web-1","startTime":30,"duration":60}],"duration":150}],"webhookUrl":"https://example.com/webhook","webhookSecret":"secret123"}},"digitalOcean":{"summary":"DigitalOcean — nyc3 zone failure + sfo3 database crash + Droplet API server kill in parallel","value":{"simulationId":"sim_do_droplets","scenarios":[{"scenarioId":"zone_failure","targetId":"nyc3","duration":120},{"scenarioId":"database_crash","targetId":"sfo3","duration":90},{"customInjections":[{"failureType":"instance_failure","targetResourceName":"droplet-web-1","startTime":10,"duration":60},{"failureType":"instance_failure","targetResourceName":"droplet-api-1","startTime":20,"duration":60}],"duration":90}],"webhookUrl":"https://your-app.example.com/webhooks/chaos","webhookSecret":"do-webhook-secret"}},"ociPreemptible":{"summary":"OCI Preemptible Ampere A1 — cpu_stress + instance_kill testing interruption tolerance of a preemptible Ampere A1 fleet","value":{"simulationId":"sim-oci-preemptible-a1-prod","scenarios":[{"customInjections":[{"failureType":"cpu_stress","targetResourceName":"vm-a1-flex-1","startTime":10,"duration":60},{"failureType":"cpu_stress","targetResourceName":"vm-a1-flex-2","startTime":10,"duration":60}],"duration":90},{"customInjections":[{"failureType":"instance_failure","targetResourceName":"vm-a1-flex-3","startTime":15,"duration":45}],"duration":75}],"webhookUrl":"https://your-app.example.com/webhooks/chaos","webhookSecret":"oci-preemptible-chaos-secret"}}}}}},"responses":{"202":{"description":"Batch chaos test job accepted and started","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"batch_xyz789"},"type":{"type":"string","enum":["batch_chaos_test"],"example":"batch_chaos_test"},"status":{"type":"string","enum":["pending","running"],"example":"running"},"totalJobs":{"type":"integer","description":"Number of child chaos tests in this batch","example":3},"createdAt":{"type":"string","format":"date-time"}}},"message":{"type":"string","example":"Batch chaos test started. Use GET /chaos/batch/{id} to check status."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"description":"Simulation not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/TooManyRequests"}}}},"/analysis/optimize":{"x-stability":"stable","post":{"tags":["Optimize & Predict"],"summary":"Submit an infrastructure optimization job","description":"Analyzes your current architecture and generates 50+ tested variations\nwith ranked recommendations for cost/performance optimization.\n\n**Ownership requirement:** The `simulationId` must refer to a simulation\nthat was created with, or claimed by, the same API key used in this\nrequest. Simulations created via the public browser workspace (i.e.\n`POST /api/simulations` without an `Authorization` header) are unowned\nand will return `403` here until claimed. To associate ownership, either:\n- Create the simulation with a Bearer token from the start:\n `POST /api/simulations` with `Authorization: Bearer <key>`, or\n- Claim an existing unowned simulation before calling this endpoint:\n `POST /api/simulations/{simulationId}/claim`.\n","operationId":"submitOptimization","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/analysis/optimize \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"simulationId\": \"sim-abc123\",\n \"goals\": {\n \"primary\": \"minimize_cost\",\n \"constraints\": {\n \"max_cost_per_hour\": 10.0,\n \"min_throughput\": 5000,\n \"max_latency_p95\": 200\n }\n },\n \"testScenario\": {\n \"traffic_pattern\": \"spike\",\n \"duration_steps\": 100,\n \"include_failures\": true\n }\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/analysis/optimize\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"simulationId\": \"sim-abc123\",\n \"goals\": {\n \"primary\": \"minimize_cost\",\n \"constraints\": {\n \"max_cost_per_hour\": 10.0,\n \"min_throughput\": 5000,\n \"max_latency_p95\": 200,\n },\n },\n \"testScenario\": {\n \"traffic_pattern\": \"spike\",\n \"duration_steps\": 100,\n \"include_failures\": True,\n },\n },\n)\nresp.raise_for_status()\njob = resp.json()[\"job\"]\nprint(\"Job ID:\", job[\"id\"], \"Status:\", job[\"status\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/analysis/optimize`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n simulationId: \"sim-abc123\",\n goals: {\n primary: \"minimize_cost\",\n constraints: {\n max_cost_per_hour: 10.0,\n min_throughput: 5000,\n max_latency_p95: 200,\n },\n },\n testScenario: {\n traffic_pattern: \"spike\",\n duration_steps: 100,\n include_failures: true,\n },\n }),\n});\nconst { job } = await resp.json();\nconsole.log(\"Job ID:\", job.id, \"Status:\", job.status);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.005"}},"x-x402-call-type":"optimization.run","x-x402-price-usdc":"$0.0050","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["simulationId","goals"],"properties":{"simulationId":{"type":"string","description":"ID of simulation to optimize"},"goals":{"type":"object","required":["primary"],"properties":{"primary":{"type":"string","enum":["minimize_cost","maximize_performance","balance"],"description":"Primary optimization objective"},"constraints":{"type":"object","properties":{"max_cost_per_hour":{"type":"number","description":"Maximum acceptable cost per hour (USD)"},"min_throughput":{"type":"number","description":"Minimum required throughput (requests/second)"},"max_latency_p95":{"type":"number","description":"Maximum acceptable P95 latency (milliseconds)"}}},"weights":{"type":"object","description":"Custom weights for multi-objective optimization","properties":{"cost":{"type":"number"},"performance":{"type":"number"},"stability":{"type":"number"}}}},"example":{"primary":"minimize_cost","constraints":{"max_cost_per_hour":10,"min_throughput":5000,"max_latency_p95":200}}},"testScenario":{"type":"object","properties":{"traffic_pattern":{"type":"string"},"duration_steps":{"type":"integer","default":100},"include_failures":{"type":"boolean"}}},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when job completes","example":"https://your-app.com/webhooks/optimization"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"your-secret-key-here"}},"example":{"simulationId":"sim-abc123","goals":{"primary":"minimize_cost","constraints":{"max_cost_per_hour":10,"min_throughput":5000,"max_latency_p95":200}}}}}}},"responses":{"202":{"description":"Optimization job accepted","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"createdAt":{"type":"string"}}},"message":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"403":{"description":"Simulation not owned by this API key","content":{"application/json":{"schema":{"type":"object","required":["error","reason","remedy"],"properties":{"error":{"type":"string","example":"Simulation not owned by this API key"},"reason":{"type":"string","example":"The simulationId refers to a simulation that was not created with, or claimed by, the API key used in this request. Simulations created via the public browser workspace (without an Authorization header) have no owner and cannot be used here until claimed."},"remedy":{"type":"string","example":"Either create an owned simulation via POST /api/simulations with a write-scoped Bearer token, or claim an existing unowned simulation via POST /api/simulations/{simulationId}/claim before calling this endpoint."}}}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/analysis/jobs/{id}":{"x-stability":"stable","get":{"tags":["Infrastructure Optimization"],"summary":"Fetch optimization job status","description":"Check the progress and status of an optimization job","operationId":"getOptimizationJob","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl https://www.cloudworldmodel.ai/api/analysis/jobs/job_abc123 \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\nJOB_ID = \"job_abc123\"\n\nresp = requests.get(\n f\"{BASE_URL}/analysis/jobs/{JOB_ID}\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\njob = resp.json()\nprint(\"Status:\", job[\"status\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\nconst JOB_ID = \"job_abc123\";\n\nconst resp = await fetch(`${BASE_URL}/analysis/jobs/${JOB_ID}`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n});\nconst job = await resp.json();\nconsole.log(\"Status:\", job.status);\n"}],"security":[],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Identifier of the optimization job whose status is requested."}],"responses":{"200":{"description":"Job status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OptimizationJob"}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/analysis/jobs/{id}/recommendations":{"x-stability":"stable","get":{"tags":["Infrastructure Optimization"],"summary":"Fetch optimization recommendations","description":"Retrieve ranked recommendations from a completed optimization job created\nvia `POST /api/analysis/optimize`. Returns HTTP 202 while the job is\nstill running — poll until the status endpoint shows `completed`.\n\n**Authentication:** Requires an API key (`read` scope) or an x402\nmicro-payment (3 credits — $0.0030 USDC on Base). Pass\n`Authorization: Bearer <key>` for API key access, or include a signed\nx402 payment header with call type `ai.recommendations` (no account\nrequired). x402 callers that paid to create the optimization job carry a\nwallet-session JWT that is accepted here without a second payment.\nUse `GET /api/billing/x402/config` to discover the live price.\n","operationId":"getRecommendations","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl https://www.cloudworldmodel.ai/api/analysis/jobs/job_abc123/recommendations \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\nJOB_ID = \"job_abc123\"\n\nresp = requests.get(\n f\"{BASE_URL}/analysis/jobs/{JOB_ID}/recommendations\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\ndata = resp.json()\nfor rec in data[\"recommendations\"]:\n print(rec[\"title\"], \"— savings:\", rec.get(\"estimatedSavings\"))\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\nconst JOB_ID = \"job_abc123\";\n\nconst resp = await fetch(`${BASE_URL}/analysis/jobs/${JOB_ID}/recommendations`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n});\nconst { recommendations } = await resp.json();\nrecommendations.forEach(rec =>\n console.log(rec.title, \"— savings:\", rec.estimatedSavings)\n);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.003"}},"x-x402-call-type":"ai.recommendations","x-x402-price-usdc":"$0.0030","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Identifier of the optimization job whose recommendations are requested."},{"name":"maxReversibilityTier","in":"query","required":false,"schema":{"type":"string","enum":["instant","minutes","hours","days"]},"description":"Filter recommendations to only those whose reversibility tier is at or below this value.\nTiers are ordered from easiest to hardest to undo: `instant` < `minutes` < `hours` < `days`.\nFor example, `?maxReversibilityTier=minutes` returns only recommendations that can be\nreverted in under 10 minutes (instant or minutes tier). Omit to receive all recommendations\nregardless of tier. Invalid values return a 400 error.\n"}],"responses":{"200":{"description":"Ranked recommendations","content":{"application/json":{"schema":{"type":"object","properties":{"recommendations":{"type":"array","items":{"$ref":"#/components/schemas/OptimizationRecommendation"}},"totalVariants":{"type":"integer"},"goals":{"$ref":"#/components/schemas/OptimizationGoals"}}}}}},"202":{"description":"Job still in progress — poll the job status endpoint until status is 'completed'","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["pending","running"],"description":"Current job status"},"recommendations":{"type":"null","description":"Always null while the job is in progress"}}}}}},"400":{"description":"Invalid query parameter value","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"INVALID_FIELD"},"pointer":{"type":"string","example":"/maxReversibilityTier"},"message":{"type":"string","example":"Invalid tier value \"weekly\""},"suggestion":{"type":"string","example":"Valid values: instant, minutes, hours, days"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/predictions/validate":{"x-stability":"stable","post":{"tags":["Optimize & Predict"],"summary":"Validate infrastructure against a traffic forecast","description":"Tests whether current infrastructure can handle a predicted traffic pattern.\nReturns validation results with bottlenecks and recommendations.\n\nWhen a `webhookUrl` is provided, the following payload is POSTed to that URL\nwhen the job completes (example shown for the AWS EC2 validation request above):\n\n```json\n{\n \"event\": \"prediction.completed\",\n \"jobId\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n \"jobType\": \"prediction_validation\",\n \"status\": \"completed\",\n \"completedAt\": \"2025-11-23T10:35:00Z\",\n \"data\": {\n \"validationResult\": {\n \"passed\": false,\n \"summary\": \"Infrastructure will fail under peak load due to CPU saturation\",\n \"peakMetrics\": {\n \"timestamp\": 60,\n \"traffic\": 12000,\n \"cpuUsage\": 98,\n \"latencyP95\": 820,\n \"errorRate\": 8.4,\n \"costPerHour\": 3.20\n },\n \"bottlenecksDetected\": [\n \"CPU saturation at 98%\",\n \"Error rate exceeds 5%\"\n ],\n \"failurePoints\": [\n { \"timestamp\": 55, \"traffic\": 10500, \"reason\": \"CPU saturation\" }\n ],\n \"recommendations\": [\n \"Scale out to 5 instances before peak\",\n \"Increase CPU threshold to 75%\"\n ]\n }\n }\n}\n```\n\nThe request is signed with HMAC-SHA256; verify the `X-Webhook-Signature` header\nagainst your `webhookSecret` before processing the payload.\n","operationId":"validateTrafficForecast","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/predictions/validate \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"simulationId\": \"sim-aws-ec2-prod\",\n \"trafficForecast\": {\n \"name\": \"Black Friday 2025\",\n \"dataPoints\": [\n {\"timestamp\": 0, \"rps\": 2000, \"label\": \"Baseline\"},\n {\"timestamp\": 60, \"rps\": 12000, \"label\": \"Peak\"},\n {\"timestamp\": 100, \"rps\": 2500, \"label\": \"Return to baseline\"}\n ]\n },\n \"testSteps\": 100\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/predictions/validate\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"simulationId\": \"sim-aws-ec2-prod\",\n \"trafficForecast\": {\n \"name\": \"Black Friday 2025\",\n \"dataPoints\": [\n {\"timestamp\": 0, \"rps\": 2000, \"label\": \"Baseline\"},\n {\"timestamp\": 60, \"rps\": 12000, \"label\": \"Peak\"},\n {\"timestamp\": 100, \"rps\": 2500, \"label\": \"Return to baseline\"},\n ],\n },\n \"testSteps\": 100,\n },\n)\nresp.raise_for_status()\njob = resp.json()[\"job\"]\nprint(\"Job ID:\", job[\"id\"], \"Status:\", job[\"status\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/predictions/validate`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n simulationId: \"sim-aws-ec2-prod\",\n trafficForecast: {\n name: \"Black Friday 2025\",\n dataPoints: [\n { timestamp: 0, rps: 2000, label: \"Baseline\" },\n { timestamp: 60, rps: 12000, label: \"Peak\" },\n { timestamp: 100, rps: 2500, label: \"Return to baseline\" },\n ],\n },\n testSteps: 100,\n }),\n});\nconst { job } = await resp.json();\nconsole.log(\"Job ID:\", job.id, \"Status:\", job.status);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.005"}},"x-x402-call-type":"prediction.validate","x-x402-price-usdc":"$0.0050","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["simulationId","trafficForecast"],"properties":{"simulationId":{"type":"string","description":"ID of simulation to test","example":"sim-abc123"},"trafficForecast":{"type":"object","required":["name","dataPoints"],"properties":{"name":{"type":"string","description":"Name of the traffic forecast"},"description":{"type":"string","description":"Optional description"},"dataPoints":{"type":"array","description":"Traffic data points over time","items":{"type":"object","required":["timestamp","rps"],"properties":{"timestamp":{"type":"number","description":"Time offset from the start of the forecast, in simulation steps; points must be ordered by increasing timestamps."},"rps":{"type":"number","description":"Forecast request rate in requests per second at this timestamp."},"label":{"type":"string"}}}},"peakRPS":{"type":"number","description":"Peak requests per second"},"avgRPS":{"type":"number","description":"Average requests per second"}},"example":{"name":"Peak Traffic Test","dataPoints":[{"timestamp":0,"rps":2000,"label":"Baseline"},{"timestamp":60,"rps":12000,"label":"Peak"}]}},"testSteps":{"type":"integer","default":100,"description":"Number of simulation steps to run","example":100},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when job completes","example":"https://your-app.com/webhooks/validation"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"your-secret-key-here"}},"example":{"simulationId":"sim-abc123","trafficForecast":{"name":"Peak Traffic Test","dataPoints":[{"timestamp":0,"rps":2000,"label":"Baseline"},{"timestamp":60,"rps":12000,"label":"Peak"}]},"testSteps":100}},"examples":{"awsValidation":{"summary":"AWS — validate EC2 m5.xlarge cluster against a Black Friday traffic spike","value":{"simulationId":"sim-aws-ec2-prod","trafficForecast":{"name":"Black Friday 2025 — AWS Production","description":"Predicted 6x traffic spike starting at step 30, peaking at step 60","dataPoints":[{"timestamp":0,"rps":2000,"label":"Baseline"},{"timestamp":30,"rps":6000,"label":"Pre-peak ramp"},{"timestamp":60,"rps":12000,"label":"Peak — Black Friday midnight"},{"timestamp":90,"rps":8000,"label":"Post-peak decline"},{"timestamp":100,"rps":2500,"label":"Return to baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"aws-prediction-secret"}},"gcpValidation":{"summary":"GCP — validate Cloud Run service against a seasonal holiday burst","value":{"simulationId":"sim-gcp-cloudrun-prod","trafficForecast":{"name":"GCP Seasonal Spike — Holiday 2025","description":"Gradual ramp over 70 steps peaking at 4x baseline","dataPoints":[{"timestamp":0,"rps":3000,"label":"Baseline"},{"timestamp":40,"rps":8000,"label":"Holiday ramp"},{"timestamp":70,"rps":12000,"label":"Peak — Holiday noon"},{"timestamp":90,"rps":6000,"label":"Post-holiday wind-down"},{"timestamp":100,"rps":3200,"label":"Return to baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"gcp-prediction-secret"}},"azureValidation":{"summary":"Azure — validate AKS cluster against a sudden product launch spike","value":{"simulationId":"sim-azure-aks-prod","trafficForecast":{"name":"Azure Product Launch Traffic","description":"Sudden 10x spike from launch announcement, sustained for 50 steps","dataPoints":[{"timestamp":0,"rps":1000,"label":"Pre-launch baseline"},{"timestamp":20,"rps":10000,"label":"Launch announcement — spike"},{"timestamp":50,"rps":8000,"label":"Sustained high traffic"},{"timestamp":80,"rps":3000,"label":"Gradual decline"},{"timestamp":100,"rps":1500,"label":"New elevated baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"azure-prediction-secret"}},"ociValidation":{"summary":"OCI — validate Compute + Autonomous Database against a month-end batch processing surge","value":{"simulationId":"sim-oci-compute-prod","trafficForecast":{"name":"OCI Month-End Batch Surge","description":"Recurring month-end reporting job — 3x query load for steps 25 through 80","dataPoints":[{"timestamp":0,"rps":800,"label":"Normal operations"},{"timestamp":25,"rps":2400,"label":"Month-end batch start"},{"timestamp":60,"rps":2600,"label":"Peak batch load"},{"timestamp":80,"rps":1200,"label":"Batch wind-down"},{"timestamp":100,"rps":850,"label":"Return to normal"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"oci-prediction-secret"}},"digitalOceanValidation":{"summary":"DigitalOcean — validate Droplet cluster against an unpredictable viral content spike","value":{"simulationId":"sim-do-droplets-prod","trafficForecast":{"name":"DigitalOcean Viral Traffic Event","description":"Sudden 5x baseline spike within 15 steps from a viral post","dataPoints":[{"timestamp":0,"rps":500,"label":"Normal baseline"},{"timestamp":15,"rps":2500,"label":"Viral spike onset"},{"timestamp":40,"rps":3000,"label":"Peak viral traffic"},{"timestamp":70,"rps":1500,"label":"Declining viral effect"},{"timestamp":100,"rps":700,"label":"New elevated baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"do-prediction-secret"}},"awsSpotValidation":{"summary":"AWS EC2 Spot — validate fault-tolerant batch cluster against a cost-optimized ramp forecast","value":{"simulationId":"sim-aws-spot-batch-prod","trafficForecast":{"name":"AWS Spot Batch Ramp — Cost-Optimized Workload","description":"Gradual ramp to 3x baseline reflecting a nightly batch pipeline; relaxed latency acceptable given Spot pricing savings","dataPoints":[{"timestamp":0,"rps":500,"label":"Idle baseline"},{"timestamp":20,"rps":1000,"label":"Batch job start"},{"timestamp":50,"rps":1500,"label":"Peak batch throughput"},{"timestamp":75,"rps":1000,"label":"Batch wind-down"},{"timestamp":100,"rps":500,"label":"Return to idle"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"aws-spot-prediction-secret"}},"gcpSpotValidation":{"summary":"GCP Spot VM — validate fault-tolerant batch cluster against a cost-optimized ramp forecast","value":{"simulationId":"sim-gcp-spot-batch-prod","trafficForecast":{"name":"GCP Spot Batch Ramp — Cost-Optimized Workload","description":"Gradual ramp to 3x baseline reflecting a nightly batch pipeline on GCP Spot VMs; relaxed latency acceptable given ~70% preemptible pricing savings","dataPoints":[{"timestamp":0,"rps":400,"label":"Idle baseline"},{"timestamp":20,"rps":800,"label":"Batch job start"},{"timestamp":50,"rps":1200,"label":"Peak batch throughput"},{"timestamp":75,"rps":800,"label":"Batch wind-down"},{"timestamp":100,"rps":400,"label":"Return to idle"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"gcp-spot-prediction-secret"}},"azureSpotValidation":{"summary":"Azure Spot VM — validate fault-tolerant batch cluster against a cost-optimized ramp forecast","value":{"simulationId":"sim-azure-spot-batch-prod","trafficForecast":{"name":"Azure Spot Batch Ramp — Cost-Optimized Workload","description":"Gradual ramp to 3x baseline reflecting a nightly batch pipeline on Azure Spot VMs; relaxed latency acceptable given ~70% pay-as-you-go pricing savings","dataPoints":[{"timestamp":0,"rps":300,"label":"Idle baseline"},{"timestamp":20,"rps":600,"label":"Batch job start"},{"timestamp":50,"rps":900,"label":"Peak batch throughput"},{"timestamp":75,"rps":600,"label":"Batch wind-down"},{"timestamp":100,"rps":300,"label":"Return to idle"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"azure-spot-prediction-secret"}},"ociPreemptibleValidation":{"summary":"OCI Ampere A1 Preemptible — validate fault-tolerant batch cluster against a cost-optimized ramp forecast","value":{"simulationId":"sim-oci-preemptible-batch-prod","trafficForecast":{"name":"OCI Preemptible Batch Ramp — Cost-Optimized Workload","description":"Gradual ramp to 3x baseline reflecting a nightly batch pipeline on OCI preemptible Ampere A1 instances; relaxed latency acceptable given ~50% preemptible pricing savings","dataPoints":[{"timestamp":0,"rps":350,"label":"Idle baseline"},{"timestamp":20,"rps":700,"label":"Batch job start"},{"timestamp":50,"rps":1050,"label":"Peak batch throughput"},{"timestamp":75,"rps":700,"label":"Batch wind-down"},{"timestamp":100,"rps":350,"label":"Return to idle"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"oci-preemptible-prediction-secret"}}}}}},"responses":{"202":{"description":"Validation job accepted","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","running"]},"type":{"type":"string","example":"validation"},"createdAt":{"type":"string","format":"date-time"}}},"message":{"type":"string","example":"Validation job started. Poll /predictions/jobs/{id} for status."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/predictions/optimize-thresholds":{"x-stability":"stable","post":{"tags":["Optimize & Predict"],"summary":"Generate optimal autoscaling thresholds","description":"Tests multiple threshold combinations to find the best autoscaling configuration\nfor the predicted traffic pattern.\n\nWhen a `webhookUrl` is provided, the following payload is POSTed to that URL\nwhen the job completes (example shown for the AWS EC2 Auto Scaling request above):\n\n```json\n{\n \"event\": \"prediction.completed\",\n \"jobId\": \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\",\n \"jobType\": \"prediction_optimization\",\n \"status\": \"completed\",\n \"completedAt\": \"2025-11-23T10:35:00Z\",\n \"data\": {\n \"bestThresholds\": {\n \"scaleOutCpuThreshold\": 70,\n \"scaleInCpuThreshold\": 30,\n \"scaleOutThroughputThreshold\": 75,\n \"scaleInThroughputThreshold\": 35,\n \"scaleOutLatencyThreshold\": 120,\n \"cooldownSeconds\": 180,\n \"minInstances\": 3,\n \"maxInstances\": 15\n }\n }\n}\n```\n\nThe request is signed with HMAC-SHA256; verify the `X-Webhook-Signature` header\nagainst your `webhookSecret` before processing the payload.\n","operationId":"optimizeThresholds","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/predictions/optimize-thresholds \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"simulationId\": \"sim-aws-ec2-prod\",\n \"trafficForecast\": {\n \"name\": \"Black Friday 2025\",\n \"dataPoints\": [\n {\"timestamp\": 0, \"rps\": 2000, \"label\": \"Baseline\"},\n {\"timestamp\": 60, \"rps\": 12000, \"label\": \"Peak\"},\n {\"timestamp\": 100, \"rps\": 2500, \"label\": \"Return to baseline\"}\n ]\n },\n \"testSteps\": 100\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/predictions/optimize-thresholds\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"simulationId\": \"sim-aws-ec2-prod\",\n \"trafficForecast\": {\n \"name\": \"Black Friday 2025\",\n \"dataPoints\": [\n {\"timestamp\": 0, \"rps\": 2000, \"label\": \"Baseline\"},\n {\"timestamp\": 60, \"rps\": 12000, \"label\": \"Peak\"},\n {\"timestamp\": 100, \"rps\": 2500, \"label\": \"Return to baseline\"},\n ],\n },\n \"testSteps\": 100,\n },\n)\nresp.raise_for_status()\njob = resp.json()[\"job\"]\nprint(\"Job ID:\", job[\"id\"], \"Status:\", job[\"status\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/predictions/optimize-thresholds`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n simulationId: \"sim-aws-ec2-prod\",\n trafficForecast: {\n name: \"Black Friday 2025\",\n dataPoints: [\n { timestamp: 0, rps: 2000, label: \"Baseline\" },\n { timestamp: 60, rps: 12000, label: \"Peak\" },\n { timestamp: 100, rps: 2500, label: \"Return to baseline\" },\n ],\n },\n testSteps: 100,\n }),\n});\nconst { job } = await resp.json();\nconsole.log(\"Job ID:\", job.id, \"Status:\", job.status);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.005"}},"x-x402-call-type":"prediction.optimize_thresholds","x-x402-price-usdc":"$0.0050","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["simulationId","trafficForecast"],"properties":{"simulationId":{"type":"string","description":"ID of simulation to optimize","example":"sim-abc123"},"trafficForecast":{"type":"object","required":["name","dataPoints"],"properties":{"name":{"type":"string","description":"Name of the traffic forecast"},"description":{"type":"string","description":"Optional description"},"dataPoints":{"type":"array","description":"Traffic data points over time","items":{"type":"object","required":["timestamp","rps"],"properties":{"timestamp":{"type":"number","description":"Time offset from the start of the forecast, in simulation steps; points must be ordered by increasing timestamps."},"rps":{"type":"number","description":"Forecast request rate in requests per second at this timestamp."},"label":{"type":"string"}}}},"peakRPS":{"type":"number","description":"Peak requests per second"},"avgRPS":{"type":"number","description":"Average requests per second"}},"example":{"name":"Peak Traffic Test","dataPoints":[{"timestamp":0,"rps":2000,"label":"Baseline"},{"timestamp":60,"rps":12000,"label":"Peak"}]}},"testSteps":{"type":"integer","default":100,"description":"Number of simulation steps to run per test","example":100},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when job completes","example":"https://your-app.com/webhooks/threshold-optimization"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"your-secret-key-here"}},"example":{"simulationId":"sim-abc123","trafficForecast":{"name":"Peak Traffic Test","dataPoints":[{"timestamp":0,"rps":2000,"label":"Baseline"},{"timestamp":60,"rps":12000,"label":"Peak"}]},"testSteps":100}},"examples":{"awsOptimization":{"summary":"AWS — find optimal EC2 Auto Scaling thresholds for Black Friday traffic","value":{"simulationId":"sim-aws-ec2-prod","trafficForecast":{"name":"Black Friday 2025 — AWS Production","description":"Predicted 6x traffic spike starting at step 30, peaking at step 60","dataPoints":[{"timestamp":0,"rps":2000,"label":"Baseline"},{"timestamp":30,"rps":6000,"label":"Pre-peak ramp"},{"timestamp":60,"rps":12000,"label":"Peak — Black Friday midnight"},{"timestamp":90,"rps":8000,"label":"Post-peak decline"},{"timestamp":100,"rps":2500,"label":"Return to baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"aws-optimization-secret"}},"gcpOptimization":{"summary":"GCP — find optimal Cloud Run thresholds for a seasonal holiday burst","value":{"simulationId":"sim-gcp-cloudrun-prod","trafficForecast":{"name":"GCP Seasonal Spike — Holiday 2025","description":"Gradual ramp over 70 steps peaking at 4x baseline","dataPoints":[{"timestamp":0,"rps":3000,"label":"Baseline"},{"timestamp":40,"rps":8000,"label":"Holiday ramp"},{"timestamp":70,"rps":12000,"label":"Peak — Holiday noon"},{"timestamp":90,"rps":6000,"label":"Post-holiday wind-down"},{"timestamp":100,"rps":3200,"label":"Return to baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"gcp-optimization-secret"}},"azureOptimization":{"summary":"Azure — find optimal AKS HPA thresholds for a product launch spike","value":{"simulationId":"sim-azure-aks-prod","trafficForecast":{"name":"Azure Product Launch Traffic","description":"Sudden 10x spike from launch announcement, sustained for 50 steps","dataPoints":[{"timestamp":0,"rps":1000,"label":"Pre-launch baseline"},{"timestamp":20,"rps":10000,"label":"Launch announcement — spike"},{"timestamp":50,"rps":8000,"label":"Sustained high traffic"},{"timestamp":80,"rps":3000,"label":"Gradual decline"},{"timestamp":100,"rps":1500,"label":"New elevated baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"azure-optimization-secret"}},"ociOptimization":{"summary":"OCI — find optimal autoscaling thresholds for a month-end batch processing surge","value":{"simulationId":"sim-oci-compute-prod","trafficForecast":{"name":"OCI Month-End Batch Surge","description":"Recurring month-end reporting job — 3x query load for steps 25 through 80","dataPoints":[{"timestamp":0,"rps":800,"label":"Normal operations"},{"timestamp":25,"rps":2400,"label":"Month-end batch start"},{"timestamp":60,"rps":2600,"label":"Peak batch load"},{"timestamp":80,"rps":1200,"label":"Batch wind-down"},{"timestamp":100,"rps":850,"label":"Return to normal"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"oci-optimization-secret"}},"digitalOceanOptimization":{"summary":"DigitalOcean — find optimal Droplet autoscaling thresholds for a viral content spike","value":{"simulationId":"sim-do-droplets-prod","trafficForecast":{"name":"DigitalOcean Viral Traffic Event","description":"Sudden 5x baseline spike within 15 steps from a viral post","dataPoints":[{"timestamp":0,"rps":500,"label":"Normal baseline"},{"timestamp":15,"rps":2500,"label":"Viral spike onset"},{"timestamp":40,"rps":3000,"label":"Peak viral traffic"},{"timestamp":70,"rps":1500,"label":"Declining viral effect"},{"timestamp":100,"rps":700,"label":"New elevated baseline"}]},"testSteps":100,"webhookUrl":"https://your-app.example.com/webhooks/predictions","webhookSecret":"do-optimization-secret"}}}}}},"responses":{"202":{"description":"Optimization job accepted","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","running"]},"type":{"type":"string","example":"threshold_optimization"},"createdAt":{"type":"string","format":"date-time"}}},"message":{"type":"string","example":"Threshold optimization job started. Poll /predictions/jobs/{id} for status."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/multi-cloud/explore":{"x-stability":"stable","post":{"tags":["Optimize & Predict"],"summary":"Search multi-cloud deployment strategies","description":"Analyze a workload profile and generate optimized multi-cloud deployment strategies.\nThe system evaluates different provider combinations across AWS, GCP, Azure, OCI, and DigitalOcean\nbased on cost, latency, and vendor lock-in considerations.\n\n**DigitalOcean as a candidate provider:** DigitalOcean is a first-class candidate in every\nexploration run. It is particularly well-suited for cost-optimized workloads — Droplets and\nManaged Databases typically produce the lowest monthly spend in the comparison report. Set a\nhigh `cost` weight (e.g. 0.7+) and a moderate budget to see DigitalOcean-primary strategies\nappear at the top of `topStrategies` in the results.\n\n**Request body fields:** The request body has two top-level fields that are independent of\neach other. `workloadProfile` describes the workload being evaluated (compute/database\ninstances, traffic, latency requirements, and primary region). `optimizationWeights` is a\nseparate top-level object — **not** nested inside `workloadProfile` — that controls how\nstrategies are ranked: `cost` (default 0.4), `latency` (default 0.4), and `vendorLockIn`\n(default 0.2). Weights need not sum to 1.0; the engine normalises them during scoring. A\ncost-heavy weight (e.g. `cost: 0.9`) surfaces the cheapest strategies at the top of\n`topStrategies`; a latency-heavy weight (e.g. `latency: 0.9`) surfaces the lowest-latency\nstrategies instead.\n","operationId":"exploreMultiCloudStrategies","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/multi-cloud/explore \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"workloadProfile\": {\n \"computeInstances\": 8,\n \"databaseInstances\": 2,\n \"storageGB\": 500,\n \"trafficRPS\": 2500,\n \"latencyRequirementMs\": 100,\n \"primaryRegion\": \"us-east-1\",\n \"secondaryRegions\": [\"eu-west-1\"]\n },\n \"optimizationWeights\": {\n \"cost\": 0.4,\n \"latency\": 0.4,\n \"vendorLockIn\": 0.2\n },\n \"webhookUrl\": \"https://your-app.com/webhooks/multicloud\"\n }'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\npayload = {\n \"workloadProfile\": {\n \"computeInstances\": 8,\n \"databaseInstances\": 2,\n \"storageGB\": 500,\n \"trafficRPS\": 2500,\n \"latencyRequirementMs\": 100,\n \"primaryRegion\": \"us-east-1\",\n \"secondaryRegions\": [\"eu-west-1\"],\n },\n \"optimizationWeights\": {\"cost\": 0.4, \"latency\": 0.4, \"vendorLockIn\": 0.2},\n \"webhookUrl\": \"https://your-app.com/webhooks/multicloud\",\n}\n\nresp = requests.post(\n f\"{BASE_URL}/multi-cloud/explore\",\n json=payload,\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\ndata = resp.json()\njob = data[\"job\"]\nprint(f\"Job started: id={job['id']} status={job['status']}\")\nprint(f\"Poll status at: GET /api/multi-cloud/jobs/{job['id']}\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst payload = {\n workloadProfile: {\n computeInstances: 8,\n databaseInstances: 2,\n storageGB: 500,\n trafficRPS: 2500,\n latencyRequirementMs: 100,\n primaryRegion: \"us-east-1\",\n secondaryRegions: [\"eu-west-1\"],\n },\n optimizationWeights: { cost: 0.4, latency: 0.4, vendorLockIn: 0.2 },\n webhookUrl: \"https://your-app.com/webhooks/multicloud\",\n};\n\nconst resp = await fetch(`${BASE_URL}/multi-cloud/explore`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n});\nconst data = await resp.json();\nconst { job } = data;\nconsole.log(`Job started: id=${job.id} status=${job.status}`);\nconsole.log(`Poll status at: GET /api/multi-cloud/jobs/${job.id}`);\n"},{"lang":"curl","label":"curl (OCI Preemptible)","source":"curl -X POST https://www.cloudworldmodel.ai/api/multi-cloud/explore \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\n \"workloadProfile\": {\n \"computeInstances\": 4,\n \"databaseInstances\": 1,\n \"storageGB\": 200,\n \"trafficRPS\": 700,\n \"latencyRequirementMs\": 600,\n \"primaryRegion\": \"us-ashburn-1\"\n },\n \"optimizationWeights\": {\n \"cost\": 0.75,\n \"latency\": 0.15,\n \"vendorLockIn\": 0.10\n },\n \"webhookUrl\": \"https://your-app.example.com/webhooks/multicloud\",\n \"webhookSecret\": \"oci-preemptible-secret\"\n }'\n"},{"lang":"Python","label":"Python (OCI Preemptible)","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\npayload = {\n \"workloadProfile\": {\n \"computeInstances\": 4,\n \"databaseInstances\": 1,\n \"storageGB\": 200,\n \"trafficRPS\": 700,\n \"latencyRequirementMs\": 600,\n \"primaryRegion\": \"us-ashburn-1\",\n },\n \"optimizationWeights\": {\"cost\": 0.75, \"latency\": 0.15, \"vendorLockIn\": 0.10},\n \"webhookUrl\": \"https://your-app.example.com/webhooks/multicloud\",\n \"webhookSecret\": \"oci-preemptible-secret\",\n}\n\nresp = requests.post(\n f\"{BASE_URL}/multi-cloud/explore\",\n json=payload,\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\ndata = resp.json()\njob = data[\"job\"]\nprint(f\"Job started: id={job['id']} status={job['status']}\")\nprint(f\"Poll status at: GET /api/multi-cloud/jobs/{job['id']}\")\n"},{"lang":"Node.js","label":"Node.js (OCI Preemptible)","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst payload = {\n workloadProfile: {\n computeInstances: 4,\n databaseInstances: 1,\n storageGB: 200,\n trafficRPS: 700,\n latencyRequirementMs: 600,\n primaryRegion: \"us-ashburn-1\",\n },\n optimizationWeights: { cost: 0.75, latency: 0.15, vendorLockIn: 0.10 },\n webhookUrl: \"https://your-app.example.com/webhooks/multicloud\",\n webhookSecret: \"oci-preemptible-secret\",\n};\n\nconst resp = await fetch(`${BASE_URL}/multi-cloud/explore`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n});\nconst data = await resp.json();\nconst { job } = data;\nconsole.log(`Job started: id=${job.id} status=${job.status}`);\nconsole.log(`Poll status at: GET /api/multi-cloud/jobs/${job.id}`);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.005"}},"x-x402-call-type":"multicloud.explore","x-x402-price-usdc":"$0.0050","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["workloadProfile"],"properties":{"workloadProfile":{"type":"object","description":"Workload characteristics to evaluate. Must contain only the documented properties listed below. Unknown keys are rejected with HTTP 400 — place optimizationWeights as a separate top-level field alongside workloadProfile, not inside it.","required":["computeInstances","databaseInstances","storageGB","trafficRPS","latencyRequirementMs","primaryRegion"],"properties":{"computeInstances":{"type":"integer","minimum":1,"description":"Number of compute instances required"},"databaseInstances":{"type":"integer","minimum":1,"description":"Number of database instances required"},"storageGB":{"type":"integer","minimum":1,"description":"Storage capacity in gigabytes"},"trafficRPS":{"type":"integer","minimum":1,"description":"Expected traffic in requests per second"},"latencyRequirementMs":{"type":"integer","minimum":1,"description":"Maximum acceptable latency in milliseconds"},"primaryRegion":{"type":"string","description":"Primary deployment region"},"secondaryRegions":{"type":"array","description":"Optional secondary regions for multi-region deployment","items":{"type":"string"}},"requiresMultiRegion":{"type":"boolean","description":"Whether multi-region deployment is required","default":false},"dataResidencyRequirements":{"type":"array","description":"Data residency constraints (e.g., GDPR regions)","items":{"type":"string"}},"sourceProvider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"],"description":"The cloud provider you are currently on"},"workloadType":{"type":"string","enum":["standard","inference"],"default":"standard","description":"Workload category. `standard` (default) generates CPU compute strategies only. `inference` additionally generates one GPU Kubernetes strategy per provider — EKS g5.xlarge (A10G), GKE n1-standard-4+T4, AKS NC8ads_A10_v4 (fractional A10), OKE VM.GPU.A10.1, and DOKS gpu-h100x1-80gb — priced from GPU_NODE_HOURLY_RATES in shared/provider-pricing.ts."},"targetTokensPerSec":{"type":"number","minimum":1,"description":"Target sustained output-token throughput (tokens/sec) for `workloadType: inference` workloads. When set, the generator sizes each provider's GPU node count to meet this target (ceil(targetTokensPerSec / acceleratorThroughput)). If omitted, `computeInstances` is used as the GPU node count directly. Ignored for `workloadType: standard`."}},"additionalProperties":false,"example":{"computeInstances":8,"databaseInstances":2,"storageGB":500,"trafficRPS":2500,"latencyRequirementMs":100,"primaryRegion":"us-east-1"}},"optimizationWeights":{"type":"object","description":"Weights for optimization objectives. Each field is a number 0–1; a per-field value > 1 returns 400. The engine normalises the weights during scoring so the sum need not equal 1.0 — e.g. {cost: 0.9, latency: 0.3} is valid. Unknown keys return 400.","additionalProperties":false,"properties":{"cost":{"type":"number","minimum":0,"maximum":1,"default":0.4,"description":"Weight for cost optimization","example":0.4},"latency":{"type":"number","minimum":0,"maximum":1,"default":0.4,"description":"Weight for latency optimization","example":0.4},"vendorLockIn":{"type":"number","minimum":0,"maximum":1,"default":0.2,"description":"Weight for minimizing vendor lock-in","example":0.2}}},"maxCostPerHour":{"type":"number","exclusiveMinimum":0,"description":"Optional hard budget ceiling (USD/hr). Strategies whose `totalCostPerHour` exceeds this value are ranked last in the results rather than filtered out, so you can see what is available within and outside your budget in a single response.\n","example":5},"errorBudgetPct":{"type":"number","minimum":0,"maximum":100,"description":"Optional SLO error-budget constraint (%). Strategies whose implied error rate (derived from provider-count redundancy: single-provider ~0.5%, two-provider ~0.15%, three+ ~0.05%) exceeds this threshold are ranked last. For example, setting `0.1` means any single-provider strategy is demoted below multi-provider alternatives.\n","example":0.1},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when job completes","example":"https://your-app.com/webhooks/multicloud"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"your-secret-key-here"},"modifiers":{"type":"object","description":"Optional pricing modifier overrides for this exploration run. All fields default to off/on-demand. Pass these to apply discount-adjusted cost estimates in the returned strategies and comparison report.","additionalProperties":false,"properties":{"awsCommitment":{"type":"string","enum":["on-demand","1yr","3yr"],"default":"on-demand","description":"AWS Savings Plan commitment tier. `1yr` applies ~40% off EC2 compute costs; `3yr` applies ~60% off. Only affects AWS allocations in the returned strategies."},"azureHybridBenefit":{"type":"boolean","default":false,"description":"When true, applies Azure Hybrid Benefit (~40% off Azure compute and database) for existing Windows Server / SQL Server licence holders."},"spotEligible":{"type":"boolean","default":false,"description":"When true, shows estimated Spot / Preemptible VM pricing alongside on-demand costs for eligible providers (AWS ~70% off, GCP ~80% off, Azure ~75% off, OCI ~50% off). DigitalOcean has no spot offering."},"oracleLicenseHolder":{"type":"boolean","default":false,"description":"When true, notes an OCI BYOL (Bring Your Own Oracle Licence) cost impact in the comparison report (~50% off OCI database costs for Standard Edition or Enterprise Edition licence holders). BYOL is informational — it does not change strategy cost figures."}}}},"example":{"workloadProfile":{"computeInstances":8,"databaseInstances":2,"storageGB":500,"trafficRPS":2500,"latencyRequirementMs":100,"primaryRegion":"us-east-1"},"optimizationWeights":{"cost":0.4,"latency":0.4,"vendorLockIn":0.2}}},"examples":{"balanced":{"summary":"Balanced multi-cloud workload (equal cost, latency, lock-in weights)","value":{"workloadProfile":{"computeInstances":10,"databaseInstances":2,"storageGB":500,"trafficRPS":2500,"latencyRequirementMs":100,"primaryRegion":"us-east-1","secondaryRegions":["eu-west-1"],"dataResidencyRequirements":["us","eu"]},"optimizationWeights":{"cost":0.4,"latency":0.4,"vendorLockIn":0.2},"webhookUrl":"https://your-app.com/webhooks/multicloud"}},"costOptimized":{"summary":"Cost-optimized workload favouring DigitalOcean","value":{"workloadProfile":{"computeInstances":4,"databaseInstances":1,"storageGB":200,"trafficRPS":1000,"latencyRequirementMs":150,"primaryRegion":"us-east-1"},"optimizationWeights":{"cost":0.7,"latency":0.2,"vendorLockIn":0.1},"webhookUrl":"https://your-app.com/webhooks/multicloud"}},"digitalOceanPrimary":{"summary":"DigitalOcean-primary strategy — maximize cost savings with DO Droplets","value":{"workloadProfile":{"computeInstances":3,"databaseInstances":1,"storageGB":100,"trafficRPS":800,"latencyRequirementMs":200,"primaryRegion":"nyc3","dataResidencyRequirements":["us"],"sourceProvider":"digitalocean"},"optimizationWeights":{"cost":0.75,"latency":0.15,"vendorLockIn":0.1},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"do-multicloud-secret"}},"digitaloceanAMDNVMe":{"summary":"DigitalOcean AMD NVMe Droplet — cost-optimized I/O-intensive workload targeting s-2vcpu-4gb-amd","value":{"workloadProfile":{"computeInstances":2,"databaseInstances":1,"storageGB":100,"trafficRPS":600,"latencyRequirementMs":180,"primaryRegion":"nyc3","dataResidencyRequirements":["us"],"sourceProvider":"digitalocean"},"optimizationWeights":{"cost":0.8,"latency":0.15,"vendorLockIn":0.05},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"do-amd-nvme-secret"}},"awsPrimary":{"summary":"AWS-primary strategy — high-performance enterprise workload on EC2 and RDS Multi-AZ","value":{"workloadProfile":{"computeInstances":20,"databaseInstances":3,"storageGB":2000,"trafficRPS":5000,"latencyRequirementMs":50,"primaryRegion":"us-east-1","secondaryRegions":["us-west-2"],"dataResidencyRequirements":["us"],"sourceProvider":"aws"},"optimizationWeights":{"cost":0.3,"latency":0.5,"vendorLockIn":0.2},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"aws-multicloud-secret"}},"awsSpot":{"summary":"AWS EC2 Spot strategy — cost-optimized fault-tolerant batch workload using Spot Instances","value":{"workloadProfile":{"computeInstances":4,"databaseInstances":1,"storageGB":200,"trafficRPS":1000,"latencyRequirementMs":600,"primaryRegion":"us-east-1","sourceProvider":"aws"},"optimizationWeights":{"cost":0.75,"latency":0.15,"vendorLockIn":0.1},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"aws-spot-secret"}},"gcpPrimary":{"summary":"GCP-primary strategy — ML analytics workload on Cloud Run and Cloud SQL","value":{"workloadProfile":{"computeInstances":12,"databaseInstances":2,"storageGB":1000,"trafficRPS":3000,"latencyRequirementMs":80,"primaryRegion":"us-central1","secondaryRegions":["europe-west1"],"dataResidencyRequirements":["us","eu"],"sourceProvider":"gcp"},"optimizationWeights":{"cost":0.3,"latency":0.5,"vendorLockIn":0.2},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"gcp-multicloud-secret"}},"gcpSpot":{"summary":"GCP Spot VM strategy — cost-optimized batch workload using preemptible compute","value":{"workloadProfile":{"computeInstances":3,"databaseInstances":1,"storageGB":150,"trafficRPS":800,"latencyRequirementMs":500,"primaryRegion":"us-central1","sourceProvider":"gcp"},"optimizationWeights":{"cost":0.75,"latency":0.15,"vendorLockIn":0.1},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"gcp-spot-secret"}},"azurePrimary":{"summary":"Azure-primary strategy — compliance-heavy enterprise workload on AKS and Azure Database","value":{"workloadProfile":{"computeInstances":8,"databaseInstances":2,"storageGB":800,"trafficRPS":2000,"latencyRequirementMs":100,"primaryRegion":"eastus","secondaryRegions":["westeurope"],"dataResidencyRequirements":["us","eu"],"sourceProvider":"azure"},"optimizationWeights":{"cost":0.2,"latency":0.4,"vendorLockIn":0.4},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"azure-multicloud-secret"}},"azureSpot":{"summary":"Azure Spot VM strategy — cost-optimized fault-tolerant workload using Azure Spot instances","value":{"workloadProfile":{"computeInstances":2,"databaseInstances":1,"storageGB":100,"trafficRPS":600,"latencyRequirementMs":400,"primaryRegion":"eastus","sourceProvider":"azure"},"optimizationWeights":{"cost":0.75,"latency":0.15,"vendorLockIn":0.1},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"azure-spot-secret"}},"ociPrimary":{"summary":"OCI-primary strategy — database-intensive workload on OCI Compute and Autonomous Database","value":{"workloadProfile":{"computeInstances":6,"databaseInstances":2,"storageGB":600,"trafficRPS":1500,"latencyRequirementMs":120,"primaryRegion":"us-ashburn-1","dataResidencyRequirements":["us"],"sourceProvider":"oci"},"optimizationWeights":{"cost":0.5,"latency":0.3,"vendorLockIn":0.2},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"oci-multicloud-secret"}},"ociSpot":{"summary":"OCI Ampere A1 Spot strategy — cost-optimized fault-tolerant batch workload using OCI preemptible Ampere A1 compute","value":{"workloadProfile":{"computeInstances":3,"databaseInstances":1,"storageGB":200,"trafficRPS":700,"latencyRequirementMs":600,"primaryRegion":"us-ashburn-1","sourceProvider":"oci"},"optimizationWeights":{"cost":0.75,"latency":0.15,"vendorLockIn":0.1},"webhookUrl":"https://your-app.example.com/webhooks/multicloud","webhookSecret":"oci-spot-secret"}}}}}},"responses":{"202":{"description":"Multi-cloud exploration job started","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"job-abc123"},"type":{"type":"string","enum":["multicloud_exploration"],"example":"multicloud_exploration"},"status":{"type":"string","enum":["pending","running"],"example":"running"},"createdAt":{"type":"string","format":"date-time"}}},"message":{"type":"string","example":"Multi-cloud exploration started. Use GET /multi-cloud/jobs/{id} to check status."}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"}}}},"/multi-cloud/jobs/{jobId}":{"x-stability":"stable","get":{"tags":["Multi-Cloud Strategy"],"summary":"Fetch multi-cloud exploration job status","description":"Get the current status and progress of a multi-cloud strategy exploration job","operationId":"getMultiCloudJob","x-x402-call-type":"multicloud.status","x-x402-price-usdc":"$0.0030","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl https://www.cloudworldmodel.ai/api/multi-cloud/jobs/job_abc123 \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\nJOB_ID = \"job_abc123\"\n\nresp = requests.get(\n f\"{BASE_URL}/multi-cloud/jobs/{JOB_ID}\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\njob = resp.json()\nprint(f\"Job {JOB_ID} status={job['status']} progress={job.get('progress', 0)}% \"\n f\"strategies={job.get('strategiesGenerated', 0)}\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\nconst JOB_ID = \"job_abc123\";\n\nconst resp = await fetch(`${BASE_URL}/multi-cloud/jobs/${JOB_ID}`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n});\nconst job = await resp.json();\nconsole.log(`Job ${JOB_ID} status=${job.status} progress=${job.progress ?? 0}% strategies=${job.strategiesGenerated ?? 0}`);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the multi-cloud job"}],"responses":{"200":{"description":"Multi-cloud job status","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","running","completed","failed"]},"progress":{"type":"number","description":"Completion progress (0-100)","example":75},"strategiesGenerated":{"type":"integer","description":"Number of strategies generated so far","example":12},"workloadProfile":{"$ref":"#/components/schemas/WorkloadProfile"},"optimizationWeights":{"type":"object","properties":{"cost":{"type":"number"},"latency":{"type":"number"},"vendorLockIn":{"type":"number"}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"error":{"type":"string"},"estimateDisclaimer":{"type":"string","description":"Present only when status is `completed`. Reminder that cost and latency figures are baseline planning estimates; validate with materialize + step under production load."}}}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/multi-cloud/jobs/{jobId}/results":{"x-stability":"stable","get":{"tags":["Multi-Cloud Strategy"],"summary":"Fetch multi-cloud exploration results","description":"Retrieve the **final** results from a multi-cloud exploration job.\n\n**Only returns data once the job has finished.** This endpoint returns\nresults exclusively when the job `status` is `completed`. While the job\nis still `pending` or `running` it returns `400 INVALID_REQUEST`; a\n`failed` job returns `500`.\n\n**Polling pattern:**\n- Poll `GET /multi-cloud/jobs/{jobId}` (or subscribe to `/stream`) until `status` is `completed`, then call this endpoint once for the final ranked strategies and comparison report.\n- To read strategies as they accumulate while the job is still running, use `GET /multi-cloud/jobs/{jobId}/partial-results` instead — that endpoint returns `isComplete: false` until the job reaches a terminal state.\n- For real-time streaming of progress, use the `/stream` endpoint.\n\n**Response field names:**\nThe successful `200` response contains `topStrategies` (the highest-scoring strategies,\nsorted by `metrics.compositeScore` descending) and `allStrategies` (every strategy\nevaluated, also sorted). Do **not** use the field names `variants`, `strategies`, or\n`ranked` — those do not exist in the response.\n","operationId":"getMultiCloudResults","x-x402-call-type":"multicloud_results","x-x402-price-usdc":"$0.0030","x-codeSamples":[{"lang":"curl","label":"curl","source":"# Poll status until the job is completed, then fetch the final results.\n# /results returns 400 while the job is still pending or running.\nuntil curl -sf \"https://www.cloudworldmodel.ai/api/multi-cloud/jobs/job_abc123\" \\\n -H \"Authorization: Bearer $API_KEY\" | grep -q '\"status\":\"completed\"'; do\n sleep 2\ndone\n\ncurl \"https://www.cloudworldmodel.ai/api/multi-cloud/jobs/job_abc123/results\" \\\n -H \"Authorization: Bearer $API_KEY\"\n\n# Filter results to DigitalOcean and AWS strategies only\ncurl \"https://www.cloudworldmodel.ai/api/multi-cloud/jobs/job_abc123/results?providers=digitalocean,aws\" \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import time\nimport requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\nJOB_ID = \"job_abc123\"\nHEADERS = {\"Authorization\": f\"Bearer {API_KEY}\"}\n\n# Poll status until the job is finished. /results returns 400 while\n# the job is still pending or running; use /partial-results to read\n# strategies as they accumulate during a run.\nwhile True:\n status = requests.get(\n f\"{BASE_URL}/multi-cloud/jobs/{JOB_ID}\", headers=HEADERS,\n ).json()\n if status[\"status\"] in (\"completed\", \"failed\", \"cancelled\"):\n break\n time.sleep(2)\n\nresp = requests.get(\n f\"{BASE_URL}/multi-cloud/jobs/{JOB_ID}/results\",\n headers=HEADERS,\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(f\"Job {JOB_ID} status={data['status']} \"\n f\"complete={data['isComplete']} strategies={data['strategiesGenerated']}\")\nfor strategy in data.get(\"topStrategies\", []):\n cost = strategy[\"metrics\"][\"monthlyCost\"]\n latency = strategy[\"metrics\"][\"avgLatencyMs\"]\n print(f\" {strategy['name']} cost=${cost}/mo latency={latency}ms\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\nconst JOB_ID = \"job_abc123\";\nconst HEADERS = { \"Authorization\": `Bearer ${API_KEY}` };\n\n// Poll status until the job is finished. /results returns 400 while\n// the job is still pending or running; use /partial-results to read\n// strategies as they accumulate during a run.\nlet status;\ndo {\n await new Promise(r => setTimeout(r, 2000));\n status = await fetch(`${BASE_URL}/multi-cloud/jobs/${JOB_ID}`, {\n headers: HEADERS,\n }).then(r => r.json());\n} while (![\"completed\", \"failed\", \"cancelled\"].includes(status.status));\n\nconst resp = await fetch(`${BASE_URL}/multi-cloud/jobs/${JOB_ID}/results`, {\n headers: HEADERS,\n});\nconst data = await resp.json();\nconsole.log(`Job ${JOB_ID} status=${data.status} complete=${data.isComplete} strategies=${data.strategiesGenerated}`);\nfor (const strategy of data.topStrategies ?? []) {\n const { monthlyCost, avgLatencyMs } = strategy.metrics;\n console.log(` ${strategy.name} cost=$${monthlyCost}/mo latency=${avgLatencyMs}ms`);\n}\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the multi-cloud job"},{"name":"providers","in":"query","required":false,"schema":{"type":"string","example":"digitalocean,aws"},"description":"Comma-separated list of cloud provider names to filter results by preferred primary provider.\nA strategy is included if any of the specified providers holds ≥ 50 % of the traffic allocation.\nSupported values: `aws`, `gcp`, `azure`, `digitalocean`, `oci`.\nIf omitted, all strategies are returned.\n"}],"responses":{"200":{"description":"Job results (partial or complete based on job status)","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string"},"status":{"type":"string","enum":["pending","running","completed","failed","cancelled"]},"progress":{"type":"number","description":"Job progress percentage (0-100)"},"isComplete":{"type":"boolean","description":"True if job is finished, false if still running or pending"},"strategiesGenerated":{"type":"number","description":"Number of strategies generated so far"},"allStrategies":{"type":"array","items":{"$ref":"#/components/schemas/Strategy"},"description":"All raw strategies generated so far (available during and after generation)"},"topStrategies":{"type":"array","items":{"$ref":"#/components/schemas/Strategy"},"description":"Top 10 optimized strategies (only available when status is completed)"},"comparisonReport":{"type":"string","description":"Markdown comparison report (only available when complete)"},"providerTrustLevels":{"type":"object","additionalProperties":{"type":"string","enum":["on-demand","spot","estimated"]},"description":"Per-provider rate trust level — the worst rateProvenance.basis across the pricing constants behind that provider's cost figures. \"on-demand\" means every contributing rate is a verified published list price; \"estimated\" means at least one contributing rate is a flat estimate, so treat that provider's ranking as approximate.","example":{"aws":"on-demand","gcp":"on-demand"}},"preProvisionVerdict":{"$ref":"#/components/schemas/PreProvisionVerdict"},"completedAt":{"type":"string","format":"date-time"},"estimateDisclaimer":{"type":"string","description":"Reminder that cost and latency figures are baseline planning estimates; validate with materialize + step under production load."},"methodology":{"type":"object","description":"Describes the data origin and validation basis for these results. Agents should read this object before interpreting cross-provider rankings or citing consistency across multiple independent runs as external evidence.","properties":{"dataSource":{"type":"string","enum":["simulation-model"],"description":"Origin of the result data. simulation-model means results come from the Cloud World Model deterministic simulation engine, not from live cloud APIs or external benchmark tooling."},"validationBasis":{"type":"string","enum":["simulation_only","calibrated_public_data","provider_benchmark","customer_observed","mixed"],"description":"Characterises how the underlying data has been validated externally. Currently only simulation_only is emitted. Future values (calibrated_public_data, provider_benchmark) can be added without removing or retyping this field."},"externallyValidated":{"type":"boolean","description":"Convenience boolean for agents that need a simple truth test without pattern-matching validationBasis strings. false whenever validationBasis is simulation_only."},"seedCount":{"type":"integer","description":"The number of random seeds used in this job own simulation. Always 1 per job. Running multiple independent jobs and observing consistent rankings reflects model stability, not external performance validation."},"note":{"type":"string","description":"Authoritative plain-English statement suitable for direct use by agents calibrating recommendation language. Static across all runs."}},"example":{"dataSource":"simulation-model","validationBasis":"simulation_only","externallyValidated":false,"seedCount":1,"note":"Results describe Cloud World Model simulation outcomes and should not be interpreted as measured cloud-provider benchmarks. Consistency across multiple independent runs confirms model stability, not external performance validation."}},"interpretationScope":{"type":"object","description":"Machine-readable interpretation constraints attached alongside methodology. Agents must consult this object before forming any conclusion about provider performance.","properties":{"providerPerformanceScope":{"type":"string","enum":["simulation-only"],"description":"Scope of any provider-performance statement derivable from these results. Currently only simulation-only is emitted; future values (calibrated, empirically-validated) may be added without breaking change."},"externalValidation":{"type":"boolean","description":"Mirrors methodology.externallyValidated so agents that reach interpretationScope first get an unambiguous false without cross-referencing."},"crossSeedConsistencyMeans":{"type":"string","enum":["simulation-model-consistency"],"description":"Agreement across seeds proves the model is deterministic, not that cloud providers behave that way."},"untestedLoadExtrapolationAllowed":{"type":"boolean","description":"When false, any load level outside testedLoadLevels must be presented as a hypothesis requiring a new simulation, not a derived conclusion."},"prohibitedClaimCategories":{"type":"array","items":{"type":"string"},"description":"Machine-readable constraint list; agents must check planned conclusions against each entry. Category labels are hyphenated enum tokens, not prose."},"permittedUses":{"type":"array","items":{"type":"string"},"description":"What these results may legitimately be used for."},"testedLoadLevels":{"type":"array","items":{"type":"string"},"description":"Derived from job config; only these levels are validated by this simulation run."},"untestedScenariosNote":{"type":"string","description":"Authoritative sentence defining how untested scenarios must be labelled."}},"example":{"providerPerformanceScope":"simulation-only","externalValidation":false,"crossSeedConsistencyMeans":"simulation-model-consistency","untestedLoadExtrapolationAllowed":false,"prohibitedClaimCategories":["provider-performance-superiority","real-world-benchmark-equivalence","untested-load-extrapolation","cross-seed-as-external-validation"],"permittedUses":["compare simulation-model cost and latency trade-offs under the tested scenarios","identify which provider the model favours given the configured resource shapes and traffic levels","use rankings as a structured starting point for further empirical benchmarking"],"testedLoadLevels":["200 RPS (baseline)"],"untestedScenariosNote":"Load levels or failure modes not present in testedLoadLevels are hypotheses requiring a new simulation run, not conclusions derivable from this result."}}}},"examples":{"partialResults":{"value":{"jobId":"job_abc123","status":"running","progress":45,"isComplete":false,"strategiesGenerated":15,"allStrategies":[{"name":"AWS-Dominant Strategy","description":"Primary AWS deployment with GCP failover","allocations":[{"provider":"aws","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":12500,"avgLatencyMs":45,"vendorLockInScore":65}},{"name":"DigitalOcean Cost-Optimized","description":"DigitalOcean primary with AWS failover — lowest monthly spend","allocations":[{"provider":"digitalocean","percentage":80},{"provider":"aws","percentage":20}],"metrics":{"monthlyCost":7800,"avgLatencyMs":55,"vendorLockInScore":48}}],"topStrategies":[]}},"completeResults":{"value":{"jobId":"job_abc123","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":42,"allStrategies":[{"name":"AWS-Dominant Strategy","description":"Primary AWS deployment with GCP failover","allocations":[{"provider":"aws","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":12500,"avgLatencyMs":45,"vendorLockInScore":65}},{"name":"DigitalOcean Cost-Optimized","description":"DigitalOcean primary with AWS failover — lowest monthly spend","allocations":[{"provider":"digitalocean","percentage":80},{"provider":"aws","percentage":20}],"metrics":{"monthlyCost":7800,"avgLatencyMs":55,"vendorLockInScore":48}}],"topStrategies":[{"name":"Multi-Cloud Balanced","description":"Optimized multi-cloud strategy for cost and performance","allocations":[{"provider":"aws","percentage":40},{"provider":"gcp","percentage":35},{"provider":"azure","percentage":25}],"metrics":{"monthlyCost":11200,"avgLatencyMs":42,"vendorLockInScore":35}},{"name":"DigitalOcean Cost-Optimized","description":"DigitalOcean primary with AWS failover — lowest monthly spend","allocations":[{"provider":"digitalocean","percentage":80},{"provider":"aws","percentage":20}],"metrics":{"monthlyCost":7800,"avgLatencyMs":55,"vendorLockInScore":48}}],"comparisonReport":"# Multi-Cloud Strategy Comparison Report...","completedAt":"2024-01-15T10:30:00Z"}},"digitalOceanWinner":{"summary":"DigitalOcean wins — cost-optimized workload where DO-primary ranks","value":{"jobId":"job_do_xyz789","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":38,"allStrategies":[{"name":"DigitalOcean-Primary Strategy","description":"DigitalOcean Droplets (s-4vcpu-8gb) as primary compute, DigitalOcean Managed Databases (PostgreSQL) for persistence, and Spaces for S3-compatible object storage — lowest total monthly spend in the comparison","allocations":[{"provider":"digitalocean","percentage":80},{"provider":"aws","percentage":20}],"metrics":{"monthlyCost":7800,"avgLatencyMs":55,"vendorLockInScore":48}},{"name":"AWS-Dominant Strategy","description":"Primary AWS deployment with GCP failover","allocations":[{"provider":"aws","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":14200,"avgLatencyMs":43,"vendorLockInScore":74}},{"name":"GCP-Primary Strategy","description":"GCP Cloud Run primary with Azure failover","allocations":[{"provider":"gcp","percentage":60},{"provider":"azure","percentage":40}],"metrics":{"monthlyCost":11600,"avgLatencyMs":46,"vendorLockInScore":61}}],"topStrategies":[{"name":"DigitalOcean-Primary Strategy","description":"DigitalOcean Droplets (s-4vcpu-8gb) as primary compute, DigitalOcean Managed Databases (PostgreSQL) for persistence, and Spaces for S3-compatible object storage — lowest total monthly spend in the comparison","allocations":[{"provider":"digitalocean","percentage":80},{"provider":"aws","percentage":20}],"metrics":{"monthlyCost":7800,"avgLatencyMs":55,"vendorLockInScore":48}},{"name":"DigitalOcean + GCP Balanced","description":"DigitalOcean Droplets for primary API tier with DigitalOcean Managed Databases, GCP Cloud Run for burst compute — good cost/latency balance","allocations":[{"provider":"digitalocean","percentage":65},{"provider":"gcp","percentage":35}],"metrics":{"monthlyCost":9400,"avgLatencyMs":50,"vendorLockInScore":41}},{"name":"AWS-Dominant Strategy","description":"Primary AWS deployment with GCP failover","allocations":[{"provider":"aws","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":14200,"avgLatencyMs":43,"vendorLockInScore":74}}],"comparisonReport":"# Multi-Cloud Strategy Comparison Report\n\n## Winner: DigitalOcean-Primary Strategy\n\nMonthly cost: **$7,800** — 45% lower than the AWS-dominant baseline ($14,200).\nAverage latency: **55 ms** — comfortably within the 150 ms SLA requirement.\nVendor lock-in score: **48/100** — moderate lock-in, significantly lower than the AWS-only baseline (74/100).\n\n## Key Resources\n\n- **Compute:** DigitalOcean Droplets (s-4vcpu-8gb, nyc3) — predictable hourly pricing with no data-transfer surprises within the region.\n- **Database:** DigitalOcean Managed Databases (PostgreSQL, 2-node HA cluster) — automated failover, daily backups, and connection pooling included.\n- **Object Storage:** DigitalOcean Spaces — S3-compatible API, 250 GB included, CDN edge caching available at no extra charge.\n\n## Trade-offs\n\n| Metric | DO-Primary | AWS-Dominant | DO + GCP |\n|---|---|---|---|\n| Monthly cost | $7,800 | $14,200 | $9,400 |\n| Avg latency | 55 ms | 43 ms | 50 ms |\n| Lock-in score | 48 | 74 | 41 |\n\n## Recommendation\n\nFor cost-sensitive workloads with moderate latency requirements, DigitalOcean Droplets\npaired with DigitalOcean Managed Databases and Spaces deliver the best cost efficiency.\nThe 12 ms latency difference versus the AWS baseline is unlikely to impact end-user\nexperience for the given SLA. Consider the DigitalOcean + GCP Balanced strategy if\nfuture burst capacity beyond current Droplet limits is anticipated.","completedAt":"2024-01-16T09:45:00Z"}},"awsPrimaryWinner":{"summary":"AWS wins — performance-optimized workload where EC2 m5.xlarge + RDS Multi-AZ ranks","value":{"jobId":"job_aws_perf123","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":40,"allStrategies":[{"name":"AWS-Primary Strategy","description":"EC2 m5.xlarge Auto Scaling group across us-east-1a/1b with RDS db.r5.large Multi-AZ and CloudFront CDN — lowest p95 latency in comparison","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":14500,"avgLatencyMs":38,"vendorLockInScore":72}},{"name":"AWS + GCP Balanced","description":"EC2 primary with GCP Cloud Run burst capacity","allocations":[{"provider":"aws","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":13200,"avgLatencyMs":41,"vendorLockInScore":58}},{"name":"DigitalOcean Cost-Optimized","description":"DigitalOcean Droplets primary — lowest cost but 17 ms higher latency","allocations":[{"provider":"digitalocean","percentage":80},{"provider":"aws","percentage":20}],"metrics":{"monthlyCost":7800,"avgLatencyMs":55,"vendorLockInScore":48}}],"topStrategies":[{"name":"AWS-Primary Strategy","description":"EC2 m5.xlarge Auto Scaling group across us-east-1a/1b with RDS db.r5.large Multi-AZ and CloudFront CDN — lowest p95 latency in comparison","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":14500,"avgLatencyMs":38,"vendorLockInScore":72}},{"name":"AWS + GCP Balanced","description":"EC2 primary with GCP Cloud Run burst capacity","allocations":[{"provider":"aws","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":13200,"avgLatencyMs":41,"vendorLockInScore":58}}],"comparisonReport":"# Multi-Cloud Strategy Comparison Report\n\n## Winner: AWS-Primary Strategy\n\nMonthly cost: **$14,500** — within the $25,000 budget.\nAverage latency: **38 ms** — best in class, comfortably within the 50 ms SLA.\nVendor lock-in score: **72/100** — acceptable given the performance requirements.\n\n## Key Resources\n\n- **Compute:** EC2 m5.xlarge (4 vCPU / 16 GB) Auto Scaling group, 3–12 instances, us-east-1a/1b.\n- **Database:** RDS db.r5.large Multi-AZ PostgreSQL — automatic failover within 60 seconds.\n- **CDN:** CloudFront with edge caching reduces origin load by ~40%.\n\n## Trade-offs\n\n| Metric | AWS-Primary | AWS + GCP | DO Cost-Optimized |\n|---|---|---|---|\n| Monthly cost | $14,500 | $13,200 | $7,800 |\n| Avg latency | 38 ms | 41 ms | 55 ms |\n| Lock-in score | 72 | 58 | 48 |\n\n## Recommendation\n\nFor workloads with a 50 ms SLA, AWS-Primary delivers the best latency. If budget is a constraint, the AWS + GCP Balanced strategy saves $1,300/month with only 3 ms latency degradation.","completedAt":"2024-01-17T08:20:00Z"}},"gcpPrimaryWinner":{"summary":"GCP wins — ML analytics workload where Cloud Run + Cloud SQL ranks","value":{"jobId":"job_gcp_ml456","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":35,"allStrategies":[{"name":"GCP-Primary Strategy","description":"Cloud Run (fully managed, us-central1) with Cloud SQL for PostgreSQL (HA) and Cloud Storage — best autoscaling fit for variable ML inference load","allocations":[{"provider":"gcp","percentage":100}],"metrics":{"monthlyCost":11800,"avgLatencyMs":42,"vendorLockInScore":63}},{"name":"GCP + AWS Hybrid","description":"GCP Cloud Run primary with AWS Lambda for async processing jobs","allocations":[{"provider":"gcp","percentage":65},{"provider":"aws","percentage":35}],"metrics":{"monthlyCost":12400,"avgLatencyMs":44,"vendorLockInScore":51}},{"name":"AWS-Dominant Strategy","description":"EC2 m5.large fleet — higher cost, comparable latency","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":15600,"avgLatencyMs":45,"vendorLockInScore":74}}],"topStrategies":[{"name":"GCP-Primary Strategy","description":"Cloud Run (fully managed, us-central1) with Cloud SQL for PostgreSQL (HA) and Cloud Storage — best autoscaling fit for variable ML inference load","allocations":[{"provider":"gcp","percentage":100}],"metrics":{"monthlyCost":11800,"avgLatencyMs":42,"vendorLockInScore":63}},{"name":"GCP + AWS Hybrid","description":"GCP Cloud Run primary with AWS Lambda for async processing jobs","allocations":[{"provider":"gcp","percentage":65},{"provider":"aws","percentage":35}],"metrics":{"monthlyCost":12400,"avgLatencyMs":44,"vendorLockInScore":51}}],"comparisonReport":"# Multi-Cloud Strategy Comparison Report\n\n## Winner: GCP-Primary Strategy\n\nMonthly cost: **$11,800** — $3,800 below the AWS baseline.\nAverage latency: **42 ms** — within the 80 ms SLA requirement.\nVendor lock-in score: **63/100** — moderate, lower than an AWS-only approach.\n\n## Key Resources\n\n- **Compute:** Cloud Run (fully managed) in us-central1 — scales to zero between inference jobs, eliminating idle compute cost.\n- **Database:** Cloud SQL for PostgreSQL (HA, db-n1-standard-4) — regional failover with 99.95% SLA.\n- **Storage:** Cloud Storage Standard — multi-region bucket with CDN integration for model artefacts.\n\n## Trade-offs\n\n| Metric | GCP-Primary | GCP + AWS | AWS-Dominant |\n|---|---|---|---|\n| Monthly cost | $11,800 | $12,400 | $15,600 |\n| Avg latency | 42 ms | 44 ms | 45 ms |\n| Lock-in score | 63 | 51 | 74 |\n\n## Recommendation\n\nGCP-Primary is optimal for variable ML inference loads. Cloud Run's scale-to-zero behaviour saves up to 35% on compute versus always-on EC2 instances at comparable load.","completedAt":"2024-01-18T11:35:00Z"}},"azurePrimaryWinner":{"summary":"Azure wins — compliance workload where AKS + Azure Database for PostgreSQL ranks","value":{"jobId":"job_azure_comp789","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":38,"allStrategies":[{"name":"Azure-Primary Strategy","description":"AKS (eastus, Standard_D4s_v3 nodes) with Azure Database for PostgreSQL Flexible Server and Azure Front Door — strongest GDPR/HIPAA compliance posture","allocations":[{"provider":"azure","percentage":100}],"metrics":{"monthlyCost":13200,"avgLatencyMs":44,"vendorLockInScore":67}},{"name":"Azure + AWS Hybrid","description":"Azure primary with AWS S3 for object storage overflow","allocations":[{"provider":"azure","percentage":75},{"provider":"aws","percentage":25}],"metrics":{"monthlyCost":14100,"avgLatencyMs":46,"vendorLockInScore":55}},{"name":"GCP-Primary Strategy","description":"GCP Cloud Run with Cloud SQL — lower lock-in, weaker native compliance tooling","allocations":[{"provider":"gcp","percentage":100}],"metrics":{"monthlyCost":11800,"avgLatencyMs":42,"vendorLockInScore":63}}],"topStrategies":[{"name":"Azure-Primary Strategy","description":"AKS (eastus, Standard_D4s_v3 nodes) with Azure Database for PostgreSQL Flexible Server and Azure Front Door — strongest GDPR/HIPAA compliance posture","allocations":[{"provider":"azure","percentage":100}],"metrics":{"monthlyCost":13200,"avgLatencyMs":44,"vendorLockInScore":67}},{"name":"Azure + AWS Hybrid","description":"Azure primary with AWS S3 for object storage overflow","allocations":[{"provider":"azure","percentage":75},{"provider":"aws","percentage":25}],"metrics":{"monthlyCost":14100,"avgLatencyMs":46,"vendorLockInScore":55}}],"comparisonReport":"# Multi-Cloud Strategy Comparison Report\n\n## Winner: Azure-Primary Strategy\n\nMonthly cost: **$13,200** — within the $20,000 budget.\nAverage latency: **44 ms** — within the 100 ms SLA.\nVendor lock-in score: **67/100** — justified by compliance requirements (GDPR, HIPAA, ISO 27001).\n\n## Key Resources\n\n- **Compute:** AKS cluster (Standard_D4s_v3, 3–10 nodes) in eastus with Availability Zones — meets HA requirements for HIPAA.\n- **Database:** Azure Database for PostgreSQL Flexible Server (General Purpose, 4 vCores) with geo-redundant backup.\n- **Networking:** Azure Front Door with WAF — OWASP rule sets satisfy PCI-DSS network security controls.\n\n## Trade-offs\n\n| Metric | Azure-Primary | Azure + AWS | GCP-Primary |\n|---|---|---|---|\n| Monthly cost | $13,200 | $14,100 | $11,800 |\n| Avg latency | 44 ms | 46 ms | 42 ms |\n| Lock-in score | 67 | 55 | 63 |\n\n## Recommendation\n\nFor GDPR/HIPAA/ISO 27001 workloads, Azure-Primary provides the most complete native compliance toolkit. GCP is $1,400/month cheaper but requires third-party tooling to meet the same compliance bar.","completedAt":"2024-01-19T14:00:00Z"}},"ociPrimaryWinner":{"summary":"OCI wins — database-intensive workload where Compute + Autonomous Database ranks","value":{"jobId":"job_oci_db321","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":33,"allStrategies":[{"name":"OCI-Primary Strategy","description":"OCI Compute VM.Standard.E4.Flex (4 OCPU / 64 GB) with Autonomous Database (ATP, 4 OCPU) in us-ashburn-1 — best price-performance for database-heavy workloads","allocations":[{"provider":"oci","percentage":100}],"metrics":{"monthlyCost":9600,"avgLatencyMs":48,"vendorLockInScore":55}},{"name":"OCI + AWS Hybrid","description":"OCI primary database tier with AWS EC2 for the web and API layer","allocations":[{"provider":"oci","percentage":60},{"provider":"aws","percentage":40}],"metrics":{"monthlyCost":11200,"avgLatencyMs":46,"vendorLockInScore":48}},{"name":"AWS-Dominant Strategy","description":"EC2 + RDS — higher cost for equivalent database throughput","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":16400,"avgLatencyMs":42,"vendorLockInScore":74}}],"topStrategies":[{"name":"OCI-Primary Strategy","description":"OCI Compute VM.Standard.E4.Flex (4 OCPU / 64 GB) with Autonomous Database (ATP, 4 OCPU) in us-ashburn-1 — best price-performance for database-heavy workloads","allocations":[{"provider":"oci","percentage":100}],"metrics":{"monthlyCost":9600,"avgLatencyMs":48,"vendorLockInScore":55}},{"name":"OCI + AWS Hybrid","description":"OCI primary database tier with AWS EC2 for the web and API layer","allocations":[{"provider":"oci","percentage":60},{"provider":"aws","percentage":40}],"metrics":{"monthlyCost":11200,"avgLatencyMs":46,"vendorLockInScore":48}}],"comparisonReport":"# Multi-Cloud Strategy Comparison Report\n\n## Winner: OCI-Primary Strategy\n\nMonthly cost: **$9,600** — 41% lower than the AWS-dominant baseline ($16,400).\nAverage latency: **48 ms** — within the 120 ms SLA requirement.\nVendor lock-in score: **55/100** — moderate, offset by significant cost savings.\n\n## Key Resources\n\n- **Compute:** OCI VM.Standard.E4.Flex (4 OCPU / 64 GB RAM) — flexible OCPU allocation reduces idle-time waste.\n- **Database:** Autonomous Database (ATP, 4 OCPU) — self-tuning, automatic patching, and built-in connection pooling eliminate DBA overhead.\n- **Networking:** OCI FastConnect to AWS for the OCI + AWS hybrid variant — sub-5 ms inter-cloud latency.\n\n## Trade-offs\n\n| Metric | OCI-Primary | OCI + AWS | AWS-Dominant |\n|---|---|---|---|\n| Monthly cost | $9,600 | $11,200 | $16,400 |\n| Avg latency | 48 ms | 46 ms | 42 ms |\n| Lock-in score | 55 | 48 | 74 |\n\n## Recommendation\n\nFor database-heavy workloads, OCI Autonomous Database delivers the best cost efficiency. The OCI + AWS Hybrid strategy is worth considering if the web/API tier already has AWS dependencies, adding only $1,600/month for a 2 ms latency improvement.","completedAt":"2024-01-20T16:45:00Z"}},"digitaloceanAMDNVMeMultiCloud":{"summary":"DigitalOcean AMD NVMe Droplets — cost-optimized strategy using s-2vcpu-4gb-amd for maximum price-performance","value":{"jobId":"job_do_amd_nvme_001","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":36,"allStrategies":[{"name":"DigitalOcean AMD NVMe Primary Strategy","description":"DigitalOcean AMD NVMe Droplets (s-2vcpu-4gb-amd, nyc3) as primary compute — NVMe-backed local SSD storage delivers higher I/O throughput than standard Droplets at the same price point, with DigitalOcean Managed Databases (PostgreSQL) and Spaces for object storage","allocations":[{"provider":"digitalocean","percentage":85},{"provider":"aws","percentage":15}],"metrics":{"monthlyCost":6200,"avgLatencyMs":52,"vendorLockInScore":44}},{"name":"DigitalOcean AMD NVMe + GCP Balanced","description":"s-2vcpu-4gb-amd Droplets for primary API tier with GCP Cloud Run for burst compute — good cost/latency balance","allocations":[{"provider":"digitalocean","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":8100,"avgLatencyMs":49,"vendorLockInScore":38}},{"name":"AWS-Dominant Strategy","description":"EC2 t3.medium fleet — higher baseline cost, comparable single-request latency","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":13800,"avgLatencyMs":41,"vendorLockInScore":74}}],"topStrategies":[{"name":"DigitalOcean AMD NVMe Primary Strategy","description":"DigitalOcean AMD NVMe Droplets (s-2vcpu-4gb-amd, nyc3) as primary compute — NVMe-backed local SSD storage delivers higher I/O throughput than standard Droplets at the same price point, with DigitalOcean Managed Databases (PostgreSQL) and Spaces for object storage","allocations":[{"provider":"digitalocean","percentage":85},{"provider":"aws","percentage":15}],"metrics":{"monthlyCost":6200,"avgLatencyMs":52,"vendorLockInScore":44}},{"name":"DigitalOcean AMD NVMe + GCP Balanced","description":"s-2vcpu-4gb-amd Droplets for primary API tier with GCP Cloud Run for burst compute — good cost/latency balance","allocations":[{"provider":"digitalocean","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":8100,"avgLatencyMs":49,"vendorLockInScore":38}},{"name":"AWS-Dominant Strategy","description":"EC2 t3.medium fleet — higher baseline cost, comparable single-request latency","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":13800,"avgLatencyMs":41,"vendorLockInScore":74}}],"comparisonReport":"# Multi-Cloud Strategy Comparison Report\n\n## Winner: DigitalOcean AMD NVMe Primary Strategy\n\nMonthly cost: **$6,200** — 55% lower than the AWS-dominant baseline ($13,800).\nAverage latency: **52 ms** — within the 150 ms SLA requirement.\nVendor lock-in score: **44/100** — low lock-in, easy to migrate if requirements change.\n\n## Key Resources\n\n- **Compute:** DigitalOcean AMD NVMe Droplets (s-2vcpu-4gb-amd, nyc3) — AMD EPYC processors with NVMe-backed local SSD storage deliver higher disk I/O throughput than standard Intel Droplets at the same hourly rate ($0.036/hr per Droplet). Ideal for workloads with frequent local reads/writes or ephemeral scratch space.\n- **Database:** DigitalOcean Managed Databases (PostgreSQL, 2-node HA cluster, nyc3) — automated failover, daily backups, and PgBouncer connection pooling included at no extra charge.\n- **Object Storage:** DigitalOcean Spaces — S3-compatible API, 250 GB included, optional CDN edge caching.\n\n## AMD NVMe vs Standard Droplets\n\nThe `s-2vcpu-4gb-amd` slug selects the AMD NVMe variant of the standard shared-CPU tier. Compared to the equivalent Intel Droplet (`s-2vcpu-4gb`):\n- Same vCPU count and RAM\n- Same hourly price\n- NVMe local SSD instead of spinning disk — up to 3× higher sequential read throughput\n- AMD EPYC \"Milan\" or \"Rome\" core depending on host availability\n\nChoose the AMD NVMe variant when your workload is I/O-bound (e.g. log processing, local caching, build pipelines) or when you want deterministic low-latency disk access without paying for a dedicated CPU plan.\n\n## Trade-offs\n\n| Metric | DO AMD NVMe Primary | DO AMD NVMe + GCP | AWS-Dominant |\n|---|---|---|---|\n| Monthly cost | $6,200 | $8,100 | $13,800 |\n| Avg latency | 52 ms | 49 ms | 41 ms |\n| Lock-in score | 44 | 38 | 74 |\n\n## Recommendation\n\nFor cost-sensitive workloads with moderate I/O requirements, the DigitalOcean AMD NVMe Primary strategy delivers the best value. The 11 ms latency gap versus the AWS baseline is unlikely to affect end-user experience for the given SLA. If burst capacity beyond current Droplet limits is anticipated, consider the DO AMD NVMe + GCP Balanced strategy, which adds only $1,900/month for a 3 ms latency improvement and lower vendor lock-in.","completedAt":"2024-01-21T10:15:00Z"}}}}}},"400":{"description":"Job not completed yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/multi-cloud/jobs/{jobId}/partial-results":{"x-stability":"stable","get":{"tags":["Multi-Cloud Strategy"],"summary":"Fetch partial multi-cloud results from a running job","description":"Retrieve strategies accumulated so far for a multi-cloud exploration job, even while the job is still running.\n\nThis endpoint is the dedicated channel for polling partial results during job execution.\nThe `/results` endpoint requires the job to be completed; use this endpoint instead when you want\nto start analyzing strategies before the full exploration finishes.\n\n**Polling pattern:**\n```\nwhile true:\n data = GET /api/multi-cloud/jobs/{jobId}/partial-results\n show data.allStrategies to user\n if data.isComplete: break\n sleep(2s)\n# Fetch ranked results and comparison report from /results once complete\nfull = GET /api/multi-cloud/jobs/{jobId}/results\n```\n\n**isComplete flag:**\n- `false` — job is still `pending` or `running`; more strategies may arrive\n- `true` — job has reached a terminal state (`completed`, `failed`, or `cancelled`)\n\n**When `isComplete` is true and `status` is `completed`**, fetch the ranked\n`topStrategies` and `comparisonReport` from `GET /api/multi-cloud/jobs/{jobId}/results`.\n","operationId":"getMultiCloudPartialResults","x-x402-call-type":"multicloud_partial_results","x-x402-price-usdc":"$0.0030","x-codeSamples":[{"lang":"curl","label":"curl","source":"# Poll partial results until the job is complete\nwhile true; do\n DATA=$(curl -s \"https://www.cloudworldmodel.ai/api/multi-cloud/jobs/job_abc123/partial-results\" \\\n -H \"Authorization: Bearer $API_KEY\")\n echo \"$DATA\" | jq '{strategies: (.allStrategies | length), isComplete: .isComplete}'\n [ \"$(echo \"$DATA\" | jq -r '.isComplete')\" = \"true\" ] && break\n sleep 2\ndone\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\nconst JOB_ID = \"job_abc123\";\n\nasync function pollPartialResults(jobId) {\n while (true) {\n const resp = await fetch(`${BASE_URL}/multi-cloud/jobs/${jobId}/partial-results`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n });\n const data = await resp.json();\n console.log(`strategies so far: ${data.allStrategies?.length ?? 0}, isComplete: ${data.isComplete}`);\n if (data.isComplete) break;\n await new Promise(r => setTimeout(r, 2000));\n }\n // Fetch full ranked results once complete\n const full = await fetch(`${BASE_URL}/multi-cloud/jobs/${jobId}/results`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n });\n return full.json();\n}\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"ID of the multi-cloud job"},{"name":"providers","in":"query","required":false,"schema":{"type":"string","example":"digitalocean,aws"},"description":"Comma-separated list of cloud provider names to filter results by preferred primary provider.\nA strategy is included if any of the specified providers holds ≥ 50 % of the traffic allocation.\nSupported values: `aws`, `gcp`, `azure`, `digitalocean`, `oci`.\nIf omitted, all strategies are returned.\n"}],"responses":{"200":{"description":"Partial or terminal results for the job","content":{"application/json":{"schema":{"type":"object","required":["jobId","status","progress","isComplete","strategiesGenerated","allStrategies"],"properties":{"jobId":{"type":"string"},"status":{"type":"string","enum":["pending","running","completed","failed","cancelled"]},"progress":{"type":"number","description":"Job progress percentage (0-100)"},"isComplete":{"type":"boolean","description":"True if job has reached a terminal state, false if still pending or running"},"strategiesGenerated":{"type":"number","description":"Number of strategies generated so far"},"allStrategies":{"type":"array","items":{"$ref":"#/components/schemas/Strategy"},"description":"All raw strategies generated so far"},"providerTrustLevels":{"type":"object","additionalProperties":{"type":"string","enum":["on-demand","spot","estimated"]},"description":"Per-provider rate trust level — the worst rateProvenance.basis across the pricing constants behind that provider's cost figures, derived from strategies accumulated so far. \"on-demand\" means every contributing rate is a verified published list price; \"estimated\" means at least one contributing rate is a flat or usage-based floor approximation (e.g. Aurora Serverless v2/DSQL, GPU inference families). Present for every job status; updates as strategies accumulate. Empty object while no strategies exist yet."},"preProvisionVerdict":{"$ref":"#/components/schemas/PreProvisionVerdict","description":"Present when status is completed. Summarizes the leading returned strategy's budget, latency, modeled error-rate, resilience-evidence gap, and uncertainty."},"methodology":{"type":"object","description":"Describes the data origin and validation basis for these results. Agents should read this object before interpreting cross-provider rankings or citing consistency across multiple independent runs as external evidence.","properties":{"dataSource":{"type":"string","enum":["simulation-model"],"description":"Origin of the result data. simulation-model means results come from the Cloud World Model deterministic simulation engine."},"validationBasis":{"type":"string","enum":["simulation_only","calibrated_public_data","provider_benchmark","customer_observed","mixed"],"description":"Characterises how the underlying data has been validated externally. Currently only simulation_only is emitted."},"externallyValidated":{"type":"boolean","description":"Convenience boolean — false whenever validationBasis is simulation_only."},"seedCount":{"type":"integer","description":"The number of random seeds used in this job own simulation. Always 1 per job."},"note":{"type":"string","description":"Authoritative plain-English statement for agents calibrating recommendation language. Static across all runs."}},"example":{"dataSource":"simulation-model","validationBasis":"simulation_only","externallyValidated":false,"seedCount":1,"note":"Results describe Cloud World Model simulation outcomes and should not be interpreted as measured cloud-provider benchmarks. Consistency across multiple independent runs confirms model stability, not external performance validation."}},"interpretationScope":{"type":"object","description":"Machine-readable interpretation constraints attached alongside methodology. Agents must consult this object before forming any conclusion about provider performance.","properties":{"providerPerformanceScope":{"type":"string","enum":["simulation-only"],"description":"Scope of any provider-performance statement derivable from these results. Currently only simulation-only is emitted; future values (calibrated, empirically-validated) may be added without breaking change."},"externalValidation":{"type":"boolean","description":"Mirrors methodology.externallyValidated so agents that reach interpretationScope first get an unambiguous false without cross-referencing."},"crossSeedConsistencyMeans":{"type":"string","enum":["simulation-model-consistency"],"description":"Agreement across seeds proves the model is deterministic, not that cloud providers behave that way."},"untestedLoadExtrapolationAllowed":{"type":"boolean","description":"When false, any load level outside testedLoadLevels must be presented as a hypothesis requiring a new simulation, not a derived conclusion."},"prohibitedClaimCategories":{"type":"array","items":{"type":"string"},"description":"Machine-readable constraint list; agents must check planned conclusions against each entry. Category labels are hyphenated enum tokens, not prose."},"permittedUses":{"type":"array","items":{"type":"string"},"description":"What these results may legitimately be used for."},"testedLoadLevels":{"type":"array","items":{"type":"string"},"description":"Derived from job config; only these levels are validated by this simulation run."},"untestedScenariosNote":{"type":"string","description":"Authoritative sentence defining how untested scenarios must be labelled."}},"example":{"providerPerformanceScope":"simulation-only","externalValidation":false,"crossSeedConsistencyMeans":"simulation-model-consistency","untestedLoadExtrapolationAllowed":false,"prohibitedClaimCategories":["provider-performance-superiority","real-world-benchmark-equivalence","untested-load-extrapolation","cross-seed-as-external-validation"],"permittedUses":["compare simulation-model cost and latency trade-offs under the tested scenarios","identify which provider the model favours given the configured resource shapes and traffic levels","use rankings as a structured starting point for further empirical benchmarking"],"testedLoadLevels":["200 RPS (baseline)"],"untestedScenariosNote":"Load levels or failure modes not present in testedLoadLevels are hypotheses requiring a new simulation run, not conclusions derivable from this result."}},"error":{"type":"string","description":"Error message — only present when status is failed"}}},"examples":{"runningPartial":{"summary":"Job still running — 12 strategies accumulated so far","value":{"jobId":"job_abc123","status":"running","progress":40,"isComplete":false,"strategiesGenerated":12,"providerTrustLevels":{"aws":"estimated","gcp":"on-demand"},"allStrategies":[{"name":"AWS-Dominant Strategy","description":"Primary AWS deployment","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":14500,"avgLatencyMs":38,"vendorLockInScore":72}}]}},"terminalComplete":{"summary":"Job completed — isComplete is true, fetch /results for ranked output","value":{"jobId":"job_abc123","status":"completed","progress":100,"isComplete":true,"strategiesGenerated":42,"allStrategies":[]}},"digitaloceanAMDNVMePartialMultiCloud":{"summary":"AMD NVMe mid-run — s-2vcpu-4gb-amd strategy visible before job completes","value":{"jobId":"job_do_amd_nvme_partial_001","status":"running","progress":55,"isComplete":false,"strategiesGenerated":20,"allStrategies":[{"name":"DigitalOcean AMD NVMe Primary Strategy","description":"DigitalOcean AMD NVMe Droplets (s-2vcpu-4gb-amd, nyc3) as primary compute — NVMe-backed local SSD storage delivers higher I/O throughput than standard Droplets at the same price point, with DigitalOcean Managed Databases (PostgreSQL) and Spaces for object storage","allocations":[{"provider":"digitalocean","percentage":85},{"provider":"aws","percentage":15}],"metrics":{"monthlyCost":6200,"avgLatencyMs":52,"vendorLockInScore":44}},{"name":"DigitalOcean AMD NVMe + GCP Balanced","description":"s-2vcpu-4gb-amd Droplets for primary API tier with GCP Cloud Run for burst compute — good cost/latency balance","allocations":[{"provider":"digitalocean","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":8100,"avgLatencyMs":49,"vendorLockInScore":38}},{"name":"AWS-Dominant Strategy","description":"EC2 t3.medium fleet — higher baseline cost, comparable single-request latency","allocations":[{"provider":"aws","percentage":100}],"metrics":{"monthlyCost":13800,"avgLatencyMs":41,"vendorLockInScore":74}}]}},"digitaloceanAMDNVMeFilteredPartial":{"summary":"AMD NVMe mid-run filtered — ?providers=digitalocean excludes AWS-dominant entries while job is still running","value":{"jobId":"job_do_amd_nvme_partial_001","status":"running","progress":55,"isComplete":false,"strategiesGenerated":14,"allStrategies":[{"name":"DigitalOcean AMD NVMe Primary Strategy","description":"DigitalOcean AMD NVMe Droplets (s-2vcpu-4gb-amd, nyc3) as primary compute — NVMe-backed local SSD storage delivers higher I/O throughput than standard Droplets at the same price point, with DigitalOcean Managed Databases (PostgreSQL) and Spaces for object storage","allocations":[{"provider":"digitalocean","percentage":85},{"provider":"aws","percentage":15}],"metrics":{"monthlyCost":6200,"avgLatencyMs":52,"vendorLockInScore":44}},{"name":"DigitalOcean AMD NVMe + GCP Balanced","description":"s-2vcpu-4gb-amd Droplets for primary API tier with GCP Cloud Run for burst compute — good cost/latency balance","allocations":[{"provider":"digitalocean","percentage":70},{"provider":"gcp","percentage":30}],"metrics":{"monthlyCost":8100,"avgLatencyMs":49,"vendorLockInScore":38}}]}}}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/multi-cloud/jobs/{jobId}/stream":{"x-stability":"stable","get":{"summary":"Fetch a real-time stream of multi-cloud exploration results","description":"Stream multi-cloud exploration results using Server-Sent Events (SSE).\nThis endpoint provides real-time updates as strategies are generated.\n\n**SSE Event Types:**\n- `init`: Initial job state when connection is established\n- `strategyGenerated`: A new strategy has been generated (sent for each strategy)\n- `progressUpdate`: Progress update (sent periodically)\n- `completed`: Job has finished successfully (final event before connection closes)\n- `failed`: Job encountered an unrecoverable error (final event before connection closes)\n- `cancelled`: Job was cancelled via `DELETE /api/multi-cloud/jobs/{jobId}` (final event before connection closes)\n\n**Terminal Event Payload Contract:**\n\nThe terminal event's `data` payload carries: `status`, `progress`,\n`strategiesGenerated`, `comparisonReport`, `latencyWarning`, `completedAt`,\n`error` (failed jobs only), `methodology`, and `interpretationScope`.\n`methodology` and `interpretationScope` follow the exact same schemas\ndocumented on `GET /multi-cloud/jobs/{jobId}/results`: `methodology`\nstates the data origin (`dataSource: simulation-model`,\n`externallyValidated: false`), and `interpretationScope` is the\nmachine-readable constraint object (`providerPerformanceScope:\nsimulation-only`, `prohibitedClaimCategories`, `testedLoadLevels`\nderived from the job's configured traffic, `untestedScenariosNote`).\nStreaming agents must consult `interpretationScope` from the terminal\nevent before forming any conclusion about provider performance — the\nsame rules as for the JSON results endpoints apply.\n\n**Connection Behavior:**\n- Connection remains open until job completes, fails, or is cancelled\n- All existing strategies are streamed immediately upon connection\n- New strategies are sent as they're generated\n- Connection automatically closes when job finishes\n\n**Agent Reconnection and Recovery Guide:**\n\nAfter receiving a terminal SSE event (`completed`, `failed`, or `cancelled`) the server\ncloses the connection. Each event requires a different agent response:\n\n- **`completed`**: The job finished successfully. No reconnection is needed. Fetch the\n full ranked results from `GET /api/multi-cloud/jobs/{jobId}/results` to retrieve\n `topStrategies`, `allStrategies`, and the `comparisonReport`. The results endpoint\n also accepts a `?providers=` filter if you only need strategies for specific clouds.\n\n- **`failed`**: The job encountered an unrecoverable error. Inspect the `error` field in\n the event payload for the root cause. Transient errors (e.g. a pricing API timeout)\n are safe to retry — submit a new job via `POST /api/multi-cloud/jobs`. Permanent errors\n (e.g. an invalid scenario ID) should not be retried without fixing the underlying\n input first. Do not attempt to reconnect to the same `jobId`; it will not recover.\n\n- **`cancelled`**: Cancellation is terminal and intentional. No retry is needed or\n recommended. If the cancellation was unintended, submit a new job.\n\n**Handling unexpected connection drops (no terminal event received):**\n\nIf the SSE connection closes without a `completed`, `failed`, or `cancelled` event —\nfor example due to a network interruption, proxy timeout, or server restart — the job\nmay still be running. Use the following fallback strategy:\n\n1. Poll `GET /api/multi-cloud/jobs/{jobId}` to check the current `status` field.\n2. If `status` is `running` or `pending`, reconnect to this stream endpoint. The\n server replays all strategies generated so far on reconnect, so no data is lost.\n3. If `status` is `completed`, `failed`, or `cancelled`, treat it the same as if you\n had received the corresponding terminal SSE event (see above).\n\nAgents should implement an exponential back-off (e.g. 1 s, 2 s, 4 s, cap at 30 s)\nbefore each reconnection attempt to avoid hammering the server during an outage.\n\n**Use Cases:**\n- Start analyzing strategies while generation continues\n- Real-time progress monitoring\n- Faster decision-making with early access to good strategies\n\n**Client Code Sample (JavaScript / Node.js):**\n\n`EventSource` does not support custom headers, so use `fetch` with a\n`ReadableStream` to pass the Bearer token:\n\n```javascript\nasync function streamMultiCloudJob(jobId, apiToken) {\n const response = await fetch(\n `https://your-host/api/multi-cloud/jobs/${jobId}/stream`,\n {\n headers: {\n Authorization: `Bearer ${apiToken}`,\n Accept: 'text/event-stream',\n },\n }\n );\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${await response.text()}`);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // SSE frames are separated by double newlines\n const frames = buffer.split(/\\n\\n/);\n buffer = frames.pop(); // keep incomplete trailing frame\n\n for (const frame of frames) {\n const eventLine = frame.match(/^event:\\s*(.+)$/m);\n const dataLine = frame.match(/^data:\\s*(.+)$/m);\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine[1].trim() : 'message';\n const payload = JSON.parse(dataLine[1]);\n\n switch (eventType) {\n case 'init':\n console.log('Stream opened. Job status:', payload.status);\n break;\n\n case 'progressUpdate':\n console.log(`Progress: ${payload.progress}% — ${payload.message}`);\n break;\n\n case 'strategyGenerated':\n console.log('New strategy:', payload.strategy.name,\n '| cost $' + payload.strategy.metrics.monthlyCost);\n break;\n\n case 'completed':\n console.log('Job complete. Top strategy:',\n payload.topStrategy?.name);\n reader.cancel(); // close the connection\n return payload;\n }\n }\n }\n}\n\n// Usage\nstreamMultiCloudJob('job_aws_perf123', process.env.API_KEY)\n .then(result => console.log('Final result:', result))\n .catch(err => console.error('Stream error:', err));\n```\n\n**Client Code Sample (Python):**\n\nUse `httpx` with streaming to consume the SSE connection.\nInstall with `pip install httpx`.\n\n```python\nimport httpx\nimport json\nimport os\n\n\ndef stream_multi_cloud_job(job_id: str, api_token: str) -> dict:\n \"\"\"Stream a multi-cloud exploration job and return the final payload.\"\"\"\n url = f\"https://your-host/api/multi-cloud/jobs/{job_id}/stream\"\n headers = {\n \"Authorization\": f\"Bearer {api_token}\",\n \"Accept\": \"text/event-stream\",\n }\n\n with httpx.stream(\"GET\", url, headers=headers, timeout=None) as response:\n response.raise_for_status()\n\n buffer = \"\"\n\n for chunk in response.iter_text():\n buffer += chunk\n\n # SSE frames are separated by double newlines\n *frames, buffer = buffer.split(\"\\n\\n\")\n\n for frame in frames:\n event_type = \"message\"\n data_str = None\n\n for line in frame.splitlines():\n if line.startswith(\"event:\"):\n event_type = line[len(\"event:\"):].strip()\n elif line.startswith(\"data:\"):\n data_str = line[len(\"data:\"):].strip()\n\n if data_str is None:\n continue\n\n payload = json.loads(data_str)\n\n if event_type == \"init\":\n print(f\"Stream opened. Job status: {payload['status']}\")\n\n elif event_type == \"progressUpdate\":\n print(f\"Progress: {payload['progress']}% — {payload.get('message', '')}\")\n\n elif event_type == \"strategyGenerated\":\n strategy = payload[\"strategy\"]\n cost = strategy[\"metrics\"][\"monthlyCost\"]\n print(f\"New strategy: {strategy['name']} | cost ${cost}\")\n\n elif event_type == \"completed\":\n top = payload.get(\"topStrategy\", {})\n print(f\"Job complete. Top strategy: {top.get('name')}\")\n return payload # connection closes when the context exits\n\n return {}\n\n\n# Usage\nif __name__ == \"__main__\":\n result = stream_multi_cloud_job(\n job_id=\"job_aws_perf123\",\n api_token=os.environ[\"API_KEY\"],\n )\n print(\"Final result:\", result)\n```\n\n**Agent Reconnection Code Sample — with exponential back-off (JavaScript / Node.js):**\n\nThe samples above cover the happy path. The snippet below adds the full\nreconnect loop: detecting a connection drop without a terminal event,\npolling the status endpoint as a fallback, and reconnecting with\nexponential back-off. All three terminal events (`completed`, `failed`,\n`cancelled`) are handled explicitly.\n\n```javascript\nconst BASE_URL = 'https://your-host/api';\n\n// Fetch the current job state without opening an SSE stream.\nasync function pollJobStatus(jobId, apiToken) {\n const res = await fetch(`${BASE_URL}/multi-cloud/jobs/${jobId}`, {\n headers: { Authorization: `Bearer ${apiToken}` },\n });\n if (!res.ok) throw new Error(`Poll failed: HTTP ${res.status}`);\n return res.json(); // { status, progress, ... }\n}\n\nasync function streamWithReconnect(jobId, apiToken) {\n let delay = 1_000; // back-off starts at 1 s\n const MAX_DELAY = 30_000;\n\n while (true) {\n let receivedTerminal = false;\n\n try {\n // ── Open the SSE connection ──────────────────────────────────\n const response = await fetch(\n `${BASE_URL}/multi-cloud/jobs/${jobId}/stream`,\n {\n headers: {\n Authorization: `Bearer ${apiToken}`,\n Accept: 'text/event-stream',\n },\n }\n );\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${await response.text()}`);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n outer: while (true) {\n const { value, done } = await reader.read();\n if (done) break; // server closed — check receivedTerminal below\n\n buffer += decoder.decode(value, { stream: true });\n const frames = buffer.split(/\\n\\n/);\n buffer = frames.pop(); // keep any incomplete trailing frame\n\n for (const frame of frames) {\n const eventLine = frame.match(/^event:\\s*(.+)$/m);\n const dataLine = frame.match(/^data:\\s*(.+)$/m);\n if (!dataLine) continue;\n\n const eventType = eventLine ? eventLine[1].trim() : 'message';\n const payload = JSON.parse(dataLine[1]);\n\n switch (eventType) {\n case 'init':\n // Successful (re)connect — reset back-off timer.\n console.log('Connected. Job status:', payload.status);\n delay = 1_000;\n break;\n\n case 'progressUpdate':\n console.log(`Progress: ${payload.progress}% — ${payload.message}`);\n break;\n\n case 'strategyGenerated':\n console.log('Strategy:', payload.strategy.name,\n '| $' + payload.strategy.metrics.monthlyCost + '/mo');\n break;\n\n // ── Terminal events ──────────────────────────────────────\n case 'completed':\n console.log('Job complete. Top strategy:', payload.topStrategy?.name);\n receivedTerminal = true;\n reader.cancel();\n return { status: 'completed', payload };\n\n case 'failed':\n console.error('Job failed:', payload.error);\n receivedTerminal = true;\n reader.cancel();\n return { status: 'failed', payload };\n\n case 'cancelled':\n console.warn('Job cancelled.');\n receivedTerminal = true;\n reader.cancel();\n return { status: 'cancelled', payload };\n }\n\n if (receivedTerminal) break outer;\n }\n }\n } catch (err) {\n // Network error, proxy timeout, or server restart.\n console.warn('SSE connection error:', err.message);\n }\n\n // Already handled cleanly — exit the retry loop.\n if (receivedTerminal) break;\n\n // ── Fallback: poll before reconnecting ───────────────────────────\n // The connection dropped without a terminal event. The job may\n // still be running, or it may have finished while we were offline.\n try {\n const job = await pollJobStatus(jobId, apiToken);\n\n if (job.status === 'completed') {\n console.log('Recovered via poll — job already completed.');\n return { status: 'completed', payload: job };\n }\n if (job.status === 'failed') {\n console.error('Recovered via poll — job failed:', job.error);\n return { status: 'failed', payload: job };\n }\n if (job.status === 'cancelled') {\n console.warn('Recovered via poll — job was cancelled.');\n return { status: 'cancelled', payload: job };\n }\n // status is 'running' or 'pending' — reconnect after back-off.\n console.log(`Job still ${job.status}. Reconnecting in ${delay / 1000}s…`);\n } catch (pollErr) {\n console.warn('Poll also failed:', pollErr.message, '— will retry.');\n }\n\n // ── Exponential back-off ─────────────────────────────────────────\n await new Promise(resolve => setTimeout(resolve, delay));\n delay = Math.min(delay * 2, MAX_DELAY);\n }\n}\n\n// Usage\nstreamWithReconnect('job_aws_perf123', process.env.API_KEY)\n .then(({ status, payload }) => console.log('Done:', status, payload))\n .catch(err => console.error('Unrecoverable error:', err));\n```\n\n**Agent Reconnection Code Sample — with exponential back-off (Python):**\n\n```python\nimport httpx\nimport json\nimport os\nimport time\n\n\nBASE_URL = \"https://your-host/api\"\nTERMINAL_STATUSES = {\"completed\", \"failed\", \"cancelled\"}\n\n\ndef poll_job_status(job_id: str, api_token: str) -> dict:\n \"\"\"Fetch the current job state without opening an SSE stream.\"\"\"\n url = f\"{BASE_URL}/multi-cloud/jobs/{job_id}\"\n headers = {\"Authorization\": f\"Bearer {api_token}\"}\n response = httpx.get(url, headers=headers, timeout=10)\n response.raise_for_status()\n return response.json() # {\"status\": ..., \"progress\": ..., ...}\n\n\ndef stream_with_reconnect(job_id: str, api_token: str) -> dict:\n \"\"\"\n Open the SSE stream and reconnect automatically after unexpected drops.\n\n Polls the status endpoint when the connection closes without a terminal\n event, and applies exponential back-off before each reconnection attempt.\n\n Returns {\"status\": <terminal_status>, \"payload\": <event_payload>}.\n \"\"\"\n url = f\"{BASE_URL}/multi-cloud/jobs/{job_id}/stream\"\n headers = {\n \"Authorization\": f\"Bearer {api_token}\",\n \"Accept\": \"text/event-stream\",\n }\n delay = 1.0 # back-off starts at 1 s\n max_delay = 30.0\n\n while True:\n received_terminal = False\n\n try:\n # ── Open the SSE connection ──────────────────────────────\n with httpx.stream(\"GET\", url, headers=headers, timeout=None) as response:\n response.raise_for_status()\n delay = 1.0 # reset back-off on successful connect\n buffer = \"\"\n\n for chunk in response.iter_text():\n buffer += chunk\n *frames, buffer = buffer.split(\"\\n\\n\")\n\n for frame in frames:\n event_type = \"message\"\n data_str = None\n\n for line in frame.splitlines():\n if line.startswith(\"event:\"):\n event_type = line[len(\"event:\"):].strip()\n elif line.startswith(\"data:\"):\n data_str = line[len(\"data:\"):].strip()\n\n if data_str is None:\n continue\n\n payload = json.loads(data_str)\n\n if event_type == \"init\":\n # Successful (re)connect — reset back-off timer.\n print(f\"Connected. Job status: {payload['status']}\")\n\n elif event_type == \"progressUpdate\":\n print(f\"Progress: {payload['progress']}% — {payload.get('message', '')}\")\n\n elif event_type == \"strategyGenerated\":\n s = payload[\"strategy\"]\n print(f\"Strategy: {s['name']} | ${s['metrics']['monthlyCost']}/mo\")\n\n # ── Terminal events ──────────────────────────\n elif event_type == \"completed\":\n top = payload.get(\"topStrategy\", {})\n print(f\"Job complete. Top strategy: {top.get('name')}\")\n received_terminal = True\n return {\"status\": \"completed\", \"payload\": payload}\n\n elif event_type == \"failed\":\n print(f\"Job failed: {payload.get('error')}\")\n received_terminal = True\n return {\"status\": \"failed\", \"payload\": payload}\n\n elif event_type == \"cancelled\":\n print(\"Job cancelled.\")\n received_terminal = True\n return {\"status\": \"cancelled\", \"payload\": payload}\n\n if received_terminal:\n break\n\n except (httpx.HTTPError, httpx.StreamError) as exc:\n # Network error, proxy timeout, or server restart.\n print(f\"SSE connection error: {exc}\")\n\n # Already handled cleanly — exit the retry loop.\n if received_terminal:\n break\n\n # ── Fallback: poll before reconnecting ───────────────────────\n # The connection dropped without a terminal event. The job may\n # still be running, or it may have finished while we were offline.\n try:\n job = poll_job_status(job_id, api_token)\n\n if job[\"status\"] in TERMINAL_STATUSES:\n print(f\"Recovered via poll — job {job['status']}.\")\n return {\"status\": job[\"status\"], \"payload\": job}\n\n # status is 'running' or 'pending' — reconnect after back-off.\n print(f\"Job still {job['status']}. Reconnecting in {delay:.0f}s…\")\n\n except httpx.HTTPError as exc:\n print(f\"Poll also failed: {exc} — will retry.\")\n\n # ── Exponential back-off ─────────────────────────────────────\n time.sleep(delay)\n delay = min(delay * 2, max_delay)\n\n return {}\n\n\n# Usage\nif __name__ == \"__main__\":\n result = stream_with_reconnect(\n job_id=\"job_aws_perf123\",\n api_token=os.environ[\"API_KEY\"],\n )\n print(\"Done:\", result[\"status\"], result.get(\"payload\", {}))\n```\n","operationId":"streamMultiCloudJob","x-x402-call-type":"multicloud_stream","x-x402-price-usdc":"$0.0030","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -N https://www.cloudworldmodel.ai/api/multi-cloud/jobs/job_abc123/stream \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Accept: text/event-stream\"\n"},{"lang":"Python","label":"Python","source":"import httpx\nimport json\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\nJOB_ID = \"job_abc123\"\n\nurl = f\"{BASE_URL}/multi-cloud/jobs/{JOB_ID}/stream\"\nheaders = {\"Authorization\": f\"Bearer {API_KEY}\", \"Accept\": \"text/event-stream\"}\n\nwith httpx.stream(\"GET\", url, headers=headers, timeout=None) as response:\n response.raise_for_status()\n buffer = \"\"\n for chunk in response.iter_text():\n buffer += chunk\n *frames, buffer = buffer.split(\"\\n\\n\")\n for frame in frames:\n event_type, data_str = \"message\", None\n for line in frame.splitlines():\n if line.startswith(\"event:\"):\n event_type = line[len(\"event:\"):].strip()\n elif line.startswith(\"data:\"):\n data_str = line[len(\"data:\"):].strip()\n if not data_str:\n continue\n payload = json.loads(data_str)\n if event_type == \"progressUpdate\":\n print(f\"Progress: {payload['progress']}%\")\n elif event_type == \"strategyGenerated\":\n s = payload[\"strategy\"]\n print(f\"Strategy: {s['name']} cost=${s['metrics']['monthlyCost']}/mo\")\n elif event_type == \"completed\":\n top = payload.get(\"topStrategy\", {})\n print(f\"Done. Top strategy: {top.get('name')}\")\n break\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\nconst JOB_ID = \"job_abc123\";\n\nconst response = await fetch(`${BASE_URL}/multi-cloud/jobs/${JOB_ID}/stream`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}`, \"Accept\": \"text/event-stream\" },\n});\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\n\nconst reader = response.body.getReader();\nconst decoder = new TextDecoder();\nlet buffer = \"\";\n\nwhile (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const frames = buffer.split(/\\n\\n/);\n buffer = frames.pop();\n for (const frame of frames) {\n const eventLine = frame.match(/^event:\\s*(.+)$/m);\n const dataLine = frame.match(/^data:\\s*(.+)$/m);\n if (!dataLine) continue;\n const eventType = eventLine ? eventLine[1].trim() : \"message\";\n const payload = JSON.parse(dataLine[1]);\n if (eventType === \"progressUpdate\") {\n console.log(`Progress: ${payload.progress}%`);\n } else if (eventType === \"strategyGenerated\") {\n const s = payload.strategy;\n console.log(`Strategy: ${s.name} cost=$${s.metrics.monthlyCost}/mo`);\n } else if (eventType === \"completed\") {\n console.log(\"Done. Top strategy:\", payload.topStrategy?.name);\n reader.cancel();\n }\n }\n}\n"}],"tags":["Multi-Cloud Strategy"],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"},"description":"Multi-cloud job ID"}],"responses":{"200":{"description":"SSE stream of job updates","content":{"text/event-stream":{"schema":{"type":"string","description":"Server-Sent Events stream"},"examples":{"awsStream":{"summary":"AWS — EC2 m5.xlarge + RDS Multi-AZ performance workload stream","value":"event: init\ndata: {\"jobId\":\"job_aws_perf123\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_aws_perf123\",\"progress\":20,\"strategiesGenerated\":8,\"message\":\"Evaluating EC2 instance families and RDS configurations\"}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_aws_perf123\",\"strategy\":{\"name\":\"AWS-Primary Strategy\",\"description\":\"EC2 m5.xlarge Auto Scaling group across us-east-1a/1b with RDS db.r5.large Multi-AZ and CloudFront CDN — lowest p95 latency in comparison\",\"allocations\":[{\"provider\":\"aws\",\"percentage\":100}],\"metrics\":{\"monthlyCost\":14500,\"avgLatencyMs\":38,\"vendorLockInScore\":72}},\"strategiesGenerated\":9}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_aws_perf123\",\"strategy\":{\"name\":\"AWS + GCP Balanced\",\"description\":\"EC2 primary with GCP Cloud Run burst capacity — reduces lock-in by 14 points with only 3 ms latency increase\",\"allocations\":[{\"provider\":\"aws\",\"percentage\":70},{\"provider\":\"gcp\",\"percentage\":30}],\"metrics\":{\"monthlyCost\":13200,\"avgLatencyMs\":41,\"vendorLockInScore\":58}},\"strategiesGenerated\":10}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_aws_perf123\",\"progress\":65,\"strategiesGenerated\":26,\"message\":\"Ranking strategies by p95 latency against 50 ms SLA\"}\n\nevent: completed\ndata: {\"jobId\":\"job_aws_perf123\",\"status\":\"completed\",\"progress\":100,\"strategiesGenerated\":40,\"topStrategy\":{\"name\":\"AWS-Primary Strategy\",\"metrics\":{\"monthlyCost\":14500,\"avgLatencyMs\":38,\"vendorLockInScore\":72}},\"comparisonReport\":\"# Multi-Cloud Strategy Comparison Report\\n\\n## Winner: AWS-Primary Strategy\\n\\nAverage latency: **38 ms** — best in class, within the 50 ms SLA.\\nMonthly cost: **$14,500** — within the $25,000 budget.\\nVendor lock-in score: **72/100** — acceptable given performance requirements.\",\"completedAt\":\"2024-01-17T08:20:00Z\",\"methodology\":{\"dataSource\":\"simulation-model\",\"validationBasis\":\"simulation_only\",\"externallyValidated\":false,\"seedCount\":1,\"note\":\"Results describe Cloud World Model simulation outcomes and should not be interpreted as measured cloud-provider benchmarks.\"},\"interpretationScope\":{\"providerPerformanceScope\":\"simulation-only\",\"externalValidation\":false,\"crossSeedConsistencyMeans\":\"simulation-model-consistency\",\"untestedLoadExtrapolationAllowed\":false,\"prohibitedClaimCategories\":[\"provider-performance-superiority\",\"real-world-benchmark-equivalence\",\"untested-load-extrapolation\",\"cross-seed-as-external-validation\"],\"permittedUses\":[\"compare simulation-model cost and latency trade-offs under the tested scenarios\"],\"testedLoadLevels\":[\"200 RPS (baseline)\"],\"untestedScenariosNote\":\"Load levels or failure modes not present in testedLoadLevels are hypotheses requiring a new simulation run, not conclusions derivable from this result.\"}}\n"},"gcpStream":{"summary":"GCP — Cloud Run + Cloud SQL ML analytics workload stream","value":"event: init\ndata: {\"jobId\":\"job_gcp_ml456\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_gcp_ml456\",\"progress\":18,\"strategiesGenerated\":6,\"message\":\"Evaluating Cloud Run autoscaling profiles for variable ML inference load\"}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_gcp_ml456\",\"strategy\":{\"name\":\"GCP-Primary Strategy\",\"description\":\"Cloud Run (fully managed, us-central1) with Cloud SQL for PostgreSQL (HA) and Cloud Storage — best autoscaling fit for variable ML inference load; scales to zero between jobs\",\"allocations\":[{\"provider\":\"gcp\",\"percentage\":100}],\"metrics\":{\"monthlyCost\":11800,\"avgLatencyMs\":42,\"vendorLockInScore\":63}},\"strategiesGenerated\":7}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_gcp_ml456\",\"strategy\":{\"name\":\"GCP + AWS Hybrid\",\"description\":\"GCP Cloud Run primary with AWS Lambda for async batch processing — reduces cost by 5% versus GCP-only while adding cross-cloud redundancy\",\"allocations\":[{\"provider\":\"gcp\",\"percentage\":65},{\"provider\":\"aws\",\"percentage\":35}],\"metrics\":{\"monthlyCost\":12400,\"avgLatencyMs\":44,\"vendorLockInScore\":51}},\"strategiesGenerated\":8}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_gcp_ml456\",\"progress\":70,\"strategiesGenerated\":24,\"message\":\"Comparing scale-to-zero savings across Cloud Run, Lambda, and Container Apps\"}\n\nevent: completed\ndata: {\"jobId\":\"job_gcp_ml456\",\"status\":\"completed\",\"progress\":100,\"strategiesGenerated\":35,\"topStrategy\":{\"name\":\"GCP-Primary Strategy\",\"metrics\":{\"monthlyCost\":11800,\"avgLatencyMs\":42,\"vendorLockInScore\":63}},\"comparisonReport\":\"# Multi-Cloud Strategy Comparison Report\\n\\n## Winner: GCP-Primary Strategy\\n\\nMonthly cost: **$11,800** — $3,800 below the AWS baseline.\\nAverage latency: **42 ms** — within the 80 ms SLA.\\nCloud Run scale-to-zero eliminates idle compute cost between ML inference jobs.\",\"completedAt\":\"2024-01-18T11:35:00Z\"}\n"},"azureStream":{"summary":"Azure — AKS + Azure Database for PostgreSQL compliance workload stream","value":"event: init\ndata: {\"jobId\":\"job_azure_comp789\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_azure_comp789\",\"progress\":22,\"strategiesGenerated\":8,\"message\":\"Evaluating GDPR/HIPAA compliance posture across AKS, GKE, and EKS configurations\"}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_azure_comp789\",\"strategy\":{\"name\":\"Azure-Primary Strategy\",\"description\":\"AKS (eastus, Standard_D4s_v3 nodes) with Azure Database for PostgreSQL Flexible Server and Azure Front Door — strongest GDPR/HIPAA compliance posture with native Policy and Defender integration\",\"allocations\":[{\"provider\":\"azure\",\"percentage\":100}],\"metrics\":{\"monthlyCost\":13200,\"avgLatencyMs\":44,\"vendorLockInScore\":67}},\"strategiesGenerated\":9}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_azure_comp789\",\"strategy\":{\"name\":\"Azure + AWS Hybrid\",\"description\":\"AKS primary with AWS S3 for object storage overflow — reduces storage cost by 12% while maintaining Azure compliance perimeter for compute and database tiers\",\"allocations\":[{\"provider\":\"azure\",\"percentage\":75},{\"provider\":\"aws\",\"percentage\":25}],\"metrics\":{\"monthlyCost\":14100,\"avgLatencyMs\":46,\"vendorLockInScore\":55}},\"strategiesGenerated\":10}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_azure_comp789\",\"progress\":60,\"strategiesGenerated\":23,\"message\":\"Scoring compliance coverage for GDPR, HIPAA, ISO 27001, and PCI-DSS controls\"}\n\nevent: completed\ndata: {\"jobId\":\"job_azure_comp789\",\"status\":\"completed\",\"progress\":100,\"strategiesGenerated\":38,\"topStrategy\":{\"name\":\"Azure-Primary Strategy\",\"metrics\":{\"monthlyCost\":13200,\"avgLatencyMs\":44,\"vendorLockInScore\":67}},\"comparisonReport\":\"# Multi-Cloud Strategy Comparison Report\\n\\n## Winner: Azure-Primary Strategy\\n\\nMonthly cost: **$13,200** — within the $20,000 budget.\\nAverage latency: **44 ms** — within the 100 ms SLA.\\nCompliance: native GDPR, HIPAA, and ISO 27001 tooling; no third-party additions required.\",\"completedAt\":\"2024-01-19T14:00:00Z\"}\n"},"ociStream":{"summary":"OCI — Compute + Autonomous Database database-intensive workload stream","value":"event: init\ndata: {\"jobId\":\"job_oci_db321\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_oci_db321\",\"progress\":25,\"strategiesGenerated\":8,\"message\":\"Benchmarking Autonomous Database ATP throughput against RDS and Cloud SQL at equivalent OCPU counts\"}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_oci_db321\",\"strategy\":{\"name\":\"OCI-Primary Strategy\",\"description\":\"OCI Compute VM.Standard.E4.Flex (4 OCPU / 64 GB) with Autonomous Database ATP (4 OCPU) in us-ashburn-1 — best price-performance for OLTP-heavy workloads; Autonomous Database self-tunes indexes and query plans\",\"allocations\":[{\"provider\":\"oci\",\"percentage\":100}],\"metrics\":{\"monthlyCost\":9600,\"avgLatencyMs\":48,\"vendorLockInScore\":55}},\"strategiesGenerated\":9}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_oci_db321\",\"strategy\":{\"name\":\"OCI + AWS Hybrid\",\"description\":\"OCI Autonomous Database for primary OLTP workload with AWS S3 and Lambda for analytics offload — keeps database cost advantage while leveraging mature AWS analytics ecosystem\",\"allocations\":[{\"provider\":\"oci\",\"percentage\":70},{\"provider\":\"aws\",\"percentage\":30}],\"metrics\":{\"monthlyCost\":11200,\"avgLatencyMs\":50,\"vendorLockInScore\":44}},\"strategiesGenerated\":10}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_oci_db321\",\"progress\":72,\"strategiesGenerated\":24,\"message\":\"Calculating total cost of ownership including Autonomous Database OCPU licensing\"}\n\nevent: completed\ndata: {\"jobId\":\"job_oci_db321\",\"status\":\"completed\",\"progress\":100,\"strategiesGenerated\":33,\"topStrategy\":{\"name\":\"OCI-Primary Strategy\",\"metrics\":{\"monthlyCost\":9600,\"avgLatencyMs\":48,\"vendorLockInScore\":55}},\"comparisonReport\":\"# Multi-Cloud Strategy Comparison Report\\n\\n## Winner: OCI-Primary Strategy\\n\\nMonthly cost: **$9,600** — 34% lower than the AWS RDS baseline ($14,500).\\nAverage latency: **48 ms** — within the 75 ms SLA.\\nAutonomous Database eliminates DBA overhead for index tuning, patching, and vacuuming.\",\"completedAt\":\"2024-01-20T09:10:00Z\"}\n"},"digitalOceanStream":{"summary":"DigitalOcean — Droplets + Managed Databases cost-optimized workload stream","value":"event: init\ndata: {\"jobId\":\"job_do_xyz789\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_do_xyz789\",\"progress\":20,\"strategiesGenerated\":7,\"message\":\"Evaluating DigitalOcean Droplet sizes and Managed Database tiers against workload traffic profile\"}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_do_xyz789\",\"strategy\":{\"name\":\"DigitalOcean-Primary Strategy\",\"description\":\"DigitalOcean Droplets (s-4vcpu-8gb, nyc3) as primary compute with Managed Databases (PostgreSQL, 2-node HA) and Spaces for S3-compatible object storage — lowest total monthly spend; predictable flat-rate pricing with no data-transfer surprises within region\",\"allocations\":[{\"provider\":\"digitalocean\",\"percentage\":80},{\"provider\":\"aws\",\"percentage\":20}],\"metrics\":{\"monthlyCost\":7800,\"avgLatencyMs\":55,\"vendorLockInScore\":48}},\"strategiesGenerated\":8}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_do_xyz789\",\"strategy\":{\"name\":\"DigitalOcean + GCP Balanced\",\"description\":\"DigitalOcean Droplets for primary API tier with Managed Databases, GCP Cloud Run for burst compute — good cost/latency balance with lower lock-in than DO-only\",\"allocations\":[{\"provider\":\"digitalocean\",\"percentage\":65},{\"provider\":\"gcp\",\"percentage\":35}],\"metrics\":{\"monthlyCost\":9400,\"avgLatencyMs\":50,\"vendorLockInScore\":41}},\"strategiesGenerated\":9}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_do_xyz789\",\"progress\":68,\"strategiesGenerated\":26,\"message\":\"Ranking strategies by monthly cost; verifying all options satisfy 150 ms SLA\"}\n\nevent: completed\ndata: {\"jobId\":\"job_do_xyz789\",\"status\":\"completed\",\"progress\":100,\"strategiesGenerated\":38,\"topStrategy\":{\"name\":\"DigitalOcean-Primary Strategy\",\"metrics\":{\"monthlyCost\":7800,\"avgLatencyMs\":55,\"vendorLockInScore\":48}},\"comparisonReport\":\"# Multi-Cloud Strategy Comparison Report\\n\\n## Winner: DigitalOcean-Primary Strategy\\n\\nMonthly cost: **$7,800** — 45% lower than the AWS-dominant baseline ($14,200).\\nAverage latency: **55 ms** — within the 150 ms SLA.\\nVendor lock-in score: **48/100** — moderate, significantly lower than the AWS-only baseline (74/100).\",\"completedAt\":\"2024-01-16T09:45:00Z\"}\n"},"digitaloceanAMDNVMeStream":{"summary":"DigitalOcean AMD NVMe Droplets — in-flight stream with s-2vcpu-4gb-amd strategies accumulating","value":"event: init\ndata: {\"jobId\":\"job_do_amd_nvme001\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_do_amd_nvme001\",\"progress\":22,\"strategiesGenerated\":6,\"message\":\"Evaluating DigitalOcean AMD NVMe Droplet sizes and Managed Database tiers against I/O-intensive workload profile\"}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_do_amd_nvme001\",\"strategy\":{\"name\":\"DigitalOcean AMD NVMe Primary Strategy\",\"description\":\"DigitalOcean AMD NVMe Droplets (s-2vcpu-4gb-amd, nyc3) as primary compute — NVMe-backed local SSD delivers higher I/O throughput than standard Droplets at the same price point, with DigitalOcean Managed Databases (PostgreSQL, 2-node HA) and Spaces for object storage\",\"allocations\":[{\"provider\":\"digitalocean\",\"percentage\":100}],\"metrics\":{\"monthlyCost\":6200,\"avgLatencyMs\":52,\"vendorLockInScore\":44}},\"strategiesGenerated\":7}\n\nevent: strategyGenerated\ndata: {\"jobId\":\"job_do_amd_nvme001\",\"strategy\":{\"name\":\"DO AMD NVMe + GCP Balanced\",\"description\":\"s-2vcpu-4gb-amd Droplets for primary API tier with GCP Cloud Run for burst compute — good cost/latency balance with lower lock-in than DO-only\",\"allocations\":[{\"provider\":\"digitalocean\",\"percentage\":65},{\"provider\":\"gcp\",\"percentage\":35}],\"metrics\":{\"monthlyCost\":8100,\"avgLatencyMs\":49,\"vendorLockInScore\":38}},\"strategiesGenerated\":8}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_do_amd_nvme001\",\"progress\":55,\"strategiesGenerated\":19,\"message\":\"Ranking strategies by monthly cost; verifying all options satisfy 150 ms SLA\"}\n"},"failedStream":{"summary":"Job failure — upstream provider pricing API unavailable mid-run","value":"event: init\ndata: {\"jobId\":\"job_aws_fail001\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_aws_fail001\",\"progress\":35,\"strategiesGenerated\":14,\"message\":\"Fetching live pricing data for EC2, RDS, and CloudFront\"}\n\nevent: failed\ndata: {\"jobId\":\"job_aws_fail001\",\"status\":\"failed\",\"progress\":35,\"error\":\"PricingFetchError: AWS Pricing API returned 503 after 3 retries — unable to compute accurate cost estimates\",\"failedAt\":\"2024-01-21T10:14:22Z\"}\n"},"cancelledStream":{"summary":"Job cancellation — agent issued DELETE before completion","value":"event: init\ndata: {\"jobId\":\"job_gcp_cancel002\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_gcp_cancel002\",\"progress\":52,\"strategiesGenerated\":21,\"message\":\"Evaluating GCP Cloud Run burst strategies against 80 ms SLA\"}\n\nevent: cancelled\ndata: {\"jobId\":\"job_gcp_cancel002\",\"status\":\"cancelled\",\"progress\":52,\"strategiesGenerated\":21,\"reason\":\"Cancelled by agent request via DELETE /api/multicloud/jobs/job_gcp_cancel002\",\"cancelledAt\":\"2024-01-21T11:03:45Z\"}\n"},"digitaloceanAMDNVMeFailedStream":{"summary":"DigitalOcean AMD NVMe — job failure mid-run (pricing API unavailable)","value":"event: init\ndata: {\"jobId\":\"job_do_amd_fail003\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_do_amd_fail003\",\"progress\":41,\"strategiesGenerated\":16,\"message\":\"Fetching live pricing data for DigitalOcean AMD NVMe Droplets (s-2vcpu-4gb-amd) and Managed Databases\"}\n\nevent: failed\ndata: {\"jobId\":\"job_do_amd_fail003\",\"status\":\"failed\",\"progress\":41,\"error\":\"PricingFetchError: DigitalOcean Pricing API returned 503 after 3 retries — unable to compute accurate cost estimates for s-2vcpu-4gb-amd AMD NVMe Droplet configurations\",\"failedAt\":\"2024-01-22T08:27:14Z\"}\n"},"digitaloceanAMDNVMeCancelledStream":{"summary":"DigitalOcean AMD NVMe — job cancellation via DELETE before completion","value":"event: init\ndata: {\"jobId\":\"job_do_amd_cancel004\",\"status\":\"running\",\"progress\":0,\"strategiesGenerated\":0}\n\nevent: progressUpdate\ndata: {\"jobId\":\"job_do_amd_cancel004\",\"progress\":47,\"strategiesGenerated\":18,\"message\":\"Ranking DigitalOcean AMD NVMe Droplet strategies (s-2vcpu-4gb-amd) by monthly cost; verifying all options satisfy 150 ms SLA\"}\n\nevent: cancelled\ndata: {\"jobId\":\"job_do_amd_cancel004\",\"status\":\"cancelled\",\"progress\":47,\"strategiesGenerated\":18,\"reason\":\"Cancelled by agent request via DELETE /api/multicloud/jobs/job_do_amd_cancel004\",\"cancelledAt\":\"2024-01-22T09:15:33Z\"}\n"}}}}},"401":{"description":"Unauthorized - invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","headers":{"Retry-After":{"description":"Seconds until the rate-limit window resets","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/simulations/{simulationId}/bulk-resize":{"x-stability":"experimental","post":{"tags":["Simulations"],"summary":"Update all compute resources to a new DigitalOcean Droplet size","description":"Resizes every `type: \"compute\"` resource in the simulation to the\nspecified DigitalOcean Droplet size tier (e.g. `s-2vcpu-4gb`,\n`s-4vcpu-8gb`, `s-8vcpu-16gb`). Useful for right-sizing experiments\nwhere you want to evaluate the cost/performance trade-off of a\nuniform resize.\n\n**Scope:** Only applies to `type: \"compute\"` resources. Database\nresources (`type: \"database\"`) are not affected and remain unchanged.\n\n**DigitalOcean-only:** if any compute resource uses a non-DigitalOcean\nprovider (AWS, GCP, Azure, OCI), the request is rejected with a\n`PROVIDER_MISMATCH` 400 naming the providers found, and no resource is\nmodified. This endpoint never changes a resource's provider. To\nrecover a failed resource on any provider, use\n`POST /simulations/{simulationId}/recover-resource` instead.\n\nThe size must be a valid DigitalOcean Droplet slug. Use\n`GET /api/description` to discover available sizes.\n\nRequires `write` scope and ownership.\n","operationId":"bulkResizeSimulation","x-x402-call-type":"simulation.resize","x-x402-price-usdc":"$0.0030","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/simulations/sim-abc123/bulk-resize \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"dropletSize\": \"s-4vcpu-8gb\"}'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/simulations/sim-abc123/bulk-resize\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\"dropletSize\": \"s-4vcpu-8gb\"},\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(f\"Resized {data['resizedCount']} compute resource(s) to s-4vcpu-8gb\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/simulations/sim-abc123/bulk-resize`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ dropletSize: \"s-4vcpu-8gb\" }),\n});\nconst data = await resp.json();\nconsole.log(`Resized ${data.resizedCount} compute resource(s) to s-4vcpu-8gb`);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["dropletSize"],"properties":{"dropletSize":{"type":"string","description":"DigitalOcean Droplet size slug to apply to all compute resources","example":"s-4vcpu-8gb"}}},"examples":{"scaleUp":{"summary":"Scale all compute nodes up to 4-vCPU Droplets","value":{"dropletSize":"s-4vcpu-8gb"}},"scaleDown":{"summary":"Right-size to 2-vCPU Droplets after a load test","value":{"dropletSize":"s-2vcpu-4gb"}}}}}},"responses":{"200":{"description":"All compute resources resized","content":{"application/json":{"schema":{"type":"object","properties":{"simulation":{"$ref":"#/components/schemas/Simulation"},"resizedCount":{"type":"integer","description":"Number of compute resources that were resized","example":3}}}}}},"400":{"description":"Unknown Droplet size slug","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/simulations/{simulationId}/apply-right-sizing":{"x-stability":"experimental","post":{"tags":["Simulations"],"summary":"Update a resource using a right-sizing hint","description":"Applies a right-sizing recommendation from\n`GET /simulations/{simulationId}/right-sizing-hint` to the targeted\nresource in one call. Pass the `affectedResourceId` and\n`recommendedSlug` from the hint response; the server validates the\nslug against the resource's available sizes before mutating anything.\n\n**Validation:** Returns `400 UNKNOWN_SIZE` with a list of valid slugs\nif `recommendedSlug` is not recognized for the resource's provider and\ntype — so callers can pass the hint slug directly without a separate\nsize lookup.\n\n**Supported resource types:** compute, database, storage (any\nprovider). GPU node-count hints (`gpu-underutilized`, `gpu-saturated`)\nand managed-API breakeven hints (`gpu-api-breakeven`) produce\nnon-slug `recommendedSlug` values and cannot be applied via this\nendpoint. SQL Server Express databases are also excluded: their\ninstance class and CPU-credit billing configuration must remain\nconsistent. This route returns `400 UNSUPPORTED_RESIZE` without\nchanging the simulation for those resources.\n\n**Cross-provider alternatives:** DigitalOcean slugs returned as\ncross-provider alternatives in right-sizing hints are accepted for\nnon-DO resources; the resource's cost characteristics are updated to\nthe DO tier without changing the provider field.\n\nRequires `write` scope and ownership.\n","operationId":"applyRightSizing","x-x402-call-type":"simulation.apply_right_sizing","x-x402-price-usdc":"$0.0030","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/simulations/sim-abc123/apply-right-sizing \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"resourceId\": \"res-1\", \"recommendedSlug\": \"t3.small\"}'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\n# Step 1: get the hint\nhint_resp = requests.get(\n f\"{BASE_URL}/simulations/sim-abc123/right-sizing-hint\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nhint = hint_resp.json()[\"hints\"][0]\n\n# Step 2: apply it\nresp = requests.post(\n f\"{BASE_URL}/simulations/sim-abc123/apply-right-sizing\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\"resourceId\": hint[\"affectedResourceId\"], \"recommendedSlug\": hint[\"recommendedSlug\"]},\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(f\"Applied {data['appliedSlug']} to {data['resourceName']}\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\n// Step 1: get the hint\nconst hintResp = await fetch(`${BASE_URL}/simulations/sim-abc123/right-sizing-hint`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n});\nconst { hints } = await hintResp.json();\nconst hint = hints[0];\n\n// Step 2: apply it in one call\nconst resp = await fetch(`${BASE_URL}/simulations/sim-abc123/apply-right-sizing`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ resourceId: hint.affectedResourceId, recommendedSlug: hint.recommendedSlug }),\n});\nconst data = await resp.json();\nconsole.log(`Applied ${data.appliedSlug} to ${data.resourceName}`);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["resourceId","recommendedSlug"],"properties":{"resourceId":{"type":"string","description":"ID of the resource to resize — use `affectedResourceId` from a right-sizing-hint response","example":"res-1"},"recommendedSlug":{"type":"string","description":"Size slug to apply — use `recommendedSlug` from a right-sizing-hint response (e.g. `t3.small`, `n1-standard-1`, `s-1vcpu-2gb`)","example":"t3.small"}}},"examples":{"computeDownsize":{"summary":"Downsize an AWS compute resource per a hint","value":{"resourceId":"res-1","recommendedSlug":"t3.small"}},"doAlternative":{"summary":"Apply a DigitalOcean cross-provider alternative","value":{"resourceId":"res-2","recommendedSlug":"s-1vcpu-2gb"}}}}}},"responses":{"200":{"description":"Size applied successfully","content":{"application/json":{"schema":{"type":"object","properties":{"resourceId":{"type":"string","description":"ID of the resized resource"},"resourceName":{"type":"string","description":"Display name of the resized resource"},"appliedSlug":{"type":"string","description":"The size slug that was applied"},"costMultiplier":{"type":"number","description":"Cost multiplier for the new size tier"},"isDoAlternative":{"type":"boolean","description":"True when the applied slug is a DigitalOcean cross-provider alternative"}}}}}},"400":{"description":"Unknown size slug or resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/simulations/{simulationId}/recover-resource":{"x-stability":"experimental","post":{"tags":["Simulations"],"summary":"Run recovery for a single failed resource","description":"Clears a specific failed resource's failure state so the simulation\nengine restores it to healthy over subsequent steps. Deactivates any\nin-effect **reversible** failure injection targeting the resource\n(`instance_down`, `database_overload`), restores snapshotted\ncharacteristics where available, and resets the resource's health\ncounters to zero so only subsequent qualifying simulation steps count.\n\nReturns 409 `PERMANENT_DATA_LOSS` for destroyed data that has not been\nexplicitly rebuilt. Generic recovery never clears `dataState`; an empty\nrebuilt database can serve again while its original data remains lost.\n\nThe quick `inject-failure` endpoint is intentionally different: its\npositive `failureParkSteps` window is automatic, so the node may rejoin\nrouting after the window without this endpoint. Use `recover-resource`\nwhen an explicit recovery is required for a persistent reversible\n`instance_down` failure; poll `isRoutable`, `routedRps`, and\n`recoveryProgress` after either flow.\n\n**`instance_kill` cannot be recovered** — that failure type\npermanently removes the target resource from the simulation. Calling\nthis endpoint with the killed resource's id returns `400\nRESOURCE_KILLED`; re-add the resource via\n`PATCH /simulations/{simulationId}`, or prefer `instance_down` when\nyou want a reversible outage.\n\n**Scoped to the targeted resource only** — other resources that remain\nfailed are not affected.\n\nThe response echoes the applied outcome, including `stepsToHealthy`:\na lower-bound estimate of the `POST /step` (or `step-hybrid`) calls\nneeded for the engine to demote the resource back to `healthy` under\nthe current recovery policy (defaults: critical→warning after 4 steps\nbelow 80% CPU, warning→healthy after 3 steps below 70%; the default\nlower bound is 7 steps from critical). `stepsToHealthyIsLowerBound` is always true:\nexplicitly failed resources may require an additional failure-park\ntransition before cooldown resumes, and a CPU threshold breach resets\nthe cooldown counter. Callers should take at least `stepsToHealthy`\nsteps, then poll `GET /simulations/{simulationId}` until the resource\nis healthy.\n\nWorks for every provider (AWS, GCP, Azure, OCI, DigitalOcean) —\nunlike `bulk-resize`, which is DigitalOcean-only.\n\nProvide `resourceName` or `resourceId`. Returns 400 when the resource\ncannot be found, was permanently removed by `instance_kill`, or is\nalready healthy.\n\nRequires `write` scope and ownership.\n","operationId":"recoverSimulationResource","x-x402-call-type":"simulation.recover_resource","x-x402-price-usdc":"$0.0030","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/simulations/sim-abc123/recover-resource \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"resourceName\": \"app-server-01\"}'\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"resourceName":{"type":"string","description":"Name of the failed resource to recover (case-insensitive exact match). Provide this or resourceId.","example":"app-server-01"},"resourceId":{"type":"string","description":"ID of the failed resource to recover. Provide this or resourceName."}}},"examples":{"byName":{"summary":"Recover a failed app instance by name","value":{"resourceName":"app-server-01"}},"byId":{"summary":"Recover by resource ID","value":{"resourceId":"res-1"}}}}}},"responses":{"200":{"description":"Recovery initiated for the targeted resource","content":{"application/json":{"schema":{"type":"object","properties":{"resolvedResourceId":{"type":"string"},"resolvedResourceName":{"type":"string"},"previousHealth":{"type":"string","description":"Resource status before recovery (e.g. critical, warning)"},"recoveryState":{"type":"string","enum":["recovering"]},"stepsToHealthy":{"type":"integer","description":"Lower-bound estimate — callers should take at least this many simulation steps before polling for healthy. Explicitly failed resources may require an additional failure-park transition before cooldown resumes, and a CPU threshold breach resets the cooldown counter."},"stepsToHealthyIsLowerBound":{"type":"boolean","description":"Always true; stepsToHealthy is a lower bound, not a completion guarantee."},"recoveryProgress":{"$ref":"#/components/schemas/RecoveryProgress"},"deactivatedFailureIds":{"type":"array","items":{"type":"string"},"description":"IDs of failure injections that were deactivated as part of the recovery"}}},"example":{"resolvedResourceId":"res-1","resolvedResourceName":"app-server-01","previousHealth":"critical","recoveryState":"recovering","stepsToHealthy":7,"stepsToHealthyIsLowerBound":true,"deactivatedFailureIds":["fail-1"]}}}},"400":{"description":"Resource not found, already healthy, or neither resourceName nor resourceId supplied"},"402":{"description":"Payment required (x402)"},"403":{"description":"Access denied (not the owner)"},"404":{"description":"Simulation not found"}}}},"/simulations/{simulationId}/right-sizing-hint":{"x-stability":"experimental","get":{"tags":["Simulations"],"summary":"Fetch right-sizing recommendations","description":"Analyses the simulation's recent metrics and resource configuration to\nidentify over-provisioned resources. Returns actionable hints with the\nrecommended smaller size slug, estimated hourly rate, estimated savings\npercentage, and a trade-off note explaining the operational impact.\n\nReturns `{ hasHint: false }` when the simulation is appropriately sized\nand no generic downsizing is recommended. The\n`rdsSqlServerComparisons` array is still returned for every resource\nusing the bounded SQL Server Express billing contract. Those entries\nhold absolute vCPU demand and the caller-defined interval constant,\nreset alternative credit balances to zero, and deliberately set\n`recommendation` to null.\n\nRequires `read` scope and ownership.\n","operationId":"getRightSizingHint","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl https://www.cloudworldmodel.ai/api/simulations/sim-abc123/right-sizing-hint \\\n -H \"Authorization: Bearer $API_KEY\"\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.get(\n f\"{BASE_URL}/simulations/sim-abc123/right-sizing-hint\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n)\nresp.raise_for_status()\ndata = resp.json()\nif data.get(\"hasHint\"):\n for hint in data[\"hints\"]:\n print(f\"Resize {hint['affectedResourceName']} → {hint['recommendedSlug']} (save ~{hint['estimatedSavingsPct']}%)\")\nelse:\n print(\"Simulation is appropriately sized — no downsizing recommended.\")\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/simulations/sim-abc123/right-sizing-hint`, {\n headers: { \"Authorization\": `Bearer ${API_KEY}` },\n});\nconst data = await resp.json();\nif (data.hasHint) {\n for (const hint of data.hints) {\n console.log(`Resize ${hint.affectedResourceName} → ${hint.recommendedSlug} (save ~${hint.estimatedSavingsPct}%)`);\n }\n} else {\n console.log(\"Simulation is appropriately sized — no downsizing recommended.\");\n}\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-x402-call-type":"right_sizing_hint","x-x402-price-usdc":"$0.0030","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"responses":{"200":{"description":"Right-sizing hints (or empty result if no hints)","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"hasHint":{"type":"boolean","example":false},"rdsSqlServerComparisons":{"type":"array","description":"Bounded RDS SQL Server Express cost comparisons, returned independently of whether generic right-sizing hints exist.","items":{"$ref":"#/components/schemas/RdsSqlServerComparison"}}}},{"type":"object","properties":{"hasHint":{"type":"boolean","example":true},"rdsSqlServerComparisons":{"type":"array","description":"Bounded RDS SQL Server Express cost comparisons, returned independently of generic right-sizing hints. Read the comparison basis rather than treating entries as recommendations.","items":{"$ref":"#/components/schemas/RdsSqlServerComparison"}},"hints":{"type":"array","items":{"type":"object","properties":{"resourceType":{"type":"string","enum":["compute","database","network","storage","kubernetes"]},"reason":{"type":"string","enum":["low_cpu","scale_in","both","low_connection_util","low_throughput","low_throughput_util","high_throughput_util","low_cpu_util","cross_provider_alternative","gpu-underutilized","gpu-saturated","gpu-api-breakeven"],"description":"What triggered the hint.\n- `low_cpu` — CPU utilization has been consistently below 40%.\n- `scale_in` — a scale-in event was observed recently.\n- `both` — both low CPU and a recent scale-in apply.\n- `low_cpu_util` — CPU-based trigger for OCI/HeatWave shapes.\n- `low_connection_util` — database connection utilization below 40%.\n- `low_throughput` / `low_throughput_util` — I/O or network throughput well below capacity.\n- `high_throughput_util` — approaching a throughput ceiling (scale-up signal).\n- `cross_provider_alternative` — the recommended slug belongs to a *different* cloud provider than the simulation's current resources. Treat this as a migration option, not a direct resize; additional setup is required to move workloads across providers.\n- `gpu-underutilized` / `gpu-saturated` / `gpu-api-breakeven` — GPU inference right-sizing signals on `kubernetes` resources running in inference mode (based on `metrics.gpuUtilization`).\n"},"affectedResourceId":{"type":"string","nullable":true},"affectedResourceName":{"type":"string","nullable":true},"recommendedSlug":{"type":"string","description":"Recommended smaller size slug","example":"s-2vcpu-4gb"},"hourlyRate":{"type":"number","description":"Estimated hourly cost of the recommended size (USD)","example":0.036},"estimatedSavingsPct":{"type":"integer","description":"Estimated percentage cost saving","example":35},"tradeOffNote":{"type":"string","description":"Operational trade-off to consider before resizing"},"currentUtilization":{"type":"number","nullable":true,"description":"Current utilization percentage driving the recommendation"},"utilizationMetric":{"type":"string","enum":["cpu","gpu","memory","connections","iops","unknown"],"description":"Which metric produced `currentUtilization` — without this\nlabel a consumer cannot tell whether `3` means 3% CPU,\n3% GPU, 3% connection-pool utilization, or something else.\n`unknown` when the source metric is not a percentage of a\nlabelled metric (e.g. raw RPS on network hints).\n"},"reversibility":{"type":"object","description":"How hard this recommendation is to reverse, classified by the\nsame shared rule-based engine used by\n`GET /api/analysis/jobs/{jobId}/recommendations`.\n","properties":{"tier":{"type":"string","enum":["instant","minutes","hours","days"]},"estimatedMinutesMin":{"type":"number"},"estimatedMinutesMax":{"type":"number"},"reason":{"type":"string","description":"Human-readable explanation naming the dominant action."},"basis":{"type":"string","enum":["rule_based"]}}}}}}}}]},"examples":{"hasHint":{"summary":"Simulation is over-provisioned — downsize recommended","value":{"hasHint":true,"hints":[{"resourceType":"compute","reason":"low_cpu","affectedResourceId":"r2","affectedResourceName":"App Server","recommendedSlug":"s-2vcpu-4gb","hourlyRate":0.036,"estimatedSavingsPct":35,"tradeOffNote":"s-2vcpu-4gb has half the CPU capacity — monitor p95 latency closely after resize","currentUtilization":22.4}]}},"noHint":{"summary":"Simulation is appropriately sized — no downsize recommended","value":{"hasHint":false}}}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/validate-cost-accuracy":{"x-stability":"experimental","get":{"tags":["Simulations"],"summary":"Validate simulation cost accuracy against provider benchmarks","description":"Compares the simulation's cost estimates against known real-world provider\npricing benchmarks. Returns a validation result indicating whether the\nsimulated cost per hour is within the acceptable tolerance (±10%).\n\nUseful for confirming the simulation is faithfully modelling provider\npricing before using it for cost-optimisation decisions.\n\n**Skip semantics:** Resources without a `serviceFamily` characteristic,\nor whose `provider`/`serviceFamily`/`size` combination has no pricing\nbenchmark entry, are silently skipped. When all resources are skipped,\n`validations` is empty, `summary.total` is `0`, and `checked` is `false`.\nIn that state `valid: true` means \"no failures among the zero resources\nchecked\" — it does **not** mean the simulation's cost accuracy was\nverified. Always check `checked: true` before acting on `valid`.\n\nRequires `read` scope and ownership.\n","operationId":"validateCostAccuracy","security":[],"x-x402-call-type":"validate_cost_accuracy","x-x402-price-usdc":"$0.0030","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"responses":{"200":{"description":"Cost accuracy validation result","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean","description":"Whether all checked resources are within the ±10% cost tolerance. Only meaningful when `checked` is `true`.\n","example":true},"checked":{"type":"boolean","description":"`true` when at least one resource had a matching pricing benchmark and was evaluated. `false` means no comparable data was found — the simulation resources may lack `serviceFamily` or have no pricing benchmark entry. When `checked` is `false`, `valid: true` does **not** mean accuracy was verified.\n","example":true},"validations":{"type":"array","description":"Per-resource cost validation results","items":{"type":"object","properties":{"resource":{"type":"string","description":"Resource name","example":"web-server"},"provider":{"type":"string","example":"aws"},"serviceFamily":{"type":"string","example":"compute"},"size":{"type":"string","example":"m5.large"},"simulatedCost":{"type":"number","description":"Simulated hourly cost (USD)","example":0.095},"benchmarkCost":{"type":"number","description":"Benchmark hourly cost (USD)","example":0.096},"deviation":{"type":"number","description":"Absolute percentage deviation from benchmark","example":1.04},"withinThreshold":{"type":"boolean","example":true},"source":{"type":"string","description":"Source citation for the benchmark value","example":"AWS EC2 On-Demand pricing (us-east-1)"}}}},"summary":{"type":"object","properties":{"total":{"type":"integer","description":"Number of resources that had a matching benchmark","example":3},"passed":{"type":"integer","example":3},"failed":{"type":"integer","example":0},"avgDeviation":{"type":"number","description":"Average deviation across all checked resources","example":0.82},"skippedCount":{"type":"integer","description":"Number of resources (or resource-metric pairs) that were evaluated but had no matching benchmark entry and were therefore skipped.\n","example":1},"skippedReasons":{"type":"array","description":"Human-readable explanation for each skipped resource. Common reasons: no `serviceFamily` set, or no pricing benchmark found for the provider/serviceFamily/size combination.\n","items":{"type":"string"},"example":["\"cache-layer\": no serviceFamily set"]}}}}}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/validate-performance-accuracy":{"x-stability":"experimental","get":{"tags":["Simulations"],"summary":"Validate performance accuracy against provider benchmarks","description":"Compares the simulation's throughput and latency estimates against known\nreal-world provider benchmarks. Returns a validation result indicating\nwhether the simulated performance metrics are within the acceptable\ntolerance (±15%).\n\n**Skip semantics:** Resources without a `serviceFamily` characteristic\nare skipped entirely. For resources that do have a `serviceFamily`,\nindividual metrics (`max_throughput`, `baseline_latency`,\n`max_connections`) are skipped when no benchmark entry exists for that\nprovider/metric combination. When all resource-metric pairs are skipped,\n`validations` is empty, `summary.total` is `0`, and `checked` is `false`.\nIn that state `valid: true` means \"no failures among the zero pairs\nchecked\" — it does **not** mean the simulation's performance accuracy\nwas verified. Always check `checked: true` before acting on `valid`.\n\nRequires `read` scope and ownership.\n","operationId":"validatePerformanceAccuracy","security":[],"x-x402-call-type":"validate_performance_accuracy","x-x402-price-usdc":"$0.0030","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"responses":{"200":{"description":"Performance accuracy validation result","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean","description":"Whether all checked resource-metric pairs are within the ±15% performance tolerance. Only meaningful when `checked` is `true`.\n","example":true},"checked":{"type":"boolean","description":"`true` when at least one resource-metric pair had a matching performance benchmark and was evaluated. `false` means no comparable data was found — the simulation resources may lack `serviceFamily`, or no performance benchmark exists for the provider/metric combination. When `checked` is `false`, `valid: true` does **not** mean accuracy was verified.\n","example":true},"validations":{"type":"array","description":"Per-resource-metric performance validation results","items":{"type":"object","properties":{"resource":{"type":"string","example":"web-server"},"provider":{"type":"string","example":"aws"},"serviceFamily":{"type":"string","example":"compute"},"size":{"type":"string","example":"m5.large"},"metric":{"type":"string","description":"The metric that was validated. One of: `max_throughput`, `baseline_latency`, `max_connections`.\n","example":"max_throughput"},"simulatedValue":{"type":"number","example":9800},"benchmarkValue":{"type":"number","example":10000},"deviation":{"type":"number","description":"Absolute percentage deviation from benchmark","example":2},"withinThreshold":{"type":"boolean","example":true},"unit":{"type":"string","example":"RPS"},"source":{"type":"string","example":"AWS EC2 m5.large benchmark"}}}},"summary":{"type":"object","properties":{"total":{"type":"integer","description":"Number of resource-metric pairs that had a matching benchmark","example":4},"passed":{"type":"integer","example":4},"failed":{"type":"integer","example":0},"avgDeviation":{"type":"number","example":1.5},"skippedCount":{"type":"integer","description":"Number of resource-metric pairs that were skipped because no benchmark was found.\n","example":2},"skippedReasons":{"type":"array","description":"Human-readable explanation for each skipped resource-metric pair.\n","items":{"type":"string"},"example":["\"cache-layer\": no serviceFamily set","\"db-primary\" max_throughput: no benchmark for aws/database/db.r5.large"]}}}}}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/validate-accuracy":{"x-stability":"experimental","get":{"tags":["Simulations"],"summary":"Validate both cost and performance accuracy","description":"Convenience endpoint that runs both cost and performance accuracy\nvalidations in a single call. Returns a combined result with an\n`overallValid` flag that is `true` only if both checks pass.\n\nRequires `read` scope and ownership.\n","operationId":"validateAccuracy","security":[],"x-x402-call-type":"benchmark.validate","x-x402-price-usdc":"$0.0030","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"responses":{"200":{"description":"Combined accuracy validation result","content":{"application/json":{"schema":{"type":"object","properties":{"cost":{"type":"object","description":"Full cost accuracy validation result — same shape as `GET /validate-cost-accuracy`. Check `cost.checked` before trusting `cost.valid`.\n","properties":{"valid":{"type":"boolean"},"checked":{"type":"boolean"},"validations":{"type":"array","description":"Per-resource cost validation results","items":{"type":"object","properties":{"resource":{"type":"string","example":"web-server"},"provider":{"type":"string","example":"aws"},"serviceFamily":{"type":"string","example":"compute"},"size":{"type":"string","example":"m5.large"},"simulatedCost":{"type":"number","description":"Simulated hourly cost (USD)","example":0.098},"benchmarkCost":{"type":"number","description":"Expected hourly cost from provider benchmark (USD)","example":0.096},"deviation":{"type":"number","description":"Absolute percentage deviation from benchmark","example":2.08},"withinThreshold":{"type":"boolean","example":true},"source":{"type":"string","example":"AWS EC2 m5.large pricing"}}}},"summary":{"type":"object","properties":{"total":{"type":"integer"},"passed":{"type":"integer"},"failed":{"type":"integer"},"avgDeviation":{"type":"number"},"skippedCount":{"type":"integer"},"skippedReasons":{"type":"array","items":{"type":"string"}}}}}},"performance":{"type":"object","description":"Full performance accuracy validation result — same shape as `GET /validate-performance-accuracy`. Check `performance.checked` before trusting `performance.valid`.\n","properties":{"valid":{"type":"boolean"},"checked":{"type":"boolean"},"validations":{"type":"array","description":"Per-resource-metric performance validation results","items":{"type":"object","properties":{"resource":{"type":"string","example":"web-server"},"provider":{"type":"string","example":"aws"},"serviceFamily":{"type":"string","example":"compute"},"size":{"type":"string","example":"m5.large"},"metric":{"type":"string","description":"The metric that was validated. One of: `max_throughput`, `baseline_latency`, `max_connections`.\n","example":"max_throughput"},"simulatedValue":{"type":"number","example":9800},"benchmarkValue":{"type":"number","example":10000},"deviation":{"type":"number","description":"Absolute percentage deviation from benchmark","example":2},"withinThreshold":{"type":"boolean","example":true},"unit":{"type":"string","example":"RPS"},"source":{"type":"string","example":"AWS EC2 m5.large benchmark"}}}},"summary":{"type":"object","properties":{"total":{"type":"integer"},"passed":{"type":"integer"},"failed":{"type":"integer"},"avgDeviation":{"type":"number"},"skippedCount":{"type":"integer"},"skippedReasons":{"type":"array","items":{"type":"string"}}}}}},"checked":{"type":"boolean","description":"`true` when at least one of the two sub-validations (cost or performance) found comparable benchmark data and ran a real check. `false` means neither sub-result found matching benchmarks — `overallValid: true` in this state does **not** mean accuracy was verified.\n","example":true},"overallValid":{"type":"boolean","description":"`true` only when both cost and performance `valid` flags are `true`. Only meaningful when `checked` is `true`.\n","example":true},"thresholds":{"type":"object","properties":{"cost":{"type":"string","example":"±10%"},"performance":{"type":"string","example":"±15%"}}}}}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/explain":{"x-stability":"stable","post":{"tags":["Understand"],"summary":"Generate a natural-language explanation of simulation behaviour","description":"Uses GPT-5 to generate a natural-language explanation of what is currently\nhappening in the simulation: why latency is high, why autoscaling is or\nisn't triggering, what the dominant cost drivers are, etc.\n\nSet `beginnerMode: true` to receive a simplified, jargon-free explanation\nsuitable for cloud newcomers.\n\n**Authentication:** Requires an API key (`write` scope) or an x402\nmicro-payment (3 credits — $0.0030 USDC on Base). Pass\n`Authorization: Bearer <key>` for API key access, or include a signed\nx402 payment header (no account required). Use\n`GET /api/billing/x402/config` to discover the live price.\n","operationId":"explainSimulation","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/simulations/sim-abc123/explain \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"beginnerMode\": false}'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/simulations/sim-abc123/explain\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\"beginnerMode\": False},\n)\nresp.raise_for_status()\nprint(resp.json()[\"explanation\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/simulations/sim-abc123/explain`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ beginnerMode: false }),\n});\nconst data = await resp.json();\nconsole.log(data.explanation);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.003"}},"x-x402-call-type":"ai.explain","x-x402-price-usdc":"$0.0030","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"beginnerMode":{"type":"boolean","default":false,"description":"Return a simplified, jargon-free explanation","example":false}}},"examples":{"expertMode":{"summary":"Request an expert-level explanation","value":{"beginnerMode":false}},"beginnerMode":{"summary":"Request a beginner-friendly, jargon-free explanation","value":{"beginnerMode":true}}}}}},"responses":{"200":{"description":"AI explanation","content":{"application/json":{"schema":{"type":"object","properties":{"explanation":{"type":"string","description":"Natural-language explanation of simulation behaviour","example":"Your simulation is experiencing high CPU utilization (82%) because the traffic load of 8 000 RPS exceeds the capacity of the current 3-node cluster. The autoscaler has not yet triggered because CPU has not been above the 75% threshold for the required 2-step cooldown window."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"description":"Rate limit exceeded","headers":{"Retry-After":{"description":"Seconds until the rate-limit window resets","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/optimize":{"x-stability":"stable","post":{"tags":["Understand"],"summary":"Generate AI optimization suggestions for a simulation","description":"Uses GPT-5 to analyse the simulation's current resource configuration and\nmetric history and return prioritised suggestions for reducing cost,\nimproving reliability, or increasing performance.\n\nSet `beginnerMode: true` for simplified language.\n\n**Authentication:** Requires an API key (`write` scope) or an x402\nmicro-payment (3 credits — $0.0030 USDC on Base). Pass\n`Authorization: Bearer <key>` for API key access, or include a signed\nx402 payment header (no account required). Use\n`GET /api/billing/x402/config` to discover the live price.\n","operationId":"optimizeSimulation","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/simulations/sim-abc123/optimize \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"beginnerMode\": false}'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/simulations/sim-abc123/optimize\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\"beginnerMode\": False},\n)\nresp.raise_for_status()\nprint(resp.json()[\"suggestions\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/simulations/sim-abc123/optimize`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ beginnerMode: false }),\n});\nconst data = await resp.json();\nconsole.log(data.suggestions);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.003"}},"x-x402-call-type":"ai.optimize","x-x402-price-usdc":"$0.0030","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"beginnerMode":{"type":"boolean","default":false,"description":"Return simplified suggestions"}}},"examples":{"expertOptimize":{"summary":"Request expert-level optimization suggestions","value":{"beginnerMode":false}},"beginnerOptimize":{"summary":"Request beginner-friendly optimization suggestions","value":{"beginnerMode":true}}}}}},"responses":{"200":{"description":"AI optimisation suggestions","content":{"application/json":{"schema":{"type":"object","properties":{"suggestions":{"type":"string","description":"Prioritised optimisation recommendations"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"description":"Rate limit exceeded","headers":{"Retry-After":{"description":"Seconds until the rate-limit window resets","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/troubleshoot":{"x-stability":"stable","post":{"tags":["Understand"],"summary":"Generate AI troubleshooting guidance for a simulation issue","description":"Accepts a plain-text description of a problem (e.g. \"latency spikes every\n30 seconds\") and uses GPT-5 to analyse the simulation's current state and\nevent history to produce step-by-step troubleshooting guidance.\n\n**Authentication:** Requires an API key (`write` scope) or an x402\nmicro-payment (3 credits — $0.0030 USDC on Base). Pass\n`Authorization: Bearer <key>` for API key access, or include a signed\nx402 payment header (no account required). Use\n`GET /api/billing/x402/config` to discover the live price.\n","operationId":"troubleshootSimulation","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/simulations/sim-abc123/troubleshoot \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"issue\": \"Latency spikes every 30 seconds and error rate climbs to 5% during spikes\"}'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/simulations/sim-abc123/troubleshoot\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\"issue\": \"Latency spikes every 30 seconds and error rate climbs to 5% during spikes\"},\n)\nresp.raise_for_status()\nprint(resp.json()[\"guidance\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/simulations/sim-abc123/troubleshoot`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n issue: \"Latency spikes every 30 seconds and error rate climbs to 5% during spikes\",\n }),\n});\nconst data = await resp.json();\nconsole.log(data.guidance);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.003"}},"x-x402-call-type":"ai.troubleshoot","x-x402-price-usdc":"$0.0030","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["issue"],"properties":{"issue":{"type":"string","description":"Plain-text description of the problem to investigate","example":"Latency spikes every 30 seconds and error rate climbs to 5% during spikes"}}},"examples":{"latencySpike":{"summary":"Investigate periodic latency spikes","value":{"issue":"Latency spikes every 30 seconds and error rate climbs to 5% during spikes"}},"autoscalingStuck":{"summary":"Debug why autoscaling is not triggering","value":{"issue":"CPU is consistently above 80% but autoscaler has not added instances in the last 10 steps"}}}}}},"responses":{"200":{"description":"Troubleshooting guidance","content":{"application/json":{"schema":{"type":"object","properties":{"guidance":{"type":"string","description":"Step-by-step troubleshooting guidance"}}}}}},"400":{"description":"Missing issue description","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"description":"Rate limit exceeded","headers":{"Retry-After":{"description":"Seconds until the rate-limit window resets","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/analyze-bottlenecks":{"x-stability":"stable","post":{"tags":["Understand"],"summary":"Generate an AI analysis of performance bottlenecks","description":"Uses GPT-5 to identify performance bottlenecks in the simulation based on\nthe current resource configuration, metric history, and event log.\nReturns a detailed analysis and, where applicable, a DigitalOcean-specific\nmigration recommendation.\n\nSet `beginnerMode: true` for simplified language.\n\n**Authentication:** Requires an API key (`write` scope) or an x402\nmicro-payment (3 credits — $0.0030 USDC on Base). Pass\n`Authorization: Bearer <key>` for API key access, or include a signed\nx402 payment header (no account required). Use\n`GET /api/billing/x402/config` to discover the live price.\n","operationId":"analyzeBottlenecks","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/simulations/sim-abc123/analyze-bottlenecks \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"beginnerMode\": false}'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/simulations/sim-abc123/analyze-bottlenecks\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\"beginnerMode\": False},\n)\nresp.raise_for_status()\ndata = resp.json()\nprint(data[\"analysis\"])\nif data.get(\"doRecommendation\"):\n print(\"DO recommendation:\", data[\"doRecommendation\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/simulations/sim-abc123/analyze-bottlenecks`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ beginnerMode: false }),\n});\nconst data = await resp.json();\nconsole.log(data.analysis);\nif (data.doRecommendation) {\n console.log(\"DO recommendation:\", data.doRecommendation);\n}\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.003"}},"x-x402-call-type":"ai_bottleneck","x-x402-price-usdc":"$0.0030","parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"beginnerMode":{"type":"boolean","default":false,"description":"Return simplified analysis"}}},"examples":{"expertAnalysis":{"summary":"Request expert-level bottleneck analysis","value":{"beginnerMode":false}},"beginnerAnalysis":{"summary":"Request beginner-friendly bottleneck analysis","value":{"beginnerMode":true}}}}}},"responses":{"200":{"description":"Bottleneck analysis","content":{"application/json":{"schema":{"type":"object","properties":{"analysis":{"type":"string","description":"Detailed bottleneck analysis"},"doRecommendation":{"nullable":true,"description":"DigitalOcean-specific migration/right-sizing recommendation (if applicable)","oneOf":[{"type":"null"},{"type":"object","properties":{"suggestedDropletSize":{"type":"string","description":"Recommended Droplet size slug (e.g. \"s-2vcpu-4gb\")"},"estimatedHourlyRate":{"type":"number","description":"Hourly rate for the suggested Droplet size in USD"},"estimatedMonthlyCost":{"type":"number","description":"Estimated monthly cost for all Droplets in USD"},"currentHourlyCost":{"type":"number","description":"Current compute-tier hourly cost (compute resources only — see savingsBasis)"},"estimatedHourlySavings":{"type":"number","description":"Estimated hourly savings vs current compute-tier cost"},"estimatedMonthlySavings":{"type":"number","description":"Estimated monthly savings in USD"},"savingsPercent":{"type":"number","description":"Savings as a percentage of the compute-tier baseline (0–70)"},"savingsIsComputed":{"type":"boolean","description":"True when savings are derived from real cost data rather than a default estimate"},"savingsBasis":{"type":"string","enum":["compute-only"],"description":"Indicates that savings are computed against compute-tier costs only; non-compute resources (managed DB, cache, load balancer) are excluded from the baseline"},"reason":{"type":"string","enum":["right-sizing","migration","scale-out"],"description":"The primary recommendation category"},"numDroplets":{"type":"number","description":"Suggested number of Droplets"},"reversibilityTier":{"type":"string","enum":["instant","minutes","hours","days"],"description":"How hard this recommendation is to reverse — cross-provider migrations classify as \"days\""},"reversibilityReason":{"type":"string","description":"Human-readable explanation of the dominant reversibility factor"},"estimatedMinutesMin":{"type":"number","description":"Lower bound of estimated rollback time in minutes"},"estimatedMinutesMax":{"type":"number","description":"Upper bound of estimated rollback time in minutes"},"basis":{"type":"string","enum":["rule_based"],"description":"Always \"rule_based\" — reversibility is classified deterministically, not via LLM"}},"required":["suggestedDropletSize","estimatedHourlyRate","estimatedMonthlyCost","currentHourlyCost","estimatedHourlySavings","estimatedMonthlySavings","savingsPercent","savingsIsComputed","savingsBasis","reason","numDroplets","reversibilityTier","reversibilityReason","estimatedMinutesMin","estimatedMinutesMax","basis"]}]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ai-jobs":{"x-stability":"stable","post":{"tags":["AI Jobs"],"summary":"Submit async AI analysis job","description":"Creates an async AI analysis job for a simulation. Immediately returns a\n`jobId` (HTTP 202) so callers are never blocked by the ~11 s gateway\ntimeout. Supported job types:\n\n- `explain` — natural-language explanation of simulation behaviour\n- `troubleshoot` — diagnosis and remediation steps for a described issue\n- `analyze_bottlenecks` — ranked bottleneck detection with remediation\n- `optimize` — infrastructure optimisation suggestions\n\n**Poll flow:**\n1. `POST /api/ai-jobs` → receive `jobId`\n2. `GET /api/ai-jobs/{jobId}` → poll until `status` is `completed`\n3. `GET /api/ai-jobs/{jobId}/results` → retrieve the AI output\n\n**Authentication:** Required. Provide `Authorization: Bearer cwm_live_…`\nor an x402 payment header with call type `ai_analysis`.\n","operationId":"createAiJob","security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.003"}},"x-x402-call-type":"ai_analysis","x-x402-price-usdc":"$0.0030","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["simulationId","type"],"properties":{"simulationId":{"type":"string","description":"ID of the simulation to analyze"},"type":{"type":"string","enum":["explain","troubleshoot","analyze_bottlenecks","optimize"],"description":"Type of AI analysis to run"},"beginnerMode":{"type":"boolean","default":false,"description":"Return simplified, jargon-free output"},"issue":{"type":"string","description":"Required for troubleshoot — describe the problem"}}},"examples":{"explainJob":{"summary":"Submit an explain job","value":{"simulationId":"sim-abc123","type":"explain","beginnerMode":false}},"troubleshootJob":{"summary":"Submit a troubleshoot job","value":{"simulationId":"sim-abc123","type":"troubleshoot","issue":"Latency spiking after 500 RPS"}}}}}},"responses":{"202":{"description":"AI job accepted","content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string","description":"AI job ID — use with /ai-jobs/{jobId} and /ai-jobs/{jobId}/results"},"status":{"type":"string","enum":["pending"]},"createdAt":{"type":"string","format":"date-time"},"message":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ai-jobs/{jobId}":{"x-stability":"stable","get":{"tags":["AI Jobs"],"summary":"Fetch the current AI job status","description":"Returns the current status of an async AI analysis job. Poll until\n`status` is `completed` (or `failed`/`cancelled`), then call\n`GET /ai-jobs/{jobId}/results` to retrieve the AI output.\n\nAlso accepts job IDs returned by `POST /api/analysis/optimize` —\noptimization jobs are reported here with `jobType: \"optimize\"`, so\npolling an optimize job at this endpoint never returns 404.\n\n**Authentication:** Required (API key, read scope).\n","operationId":"getAiJobStatus","security":[{"BearerAuth":[]}],"x-x402-call-type":"ai.status","x-x402-price-usdc":"$0.0030","parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"},"description":"AI job ID"}],"responses":{"200":{"description":"AI job status","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending","running","completed","failed","cancelled"]},"jobType":{"type":"string","enum":["explain","troubleshoot","analyze_bottlenecks","optimize"]},"simulationId":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time","nullable":true},"error":{"type":"string","nullable":true}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}},"delete":{"tags":["AI Jobs"],"summary":"Delete a pending asynchronous AI job","description":"Cancels a pending or running AI analysis job. Idempotent — cancelling\nan already-terminal job returns 200 with the terminal state.\n\n**Authentication:** Required (API key, write scope).\n","operationId":"cancelAiJob","security":[{"BearerAuth":[]}],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"},"description":"AI job ID"}],"responses":{"200":{"description":"Cancellation result","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["cancelled","completed","failed"]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/ai-jobs/{jobId}/results":{"x-stability":"stable","get":{"tags":["AI Jobs"],"summary":"Fetch completed AI job results","description":"Retrieves the full AI output for a completed job. Returns HTTP 400 if\nthe job is not yet completed. Call `GET /ai-jobs/{jobId}` first to\nconfirm `status === \"completed\"`.\n\nResult shape varies by `jobType`:\n- `explain` → `{ explanation: string }`\n- `troubleshoot` → `{ guidance: string }`\n- `analyze_bottlenecks` → `{ analysis: string, doRecommendation: object|null }`\n- `optimize` → `{ suggestions: string[] }`\n\n**Authentication:** Requires an API key (`read` scope) or an x402\nmicro-payment (3 credits — $0.0030 USDC on Base). Pass\n`Authorization: Bearer <key>` for API key access, or include a signed\nx402 payment header with call type `ai.results` (no account required).\nx402 callers that paid to create the job carry a wallet-session JWT that\nis accepted here without a second payment round-trip.\nUse `GET /api/billing/x402/config` to discover the live price.\n","operationId":"getAiJobResults","security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.003"}},"x-x402-call-type":"ai.results","x-x402-price-usdc":"$0.0030","parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"},"description":"AI job ID"}],"responses":{"200":{"description":"AI job results","content":{"application/json":{"schema":{"type":"object","properties":{"explanation":{"type":"string","nullable":true},"guidance":{"type":"string","nullable":true},"analysis":{"type":"string","nullable":true},"doRecommendation":{"nullable":true,"oneOf":[{"type":"null"},{"type":"object","properties":{"suggestedDropletSize":{"type":"string","description":"Recommended Droplet size slug (e.g. \"s-2vcpu-4gb\")"},"estimatedHourlyRate":{"type":"number","description":"Hourly rate for the suggested Droplet size in USD"},"estimatedMonthlyCost":{"type":"number","description":"Estimated monthly cost for all Droplets in USD"},"currentHourlyCost":{"type":"number","description":"Current compute-tier hourly cost (compute resources only — see savingsBasis)"},"estimatedHourlySavings":{"type":"number","description":"Estimated hourly savings vs current compute-tier cost"},"estimatedMonthlySavings":{"type":"number","description":"Estimated monthly savings in USD"},"savingsPercent":{"type":"number","description":"Savings as a percentage of the compute-tier baseline (0–70)"},"savingsIsComputed":{"type":"boolean","description":"True when savings are derived from real cost data rather than a default estimate"},"savingsBasis":{"type":"string","enum":["compute-only"],"description":"Indicates that savings are computed against compute-tier costs only; non-compute resources (managed DB, cache, load balancer) are excluded from the baseline"},"reason":{"type":"string","enum":["right-sizing","migration","scale-out"],"description":"The primary recommendation category"},"numDroplets":{"type":"number","description":"Suggested number of Droplets"},"reversibilityTier":{"type":"string","enum":["instant","minutes","hours","days"],"description":"How hard this recommendation is to reverse — cross-provider migrations classify as \"days\""},"reversibilityReason":{"type":"string","description":"Human-readable explanation of the dominant reversibility factor"},"estimatedMinutesMin":{"type":"number","description":"Lower bound of estimated rollback time in minutes"},"estimatedMinutesMax":{"type":"number","description":"Upper bound of estimated rollback time in minutes"},"basis":{"type":"string","enum":["rule_based"],"description":"Always \"rule_based\" — reversibility is classified deterministically, not via LLM"}},"required":["suggestedDropletSize","estimatedHourlyRate","estimatedMonthlyCost","currentHourlyCost","estimatedHourlySavings","estimatedMonthlySavings","savingsPercent","savingsIsComputed","savingsBasis","reason","numDroplets","reversibilityTier","reversibilityReason","estimatedMinutesMin","estimatedMinutesMax","basis"],"additionalProperties":true}]},"suggestions":{"type":"array","items":{"type":"string"},"nullable":true},"recommendations":{"type":"array","nullable":true,"description":"Present only when the jobId belongs to an optimization job created\nvia `POST /api/analysis/optimize` — the structured recommendation\nobjects, in addition to the flattened `suggestions` strings.\n","items":{"type":"object","additionalProperties":true}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/X402PaymentRequired"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/simulations/{simulationId}/failures":{"x-stability":"stable","post":{"tags":["Simulations"],"summary":"Create a scheduled failure injection","description":"Creates a new failure injection that will be applied to the simulation\nduring the specified time window. The injection is applied immediately\nto the resource state (the affected resource's status changes) and an\nevent is recorded.\n\n**Failure types:**\n- `instance_kill` — PERMANENTLY removes a compute instance from the simulation (deleting the failure does not restore it)\n- `instance_down` — reversible single-node outage: marks a compute instance critical without removing it; deactivating (`isActive: false`) or deleting the failure restores the node\n- `az_outage` — simulates an availability zone outage\n- `database_overload` — modeled load-sensitive database CPU, bounded Aurora ACU, latency and failed requests; thresholds are assumptions, not AWS guarantees. Set top-level `severity` (minor/moderate/severe, default moderate) or `intensity` (0–1, mapped into those three bands), not both; the response echoes effective severity. Do not place controls in `parameters`.\n- `network_latency` — adds latency to all network resources\n\n**Precondition — already-critical targets:** an `instance_down` or\n`instance_kill` element whose `targetResourceId` refers to a resource\nthat is already in `critical` status is rejected with `400` and error\ncode `RESOURCE_NOT_FAILEABLE` — the same code and reason semantics as\n`POST /simulations/{simulationId}/inject-failure`. Consistent with the\nbatch contract, one rejected element aborts the whole request\n(all-or-nothing) and no failure injection is created or applied. In an\narray (batch) body the error pointer is index-prefixed\n(`/2/targetResourceId`) and the message is prefixed with `failures[2]: `.\n\nRequires `write` scope and ownership.\n","operationId":"createFailureInjection","x-x402-call-type":"simulation.inject_failure_create","x-x402-price-usdc":"$0.0030","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X POST https://www.cloudworldmodel.ai/api/simulations/sim-abc123/failures \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"name\": \"AZ Outage — us-east-1a\", \"type\": \"az_outage\", \"startTime\": 5, \"endTime\": 25, \"targetZone\": \"us-east-1a\", \"isActive\": true}'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.post(\n f\"{BASE_URL}/simulations/sim-abc123/failures\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\n \"name\": \"AZ Outage — us-east-1a\",\n \"type\": \"az_outage\",\n \"startTime\": 5,\n \"endTime\": 25,\n \"targetZone\": \"us-east-1a\",\n \"isActive\": True,\n },\n)\nresp.raise_for_status()\nfailure = resp.json()\nprint(\"Created failure injection:\", failure[\"id\"])\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/simulations/sim-abc123/failures`, {\n method: \"POST\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n name: \"AZ Outage — us-east-1a\",\n type: \"az_outage\",\n startTime: 5,\n endTime: 25,\n targetZone: \"us-east-1a\",\n isActive: true,\n }),\n});\nconst failure = await resp.json();\nconsole.log(\"Created failure injection:\", failure.id);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"parameters":[{"name":"simulationId","in":"path","required":true,"schema":{"type":"string"},"description":"Simulation UUID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","type","startTime"],"properties":{"name":{"type":"string","description":"Human-readable label for this failure","example":"Simulate AZ outage"},"type":{"type":"string","enum":["instance_kill","instance_down","az_outage","region_outage","permanent_data_loss","database_overload","network_latency","spot_interruption"],"description":"Type of failure to inject","example":"az_outage"},"startTime":{"type":"integer","description":"Simulation time step at which to begin the failure","example":10},"endTime":{"type":"integer","nullable":true,"description":"Simulation time step at which the failure resolves (null = permanent)","example":30},"targetResourceId":{"type":"string","nullable":true,"description":"ID of the specific resource to target (optional)"},"targetZone":{"type":"string","nullable":true,"description":"Availability zone to target for az_outage (optional)","example":"us-east-1a"},"targetRegion":{"type":"string","description":"Canonical region key. region_outage requires this and targetProvider. Permanent data loss requires a resource ID or provider-qualified region/zone scope; expiration never restores destroyed data."},"targetProvider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"]},"severity":{"type":"string","enum":["minor","moderate","severe"],"default":"moderate","description":"Top-level database_overload severity; mutually exclusive with intensity."},"intensity":{"type":"number","minimum":0,"maximum":1,"description":"Alternative database_overload control, mapped to minor below 0.34, moderate below 0.67, severe otherwise; mutually exclusive with severity."},"isActive":{"type":"boolean","default":true}}},"examples":{"azOutage":{"summary":"Simulate a brief AZ outage on us-east-1a (steps 5–25)","value":{"name":"AZ Outage — us-east-1a","type":"az_outage","startTime":5,"endTime":25,"targetZone":"us-east-1a","isActive":true}},"dbOverload":{"summary":"Permanent database overload starting at step 10","value":{"name":"DB Overload Injection","type":"database_overload","startTime":10,"endTime":null,"targetResourceId":"r3","severity":"moderate","isActive":true}}}}}},"responses":{"201":{"description":"Failure injection created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"simulationId":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"severity":{"type":"string","enum":["minor","moderate","severe"],"description":"Effective severity (including when intensity was supplied)."},"isActive":{"type":"boolean"},"startTime":{"type":"integer"},"endTime":{"type":"integer","nullable":true},"parameters":{"type":"object","additionalProperties":true,"properties":{"_databaseFailoverSucceeded":{"type":"boolean"},"_databaseFailoverReason":{"type":"string","enum":["primary_crash","database_overload"]}}}}}}}},"400":{"description":"Validation failed, or the target resource is not in a faileable\nstate. For `instance_down` / `instance_kill` with a\n`targetResourceId` that resolves to an already-critical resource,\nthe error code is `RESOURCE_NOT_FAILEABLE` — identical to the\nprecondition enforced by `POST /inject-failure`. Nothing is\npersisted or applied (all-or-nothing for batch bodies).\n","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"RESOURCE_NOT_FAILEABLE"},"pointer":{"type":"string","example":"/targetResourceId"},"message":{"type":"string","example":"Resource 'gpu-cluster' is not in a faileable state — current status is 'critical'"},"suggestion":{"type":"string"}}}}}}}},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/failures/{failureId}":{"x-stability":"stable","parameters":[{"name":"failureId","in":"path","required":true,"schema":{"type":"string"},"description":"Failure injection UUID"}],"patch":{"tags":["Simulations"],"summary":"Update a failure injection","description":"Partially updates a failure injection's fields (e.g. deactivate it by\nsetting `isActive: false`, or change the end time). Ownership of the\nparent simulation is enforced.\nRequires `write` scope.\n","operationId":"updateFailureInjection","x-x402-call-type":"simulation.inject_failure_update","x-x402-price-usdc":"$0.0030","x-codeSamples":[{"lang":"curl","label":"curl","source":"curl -X PATCH https://www.cloudworldmodel.ai/api/failures/fail-001 \\\n -H \"Authorization: Bearer $API_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"isActive\": false}'\n"},{"lang":"Python","label":"Python","source":"import requests\n\nBASE_URL = \"https://www.cloudworldmodel.ai/api\"\nAPI_KEY = \"your-api-key\"\n\nresp = requests.patch(\n f\"{BASE_URL}/failures/fail-001\",\n headers={\"Authorization\": f\"Bearer {API_KEY}\"},\n json={\"isActive\": False},\n)\nresp.raise_for_status()\nprint(\"Failure injection updated:\", resp.json())\n"},{"lang":"Node.js","label":"Node.js","source":"const BASE_URL = \"https://www.cloudworldmodel.ai/api\";\nconst API_KEY = \"your-api-key\";\n\nconst resp = await fetch(`${BASE_URL}/failures/fail-001`, {\n method: \"PATCH\",\n headers: {\n \"Authorization\": `Bearer ${API_KEY}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ isActive: false }),\n});\nconst failure = await resp.json();\nconsole.log(\"Failure injection updated:\", failure);\n"}],"security":[{"BearerAuth":[]},{"x402":[]},{"x402Solana":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"isActive":{"type":"boolean","description":"Set to false to deactivate the failure early"},"endTime":{"type":"integer","nullable":true}}},"examples":{"deactivate":{"summary":"Deactivate a failure injection early","value":{"isActive":false}},"extendWindow":{"summary":"Extend the failure window by 10 more steps","value":{"endTime":40}}}}}},"responses":{"200":{"description":"Updated failure injection","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"simulationId":{"type":"string"},"name":{"type":"string"},"isActive":{"type":"boolean"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Simulations"],"summary":"Delete a failure injection","description":"Permanently removes a failure injection. The failure will no longer be\napplied on subsequent simulation steps. Ownership of the parent simulation\nis enforced.\nRequires `write` scope.\n","operationId":"deleteFailureInjection","x-x402-call-type":"simulation.inject_failure_delete","x-x402-price-usdc":"$0.0030","security":[],"responses":{"204":{"description":"Failure injection deleted"},"401":{"$ref":"#/components/responses/ApiKeyUnauthorized"},"403":{"description":"Access denied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalError"}}}}},"components":{"schemas":{"CdnTraffic":{"type":"object","additionalProperties":false,"required":["cacheableFraction","cacheableMissDatabaseFraction","dynamicDatabaseFraction"],"description":"Optional network-CDN request mix, an assumption rather than observed traffic. cacheHitRate applies only to the cacheable fraction. Critical CDN status bypasses edge caching. Omission preserves legacy aggregate-cache semantics.\n","properties":{"cacheableFraction":{"type":"number","minimum":0,"maximum":1},"cacheableMissDatabaseFraction":{"type":"number","minimum":0,"maximum":1},"dynamicDatabaseFraction":{"type":"number","minimum":0,"maximum":1}}},"CdnFlow":{"type":"object","description":"Deterministic per-step modeled request flow, not live CloudFront measurements. Edge hits plus static/cacheable misses plus dynamic bypass equal offered requests. Origin demand is misses plus dynamic bypass; compute rows report routed service before autoscale reconciliation. Database rows report DB-bound demand and estimated connections. Static misses need not query a database.\n","required":["resourceId","basis","offeredRps","cacheableRps","dynamicRps","cacheHitRate","edgeHitRps","cacheMissRps","originRps","databaseRps","compute","databases"],"properties":{"resourceId":{"type":"string"},"basis":{"type":"string","enum":["scenario-assumption"]},"offeredRps":{"type":"number","minimum":0},"cacheableRps":{"type":"number","minimum":0},"dynamicRps":{"type":"number","minimum":0},"cacheHitRate":{"type":"number","minimum":0,"maximum":1},"edgeHitRps":{"type":"number","minimum":0},"cacheMissRps":{"type":"number","minimum":0},"originRps":{"type":"number","minimum":0},"databaseRps":{"type":"number","minimum":0},"compute":{"type":"array","items":{"type":"object","required":["resourceId","name","routedRps"],"properties":{"resourceId":{"type":"string"},"name":{"type":"string"},"routedRps":{"type":"number","minimum":0}}}},"databases":{"type":"array","items":{"type":"object","required":["resourceId","name","offeredRps","estimatedConnections","connectionLimit"],"properties":{"resourceId":{"type":"string"},"name":{"type":"string"},"offeredRps":{"type":"number","minimum":0},"estimatedConnections":{"type":"number","minimum":0},"connectionLimit":{"type":"number","minimum":0}}}}}},"TypicalAccuracyHoldout":{"type":"object","description":"Canonical AWS GET only. Frozen, independently measured typical-weight campaign scored against engine 1.2.3 pre-measurement predictions. beforeFit is preserved beside the independent afterFit 300 RPS holdout. Never combine with the lean overallScore.\n","required":["appWeight","source","campaignId","measurementCommit","reportUrl","summaryUrl","scoresUrl","predictionsUrl","scope","beforeFit","afterFit","dayAndRegionHoldouts","extrapolatedDiagnostic","context"],"properties":{"appWeight":{"type":"string","const":"typical"},"source":{"type":"string","const":"owned_cwm_bench"},"campaignId":{"type":"string","example":"typical-v1-20260927c"},"measurementCommit":{"type":"string"},"reportUrl":{"type":"string","format":"uri"},"summaryUrl":{"type":"string","format":"uri"},"scoresUrl":{"type":"string","format":"uri"},"predictionsUrl":{"type":"string","format":"uri"},"scope":{"type":"object","required":["campaigns","days","region","attemptsPerRung","engineVersion","seed","predictionStep","predictionsFrozenBeforeMeasurement","measuredCost","topology"],"properties":{"campaigns":{"type":"integer","example":1},"days":{"type":"integer","example":1},"region":{"type":"string","example":"us-east-2"},"attemptsPerRung":{"type":"integer","example":1},"engineVersion":{"type":"string","example":"1.2.3"},"seed":{"type":"integer","example":20240601},"predictionStep":{"type":"integer","example":6},"predictionsFrozenBeforeMeasurement":{"type":"boolean","const":true},"measuredCost":{"type":"boolean","const":false},"topology":{"type":"string"}}},"beforeFit":{"type":"object","required":["targetRps","split","score","scoreWithoutCost","metrics","unweightedP99","throughputBasis","costBasis","steadyWindowAlternative"],"properties":{"targetRps":{"type":"integer","example":300},"split":{"type":"string","const":"holdout"},"score":{"type":"number","example":24.3},"scoreWithoutCost":{"type":"number","example":27},"metrics":{"type":"array","description":"Score-source precision for measured values; displayed rounding and weighted contributions do not replace the frozen total.","items":{"type":"object","required":["metric","label","unit","predicted","measured","measuredSource","score","weight","weightedContribution"],"properties":{"metric":{"type":"string","enum":["p50","p95","appCpu","throughput","errorRate","cost"]},"label":{"type":"string"},"unit":{"type":"string","enum":["ms","%","RPS","USD/hour"]},"predicted":{"type":"number"},"measured":{"type":"number"},"measuredSource":{"type":"string","enum":["owned_measurement","list_price_reference"]},"score":{"type":"number"},"weight":{"type":"number"},"weightedContribution":{"type":"number"}}}},"unweightedP99":{"type":"object","required":["predicted","measured","unit","score"],"properties":{"predicted":{"type":"number"},"measured":{"type":"number"},"unit":{"type":"string","const":"ms"},"score":{"type":"number"}}},"throughputBasis":{"type":"string","description":"Whole-run measured goodput versus point-rate prediction."},"costBasis":{"type":"string","description":"Cost was not measured; reference is list price, not a bill."},"steadyWindowAlternative":{"type":"object","description":"Optional diagnostic, not the scored holdout.","required":["scored","throughputRps","throughputScore","totalScore"],"properties":{"scored":{"type":"boolean","const":false},"throughputRps":{"type":"number","example":300},"throughputScore":{"type":"number","example":99.7},"totalScore":{"type":"number","example":26.3}}}}},"afterFit":{"type":"object","description":"Frozen step-6 owned typical 300 RPS prediction scored with the same measured references and page methodology. The fit uses only 20/100/200 RPS; 300 is an independent holdout and 500 diagnostic only.\n","required":["targetRps","split","engineVersion","calibrationId","fitRungs","score","scoreWithoutCost","metrics","throughputBasis","costBasis"],"properties":{"targetRps":{"type":"integer","const":300},"split":{"type":"string","const":"independent holdout"},"engineVersion":{"type":"string"},"calibrationId":{"type":"string","description":"Campaign typical-v1-20260927c and full measurement commit 6aa574d7ff9d3080b88b221bcd59f7d218ae37f0."},"fitRungs":{"type":"array","items":{"type":"string","enum":["typical-fit-20","typical-fit-100","typical-fit-200"]}},"score":{"type":"number"},"scoreWithoutCost":{"type":"number"},"metrics":{"$ref":"#/components/schemas/TypicalAccuracyHoldout/properties/beforeFit/properties/metrics"},"throughputBasis":{"type":"string"},"costBasis":{"type":"string","description":"List price, not measured; post-fit scored prediction excludes egress unlike the historical before-fit prediction."}}},"dayAndRegionHoldouts":{"type":"object","description":"Supplemental observed campaign results; these do not replace or change the original scored afterFit holdout.","required":["campaignId","reportUrl","comparisonBasis","original","observed","repeatability100Rps"],"properties":{"campaignId":{"type":"string","const":"typical-holdouts-v1-20261006"},"reportUrl":{"type":"string","format":"uri"},"comparisonBasis":{"type":"string","const":"page cost basis"},"original":{"type":"object","required":["campaignId","targetRps","score","scoreWithoutCost","p95Ms"],"properties":{"campaignId":{"type":"string","const":"typical-v1-20260927c"},"targetRps":{"type":"integer","const":300},"score":{"type":"number","example":84.2},"scoreWithoutCost":{"type":"number","example":82.4},"p95Ms":{"type":"number","example":26.9}}},"observed":{"type":"array","minItems":2,"maxItems":2,"description":"Later-day and second-region observed results, kept separate from the original afterFit prediction.","items":{"type":"object","required":["id","label","region","targetRps","score","scoreWithoutCost","p95Ms"],"properties":{"id":{"type":"string","enum":["later-day","second-region"]},"label":{"type":"string"},"region":{"type":"string","enum":["us-east-2","us-west-2"]},"targetRps":{"type":"integer","const":300},"score":{"type":"number"},"scoreWithoutCost":{"type":"number"},"p95Ms":{"type":"number"}}}},"repeatability100Rps":{"type":"object","required":["targetRps","scores"],"properties":{"targetRps":{"type":"integer","const":100},"scores":{"type":"array","minItems":2,"maxItems":2,"items":{"type":"number"}}}}}},"extrapolatedDiagnostic":{"type":"object","description":"Compatibility property name for the 500 RPS owned-fit prediction. The fit uses 20, 100, and 200 RPS, is validated at the 300 RPS holdout, and is compared with a separate measured 500 RPS diagnostic run. This comparison is excluded from headline accuracy and is not a validated saturation finding. Neither 300 nor 500 RPS is fit data.\n","required":["targetRps","label","engineVersion","calibrationId","fitRungs","score","scoreWithoutCost","metrics","throughputBasis","costBasis"],"properties":{"targetRps":{"type":"integer","const":500},"label":{"type":"string","const":"diagnostic extrapolation beyond the calibrated and validated range (fit at 20, 100, and 200 RPS; validated at 300 RPS), compared with a measured 500 RPS diagnostic run; excluded from headline accuracy and not a validated saturation finding"},"engineVersion":{"type":"string"},"calibrationId":{"type":"string","example":"aws-crud/typical:typical-v1-20260927c:6aa574d7ff9d3080b88b221bcd59f7d218ae37f0"},"fitRungs":{"type":"array","minItems":3,"maxItems":3,"description":"Existing owned typical fit data only; 500 RPS is not a fit rung.","items":{"type":"string","enum":["typical-fit-20","typical-fit-100","typical-fit-200"]}},"score":{"type":"number"},"scoreWithoutCost":{"type":"number"},"metrics":{"$ref":"#/components/schemas/TypicalAccuracyHoldout/properties/beforeFit/properties/metrics"},"throughputBasis":{"type":"string"},"costBasis":{"type":"string","description":"No-egress public list-price reference, not a measured bill."}}},"context":{"type":"array","description":"Fit rungs are not independent holdouts; saturation is diagnostic only.","items":{"type":"object","required":["rung","role","score","scoreWithoutCost"],"properties":{"rung":{"type":"string","enum":["fit-20","fit-100","fit-200","saturation-500"]},"role":{"type":"string","enum":["fit_data_not_holdout","diagnostic_only"]},"score":{"type":"number"},"scoreWithoutCost":{"type":"number"}}}}}},"AccuracyCanonicalProviderBasis":{"type":"object","description":"Canonical provider scoring basis, classified by the same rule as the selected provider's scoringBasis.","required":["type","costReference","label"],"properties":{"label":{"type":"string","description":"Display label used by the provider comparison table."},"type":{"type":"string","enum":["measured_cwm_bench","documentation_reference","modeling_assumption"]},"costReference":{"type":"string","enum":["public_price_list","documentation_reference","modeling_assumption"]}}},"X402SettlementResolution":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["confirmed","pending","failed"],"description":"Current facilitator settlement state.","example":"confirmed"},"facilitatorStatus":{"type":"string","description":"Raw facilitator status when one was supplied.","example":"settled"},"transactionHash":{"type":"string","description":"On-chain transaction hash when settlement is confirmed.","example":"0xabc123..."},"transaction":{"type":"string","description":"Facilitator transaction identifier when supplied."},"network":{"type":"string","description":"Settlement network reported by the facilitator.","example":"base"},"payer":{"type":"string","description":"Payer address reported by the facilitator."},"error":{"type":"string","description":"Facilitator-declared settlement error, when present."},"httpStatus":{"type":"integer","description":"Facilitator HTTP status when the settlement failed.","example":402},"responseData":{"type":"object","description":"Redacted facilitator response data, when available.","additionalProperties":true}}},"RecoveryProgress":{"type":"object","description":"Read-only recovery progress derived from the engine's current resource state. Poll `GET /simulations/{simulationId}` or the next step response until `state` is `healthy`; `stepsToHealthy` is only a lower-bound estimate.\n","required":["state","parkWindow","cooldown"],"properties":{"state":{"type":"string","enum":["parked","blocked","cooling_down","healthy"],"description":"Current recovery stage; blocked means an active failure prevents cooldown."},"parkWindow":{"type":"object","description":"Progress through the forced-unavailability window before cooldown recovery can advance.","required":["totalSteps","completedSteps","remainingSteps"],"properties":{"totalSteps":{"type":"integer","minimum":0,"description":"Total forced-unavailability steps for an explicitly failed compute/Kubernetes resource; zero when not applicable."},"completedSteps":{"type":"integer","minimum":0,"description":"Park-window steps completed so far."},"remainingSteps":{"type":"integer","minimum":0,"description":"Park-window steps remaining before cooldown can advance."}}},"cooldown":{"type":"object","description":"Progress through the current status-demotion cooldown window.","required":["target","completedSteps","requiredSteps","remainingSteps"],"properties":{"target":{"type":"string","nullable":true,"enum":["warning","healthy",null],"description":"Status targeted by the current cooldown, or null when already healthy."},"completedSteps":{"type":"integer","minimum":0,"description":"Consecutive qualifying simulation steps already completed toward the cooldown target."},"requiredSteps":{"type":"integer","minimum":0,"description":"Consecutive qualifying steps required to reach target."},"remainingSteps":{"type":"integer","minimum":0,"description":"Qualifying cooldown steps still needed."}}}}},"OpenShiftAccountingContract":{"type":"object","description":"Machine-readable ARO accounting contract. The published unit rate and topology-derived platform total are distinct quantities; worker and estimated control-plane charges remain separate.","required":["platform","workers","controlPlane","unknownSku"],"properties":{"platform":{"type":"object","required":["included","chargeType","pricingStatus","basis","platformFeePerHour","publishedUnitRatePerHour","billingUnit","billingUnitVcpu","workerTopology","derivedPlatformFeePerHour","pricingSource","effectiveDate","referenceRegion","pricingArithmetic"],"properties":{"included":{"type":"boolean"},"chargeType":{"type":"string","enum":["platform"]},"pricingStatus":{"type":"string","enum":["official","estimated"]},"basis":{"type":"string","enum":["published-hourly-equivalent","subscription-not-included"]},"platformFeePerHour":{"type":"number","description":"Backward-compatible alias of the published one-unit rate."},"publishedUnitRatePerHour":{"type":"number","description":"Microsoft-published East US D4s v3 hourly equivalent; $0.171 for one 4-vCPU unit."},"billingUnit":{"type":"string","enum":["4-vcpu-hour"]},"billingUnitVcpu":{"type":"number","enum":[4]},"workerTopology":{"type":"object","required":["workerCount","effectiveWorkerVcpu","nodePools"],"properties":{"workerCount":{"type":"integer"},"effectiveWorkerVcpu":{"type":"number"},"nodePools":{"type":"array","items":{"type":"object","required":["name","workerCount","workerVcpu","effectiveWorkerVcpu"],"properties":{"name":{"type":"string"},"workerCount":{"type":"integer"},"workerVcpu":{"type":"number"},"effectiveWorkerVcpu":{"type":"number"}}}}}},"derivedPlatformFeePerHour":{"type":"number","description":"Topology total: publishedUnitRatePerHour × effectiveWorkerVcpu ÷ billingUnitVcpu. Two 2-vCPU workers derive $0.171/hour; three derive $0.2565/hour."},"pricingSource":{"type":"string"},"effectiveDate":{"type":"string"},"referenceRegion":{"type":"string"},"pricingArithmetic":{"type":"string"}}},"workers":{"type":"object","required":["included","chargeType","basis"],"properties":{"included":{"type":"boolean","enum":[true]},"chargeType":{"type":"string","enum":["worker"]},"basis":{"type":"string","enum":["backing-provider-list-rates"]}}},"controlPlane":{"type":"object","required":["included","chargeType","pricingStatus","basis"],"properties":{"included":{"type":"boolean","enum":[true]},"chargeType":{"type":"string","enum":["control-plane"]},"pricingStatus":{"type":"string","enum":["official","estimated"]},"basis":{"type":"string","enum":["backing-provider-kubernetes-control-plane","openshift-specific-cluster-fee"]}}},"unknownSku":{"type":"object","required":["included","chargeType","basis"],"properties":{"included":{"type":"boolean","enum":[false]},"chargeType":{"type":"string","enum":["unknown-gpu-sku"]},"basis":{"type":"string","enum":["explicit-cost-exclusion"]}}}}},"OpenShiftAccuracyScenario":{"type":"object","required":["id","offering","provider","region","sourceDate","architecture","expectedCost","expectedLatency","componentFidelity","scorecard","sources"],"properties":{"id":{"type":"string"},"offering":{"type":"string","enum":["rosa-hcp","rosa-classic","aro","openshift-dedicated","self-managed"]},"provider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"]},"region":{"type":"string"},"sourceDate":{"type":"string"},"architecture":{"type":"string"},"expectedCost":{"type":"object","required":["platformFeePerHour","workerCharges","behavior"],"properties":{"platformFeePerHour":{"type":"number"},"workerCharges":{"type":"string"},"behavior":{"type":"string"}}},"expectedLatency":{"type":"object","required":["platformOverheadMs","behavior"],"properties":{"platformOverheadMs":{"type":"number"},"behavior":{"type":"string"}}},"componentFidelity":{"type":"object","required":["cost","latency","cpu","throughput","errorRate"],"properties":{"cost":{"type":"string","enum":["estimated","extrapolated","measured"]},"latency":{"type":"string","enum":["estimated","extrapolated","measured"]},"cpu":{"type":"string","enum":["estimated","extrapolated","measured"]},"throughput":{"type":"string","enum":["estimated","extrapolated","measured"]},"errorRate":{"type":"string","enum":["estimated","extrapolated","measured"]}}},"scorecard":{"type":"object","required":["included","reason"],"properties":{"included":{"type":"boolean"},"reason":{"type":"string"}}},"sources":{"type":"array","items":{"type":"object","required":["metric","citation","url"],"properties":{"metric":{"type":"string"},"citation":{"type":"string"},"url":{"type":"string","format":"uri"}}}},"loadTest":{"type":"object","required":["tool","workload","topology","region","runs","referencePoints"],"properties":{"tool":{"type":"string"},"workload":{"type":"string"},"topology":{"type":"object","required":["computeCount","computeType","dbType"],"properties":{"computeCount":{"type":"integer"},"computeType":{"type":"string"},"dbType":{"type":"string"}}},"region":{"type":"string"},"runs":{"type":"array","items":{"type":"object","required":["id","sourceDate","sourceUrl","points"],"properties":{"id":{"type":"string"},"sourceDate":{"type":"string"},"sourceUrl":{"type":"string","format":"uri"},"points":{"type":"array","items":{"$ref":"#/components/schemas/OpenShiftLoadTestPoint"}}}}},"referencePoints":{"type":"array","items":{"$ref":"#/components/schemas/OpenShiftLoadTestPoint"}}}},"measuredCost":{"type":"object","required":["totalCostPerHour","platformFeePerHour","region","topology","sourceDate","citation","url"],"properties":{"totalCostPerHour":{"type":"number"},"platformFeePerHour":{"type":"number"},"region":{"type":"string"},"topology":{"type":"object","required":["computeCount","computeType","dbType"],"properties":{"computeCount":{"type":"integer"},"computeType":{"type":"string"},"dbType":{"type":"string"}}},"sourceDate":{"type":"string"},"citation":{"type":"string"},"url":{"type":"string","format":"uri"}}}}},"OpenShiftLoadTestPoint":{"type":"object","required":["trafficRps","latencyP50Ms","latencyP95Ms","cpuPct","throughputRps","errorRatePct"],"properties":{"trafficRps":{"type":"number"},"latencyP50Ms":{"type":"number"},"latencyP95Ms":{"type":"number"},"cpuPct":{"type":"number"},"throughputRps":{"type":"number"},"errorRatePct":{"type":"number"}}},"HybridAgreement":{"type":"object","description":"Model agreement — how closely the ML model and rule engine agree with\neach other on a hybrid step (distinct from ML confidence, which is the\nmodel's belief in its own prediction). Derived from the largest\nnormalised deviation among cost, latency P95, latency P99, and error rate, each\nnormalised against its safety-bound threshold (maxCostDeviation,\nmaxLatencyDeviation, maxErrorRateDeviation), so one dimension way off\ncannot be washed out by agreement on the others. Sits alongside the\nper-field `comparisons` array so component signals can be inspected\ndirectly. Absent on decisions persisted before this field existed.\n\n**Safety-fallback participation:** both latencyP95 and latencyP99\ndeviations are checked against `maxLatencyDeviation` in\n`checkSafetyBounds`. A P99 disagreement that exceeds the threshold\ntriggers the rule-based fallback in the same way a P95 violation does.\nConsumers should therefore treat `largestDisagreement: \"latencyP99\"`\nas a first-class fallback signal, not merely an informational label.\n","required":["score","level","largestDisagreement"],"properties":{"score":{"type":"number","minimum":0,"maximum":1,"description":"0–1, where 1.0 = strong agreement.","example":0.82},"level":{"type":"string","enum":["high","moderate","low"],"description":"score ≥ 0.75 → high, ≥ 0.4 → moderate, else low.","example":"high"},"largestDisagreement":{"type":"string","enum":["cost","latencyP95","latencyP99","errorRate"],"description":"The dimension with the largest normalised rule/ML deviation.","example":"cost"},"normalizationBasis":{"type":"string","enum":["absolute_safety_bounds"],"description":"The agreement score is calibrated against the configured safety-bound\nthresholds, not against relative sensitivity of each metric. Latency\n(P95/P99) and error-rate deviations are absolute differences (ms and\npercentage points) divided by maxLatencyDeviation /\nmaxErrorRateDeviation; cost deviation is relative to the rule-based\ncost, divided by maxCostDeviation. A deviation that is large in\nrelative terms but small against its bound (e.g. error rate\n0.1% → 0.5% against a 5 pp bound) can therefore still score as\nhigh agreement.\n","example":"absolute_safety_bounds"}}},"HybridPredictionComparison":{"type":"object","description":"Per-field rule vs ML vs blended comparison for one hybrid step.","properties":{"field":{"type":"string","example":"costPerHour"},"ruleValue":{"type":"number","example":0.84},"mlValue":{"type":"number","example":1.13},"hybridValue":{"type":"number","example":0.98},"deviation":{"type":"number","description":"Absolute rule/ML difference in the field's native units.","example":0.29},"source":{"type":"string","enum":["rules","ml","hybrid_blend","rules_fallback"]},"confidence":{"type":"number","minimum":0,"maximum":1},"reasoning":{"type":"string"}}},"HybridDecision":{"type":"object","description":"One per-step hybrid referee decision.","properties":{"timestamp":{"type":"number","example":12},"ruleBasedMetrics":{"type":"object","description":"Metrics produced by the deterministic rule engine for this step. Percentiles are null when workload-specific latency evidence is unavailable.","properties":{"latencyP50":{"type":"number","nullable":true},"latencyP95":{"type":"number","nullable":true},"latencyP99":{"type":"number","nullable":true}}},"mlPrediction":{"type":"object","description":"Full ML prediction. When latency evidence is unavailable, latency reasoning is replaced with an explicit limitation, latency bottlenecks and latency-jitter quirks are omitted, and latency-bearing event impact text is withheld.","properties":{"metrics":{"type":"object","properties":{"latencyP50":{"type":"number","nullable":true,"description":"Null when workload-specific latency evidence is unavailable."},"latencyP95":{"type":"number","nullable":true,"description":"Null when workload-specific latency evidence is unavailable."},"latencyP99":{"type":"number","nullable":true,"description":"Null when workload-specific latency evidence is unavailable."}}},"overallConfidence":{"type":"number","example":0.78},"reasoning":{"type":"string","description":"Replaced with the latency-unavailable explanation when workload-specific latency evidence is unavailable."},"bottlenecks":{"type":"array","items":{"type":"object"}},"eventLikelihoods":{"type":"array","items":{"type":"object"}}}},"hybridMetrics":{"type":"object","description":"Final blended (or fallback) metrics used for this step. Percentiles are null when workload-specific latency evidence is unavailable.","properties":{"latencyP50":{"type":"number","nullable":true},"latencyP95":{"type":"number","nullable":true},"latencyP99":{"type":"number","nullable":true}}},"latencyAvailability":{"$ref":"#/components/schemas/PredictionLatencyAvailability"},"comparisons":{"type":"array","description":"Latency percentile comparisons are omitted when workload-specific latency evidence is unavailable.","items":{"$ref":"#/components/schemas/HybridPredictionComparison"}},"agreement":{"$ref":"#/components/schemas/HybridAgreement","description":"Omitted when latency evidence is unavailable because the agreement score includes latency comparisons."},"blendingApplied":{"type":"boolean","nullable":true,"description":"Null when workload-specific latency evidence is unavailable because blending may depend on withheld percentile comparisons."},"fallbackUsed":{"type":"boolean","nullable":true,"description":"Null when workload-specific latency evidence is unavailable because fallback may depend on withheld percentile safety checks."},"safetyBoundsViolated":{"type":"array","description":"Latency-derived safety violations are omitted when workload-specific latency evidence is unavailable; unrelated violations remain.","items":{"type":"string"}},"explanation":{"type":"string","description":"Replaced with an explicit latency-unavailable explanation when workload-specific latency evidence is unavailable."},"behaviorMismatch":{"type":"object","description":"Present when `agreement.level === \"low\"` AND at least one Kubernetes resource in the simulation carries GPU/inference characteristics (`accelerator`, `tokensPerRequest`, `inputTokensPerRequest`) without `inferenceMode: true`. Indicates that the rule engine is using `generic-kubernetes/load-saturation` for a resource that appears to be a GPU inference workload. This is a factual configuration note, not a claim about the ML predictor's internal model — enabling `inferenceMode` switches the rule engine to the `gpu-inference/ttft-decode` latency path AND activates the ML service's own independently calibrated TTFT/decode estimator (`gpu-inference/ttft-decode-ml`), so both models operate on the same workload class and the agreement score may change. Absent when agreement is not \"low\", no such misconfiguration is detected, or workload-specific latency evidence is unavailable.","required":["detected","cause","affectedResources"],"properties":{"detected":{"type":"boolean","description":"True when a rule-engine path mismatch was detected on this step.","example":true},"cause":{"type":"string","description":"Human-readable explanation of the detected rule-engine configuration issue. Names the detected GPU characteristics and states what to set to activate the GPU inference latency path in the rule engine.","example":"Rule engine used generic-kubernetes/load-saturation for resource(s) that carry GPU/inference characteristics (accelerator, tokensPerRequest, or inputTokensPerRequest) but have inferenceMode: false. If this simulation is intended to model GPU inference workloads, setting characteristics.inferenceMode: true would activate the gpu-inference/ttft-decode latency model in the rule engine, which may change the rule/ML agreement score."},"affectedResources":{"type":"array","description":"IDs of Kubernetes resources that triggered the mismatch.","items":{"type":"string"}}}},"rulesBehaviorModel":{"type":"string","description":"Provenance: the latency path the rules engine used for this step's dominant workload class (e.g. `gpu-inference/ttft-decode` for inferenceMode Kubernetes GPU clusters, `generic-kubernetes/load-saturation` otherwise).","example":"gpu-inference/ttft-decode"},"mlBehaviorModel":{"type":"string","description":"Provenance: the path/label the ML service used for this prediction (e.g. `gpu-inference/ttft-decode-ml` when the ML service applied its independently calibrated TTFT/decode token-economics estimator, or `generic/nonlinear-heuristics` for the default heuristic path).","example":"gpu-inference/ttft-decode-ml"},"behaviorAlignment":{"type":"string","enum":["aligned","misaligned"],"description":"`aligned` when the rules engine and ML service operated on the same workload class for this step; `misaligned` when they diverged (e.g. rules used the GPU-inference TTFT model but ML fell back to generic compute saturation). Lets a disagreement be diagnosed as \"same model, different numbers\" vs \"different models entirely\".","example":"aligned"},"modelingNote":{"type":"string","description":"Optional provenance note. Present on GPU inference steps (`rulesBehaviorModel: gpu-inference/ttft-decode`) to remind API consumers that TTFT and decode-throughput figures reported in this decision are produced by the CWM simulation model and are not externally measured hardware benchmarks. Absent on non-inference steps.","example":"TTFT and decode-throughput figures are CWM simulation-model estimates, not externally measured hardware benchmarks."}}},"HybridSimulationResult":{"type":"object","description":"Cumulative hybrid decision history and summary statistics for a simulation.","properties":{"simulationId":{"type":"string"},"config":{"type":"object","description":"Hybrid engine configuration used"},"decisions":{"type":"array","description":"Per-step hybrid decisions","items":{"$ref":"#/components/schemas/HybridDecision"}},"summary":{"type":"object","description":"Aggregate statistics across all steps","properties":{"totalSteps":{"type":"integer","example":50},"mlConfidenceAvg":{"type":"number","example":0.74},"blendedSteps":{"type":"integer","example":38},"fallbackSteps":{"type":"integer","example":12},"bottlenecksDetected":{"type":"integer","example":5},"eventsDetected":{"type":"integer","example":3}}},"createdAt":{"type":"string","format":"date-time"}}},"EksSpotInterruptionTelemetry":{"type":"object","description":"Per-step bounded AWS EKS Spot interruption migration state. The optional migrationEvaluation is the authoritative seeded characteristics.eksSpotInterruption deadline evidence; it is additive so legacy persisted interruption records can remain evaluation-free.","required":["resourceId","name","phase","interruptionNotice","triggerAtStep","noticeWindowSeconds","elapsedNoticeSeconds","remainingDeadlineSeconds","affectedWorkloadCount","pendingWorkloadCount","pullingWorkloadCount","startingWorkloadCount","readyWorkloadCount","missedDeadlineWorkloadCount","drainProgress","imagePullProgress","startupProgress","unavailableNodeCount","replacementNodeCount","workloads"],"properties":{"resourceId":{"type":"string"},"name":{"type":"string"},"phase":{"type":"string","enum":["armed","notice","terminated","complete"]},"interruptionNotice":{"type":"boolean"},"triggerAtStep":{"type":"integer","minimum":0},"noticeWindowSeconds":{"type":"integer","const":120},"elapsedNoticeSeconds":{"type":"number","minimum":0},"remainingDeadlineSeconds":{"type":"number","minimum":0},"affectedWorkloadCount":{"type":"integer","minimum":0},"pendingWorkloadCount":{"type":"integer","minimum":0},"pullingWorkloadCount":{"type":"integer","minimum":0},"startingWorkloadCount":{"type":"integer","minimum":0},"readyWorkloadCount":{"type":"integer","minimum":0},"missedDeadlineWorkloadCount":{"type":"integer","minimum":0},"drainProgress":{"type":"number","minimum":0,"maximum":1},"imagePullProgress":{"type":"number","minimum":0,"maximum":1},"startupProgress":{"type":"number","minimum":0,"maximum":1},"unavailableNodeCount":{"type":"integer","minimum":0},"replacementNodeCount":{"type":"integer","minimum":0},"migrationEvaluation":{"$ref":"#/components/schemas/EksSpotMigrationEvaluation"},"checkpointEvidence":{"$ref":"#/components/schemas/EksSpotInterruptionCheckpointEvidence"},"workloads":{"type":"array","items":{"type":"object","required":["id","state","remainingSeconds"],"properties":{"id":{"type":"string"},"state":{"type":"string","enum":["pending","pulling","starting","ready","missed_deadline"]},"node":{"type":"string","enum":["interrupted","surviving","replacement"]},"remainingSeconds":{"type":"number","minimum":0},"missedDeadline":{"type":"boolean","description":"Historical deadline miss marker; live state may later become ready."}}}}}},"EksSpotInterruptionCheckpointEvidence":{"type":"object","additionalProperties":false,"description":"Persisted seeded-EKS per-metric checkpoint evidence. engineInputStepIndex is the engine input-step timestamp, not seconds; simulationSeconds is derived using the recorded tick. Traffic values remain in the outer metric, and the provenance fields name those outer metric paths.","required":["engineInputStepIndex","simulationSeconds","tickDurationSeconds","pointRateSemantics","intervalAttribution","fieldProvenance","trafficFieldProvenance"],"properties":{"engineInputStepIndex":{"type":"integer","minimum":0},"simulationSeconds":{"type":"number","minimum":0},"tickDurationSeconds":{"type":"number","exclusiveMinimum":0},"pointRateSemantics":{"type":"string","enum":["post_step_point_rate"]},"intervalAttribution":{"type":"null","description":"No interval attribution is persisted for this post-step point rate."},"fieldProvenance":{"type":"object","additionalProperties":false,"required":["engineInputStepIndex","simulationSeconds","tickDurationSeconds","pointRateSemantics","intervalAttribution"],"properties":{"engineInputStepIndex":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"simulationSeconds":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"tickDurationSeconds":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"pointRateSemantics":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"intervalAttribution":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"}}},"trafficFieldProvenance":{"type":"object","additionalProperties":false,"required":["throughputRps","offeredRps","errorRatePercent","latencyP50Ms"],"properties":{"throughputRps":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"offeredRps":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"errorRatePercent":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"latencyP50Ms":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"}}}}},"EksSpotEvaluationFieldProvenance":{"type":"object","description":"Field-level source classification for seeded migration evaluation evidence. recorded is direct model/runtime evidence, derived identifies source fields, and unavailable includes an explicit reason.","required":["kind"],"properties":{"kind":{"type":"string","enum":["recorded","derived","unavailable"]},"sourceFields":{"type":"array","items":{"type":"string"},"description":"Present for a derived value and names its recorded source fields."},"reason":{"type":"string","description":"Present for unavailable evidence and explains the missing runtime evidence."}}},"EksSpotMigrationEvaluationReason":{"type":"object","description":"Evidence-backed contributor to a seeded EKS Spot deadline result.","required":["code","workloadCount","pendingWorkloadCount","pullingWorkloadCount","startingWorkloadCount","residualPullSeconds","residualStartupSeconds","schedulingCapacity","interruptionHandling","provenance"],"properties":{"code":{"type":"string","enum":["no_reschedule","scheduling_capacity_exhausted","image_pull_incomplete","startup_incomplete","unclassified_runtime_work_remaining"],"description":"Stable reason code based on recorded residual pull/startup/scheduling work."},"workloadCount":{"type":"integer","minimum":0},"pendingWorkloadCount":{"type":"integer","minimum":0},"pullingWorkloadCount":{"type":"integer","minimum":0},"startingWorkloadCount":{"type":"integer","minimum":0},"residualPullSeconds":{"type":"number","minimum":0},"residualStartupSeconds":{"type":"number","minimum":0},"schedulingCapacity":{"type":"integer","minimum":0},"interruptionHandling":{"type":"string","enum":["reschedule","drain-only","fail-fast"]},"provenance":{"type":"string","const":"recorded"}}},"EksSpotMigrationEvaluation":{"type":"object","description":"Additive immutable deadline evaluation for one seeded characteristics.eksSpotInterruption lifecycle. It is independent of the resource phase and later service health. All times use the simulation clock, never HTTP retrieval or event wall-clock time. migrationDurationSeconds is derived only as allAffectedWorkloadsReadyAtSimulationSeconds minus interruptionNoticeAtSimulationSeconds when both recorded milestones exist; otherwise it is null.","required":["status","interruptionNoticeAtSimulationSeconds","deadlineAtSimulationSeconds","migrationStartedAtSimulationSeconds","allAffectedWorkloadsReadyAtSimulationSeconds","migrationDurationSeconds","deadlineSeconds","deadlineMet","affectedWorkloadCount","readyWorkloadCountAtDeadline","missedDeadlineWorkloadCount","evaluatedAtSimulationSeconds","limitingReasons","fieldProvenance"],"properties":{"status":{"type":"string","enum":["not_started","in_progress","completed"]},"deadlineSeconds":{"type":"integer","const":120},"deadlineMet":{"type":"boolean","nullable":true,"description":"Null until evaluation completes; frozen once evaluated."},"interruptionNoticeAtSimulationSeconds":{"type":"number","nullable":true},"deadlineAtSimulationSeconds":{"type":"number","nullable":true},"migrationStartedAtSimulationSeconds":{"type":"number","nullable":true},"allAffectedWorkloadsReadyAtSimulationSeconds":{"type":"number","nullable":true},"migrationDurationSeconds":{"type":"number","nullable":true,"minimum":0,"description":"Derived notice-to-all-ready duration; null when all-ready was not recorded."},"affectedWorkloadCount":{"type":"integer","nullable":true,"minimum":0},"readyWorkloadCountAtDeadline":{"type":"integer","nullable":true,"minimum":0},"missedDeadlineWorkloadCount":{"type":"integer","nullable":true,"minimum":0},"evaluatedAtSimulationSeconds":{"type":"number","nullable":true},"limitingReasons":{"type":"array","items":{"$ref":"#/components/schemas/EksSpotMigrationEvaluationReason"}},"fieldProvenance":{"type":"object","required":["status","interruptionNoticeAtSimulationSeconds","deadlineAtSimulationSeconds","migrationStartedAtSimulationSeconds","allAffectedWorkloadsReadyAtSimulationSeconds","migrationDurationSeconds","deadlineSeconds","deadlineMet","affectedWorkloadCount","readyWorkloadCountAtDeadline","missedDeadlineWorkloadCount","evaluatedAtSimulationSeconds","limitingReasons"],"properties":{"status":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"interruptionNoticeAtSimulationSeconds":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"deadlineAtSimulationSeconds":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"migrationStartedAtSimulationSeconds":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"allAffectedWorkloadsReadyAtSimulationSeconds":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"migrationDurationSeconds":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"deadlineSeconds":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"deadlineMet":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"affectedWorkloadCount":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"readyWorkloadCountAtDeadline":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"missedDeadlineWorkloadCount":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"evaluatedAtSimulationSeconds":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"limitingReasons":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"}},"additionalProperties":{"$ref":"#/components/schemas/EksSpotEvaluationFieldProvenance"},"description":"Field-name keyed provenance for every exposed evaluation value."}}},"KubernetesCpuHpaEvidence":{"type":"object","additionalProperties":false,"required":["source","reference"],"properties":{"source":{"type":"string","enum":["calibrated","external"]},"reference":{"type":"string","minLength":1,"maxLength":512}}},"KubernetesCpuHpaConfig":{"type":"object","additionalProperties":false,"description":"Opt-in bounded CPU-based workload replica model. It changes workload replica counts only; it does not scale Kubernetes node pools, call a live cluster, or claim Kubernetes HPA controller parity. CPU request and demand are labeled ASSUMED unless their corresponding evidence object is supplied. An optional reviewed CPU/RPS measurement series can derive demand through a least-squares fit constrained through zero; its declared demand must match the fit and its source and reference must be retained in cpuDemandEvidence. Omitted CPU requests are never inferred; Utilization targets then return an explicit error. Utilization is measured as a percentage of the declared request; AverageValue is absolute millicores per pod. Each recommendation is ceil(current replicas × current metric ÷ target), then clamped to replica bounds.","required":["cpuDemandMilliCoresPerRps","targets","replicas","minReplicas","maxReplicas"],"properties":{"cpuRequestMilliCores":{"type":"number","exclusiveMinimum":0,"description":"Explicit CPU request per pod in millicores. Required only for Utilization targets; never defaulted."},"cpuRequestEvidence":{"$ref":"#/components/schemas/KubernetesCpuHpaEvidence"},"cpuDemandMilliCoresPerRps":{"type":"number","exclusiveMinimum":0,"description":"Explicit assumed or evidenced CPU demand in millicores per offered RPS."},"cpuDemandEvidence":{"$ref":"#/components/schemas/KubernetesCpuHpaEvidence"},"cpuDemandCalibration":{"type":"object","additionalProperties":false,"required":["method","reviewed","measurements"],"description":"Caller-reviewed CPU-demand calibration. The fitted value is aggregate workload CPU millicores divided by offered RPS, using least squares constrained through zero. cpuDemandEvidence must retain the source and reference. Each point is measured at workload level, not inferred by CWM.","properties":{"method":{"type":"string","enum":["least-squares-through-origin"]},"reviewed":{"type":"boolean","enum":[true],"description":"Explicit caller confirmation that the displayed fit was reviewed."},"measurements":{"type":"array","minItems":2,"maxItems":100,"items":{"type":"object","additionalProperties":false,"required":["rps","cpuMilliCores"],"properties":{"rps":{"type":"number","exclusiveMinimum":0,"maximum":1000000,"description":"Offered requests per second for this measurement."},"cpuMilliCores":{"type":"number","minimum":0,"maximum":1000000000,"description":"Aggregate workload CPU usage in millicores, across its replicas."}}}}}},"targets":{"type":"array","minItems":1,"maxItems":2,"description":"Configure at most one target of each type. If both are configured, the larger bounded replica recommendation is applied.","items":{"oneOf":[{"type":"object","additionalProperties":false,"required":["type","targetPercent"],"properties":{"type":{"type":"string","const":"Utilization"},"targetPercent":{"type":"number","exclusiveMinimum":0}}},{"type":"object","additionalProperties":false,"required":["type","targetMilliCores"],"properties":{"type":{"type":"string","const":"AverageValue"},"targetMilliCores":{"type":"number","exclusiveMinimum":0}}}]}},"replicas":{"type":"integer","minimum":1,"maximum":10000,"description":"Current workload replica count."},"minReplicas":{"type":"integer","minimum":1,"maximum":10000},"maxReplicas":{"type":"integer","minimum":1,"maximum":10000},"scaleDownStabilizationSteps":{"type":"integer","minimum":1,"maximum":10000,"description":"Optional scale-down hold window measured in simulation steps."}}},"KubernetesCpuHpaTelemetry":{"type":"object","additionalProperties":false,"required":["resourceId","name","cpuRequestProvenance","cpuDemandMilliCoresPerRps","cpuDemandProvenance","offeredRps","observedCpuMilliCoresPerPod","currentReplicas","minReplicas","maxReplicas","targets","desiredReplicas","appliedReplicas","scaleDownStabilizationHeld","modelScope"],"properties":{"resourceId":{"type":"string"},"name":{"type":"string"},"cpuRequestMilliCores":{"type":"number","exclusiveMinimum":0},"cpuRequestProvenance":{"type":"object","additionalProperties":false,"required":["basis"],"properties":{"basis":{"type":"string","enum":["ASSUMED","EVIDENCE","UNDECLARED"]},"source":{"type":"string","enum":["calibrated","external"]},"reference":{"type":"string"}}},"cpuDemandMilliCoresPerRps":{"type":"number","exclusiveMinimum":0},"cpuDemandProvenance":{"type":"object","additionalProperties":false,"required":["basis"],"properties":{"basis":{"type":"string","enum":["ASSUMED","EVIDENCE"]},"source":{"type":"string","enum":["calibrated","external"]},"reference":{"type":"string"}}},"offeredRps":{"type":"number","minimum":0},"observedCpuMilliCoresPerPod":{"type":"number","minimum":0},"currentReplicas":{"type":"integer","minimum":1},"minReplicas":{"type":"integer","minimum":1},"maxReplicas":{"type":"integer","minimum":1},"targets":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["type","targetValue","desiredReplicas"],"properties":{"type":{"type":"string","enum":["Utilization","AverageValue"]},"targetValue":{"type":"number","exclusiveMinimum":0},"currentMetric":{"type":"number","minimum":0},"desiredReplicas":{"type":["integer","null"],"minimum":1},"error":{"type":"object","additionalProperties":false,"required":["code","message"],"properties":{"code":{"type":"string","const":"MISSING_CPU_REQUEST"},"message":{"type":"string"}}}}}},"desiredReplicas":{"type":["integer","null"],"minimum":1},"appliedReplicas":{"type":"integer","minimum":1},"scaleDownStabilizationSteps":{"type":"integer","minimum":1},"scaleDownStabilizationHeld":{"type":"boolean"},"scaleDownStabilizationReason":{"type":"string"},"modelScope":{"type":"string","const":"workload-replicas-only; no Kubernetes controller parity claim"}}},"KubernetesMemoryTelemetry":{"type":"object","description":"Per-replica Kubernetes memory-pressure/OOM evaluation for one simulation step. This telemetry is separate from aggregate memoryUsage and never mutates the immutable normalization receipt.","required":["resourceId","name","nodeCount","evaluated","evaluationStatus","evaluationBasis","offeredRps","routingRule","memoryAwareAutoscaling","effectiveCapacityFraction","capacityStatus","capacityStatusReason","message"],"properties":{"resourceId":{"type":"string","description":"Identifier of the Kubernetes resource this telemetry row describes."},"name":{"type":"string","description":"Display name of the Kubernetes resource."},"poolId":{"type":"string","description":"Node-pool identifier when this row represents one pool in a multi-pool cluster."},"limitGiB":{"type":"number","description":"Configured per-replica memory limit in GiB."},"requestGiB":{"type":"number","description":"Configured per-replica memory request in GiB."},"nodeCount":{"type":"integer","description":"Number of nodes or replicas represented by this row."},"evaluated":{"type":"boolean","description":"Whether the explicit memory-pressure profile was evaluated for this row."},"evaluationStatus":{"type":"string","enum":["not_evaluated","within_limit","limit_exceeded"],"description":"Result of comparing modeled working-set demand with the configured memory limit."},"evaluationBasis":{"type":"string","enum":["explicit_memory_profile","missing_memory_profile","unsupported_multi_pool_topology","native_configuration_only"],"description":"native_configuration_only reports observed manifest configuration without evaluating runtime demand or OOM behavior."},"memoryEvidence":{"type":"object","description":"Bounded observed memory configuration from a standard Kubernetes Deployment or StatefulSet. It is not runtime working-set evidence.","required":["source","status","kind","sourceFile","workload","namespace"],"properties":{"source":{"type":"string","enum":["kubernetes-manifest"],"description":"Manifest source category for the extracted configuration."},"status":{"type":"string","enum":["complete","partial","ambiguous","missing","non_literal"],"description":"Whether the configuration was fully and unambiguously extracted."},"kind":{"type":"string","enum":["Deployment","StatefulSet"],"description":"Kubernetes workload kind that declares the memory configuration."},"sourceFile":{"type":"string","description":"Repository-relative manifest path that supplied the configuration."},"workload":{"type":"string","description":"Name of the Deployment or StatefulSet that supplied the configuration."},"namespace":{"type":"string","description":"Kubernetes namespace containing the workload."},"replicas":{"type":"integer","minimum":1,"maximum":1000000,"description":"Literal replica count declared by the workload, when available."},"requestGiB":{"type":"number","exclusiveMinimum":0,"maximum":1024,"description":"Observed per-replica memory request from the manifest, in GiB."},"limitGiB":{"type":"number","exclusiveMinimum":0,"maximum":1024,"description":"Observed per-replica memory limit from the manifest, in GiB."},"reason":{"type":"string","enum":["multiple_containers","non_literal_memory","memory_not_declared"],"description":"Specific reason extraction was incomplete or unsupported."}}},"offeredRps":{"type":"number","minimum":0,"description":"Total RPS offered to this Kubernetes resource in this step."},"routingRule":{"type":"string","enum":["single_resource","even_split","unbalanced_80_20"],"description":"Existing traffic-distribution rule that produced offeredRps."},"perReplicaRps":{"type":"number","minimum":0,"description":"Offered RPS divided across the replicas represented by this row."},"modeledDemandPerReplicaGiB":{"type":"number","minimum":0,"description":"Modeled per-replica working-set demand in GiB when evaluation is available."},"headroomGiB":{"type":"number"},"source":{"type":"string","enum":["user-provided","calibrated","external"]},"confidence":{"type":"string","enum":["low","medium","high"]},"restartingReplicas":{"type":"integer","minimum":0},"preEvaluationServingReplicas":{"type":"integer","minimum":0,"description":"Serving replicas used for demand before this step's OOM effect."},"postEffectServingReplicas":{"type":"integer","minimum":0,"description":"Serving replicas after this step's OOM/restart effect."},"effectiveServingReplicas":{"type":"integer","minimum":0,"description":"Backwards-compatible alias for postEffectServingReplicas."},"oomReplicasThisStep":{"type":"integer","minimum":0},"recoveredReplicasThisStep":{"type":"integer","minimum":0},"effectiveCapacityFraction":{"type":"number","minimum":0,"maximum":1,"description":"Effective serving capacity fraction after the memory-pressure effect."},"capacityCeilingRps":{"type":"number","minimum":0,"description":"Effective Kubernetes serving ceiling after the memory-pressure effect."},"servedRps":{"type":"number","minimum":0},"shedRps":{"type":"number","minimum":0},"overloadRatio":{"type":"number","minimum":0},"capacityStatus":{"type":"string","enum":["enforced","no_ceiling"],"description":"Whether a Kubernetes throughput ceiling was available for this row."},"capacityStatusReason":{"type":"string","description":"Explicit explanation of the capacity status."},"cpuUsagePercent":{"type":"number"},"memoryPressureRatio":{"type":"number"},"memoryAwareAutoscaling":{"type":"boolean","description":"Whether the autoscaling controller used the memory signal for this row."},"autoscaleDriver":{"type":"string","enum":["cpu","memory","both","none"]},"extrapolationPolicy":{"type":"string","enum":["clamp","linear"]},"message":{"type":"string","description":"Human-readable summary of this row's evaluation or why it was not evaluated."}}},"SnapshotDiffMetricField":{"type":"object","description":"A single metric comparison between a pinned state and the current state","required":["from","to","delta"],"properties":{"from":{"type":"number","description":"Metric value at pin time","example":42.1},"to":{"type":"number","description":"Metric value now","example":78.3},"delta":{"type":"number","description":"Change since pin time (to − from)","example":36.2}}},"SnapshotDiffKubernetesMemory":{"type":"object","description":"Kubernetes memory telemetry comparison. Rows are matched by resourceId and poolId; each changed row includes the complete before and after KubernetesMemoryTelemetry payload, including memory-pressure, OOM/restart, capacity, autoscaling, and extrapolation-policy fields.\n","required":["added","removed","changed"],"properties":{"added":{"type":"array","description":"Rows present now but absent at pin time.","items":{"$ref":"#/components/schemas/KubernetesMemoryTelemetry"}},"removed":{"type":"array","description":"Rows present at pin time but absent now.","items":{"$ref":"#/components/schemas/KubernetesMemoryTelemetry"}},"changed":{"type":"array","description":"Rows whose supported Kubernetes memory telemetry fields changed.","items":{"type":"object","required":["resourceId","name","before","after"],"properties":{"resourceId":{"type":"string"},"name":{"type":"string"},"poolId":{"type":"string"},"before":{"$ref":"#/components/schemas/KubernetesMemoryTelemetry"},"after":{"$ref":"#/components/schemas/KubernetesMemoryTelemetry"}}}}}},"ValidationErrorCode":{"type":"string","description":"Machine-readable error code indicating the category of validation failure","enum":["INVALID_FIELD","MISSING_REQUIRED","UNKNOWN_PROVIDER","UNKNOWN_SIZE","UNKNOWN_REGION","INVALID_REQUEST"]},"FieldError":{"type":"object","description":"A single structured field-level error returned on HTTP 400 responses","required":["code","pointer","message"],"properties":{"code":{"$ref":"#/components/schemas/ValidationErrorCode"},"pointer":{"type":"string","description":"JSON Pointer (RFC 6901) to the field that caused the error, or an empty string for request-level errors","example":"/resources/0/provider"},"message":{"type":"string","description":"Human-readable description of the error","example":"Unknown provider 'gce'; valid values are aws, gcp, azure, oci, digitalocean"},"suggestion":{"type":"string","description":"Optional actionable hint for self-correction","example":"Use one of: aws, gcp, azure, oci, digitalocean"},"suggestions":{"type":"array","description":"For UNKNOWN_REGION errors, the 3-5 closest matching regions ranked by similarity to the invalid input","items":{"type":"object","required":["regionKey","regionLabel"],"properties":{"regionKey":{"type":"string","description":"Canonical shortcode for the region","example":"use2"},"regionLabel":{"type":"string","description":"Standard provider label for the region","example":"us-east-2"}}}}}},"ValidationErrorResponse":{"type":"object","description":"Response body for HTTP 400 errors — either a single error or an array of field errors","oneOf":[{"type":"object","required":["error"],"properties":{"error":{"$ref":"#/components/schemas/FieldError"}}},{"type":"object","required":["errors"],"properties":{"errors":{"type":"array","items":{"$ref":"#/components/schemas/FieldError"},"minItems":1}}}]},"WalletAuth401Response":{"type":"object","description":"Structured 401 returned by wallet-session-protected endpoints when no\ncredentials are provided at all. Includes a machine-readable `walletAuth`\nblock so cold agents can discover the wallet auth flow without consulting\nthe spec first.\n","required":["error","message","walletAuth"],"properties":{"error":{"type":"string","enum":["authentication_required"],"description":"Machine-readable error code","example":"authentication_required"},"message":{"type":"string","description":"Human-readable description","example":"Provide an API key or wallet session token."},"walletAuth":{"type":"object","description":"Discovery hints for the EIP-191 wallet auth flow","required":["challengeEndpoint","verifyEndpoint","flow","next"],"properties":{"challengeEndpoint":{"type":"string","description":"Path to request a sign challenge (nonce)","example":"/wallet-auth/challenge"},"verifyEndpoint":{"type":"string","description":"Path to verify the signed challenge and receive a JWT","example":"/wallet-auth/verify"},"flow":{"type":"array","description":"Ordered human-readable steps to complete authentication","items":{"type":"string"},"minItems":4,"maxItems":4,"example":["POST /wallet-auth/challenge to receive a nonce","Sign the nonce with your wallet private key (EIP-191)","POST /wallet-auth/verify with the signature to receive a JWT","Retry this request with Authorization: Bearer <token>"]},"next":{"type":"object","description":"Machine-readable pointer to the first step of the auth flow","required":["method","path"],"properties":{"method":{"type":"string","enum":["POST"],"example":"POST"},"path":{"type":"string","example":"/wallet-auth/challenge"}}}}}}},"ApiKeyAuth401Response":{"type":"object","description":"Structured 401 returned by API-key-protected endpoints when no\ncredentials are provided at all. Includes a machine-readable `apiKeyAuth`\nblock so cold agents can discover how to create an API key without\nconsulting the spec first.\n","required":["error","message","apiKeyAuth"],"properties":{"error":{"type":"string","enum":["authentication_required"],"description":"Machine-readable error code","example":"authentication_required"},"message":{"type":"string","description":"Human-readable description","example":"Provide an API key in the Authorization header."},"apiKeyAuth":{"type":"object","description":"Discovery hints for obtaining an API key","required":["keyCreationEndpoint","docsUrl","next"],"properties":{"keyCreationEndpoint":{"type":"string","description":"Path to create a new API key","example":"/keys/register"},"docsUrl":{"type":"string","description":"Path to API documentation","example":"/api-docs"},"next":{"type":"object","description":"Machine-readable pointer to the first step to get an API key","required":["method","path"],"properties":{"method":{"type":"string","enum":["POST"],"example":"POST"},"path":{"type":"string","example":"/keys/register"}}}}}}},"ApiKeyCreatedResponse":{"type":"object","description":"Response returned when an API key is successfully created (either via admin POST /keys or via POST /keys/register)","properties":{"id":{"type":"string","description":"Unique identifier for the created API key","example":"3f9a1b2c-4d5e-6f7a-8b9c-0d1e2f3a4b5c"},"key":{"type":"string","description":"The plain-text API key. Store this immediately — it is shown only once.","example":"cwm_live_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"},"keyPrefix":{"type":"string","description":"Redacted prefix used to identify the key in listings","example":"cwm_live_a1b2c3d4e5f6a1b2..."},"name":{"type":"string","example":"canvas-cloud-ai-prod"},"scopes":{"type":"array","items":{"type":"string","enum":["read","write","admin"]},"example":["read","write"]},"rateLimit":{"type":"integer","example":1000},"createdAt":{"type":"string","format":"date-time","example":"2026-05-10T17:00:00.000Z"},"expiresAt":{"type":"string","format":"date-time","nullable":true},"message":{"type":"string","example":"Store this API key securely. You won't be able to see it again."}}},"RegistrationTokenCreatedResponse":{"type":"object","description":"Response returned when a registration token is minted. The plain `token` value is shown only once.","properties":{"id":{"type":"string","description":"Unique identifier for the registration token","example":"7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d"},"token":{"type":"string","description":"The plain-text registration token. Share this with the recipient immediately — it will not be shown again.","example":"cwm_reg_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"},"tokenPrefix":{"type":"string","description":"Redacted prefix used to identify the token in listings","example":"cwm_reg_a1b2c3d4e5f6a1b2..."},"name":{"type":"string","example":"canvas-cloud-ai"},"scopes":{"type":"array","items":{"type":"string","enum":["read","write","admin"]},"example":["read","write"]},"rateLimit":{"type":"integer","example":1000},"expiresAt":{"type":"string","format":"date-time","nullable":true,"example":"2026-06-01T00:00:00Z"},"createdAt":{"type":"string","format":"date-time","example":"2026-05-10T17:00:00.000Z"},"message":{"type":"string","example":"Share this token with the client. It can only be used once and will not be shown again."}}},"RegistrationTokenSummary":{"type":"object","description":"Summary of a registration token as returned by GET /register-tokens (plain token value is never included)","properties":{"id":{"type":"string","example":"7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d"},"tokenPrefix":{"type":"string","example":"cwm_reg_a1b2c3d4e5f6a1b2..."},"name":{"type":"string","example":"canvas-cloud-ai"},"scopes":{"type":"array","items":{"type":"string","enum":["read","write","admin"]},"example":["read","write"]},"rateLimit":{"type":"integer","example":1000},"expiresAt":{"type":"string","format":"date-time","nullable":true,"example":"2026-06-01T00:00:00Z"},"usedAt":{"type":"string","format":"date-time","nullable":true,"description":"Set when the token was consumed. Null if still pending."},"isActive":{"type":"boolean","example":true},"createdByKeyId":{"type":"string","description":"ID of the admin API key that created this token","example":"1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d"},"createdAt":{"type":"string","format":"date-time","example":"2026-05-10T17:00:00.000Z"},"status":{"type":"string","enum":["pending","used","expired","revoked"],"description":"Computed status of the token","example":"pending"}}},"DataIntegrityEvidence":{"type":"array","description":"Explicit per-resource data durability, independent of service health. Infrastructure recovery never restores permanently lost data.","items":{"type":"object","required":["resourceId","resourceName","serviceAvailability","dataState","recoverySource"],"properties":{"resourceId":{"type":"string"},"resourceName":{"type":"string"},"serviceAvailability":{"type":"string","enum":["available","degraded","unavailable"]},"dataState":{"type":"string","enum":["intact","temporarily_unavailable","permanently_lost"]},"recoverySource":{"type":"string","enum":["none","surviving_backup","surviving_replica"]},"recoverySourceResourceId":{"type":"string"}}}},"RdsSqlServerBillingConfig":{"type":"object","additionalProperties":false,"description":"Bounded caller-provided billing assumptions for AWS RDS SQL Server Express. Attach this object only to an `aws` `database` resource whose `serviceFamily` is `rds-sqlserver-express`. CPU is a constant interval assumption, not inferred from traffic, database load, generic simulated CPU, or a managed-service process. The initial earned-credit and surplus-credit ledgers are separate and cannot both be positive.","required":["instanceClass","storageType","allocatedStorageGiB","durationHours","sustainedCpuPercent"],"properties":{"engine":{"type":"string","const":"sqlserver-ex","default":"sqlserver-ex","description":"SQL Server Express, license included. Other engines and editions are outside this bounded model."},"region":{"type":"string","const":"us-east-1","default":"us-east-1","description":"Pricing region supported by this model. It must agree with `resource.location.regionKey` when that field is supplied."},"deployment":{"type":"string","const":"single-az","default":"single-az","description":"Single-AZ deployment. Multi-AZ pricing is not included."},"instanceClass":{"type":"string","enum":["db.t3.micro","db.t3.small","db.t3.medium","db.t3.large","db.t3.xlarge"],"description":"Supported SQL Server Express T3 instance class. If `characteristics.size` or `instanceType` is also present, it must equal this value. Non-burstable classes are intentionally unsupported because this bounded regional Express catalog has no verified non-burstable SKU/rate; they fail validation rather than receiving a fabricated or generic fallback price.","example":"db.t3.micro"},"storageType":{"type":"string","enum":["gp2","gp3"],"description":"General Purpose SSD storage price to apply. Additional gp3 IOPS and throughput are not modeled.","example":"gp2"},"allocatedStorageGiB":{"type":"integer","minimum":20,"maximum":16384,"description":"Caller-declared allocated database storage in GiB.","example":20},"durationHours":{"type":"number","exclusiveMinimum":0,"maximum":8784,"description":"Billing interval in hours. Storage is prorated over a documented 730-hour reference month.","example":480},"sustainedCpuPercent":{"type":"number","minimum":0,"maximum":100,"description":"Caller-provided constant average CPU utilization over the interval. This is neither an application-load attribution nor evidence that RdsAdminService, SQL Server, the OS, or any other process caused it.","example":43},"creditMode":{"type":"string","const":"unlimited","default":"unlimited","description":"T3 Unlimited credit accounting. Disabling Unlimited is not modeled and would not remove the underlying CPU demand."},"initialCreditBalance":{"type":"number","minimum":0,"default":0,"description":"Earned CPU credits available at interval start, in vCPU-minutes. Bounded by the selected class's 24-hour earning limit.","example":0},"initialSurplusCreditBalance":{"type":"number","minimum":0,"default":0,"description":"Uncharged Unlimited surplus-credit debt at interval start, in vCPU-minutes. It is repaid before new credits are banked and is bounded by the selected class's 24-hour earning limit.","example":0},"settleSurplusAtEnd":{"type":"boolean","default":false,"description":"Whether to charge and clear otherwise uncharged surplus debt at interval end, representing an explicit stop/delete settlement assumption. False keeps debt up to the 24-hour cap in the ending surplus ledger."}}},"RdsSqlServerCreditLedger":{"type":"object","description":"CPU-credit ledger for the caller-defined interval. One CPU credit is one vCPU-minute. Earned and consumed credits are totals; ending earned and surplus balances remain distinct.","required":["earnedCredits","consumedCredits","creditCap","endingCreditBalance","endingSurplusCreditBalance","chargedCredits","overflowChargedCredits","settlementChargedCredits"],"properties":{"earnedCredits":{"type":"number","description":"Baseline credits earned during the interval."},"consumedCredits":{"type":"number","description":"Credits consumed by the declared vCPU utilization during the interval."},"creditCap":{"type":"number","description":"Maximum banked earned credits or uncharged surplus debt, equal to 24 hours of baseline earnings."},"endingCreditBalance":{"type":"number","description":"Earned credits remaining after consumption and repayment of any initial surplus."},"endingSurplusCreditBalance":{"type":"number","description":"Uncharged Unlimited surplus debt retained at interval end; zero when end settlement is requested."},"chargedCredits":{"type":"number","description":"Total credits billed as surplus, equal to overflow plus explicit end-settlement credits."},"overflowChargedCredits":{"type":"number","description":"Surplus credits beyond the 24-hour uncharged debt cap."},"settlementChargedCredits":{"type":"number","description":"Surplus debt charged because `settleSurplusAtEnd` was true."}}},"RdsSqlServerBillingResult":{"type":"object","description":"Deterministic interval estimate for one bounded RDS SQL Server Express configuration. This is not an AWS invoice reconstruction. It excludes free tier, discounts, taxes, backup overage, transfer, additional gp3 IOPS/throughput, and account-specific adjustments; actual CloudWatch sampling and AWS hourly settlement can also differ.","required":["input","computeCost","licensingCost","storageCost","cpuCreditCost","totalCost","effectiveHourlyCost","workloadVcpus","credits","assumptions"],"properties":{"input":{"$ref":"#/components/schemas/RdsSqlServerBillingConfig"},"computeCost":{"type":"number","description":"License-included Single-AZ instance cost over `durationHours`, USD."},"licensingCost":{"type":"number","description":"Separate SQL Server license surcharge over the interval, USD. Zero for this bounded Express license-included catalog because licensing is already included in computeCost."},"storageCost":{"type":"number","description":"Selected gp2/gp3 storage cost over `durationHours`, USD, using a 730-hour reference month."},"cpuCreditCost":{"type":"number","description":"Charged Unlimited surplus-credit cost over the interval, USD."},"totalCost":{"type":"number","description":"Sum of modeled instance, storage, and CPU-credit components, USD."},"effectiveHourlyCost":{"type":"number","description":"Effective interval total divided by durationHours, USD/hr."},"workloadVcpus":{"type":"number","description":"Fixed absolute CPU demand used for size comparisons, equal to class vCPUs times sustained CPU fraction."},"credits":{"$ref":"#/components/schemas/RdsSqlServerCreditLedger"},"assumptions":{"type":"array","items":{"type":"string"},"description":"Human-readable pricing, interval, provenance, and exclusion assumptions that qualify this estimate."}}},"RdsSqlServerComparison":{"type":"object","description":"Cost-only comparison returned by the existing right-sizing-hint route. Alternatives preserve the current result's absolute vCPU demand and billing interval while starting each alternative with zero earned and surplus balances. Fixed vCPU demand is a comparison assumption, not a prediction that managed-service/background CPU remains identical after resizing. Candidates unable to represent that demand without exceeding 100% CPU are omitted rather than clamped. Only verified T3 Express catalog entries are compared; no unsupported non-burstable SKU is synthesized. This does not select an architecture or recommend a migration.","required":["resourceId","current","alternatives","comparisonBasis","recommendation"],"properties":{"resourceId":{"type":"string"},"current":{"$ref":"#/components/schemas/RdsSqlServerBillingResult"},"alternatives":{"type":"array","items":{"$ref":"#/components/schemas/RdsSqlServerBillingResult"}},"comparisonBasis":{"type":"string","description":"Exact fixed-workload assumptions used across the alternatives."},"recommendation":{"type":"null","description":"Always null; the bounded comparison deliberately does not choose an option."}}},"SessionAffinity":{"type":"object","additionalProperties":false,"required":["mode","fleetId"],"description":"Modeled sticky session cohorts for generic compute or a Kubernetes workload. Compute peers require identical config/fleetId. Existing sessions never migrate on scale-out. Owner loss disconnects them. Omission preserves ordinary routing; min=max is not affinity. These are explicit assumptions, not measured OpenShell metrics.","properties":{"mode":{"type":"string","enum":["sticky"],"description":"Enables sticky routing of modeled sessions to their initial owner."},"fleetId":{"type":"string","minLength":1,"maxLength":128,"description":"Shared identifier grouping compute peers or Kubernetes replicas in the same session-affinity fleet."},"initialSessions":{"type":"number","minimum":0,"default":100,"description":"Number of active sessions present at the start of the simulation."},"arrivalsPerStep":{"type":"number","minimum":0,"default":0,"description":"New sessions arriving during each simulation step."},"meanLifetimeSteps":{"type":"number","minimum":1,"default":300,"description":"Mean session lifetime in simulation steps."},"requestsPerSessionPerSecond":{"type":"number","minimum":0,"exclusiveMinimum":true,"default":1,"description":"Request rate generated by each active session."},"maxSessionsPerOwner":{"type":"number","minimum":0,"exclusiveMinimum":true,"default":1000,"description":"Maximum active sessions assigned to one compute member or Kubernetes replica."},"reconnect":{"type":"string","enum":["none","next-step"],"default":"none","description":"Whether sessions disconnected by owner loss may reconnect on the next step."},"workload":{"type":"object","additionalProperties":false,"required":["replicas","minReplicas","maxReplicas","capacityRpsPerReplica"],"description":"Required only on Kubernetes. CPU-only replica HPA independent of node scaling. Assumes scheduling capacity is available. minReplicas <= replicas <= maxReplicas.","properties":{"replicas":{"type":"integer","minimum":1,"maximum":10000,"description":"Starting number of workload replicas in the Kubernetes fleet."},"minReplicas":{"type":"integer","minimum":1,"maximum":10000,"description":"Minimum workload replicas allowed by replica autoscaling."},"maxReplicas":{"type":"integer","minimum":1,"maximum":10000,"description":"Maximum workload replicas allowed by replica autoscaling."},"targetCpuPercent":{"type":"number","minimum":1,"maximum":100,"default":70},"capacityRpsPerReplica":{"type":"number","minimum":0,"exclusiveMinimum":true,"description":"Modeled request-per-second serving capacity of each workload replica."},"autoscaling":{"type":"boolean","default":true}}}}},"SessionAffinityEvidence":{"type":"object","description":"Expected-value cohorts, explicit owner receipts and goodput ceiling, not measured provider metrics.","required":["fleetId","basis","ownerKind","reconnectPolicy","offeredRps","sessionOfferedRps","statelessRps","activeSessions","newSessions","reconnectingSessions","disconnectedSessions","ownerLossDisconnects","rejectedSessionArrivals","disconnectedRps","capacityRejectedRps","servedRpsCeiling","owners","assumptions"],"properties":{"fleetId":{"type":"string"},"basis":{"type":"string","enum":["modeled-expected-session-cohorts"]},"ownerKind":{"type":"string","enum":["compute-member","workload-replica"]},"reconnectPolicy":{"type":"string","enum":["none","next-step"]},"offeredRps":{"type":"number"},"sessionOfferedRps":{"type":"number"},"statelessRps":{"type":"number"},"activeSessions":{"type":"number"},"newSessions":{"type":"number"},"reconnectingSessions":{"type":"number"},"disconnectedSessions":{"type":"number"},"ownerLossDisconnects":{"type":"number"},"rejectedSessionArrivals":{"type":"number"},"disconnectedRps":{"type":"number"},"capacityRejectedRps":{"type":"number"},"servedRpsCeiling":{"type":"number"},"assumptions":{"type":"string"},"owners":{"type":"array","items":{"type":"object","required":["ownerId","resourceId","activeSessions","newSessions","reconnectingSessions","offeredRps","capacityRps","capacityRejectedRps","cpuPercent"],"properties":{"ownerId":{"type":"string"},"resourceId":{"type":"string"},"activeSessions":{"type":"number"},"newSessions":{"type":"number"},"reconnectingSessions":{"type":"number"},"offeredRps":{"type":"number"},"capacityRps":{"type":"number"},"capacityRejectedRps":{"type":"number"},"cpuPercent":{"type":"number"}}}}}},"Route53FailoverTelemetry":{"type":"object","description":"Persisted on the configured Route 53 resource's metadata.route53Failover.\nThis is modeled DNS health/cache state, not measured AWS telemetry.\nTraffic shares are fractions of offered load (0–1), not percentages.\nreplicationLagSeconds accumulates modeled source-unavailability time\nuntil promotion and freezes at promotion; MODELED_NOT_MEASURED is not\nobserved replication lag or proof of data loss/RPO. Regional recovery\ndoes not rewrite a lag value frozen at promotion.\n","properties":{"evaluatedAtStep":{"type":"integer","description":"Engine input-step index at evaluation, not elapsed seconds."},"primaryHealthy":{"type":"boolean","description":"Health-check verdict after consecutive-probe thresholds."},"primaryAvailable":{"type":"boolean","description":"Whether the primary region currently has available modeled compute."},"consecutiveFailures":{"type":"integer"},"consecutiveSuccesses":{"type":"integer"},"lastCheckSeconds":{"type":"number","description":"Simulation-clock seconds of the last health probe."},"primaryTrafficShare":{"type":"number","minimum":0,"maximum":1},"secondaryTrafficShare":{"type":"number","minimum":0,"maximum":1},"staleDnsTrafficShare":{"type":"number","minimum":0,"maximum":1,"description":"Offered-load fraction still routed to unavailable regional compute."},"staleDnsRps":{"type":"number","minimum":0,"description":"Modeled offered RPS times staleDnsTrafficShare; not measured requests."},"transitionStartedSeconds":{"type":"number"},"transitionFromShare":{"type":"number","minimum":0,"maximum":1},"transitionTargetShare":{"type":"number","minimum":0,"maximum":1},"phase":{"type":"string","enum":["primary","detecting_failure","draining","secondary","detecting_recovery","recovering"],"description":"Probe detection and TTL drain/recovery are distinct phases; later healthy state does not erase earlier stale routing."},"replicationLagSeconds":{"type":"number","minimum":0},"replicationLagBasis":{"type":"string","enum":["MODELED_NOT_MEASURED"]},"replicationLagFrozenAtPromotion":{"type":"boolean"}}},"Route53FailoverConfig":{"type":"object","additionalProperties":false,"required":["primaryRegion","secondaryRegion"],"description":"Reusable opt-in DNS failover model on characteristics.route53Failover.\nProbe detection (consecutive health checks) and resolver cache expiry\n(DNS TTL) are separate clocks, expressed in simulated seconds.\nsimulationSecondsPerStep converts the engine input-step index to seconds;\ncurrentTime and metric timestamp remain step indices, not seconds.\nscenario.get returns the configured model for graph-copy creation.\n","properties":{"primaryRegion":{"type":"string","minLength":1},"secondaryRegion":{"type":"string","minLength":1},"healthCheckIntervalSeconds":{"type":"number","exclusiveMinimum":0,"default":10},"failureThreshold":{"type":"integer","minimum":1,"default":3},"recoveryThreshold":{"type":"integer","minimum":1,"default":3},"dnsTtlSeconds":{"type":"number","exclusiveMinimum":0,"maximum":86400,"default":60},"primaryTrafficShare":{"type":"number","minimum":0,"maximum":1,"default":0.5},"simulationSecondsPerStep":{"type":"number","exclusiveMinimum":0,"default":10}}},"Resource":{"type":"object","required":["id","type","name","provider","status"],"properties":{"metadata":{"type":"object","additionalProperties":true,"description":"Resource metadata; configured Route 53 nodes persist modeled lifecycle telemetry under route53Failover.","properties":{"route53Failover":{"$ref":"#/components/schemas/Route53FailoverTelemetry"}}},"id":{"type":"string","description":"Unique resource identifier","example":"ec2-1"},"dataState":{"type":"string","enum":["intact","temporarily_unavailable","permanently_lost"],"description":"Data durability, separate from service health; permanently_lost is irreversible."},"recoverySource":{"type":"string","enum":["none","surviving_backup","surviving_replica"]},"recoverySourceResourceId":{"type":"string"},"databaseRebuilt":{"type":"boolean","description":"Explicitly rebuilt empty database; does not restore original lost data."},"type":{"type":"string","enum":["compute","network","database","storage","kubernetes","cache","queue"],"description":"Resource type. `kubernetes` models a managed container node pool (EKS / GKE / AKS / OKE / DOKS): its `characteristics.nodeCount` worker nodes autoscale between `characteristics.minNodes` and `characteristics.maxNodes`, growing both serving capacity (`maxThroughput`) and hourly cost proportionally.\n","example":"compute"},"name":{"type":"string","description":"Human-readable resource name","example":"Web Server 1"},"provider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"],"description":"Cloud provider","example":"aws"},"status":{"type":"string","enum":["healthy","warning","critical","offline"],"description":"Current resource status","default":"healthy"},"location":{"type":"object","description":"Resource location for create and read. Owned typical requires regionKey us-east-2 on all four nodes; the server stores canonical use2. Do not set region under characteristics.","required":["regionKey"],"properties":{"regionKey":{"type":"string","example":"us-east-2"},"zoneKey":{"type":"string"},"localityType":{"type":"string","enum":["az","zone","availability_domain","fault_domain"]},"providerLabel":{"type":"string"},"faultDomainKey":{"type":"string"}}},"characteristics":{"type":"object","description":"Provider-specific resource characteristics. For AWS RDS resources, lifecycle/support and version inputs such as `extendedSupport`, `engineVersion`, `databaseEngine`, `databaseEngineVersion`, and `dbEngineVersion` are rejected with `INVALID_FIELD`: CWM models base resource rates and behavior but does not price RDS lifecycle/support surcharges. Direct `engine` is also rejected except for literal `engine: \"mysql\"` as workload-only identity on the exact AWS `db.r5.large` database in the canonical owned CRUD graph; prefer `workloadDatabaseEngine: \"mysql\"`. This alias does not declare engine version, Extended Support, lifecycle, or billing. The bounded SQL Server Express contract instead uses `rdsSqlServerBilling.engine: \"sqlserver-ex\"` inside its nested, strict configuration. Arbitrary data remains supported under resource-level `metadata`. For Aurora Serverless, set ACU limits with `config.minCapacity` and `config.maxCapacity` (or flat `minCapacity` and `maxCapacity`). For AWS aurora-serverless databases with size `db.serverless`, flat `minAcu` and `maxAcu` are aliases when the corresponding canonical field is absent. Top-level and nested config aliases are rejected. A modeled two-instance pair cannot change its topology or ACU bounds through PATCH.\nOwned AWS CRUD calibration eligibility is an exact predicate: exactly four healthy, routable AWS resources and exactly four edges, one internal ALB → each of two compute apps → one database, with no extra resources or edges. Both apps are `m5.large`; each absent `serviceFamily` is inferred as `ec2` for fit eligibility only, explicit `ec2` is accepted, and any other explicit family fails. The database is `db.r5.large`; absent `serviceFamily` is inferred as `rds` for eligibility only, explicit `rds` is accepted, and any conflicting explicit family fails. MySQL workload identity is required on that database: use `workloadDatabaseEngine: \"mysql\"` or the narrow literal `engine: \"mysql\"` alias described above. The alias is accepted on that database shape even in an incomplete graph but does not by itself make the graph fit-eligible. The sole network node must have explicit `serviceFamily: \"alb\"`, or, only when serviceFamily is absent, an unambiguous name containing `ALB` or `Application Load Balancer`; any explicit size/type discriminator must not identify NLB or another network service. A generic unnamed network/load-balancer node, explicit NLB, or contradictory discriminator is ambiguous and fails eligibility.\nEach app may explicitly set `workload: \"crud\"` or omit the flag only for this completely matched canonical graph with MySQL identity; this is a canonical CRUD modeling assumption, not proof of actual customer workload behavior, and the inference is identified in `calibrationEvidence.note`. Explicit conflicting workload values fail. No CRUD inference applies to similar-but-not-exact graphs, another instance type/provider, or an incomplete graph. Each app must have autoscaling disabled and fixed `minInstances=maxInstances=2`; traffic must be in the inclusive 10–1,000 RPS range. There must be no active failure and no failure, warmup, or snapshot metadata. App `capacityRps`, `requestsPerSecond`, non-default `maxThroughput`, and non-default `baseLatency` overrides disqualify the fit. If supplied, database `maxConnections` must be 500 and `effectiveMaxConnections` must be absent; if supplied, ALB `maxThroughput` must be 50,000. The canonical `us-east-2` / seed `20240601` pair is the parity fixture, not evidence that another region has been measured.\nThe separate owned typical fit requires appWeight=typical and the same four-node topology, explicit workload=\"crud-typical\", appRuntime=\"node\", appWorkerCount=2 and appDbPoolSize=250 per compute node, workloadDatabaseVersion=\"8.0\" and explicit maxConnections=500 on the MySQL database, and internal loadBalancerScheme on the ALB. Every node must declare location.regionKey=\"us-east-2\" (stored as canonical \"use2\"), bounds must be fixed 2→2, and load must be 20–300 RPS. Missing identity is never inferred. Only measured 20/100/200 RPS rungs from campaign typical-v1-20260927c are fitted; 300 RPS is a holdout and 500 RPS is saturation context. Generic typical apps and the public lean benchmark do not receive this fit.\nFor ineligible graphs, `calibrationEvidence.kind` is `modeled` and `calibrationEvidence.note` names every actual failed eligibility check in stable order, with resource identifiers and safe expected / actual values; it must not report only a generic “not applied”. Examples include missing or ambiguous ALB identity, wrong app size or family, non-fixed instance bounds, traffic outside the range, active failure, and conflicting workload identity. For eligible graphs, evidence identifies the owned calibration and states that latency is modeled on the in-VPC ALB path, not end-to-end. No other hardware has been validated. Lean owned evidence requires root appWeight=lean; the separate typical fit requires typical. Known-vCPU serving compute uses the workload model: proportional lean AWS M5 scaling is scaled from measured. At offered application RPS T its full-precision fitted baseline is P50 = max(1.45, 2.4833 - 0.0010466*T) ms and P95 = max(P50, 3.10, 4.3697 - 0.0012973*T) ms. Topology, congestion, storage, memory, routing and failure penalties follow this baseline. There is no generic 5 ms floor on lean scaled latency; 0.05 ms is only a physical/non-finite safety floor. Other providers and generic typical/heavy are reference estimates. An explicitly identified owned typical Node CRUD setup uses its own separate three-rung fit; it does not change the lean benchmark. Unknown-vCPU fallback retains explicitly unverified generic output with no external reference. See PredictionEvidence for assumption intervals.\n","properties":{"route53Failover":{"$ref":"#/components/schemas/Route53FailoverConfig"},"workload":{"type":"string","enum":["crud","crud-typical"],"description":"Explicit application workload identity. Set to \"crud-typical\" only for the owned typical Node CRUD setup (see the required fields below). For lean, set \"crud\" on each app; each may omit its flag only when the complete exact canonical graph and MySQL database identity match; this is a modeling assumption, not evidence of actual customer workload behavior. Explicit conflicting workload values fail. See the characteristics contract for the complete gate.\n","example":"crud"},"appRuntime":{"type":"string","description":"Explicit runtime identity for owned typical eligibility; must be node. Not inferred from EC2.","example":"node"},"appWorkerCount":{"type":"integer","minimum":1,"description":"Explicit workers per app host; owned typical requires two on each m5.large.","example":2},"appDbPoolSize":{"type":"integer","minimum":1,"description":"Explicit database connection pool size per app host; owned typical requires 250 on each host.","example":250},"loadBalancerScheme":{"type":"string","enum":["internal","internet-facing"],"description":"Explicit ALB scheme; owned typical requires internal."},"workloadDatabaseVersion":{"type":"string","description":"Workload-only MySQL version identity; owned typical requires 8.0. Does not enable RDS lifecycle or support billing inputs.","example":"8.0"},"workloadDatabaseEngine":{"type":"string","enum":["mysql"],"description":"Preferred MySQL workload identity for the exact healthy AWS db.r5.large node in the owned CRUD graph. Literal characteristics.engine=\"mysql\" is a workload-only alias on this exact shape; it does not declare engine version, Extended Support, lifecycle, or billing. Those inputs remain rejected. See the characteristics contract for the complete gate.\n","example":"mysql"},"engine":{"type":"string","description":"Narrow AWS RDS workload-only alias: only literal \"mysql\" is accepted on the exact db.r5.large canonical owned-CRUD database; prefer workloadDatabaseEngine. Does not declare engine version, Extended Support, lifecycle, or billing capability; other engine values and lifecycle/billing inputs remain rejected.\n"},"rdsSqlServerBilling":{"$ref":"#/components/schemas/RdsSqlServerBillingConfig"},"minCapacity":{"type":"number","exclusiveMinimum":0,"description":"Aurora Serverless minimum ACU (flat form; nested config takes precedence). Default 0.5. Explicit reader bounds must match the writer's cluster range; omitted reader bounds inherit it."},"maxCapacity":{"type":"number","exclusiveMinimum":0,"description":"Aurora Serverless maximum ACU (flat form; nested config takes precedence). Default 16. Explicit reader bounds must match the writer's cluster range; omitted reader bounds inherit it."},"minAcu":{"type":"number","exclusiveMinimum":0,"description":"Exact AWS Aurora Serverless v2 db.serverless shape only; alias for flat minCapacity."},"maxAcu":{"type":"number","exclusiveMinimum":0,"description":"Exact AWS Aurora Serverless v2 db.serverless shape only; alias for flat maxCapacity."},"auroraReaderScalingMode":{"type":"string","enum":["writer-mirror","independent"],"description":"AWS Aurora Serverless v2 reader scaling: writer-mirror (default, promotion tiers 0/1) follows the writer's capacity; CPU is a modeled mirror, not independently routed load. independent (promotion tiers 2–15) uses only routed work and the ACU floor. Set on an explicit reader or on the two-instance writer to configure its generated reader. Each instance bills its own ACU-hours. https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.how-it-works.html (reviewed 2026-09-25).\n"},"config":{"type":"object","description":"Aurora Serverless ACU configuration. Use minCapacity/maxCapacity, not minAcu/maxAcu. The range is cluster-wide for all Serverless v2 instances; explicit reader bounds conflicting with the writer are rejected, while omitted reader bounds inherit the writer range. https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.setting-capacity.html#aurora-serverless-v2-examples-setting-capacity-range-for-cluster (reviewed 2026-09-25).\n","properties":{"minCapacity":{"type":"number","exclusiveMinimum":0},"maxCapacity":{"type":"number","exclusiveMinimum":0}}},"serviceFamily":{"type":"string","description":"Provider-specific service family identifier. AWS values include: `ec2`, `ecsFargate`, `rds`, `rds-sqlserver-express`, `aurora-postgresql`, `aurora-dsql`, `aurora-serverless`, `dynamodb`, `s3`, `elb`, `cloudfront`, `lambda`. GCP values include: `gce`, `cloud-sql`, `cloud-spanner`, `bigtable-ssd`, `bigtable-hdd`, `cloudRun`, `cloudFunctions`, `gcs`, `cloud-load-balancing`. Azure values include: `azure_vm`, `azure-sql`, `azureFunctions`, `azureContainerApps`, `blob-storage`, `azure-lb`, `eventhub`. OCI values include: `oci_vm`, `ociFunctions`, `autonomous-db`, `oci-container`, `oci-lb`, `oci-streaming`. DigitalOcean values include: `droplets`, `managed_postgresql`, `spaces`, `load_balancer`.\nOnly for owned AWS CRUD fit eligibility, absent serviceFamily is inferred as `ec2` for exact AWS compute `m5.large` apps and `rds` for exact AWS database `db.r5.large`; explicit matching values remain valid and conflicting values fail. The sole AWS network node accepts explicit `alb`, or, only when family is absent, a resource name unambiguously containing `ALB` or `Application Load Balancer` and no contradictory size/type discriminator. These inferences are eligibility-only and do not rewrite the caller's persisted input.\nIdle / residual-billing families (billed a flat rate regardless of traffic — see `characteristics.billingState`): AWS: `nat-gateway`, `vpc-endpoint`, `eip-detached`, `ebs-snapshot`, `alb-idle`. GCP: `cloud-nat`, `psc-endpoint`, `static-ip`, `pd-snapshot`, `clb-idle`. Azure: `azure-nat-gw`, `private-endpoint`, `azure-static-ip`, `disk-snapshot`, `azure-lb-idle`. OCI: `oci-reserved-ip`, `block-backup`, `oci-lb-idle`. DigitalOcean: `do-reserved-ip`, `do-vol-snapshot`, `do-lb-idle`. Snapshot/backup families bill per GB-month × `capacityGB` ÷ 730.\n","example":"ec2"},"containerCpu":{"type":"number","exclusiveMinimum":0,"description":"Request-serving compute only — runtime CPU allocation in vCPUs. For ECS/Fargate this must be supplied together with `containerMemoryGiB` and match a supported task allocation. It is canonicalized to the same allocation represented by `size`.\n","example":0.5},"containerMemoryGiB":{"type":"number","exclusiveMinimum":0,"description":"Request-serving compute only — runtime memory allocation in GiB. For ECS/Fargate this must be supplied together with `containerCpu` and match a supported task allocation.\n","example":1},"vcpu":{"type":"number","exclusiveMinimum":0,"description":"ECS/Fargate task vCPU allocation; equivalent to containerCpu when both are equal."},"memoryGiB":{"type":"number","exclusiveMinimum":0,"description":"ECS/Fargate task memory allocation; equivalent to containerMemoryGiB when both are equal."},"cpuArchitecture":{"type":"string","enum":["X86_64","ARM64"],"description":"ECS/Fargate Linux architecture; ARM64 uses the separate Graviton vCPU and memory rates."},"runtimeMemoryProfile":{"type":"object","description":"Optional explicit per-instance application working-set assumption. CWM never infers it from configured memory, CPU, RPS, aggregate memory telemetry, or synthetic latency. It does not alter allocated memory billing.","required":["workingSetGiB","source","confidence"],"properties":{"workingSetGiB":{"type":"number","exclusiveMinimum":0,"maximum":1024,"example":0.78125},"source":{"type":"string","enum":["user-provided","calibrated","external"],"description":"Provenance category for the declared working-set profile."},"confidence":{"type":"string","enum":["low","medium","high"],"description":"Confidence level assigned to the declared working-set profile."},"limitExceededProbability":{"type":"number","minimum":0,"maximum":1,"description":"Only calibrated/external profiles may provide a probability."},"onLimitExceeded":{"type":"object","description":"Optional bounded consequences; omitted means CWM reports only the threshold condition.","properties":{"termination":{"type":"string","enum":["terminate","restart"]},"failedWorkFraction":{"type":"number","minimum":0,"maximum":1,"description":"Required when termination is declared; the explicit bounded fraction of work stranded by that termination."},"retryMultiplier":{"type":"number","minimum":1,"maximum":10},"latencyPenaltyMs":{"type":"number","minimum":0,"maximum":60000},"retryCostPerMillionRequests":{"type":"number","minimum":0,"maximum":100000}}}}},"kubernetesCpuHpa":{"$ref":"#/components/schemas/KubernetesCpuHpaConfig"},"kubernetesMemoryProfile":{"type":"object","description":"Opt-in per-replica Kubernetes memory-pressure/OOM contract. CWM never infers per-pod memory, OOM likelihood, or working-set demand from the aggregate `memoryUsage` metric, CPU, RPS, or the configured limit alone. Absent by default — kubernetes resources without a profile explicitly report `evaluationStatus: \"not_evaluated\"` in `metrics.kubernetesMemory`. Single-pool clusters emit one row; a cluster with `nodePools` emits one row per pool and evaluates only pools that provide their own profile. Node/replica billing is unaffected.","required":["limitGiB","baselineWorkingSetGiB","loadBreakpoints","source","confidence"],"properties":{"limitGiB":{"type":"number","exclusiveMinimum":0,"maximum":1024,"description":"Configured `resources.limits.memory` per replica, in GiB."},"requestGiB":{"type":"number","exclusiveMinimum":0,"maximum":1024,"description":"Configured `resources.requests.memory` per replica, in GiB."},"baselineWorkingSetGiB":{"type":"number","exclusiveMinimum":0,"maximum":1024,"description":"Declared idle/floor per-replica working set."},"burstWorkingSetGiB":{"type":"number","exclusiveMinimum":0,"maximum":1024,"description":"Declared transient/burst working set applied only while the replica is still warming up. Must be at least `baselineWorkingSetGiB`."},"headroomGiB":{"type":"number","minimum":0,"maximum":1024,"default":0,"description":"Additional declared safety margin folded into the demand/limit comparison."},"loadBreakpoints":{"type":"array","minItems":1,"maxItems":20,"description":"Declared working-set curve keyed on per-replica RPS, in strictly ascending `perReplicaRps` order. CWM interpolates linearly between breakpoints, and below the first breakpoint ramps linearly from the idle `baselineWorkingSetGiB` anchor up to the first breakpoint's declared value. Above the last breakpoint, behavior is governed by `extrapolation` — it never assumes a linear RPS-to-memory relationship beyond what is declared or implied by that policy.","items":{"type":"object","required":["perReplicaRps","workingSetGiB"],"properties":{"perReplicaRps":{"type":"number","exclusiveMinimum":0,"description":"Per-replica request rate at which the declared working-set value applies."},"workingSetGiB":{"type":"number","exclusiveMinimum":0,"maximum":1024,"description":"Declared per-replica working-set demand at this load, in GiB."}}}},"extrapolation":{"type":"string","enum":["clamp","linear"],"default":"linear","description":"Behavior above the highest declared loadBreakpoint. \"linear\" (default, preserves pre-existing behavior) extrapolates the final declared segment's slope beyond the last breakpoint. \"clamp\" holds demand flat at the last breakpoint's `workingSetGiB` value for any RPS beyond it — for curves known to plateau (e.g. a connection-pool- or cache-bounded working set). Only governs the region above the last breakpoint; the below-first-breakpoint ramp is unaffected either way."},"source":{"type":"string","enum":["user-provided","calibrated","external"],"description":"Provenance category for the declared working-set profile."},"confidence":{"type":"string","enum":["low","medium","high"],"description":"Confidence level assigned to the declared working-set profile."},"restartDelaySteps":{"type":"number","minimum":1,"maximum":600,"description":"Bounded restart/readiness delay measured in simulation steps before an OOMKilled replica rejoins serving capacity."},"restartDelaySeconds":{"type":"number","minimum":1,"maximum":600,"deprecated":true,"description":"Deprecated compatibility input. This historical spelling is normalized as simulation steps, not wall-clock seconds; use restartDelaySteps."},"maxConcurrentRestartFraction":{"type":"number","minimum":0.05,"maximum":1,"default":0.3333333333333333,"description":"Upper bound on the fraction of replicas that may be mid-restart at once, modeling k8s's staggered pod eviction/restart behavior."},"memoryAwareAutoscaling":{"type":"boolean","description":"Opt-in: when true, the modeled memory-pressure ratio can also trigger HPA scale-out alongside the existing CPU-only default. Leaving this unset/false keeps CPU-only scale-out decisions byte-identical."},"memoryScaleOutThresholdRatio":{"type":"number","minimum":0,"maximum":1,"default":0.85,"description":"Demand/limit ratio at or above which memory-aware scale-out engages. Only consulted when `memoryAwareAutoscaling` is true."}}},"kubernetesMemoryEvidence":{"type":"object","description":"Observed configuration from a standard Kubernetes Deployment or StatefulSet. These source-labelled request/limit facts are never treated as runtime working-set demand and do not activate OOM, restart, capacity, latency, error, autoscaling, or billing effects.","required":["source","sourceFile","kind","workload","namespace","status"],"properties":{"source":{"type":"string","enum":["kubernetes-manifest"],"description":"Manifest source category for the observed configuration."},"sourceFile":{"type":"string","maxLength":512,"description":"Repository-relative path of the manifest containing this configuration."},"kind":{"type":"string","enum":["Deployment","StatefulSet"],"description":"Kubernetes workload kind that declares the memory configuration."},"workload":{"type":"string","maxLength":256,"description":"Name of the Deployment or StatefulSet that supplied the configuration."},"namespace":{"type":"string","maxLength":256,"description":"Kubernetes namespace containing the workload."},"replicas":{"type":"integer","minimum":1,"maximum":1000000,"description":"Literal replica count declared by the workload, when available."},"status":{"type":"string","enum":["complete","partial","ambiguous","missing","non_literal"],"description":"Whether the manifest memory configuration was fully and unambiguously extracted."},"requestGiB":{"type":"number","exclusiveMinimum":0,"maximum":1024,"description":"Observed per-replica memory request from the manifest, in GiB."},"limitGiB":{"type":"number","exclusiveMinimum":0,"maximum":1024,"description":"Observed per-replica memory limit from the manifest, in GiB."},"reason":{"type":"string","enum":["multiple_containers","non_literal_memory","memory_not_declared"],"description":"Specific reason extraction was incomplete or unsupported."}}},"eksSpotInterruption":{"type":"object","description":"Opt-in deterministic AWS EKS Spot interruption lifecycle. Ordinary Spot pricing and risk behavior is unchanged when this object is absent. `triggerAtStep` is a simulation step; the interruption notice is always a bounded 120 simulated seconds.\n","required":["enabled"],"properties":{"enabled":{"type":"boolean","const":true,"default":true},"triggerAtStep":{"type":"integer","minimum":0,"maximum":100000,"default":0},"noticeWindowSeconds":{"type":"integer","const":120,"default":120},"simulationSecondsPerStep":{"type":"number","exclusiveMinimum":0,"maximum":60,"default":1},"workloadReplicas":{"type":"integer","minimum":1,"maximum":10000,"default":1},"imageSizeMiB":{"type":"number","minimum":0,"maximum":102400,"default":0},"imageCacheState":{"type":"string","enum":["warm","cold"],"default":"cold"},"imageCacheHit":{"type":"boolean"},"pullBandwidthMiBPerSecond":{"type":"number","exclusiveMinimum":0,"maximum":100000,"default":100},"schedulingCapacity":{"type":"integer","minimum":0,"maximum":10000,"default":1},"startupSeconds":{"type":"number","minimum":0,"maximum":3600,"default":10},"interruptionHandling":{"type":"string","enum":["reschedule","drain-only","fail-fast"],"default":"reschedule"}}},"healthChecks":{"type":"object","additionalProperties":false,"description":"Opt-in dependency-aware load-balancer and Kubernetes endpoint probes. Omission preserves existing simulation behavior. Dependency resource IDs are explicit graph references; livenessOnly checks do not depend on downstream resources.\n","properties":{"loadBalancer":{"$ref":"#/components/schemas/EndpointHealthCheck"},"livenessProbe":{"$ref":"#/components/schemas/EndpointHealthCheck"},"readinessProbe":{"$ref":"#/components/schemas/EndpointHealthCheck"},"deployment":{"type":"object","additionalProperties":false,"properties":{"maximumPercent":{"type":"integer","minimum":100,"maximum":200,"default":200},"minimumHealthyPercent":{"type":"integer","minimum":0,"maximum":100,"default":100}}},"maxConcurrentRestartFraction":{"type":"number","minimum":0.05,"maximum":1,"default":0.3333333333333333},"restartDelaySeconds":{"type":"number","minimum":0,"maximum":3600}}},"desiredTaskCount":{"type":"integer","minimum":0,"description":"ECS/Fargate only — initial desired running task count. With ECS CPU target tracking, the declared minimum is used when omitted. Fargate never uses generic compute cloning."},"minTaskCount":{"type":"integer","minimum":0,"description":"ECS/Fargate only — smallest running task fleet, including zero for scale-to-zero services."},"maxTaskCount":{"type":"integer","minimum":1,"description":"ECS/Fargate only — hard task-fleet ceiling. Offered traffic above total task capacity queues and can produce latency/errors."},"perTaskCapacityRps":{"type":"number","exclusiveMinimum":0,"description":"ECS/Fargate only — modeled serving capacity of one running task in requests per second; it may differ by service/tier and is not a provider performance guarantee."},"perTaskCapacityEvidence":{"type":"object","description":"Caller-supplied evidence. Without a documented/measured basis and supporting source, the rate is labeled an assumption. Guesses must not be called measured.","required":["basis"],"properties":{"basis":{"type":"string","enum":["assumption","documented","measured"],"description":"documented/measured are caller assertions and require a supporting source; assumption is unverified."},"source":{"type":"string","minLength":1,"maxLength":512,"description":"Supporting public document URL, report, or measurement reference. Required for documented or measured basis."}}},"applicationAutoscalingPolicy":{"type":"object","description":"Effective ECS CPU-only target-tracking policy when ecsCpuTargetTracking is enabled on create, or an explicitly supplied ECS policy.","properties":{"policyType":{"type":"string","enum":["TargetTrackingScaling","StepScaling"]},"metricType":{"type":"string","enum":["ECSServiceAverageCPUUtilization"]},"targetValue":{"type":"number"},"scaleOutCooldownSeconds":{"type":"integer","minimum":0,"maximum":86400},"scaleInCooldownSeconds":{"type":"integer","minimum":0,"maximum":86400},"simulationSecondsPerStep":{"type":"number","exclusiveMinimum":0,"maximum":60}}},"taskStartupSeconds":{"type":"number","minimum":0,"description":"ECS/Fargate only — modeled task startup wait in simulation steps (approximately seconds)."},"fargateApplicationModel":{"type":"object","description":"Explicit application assumptions, not native ECS or ALB behavior. The FIFO counts pending requests separately from goodput. Idle/wake requires an explicit timed target-tracking policy and minimum task count zero.","properties":{"queueCapacityRequests":{"type":"integer","minimum":1,"maximum":100000},"scaleToZeroOnIdle":{"type":"boolean"}}},"appReadySteps":{"type":"integer","minimum":0,"description":"Request-serving compute only — modeled application-readiness ramp after a task or instance becomes active. Zero keeps the default binary-ready behavior."},"taskScaleDownSeconds":{"type":"number","minimum":0,"description":"ECS/Fargate only — modeled quiet-period wait before a Fargate task is stopped."},"concurrency":{"type":"integer","minimum":1,"maximum":1000,"description":"Request-serving compute only — maximum simultaneous requests per\n active instance or replica. Capacity is derived from this value\n and requestDurationSeconds; Lambda is constrained to 1.\n","example":80},"requestDurationSeconds":{"type":"number","exclusiveMinimum":0,"description":"Request-serving compute only — modeled request duration in seconds.\n Each provider policy applies its documented billing granularity\n separately from capacity calculations.\n","example":0.25},"requestServingTargetUtilizationPct":{"type":"number","exclusiveMinimum":0,"maximum":100,"description":"AWS Lambda only — modeled scale-out target as a percentage of active-environment capacity. A value below 100% reserves request headroom; when omitted, scaling targets full modeled capacity.\n","example":70},"provisionedInstances":{"type":"integer","minimum":0,"description":"Explicit ready capacity. It raises the warm floor and accrues the provider's warm/provisioned capacity charge."},"idleTimeoutSeconds":{"type":"number","minimum":0,"description":"Idle period before a scale-to-zero eligible service releases non-required warm capacity."},"requestServingKind":{"type":"string","enum":["function","container"],"description":"Declares whether this request-serving resource follows function or HTTP container lifecycle semantics."},"billingState":{"type":"string","enum":["active","idle","stopped","detached","deleted"],"default":"active","description":"Residual billing state for idle-infrastructure cost simulation. `active` (default) — normal billing. `idle` — resource exists but serves no traffic; idle-family resources bill their flat residual rate. `stopped` — stopped compute/database: instance cost is $0 but any `capacityGB` storage keeps billing. `detached` — unattached IP / orphaned network artifact; bills the unattached rate for its `serviceFamily`. `deleted` — removed via idle-resource remediation; skipped entirely by the cost engine.\n","example":"stopped"},"size":{"type":"string","description":"Provider size/SKU label. For `serviceFamily: ecsFargate`, use an exact canonical allocation label such as `0.5 vCPU / 1 GB` or `1 vCPU / 2 GB`. A label and explicit `containerCpu`/`containerMemoryGiB` pair are equivalent; if both are supplied they must match. Unsupported, malformed, incomplete, or conflicting Fargate shapes return `INVALID_FARGATE_TASK_SIZE`. The default `0.25 vCPU / 0.5 GB` is used only when neither sizing form is supplied.\n","example":"m5.large"},"instanceType":{"type":"string","description":"Compatibility alias for `size`, commonly used for AWS EC2 instance types such as `t3.medium`. When `size` is omitted, this value is canonicalized into `size` and resolved through the provider catalog. If both fields are supplied they must be identical; `size` is the canonical field. Unknown values remain visible as unrecognized SKUs and use the normal estimated fallback coverage.\n","example":"t3.medium"},"ocpus":{"type":"integer","minimum":1,"maximum":126,"description":"OCI flexible compute capacity input, with family-specific documented limits validated against the selected shape. VM.Standard.E4.Flex accepts an explicit positive integer from 1 through 64 for catalog-backed capacity resolution. OCI x86 maps 1 OCPU to 2 vCPUs; this hardware specification does not establish provider-specific performance, throughput, latency, or price. Memory is independently selected and is not inferred as a fixed amount per OCPU. Invalid counts are rejected; omitting the count leaves a flexible shape explicitly generic.\n","example":4},"maxThroughput":{"type":"number","example":2000},"capacityRps":{"type":"number","description":"Compute only — the literal per-node RPS ceiling: the request rate at which this node reaches ~95% CPU (critical territory). When supplied, the engine converts it to an internal `effectiveMaxThroughput = capacityRps / criticalUtilThreshold` using the provider's calibrated saturation coefficient, so the CPU curve peaks at the stated capacity without changing any accuracy calibration. Agents that know their workload's RPS capacity can set this field instead of tuning `maxThroughput` directly. Simulations that omit `capacityRps` fall back to `maxThroughput` with byte-identical behaviour. Kubernetes create requests containing `capacityRps` are rejected; use `maxThroughput` for the total Kubernetes cluster RPS ceiling, or `nodePools[].maxThroughput` for per-node pool capacity.\n","example":100},"baseLatency":{"type":"number","example":3},"maxConnections":{"type":"integer","minimum":1,"maximum":1000000,"description":"Database only — the connection-pool capacity (maximum concurrent connections) for a database resource. The simulation models connection-pool pressure as `activeConnections / maxConnections` (surfaced as `metrics.connection_pressure`), so raising this value gives the database more headroom before the pool saturates. Aurora Serverless v2 retains an explicit limit at every ACU; when omitted, the PostgreSQL estimate derives from maximum configured ACU (16 ACU with minimum 2 gives 3360). Other databases retain their existing defaults.\n","example":800},"sessionAffinity":{"$ref":"#/components/schemas/SessionAffinity"},"connectionDemand":{"type":"object","additionalProperties":false,"required":["mode"],"description":"Database only. Omit to retain load-derived connection demand. declaredConnections and idlePoolFloor are ASSUMPTION / plan-time budgets (for example, replicas × per-pod pool size), not observed live DB connections. `declared` ignores instantaneous RPS; `max` takes the greatest of load-derived demand and the declared/floor values. Connection-pressure metrics report both load-derived and selected demand, plus the mode that drove pressure. This setting does not change cost or modeled database CPU/latency. Above the usable limit, declared demand adds a rule-based pool-saturation error signal (up to the existing 5% component cap); below the limit, provider-fit baseline errors remain load-derived unless saturationErrorThreshold is configured. That optional threshold suppresses pool errors below the configured aggregate utilization and applies the provider error curve to utilization beyond it; set the same threshold on every database in a multi-database graph. It does not change capacity, and this thresholded curve is not provider-calibrated.\n","properties":{"mode":{"type":"string","enum":["load-derived","declared","max"]},"saturationErrorThreshold":{"type":"number","minimum":0,"maximum":0.99,"description":"Optional utilization threshold for the load-derived pool error curve. Below it, pool saturation adds no errors; above it, the curve uses utilization beyond the threshold. In graphs with multiple databases, set the same value on all databases. This does not change connection capacity.\n"},"declaredConnections":{"type":"integer","minimum":0,"maximum":1000000},"idlePoolFloor":{"type":"integer","minimum":0,"maximum":1000000}},"allOf":[{"if":{"properties":{"mode":{"const":"load-derived"}},"required":["mode"]},"then":{"not":{"anyOf":[{"required":["declaredConnections"]},{"required":["idlePoolFloor"]}]}}},{"if":{"properties":{"mode":{"enum":["declared","max"]}},"required":["mode"]},"then":{"anyOf":[{"required":["declaredConnections"]},{"required":["idlePoolFloor"]}]}}]},"multiAz":{"type":"boolean","description":"For AWS Aurora Serverless v2, true with instanceCount 2 creates a separately billed reader in another AZ. Legacy true without instanceCount has no inferred reader and emits a normalized-config warning.\n"},"instanceCount":{"type":"integer","minimum":1,"maximum":100,"description":"For generic compute, this fixes the number of represented VMs (integer 1–100); capacity is aggregated and CPU is reported per VM. It cannot be combined with autoscaling:true, minInstances or maxInstances. For AWS Aurora Serverless v2, supported values remain 1 with multiAz false or 2 with multiAz true; two instances have separate ACU charges. Conflicting explicit replicas, unknown AZs and unsupported families are rejected. A writer's derived reader has id <writer-id>-reader, characteristics.replicaOf and a distinct location.zoneKey.\n"},"replicaOf":{"type":"string","description":"Database only — explicit ID of the writer this independently modeled healthy database can replace during a chaos database_crash. Promotion defaults to 30 simulation seconds; otherwise the sole writer remains down for the injection duration plus a modeled 1800-second restart delay. Chaos custom injections can override these delays. These are assumptions, not AWS SLAs. A supported Aurora Serverless v2 multiAz:true and instanceCount:2 pair creates this explicit relationship during simulation creation; multiAz alone does not.\n"},"auroraStandbyResourceId":{"type":"string","description":"Opt-in modeled Aurora writer failover. On an AWS Aurora database writer, reference the ID of a distinct healthy AWS Aurora database standby in this simulation. A supported Aurora Serverless v2 multiAz:true and instanceCount:2 pair populates this reference. multiAz alone does not imply standby capacity. Quick inject-failure models a one-step promotion interruption, then reduced DB failure impact while the writer remains parked. No AWS calls or observed failover timing.\n"},"capacityGB":{"type":"number","description":"Storage only — provisioned storage capacity in gigabytes for a storage/volume resource. Used together with `maxIops` to model block-storage throughput and IOPS utilization.\n","example":500},"maxIops":{"type":"number","description":"Storage only — the provisioned maximum IOPS (I/O operations per second) for a block-storage volume. The simulation models storage IOPS utilization against this ceiling (surfaced as `metrics.storageIopsUtilization`). Falls back to the provider's default volume profile IOPS when omitted.\n","example":3000},"inferenceMode":{"type":"boolean","description":"Kubernetes only — marks a GPU/inference cluster. When `true` the engine bills each worker node at the provider's GPU node rate and emits the inference economics metrics (`metrics.gpuUtilization`, `metrics.tokensPerSecond`, `metrics.costPerMillionTokens`). GPU clusters also participate in idle-GPU detection (sustained utilization below 15% fires an idle-candidate event with the monthly waste estimate) and the GPU right-sizing hints (`gpu-underutilized`, `gpu-saturated`, `gpu-api-breakeven`).\n","example":true},"accelerator":{"type":"string","description":"Kubernetes + `inferenceMode` only — the GPU/accelerator type of the node pool. Known values: `a10g` (AWS g5), `t4` (GCP), `a10-fractional` (Azure NC8ads_A10_v4 — 1/3 A10 partition at one-third of full-A10 throughput), `a10` (OCI VM.GPU.A10.1 — full A10), `h100` (DigitalOcean GPU Droplet). Determines the per-node token throughput used to derive `metrics.tokensPerSecond` and `metrics.costPerMillionTokens`.\n","example":"a10g"},"managedComputePlatform":{"type":"string","enum":["nvidia-dgx-cloud","nvidia-dgx-cloud-lepton"],"description":"Kubernetes + `inferenceMode` only — managed AI platform overlay running on top of the underlying cloud provider. When set to `nvidia-dgx-cloud`, the resource is an NVIDIA DGX Cloud node (e.g. `dgx-h200`, `dgx-b200`, `gb200-nvl72`) hosted on the provider named in `provider` (azure/gcp/oci) — the provider drives region and base pricing while the platform drives billing semantics (bundled control plane and fabric networking). `nvidia-dgx-cloud-lepton` is reserved for future multi-provider placement.\n","example":"nvidia-dgx-cloud"},"openshiftOffering":{"type":"string","enum":["rosa-hcp","rosa-classic","aro","openshift-dedicated","self-managed"],"description":"Kubernetes-only distribution overlay. The resource `provider` remains the backing substrate for regions, network behavior, worker-node pricing, and provider latency. `rosa-hcp` and `rosa-classic` require AWS; `aro` requires Azure; `openshift-dedicated` supports AWS or GCP; and `self-managed` supports any existing CWM backing provider. Platform/licensing charges are additive to worker-node charges, and cost breakdowns identify worker, platform, and control-plane rows separately. AWS publishes ROSA's $0.171 per 4-vCPU worker service fee uniformly across supported standard regions and its ROSA HCP $0.25 per-cluster-hour fee. Azure publishes the ARO D4s v3 (4 vCPU) East US OpenShift line at $124.830/month, separate from the $160.60/month Linux VM line ($0.171/hour using 730 hours/month). Verified 2026-08-27 at https://azure.microsoft.com/en-us/pricing/details/openshift/. A three-worker, 2-vCPU-per-worker topology derives a valid $0.2565/hour platform total from that unit rate ($0.171 × 6 ÷ 4); it is not an independent published rate. The published ARO platform unit rate is marked `official` only when the resource location is Azure East US; resources in another region, or without a region, retain the East US amount as an estimate/reference assumption. OpenShift\n Dedicated and self-managed subscription allocations remain\n estimated where no comparable universal hourly line is\n published. Omit this field for byte-compatible generic\n Kubernetes.\n","example":"rosa-hcp"},"topology":{"type":"object","description":"Kubernetes + `inferenceMode` only — categorical GPU interconnect topology of the node. Two independent fields govern two independent efficiency coefficients.\n`intraNode` (inference): bounds tensor-parallel serving efficiency within a single node (PCIe 0.70, NVLink 0.85, NVLink+NVSwitch 0.92, InfiniBand 0.80). Omitting `intraNode` yields exactly 1.0, preserving pre-topology behavior.\n`interNode` (distributed training, `trainingMode: true` + more than one node): bounds collective-communication (all-reduce) efficiency across N DGX nodes (InfiniBand 0.90, NVLink+NVSwitch 0.88, NVLink 0.82, PCIe/Ethernet 0.65). Ignored for inference resources and for single-node training (where there is no inter-node all-reduce); absent `interNode` yields exactly 1.0.\nFlat-form aliases: `characteristics.intraNode` and `characteristics.interNode` (top-level, same enum) are also accepted and normalized into this nested `topology` object at the API boundary. The nested value wins when both shapes are present; only the nested form is stored and echoed back.\n","properties":{"intraNode":{"type":"string","enum":["pcie","nvlink","nvlink-nvswitch","infiniband"],"description":"GPU-to-GPU interconnect within a single node. Applied to inference resources (default); ignored when `trainingMode: true`.\n","example":"nvlink-nvswitch"},"interNode":{"type":"string","enum":["pcie","nvlink","nvlink-nvswitch","infiniband"],"description":"Node-to-node fabric for distributed training. Applied only when `trainingMode: true` and node count > 1; ignored for inference resources and single-node training.\n","example":"infiniband"}}},"intraNode":{"type":"string","enum":["pcie","nvlink","nvlink-nvswitch","infiniband"],"description":"Flat-form alias for `topology.intraNode`. Normalized into the nested `topology` object at the API boundary before storage; the nested value wins when both are present. Only the nested form is stored and echoed back.\n","example":"nvlink-nvswitch"},"interNode":{"type":"string","enum":["pcie","nvlink","nvlink-nvswitch","infiniband"],"description":"Flat-form alias for `topology.interNode`. Normalized into the nested `topology` object at the API boundary before storage; the nested value wins when both are present. Only the nested form is stored and echoed back.\n","example":"infiniband"},"includesControlPlane":{"type":"boolean","description":"Kubernetes only — when `true`, the managed platform's node rate already bundles the Kubernetes control plane, so the per-hour managed-K8s cluster fee is skipped at every billing surface. Set by DGX Cloud resources; absent/false preserves standard provider billing.\n","example":true},"bundledNetworking":{"type":"boolean","description":"Kubernetes only — when `true`, high-performance fabric networking (e.g. InfiniBand) is bundled into the platform node rate rather than billed separately. Declarative flag set by DGX Cloud resources; informational in Phase 1.\n","example":true},"trainingMode":{"type":"boolean","description":"Kubernetes + `inferenceMode` only — when `true`, the resource models a distributed-training workload across multiple DGX nodes. The simulation engine applies the inter-node topology coefficient (`topology.interNode`) to bound aggregate throughput by the collective-communication (all-reduce) efficiency of the cross-node fabric: InfiniBand 0.90, NVLink-Switch 0.88, NVLink 0.82, PCIe/Ethernet 0.65. When `false` or absent (default), inference mode is used and `topology.interNode` is ignored — behaviour is byte-identical to resources created before this field existed.\n","example":true},"tokensPerRequest":{"type":"number","description":"Kubernetes + `inferenceMode` only — average output tokens generated per inference request. Bounds token throughput by request demand (`throughputRps × tokensPerRequest`) and drives the decode component of the inference latency model (`latencyP50/P95/P99`). Defaults to 500 when omitted.\n","example":500},"inputTokensPerRequest":{"type":"number","description":"Kubernetes + `inferenceMode` only — average prompt (prefill) tokens per inference request. Drives the time-to-first-token component of the inference latency model. Defaults to `2 × tokensPerRequest` (a typical ⅔-input / ⅓-output chat mix) when omitted.\n","example":1000},"cacheHitRate":{"type":"number","minimum":0,"maximum":1,"description":"Cache / CDN only — the expected cache hit rate as a fraction between 0 and 1 (e.g. `0.85` = 85%). Higher values reduce the load that reaches downstream origin/database resources and lower effective latency. Defaults to 0.8 when omitted for cache resources. Declared request-flow caches must specify it explicitly.\n","example":0.85},"cacheableRequestShare":{"type":"number","minimum":0,"maximum":1,"description":"Optional request-flow assumption for an explicitly modeled cache: the fraction of offered requests that are eligible for cache lookup. Cache hits reduce downstream work only within this share; remaining traffic bypasses the cache. This is a scenario assumption, not a measured production request distribution.\n","example":0.75},"asynchronousRequestShare":{"type":"number","minimum":0,"maximum":1,"description":"Optional request-flow assumption for an explicitly modeled queue: the fraction of offered requests enqueued for asynchronous processing. Queue consumers pass work downstream at their configured drain rate; queued messages do not reach databases until consumed. Cacheable and asynchronous shares must sum to at most 1.\n","example":0.15},"queueConsumerCount":{"type":"integer","minimum":1,"maximum":100000,"description":"Number of modeled consumers for an explicitly configured queue. Total drain capacity is this count multiplied by `queueDrainRatePerConsumerRps`.\n","example":4},"queueDrainRatePerConsumerRps":{"type":"number","exclusiveMinimum":0,"maximum":1000000,"description":"Modeled messages consumed per second by each queue consumer. This is a disclosed scenario assumption, not a provider-measured throughput guarantee.\n","example":125},"queueCapacityMessages":{"type":"integer","minimum":1,"maximum":10000000,"description":"Explicit queue backlog capacity in messages. Queue fill is backlog divided by this capacity, not offered RPS divided by maximum throughput.\n","example":20000},"queueSimulationStepSeconds":{"type":"number","exclusiveMinimum":0,"maximum":60,"description":"Simulation window in seconds used to convert per-second queue enqueue and drain rates into backlog messages.\n","example":1},"queueVisibilityTimeoutSeconds":{"type":"number","exclusiveMinimum":0,"maximum":43200,"description":"SQS-style visibility timeout for unacknowledged messages. Messages return to the visible queue only after an actual consumer delivery remains unacknowledged for this duration."},"queueRetentionSeconds":{"type":"number","exclusiveMinimum":0,"maximum":1209600,"description":"Maximum message age. Expired messages are deleted; retention expiry does not transfer them to the dead-letter queue."},"queueMaxReceiveCount":{"type":"integer","minimum":1,"maximum":1000,"description":"Maximum delivery attempts before an unacknowledged message is moved to the dead-letter queue."},"queueDeadLetterQueueResourceId":{"type":"string","minLength":1,"description":"ID of a separate queue resource connected from this source queue."},"queueConsumerAckFailureRatePct":{"type":"number","minimum":0,"maximum":100,"description":"Optional assumed fraction of attempted consumer deliveries that are not acknowledged. A nonzero value requires complete queue lifecycle settings; the default is zero."},"queueConsumerFor":{"type":"string","minLength":1,"description":"Compute-resource role binding to the ID of its directly connected queue. Producer scaling does not alter these consumer resources."},"cdnTraffic":{"$ref":"#/components/schemas/CdnTraffic"},"autoscaling":{"type":"boolean","description":"Compute only — marks a compute resource as the autoscaling primary. When multiple compute resources exist, the engine uses the one flagged `autoscaling: true` as the autoscaling target; otherwise it falls back to the first compute resource.\n","example":true},"maxInstances":{"type":"integer","minimum":1,"description":"Compute only — hard per-resource ceiling for RL `scale_out`. For request-serving services, this is the maximum active instances, replicas, or Lambda concurrent execution environments; it caps capacity and sheds excess demand rather than creating VM clones. For regular compute, it constrains the effective maximum below the simulation-wide autoscaling limit.\n","example":5},"minInstances":{"type":"integer","minimum":0,"description":"Compute only — hard per-resource floor for RL `scale_in`. For request-serving services, this is the minimum warm instance or replica floor (raised by `provisionedInstances` when larger). For regular compute, it prevents scale-in below the declared floor.\n","example":2},"scaleOutCpuThreshold":{"type":"number","minimum":0,"maximum":100,"description":"Compute or Kubernetes — overrides the simulation-wide CPU autoscaling scale-out target (`autoscalingConfig.scaleOutCpuThreshold`, also settable via the top-level `autoscalingTargetCpu` / `scaleOutCpuThreshold` / `scaleOutCpuPercent` / `autoscaleTargetCpuPercent` aliases) for THIS resource's scale decisions only — every other resource in the simulation keeps using the simulation-wide default. Lets, for example, one GKE cluster scale out at 60% while an EC2 fleet in the same simulation scales out at 80%. Absent → this resource follows the simulation-wide default (byte-identical to before this field existed).\n","example":60},"scaleInCpuThreshold":{"type":"number","minimum":0,"maximum":100,"description":"Compute or Kubernetes — overrides the simulation-wide CPU autoscaling scale-in target (`autoscalingConfig.scaleInCpuThreshold`) for THIS resource's scale decisions only. Absent → this resource follows the simulation-wide default.\n","example":25},"nodeCount":{"type":"number","description":"Kubernetes only — the current number of worker nodes in the managed node pool. The cluster's `maxThroughput` and hourly cost scale proportionally with this count as autoscaling adds/removes nodes.\n","example":2},"workerVcpu":{"type":"number","exclusiveMinimum":0,"description":"Kubernetes OpenShift only — vCPU capacity of each worker node used to prorate published platform or license fees billed per 4 vCPU-hours. Defaults to the canonical 2-vCPU worker when no size is recognized; set explicitly for custom shapes.\n","example":4},"minNodes":{"type":"number","description":"Kubernetes only — the minimum number of worker nodes the node pool will scale in to. Defaults to the provider autoscaling profile minimum when omitted.\n","example":2},"maxNodes":{"type":"number","description":"Kubernetes only — the maximum number of worker nodes the node pool will scale out to. Defaults to the provider autoscaling profile maximum when omitted.\n","example":10},"perNodeRate":{"type":"number","exclusiveMinimum":0,"description":"inferenceMode Kubernetes only — caller-supplied GPU node hourly rate (USD/hr per worker node). Use this when the cluster size is not in the CWM GPU catalog (GPU_LARGE_NODE_HOURLY_RATES) to restore cost modeling for the unknown SKU. Must be strictly positive; zero and negative values are rejected (a zero rate looks \"free\" to any consumer that skips the costFidelity field, which recreates the false-precision problem this field was designed to solve). When absent and the SKU is also unrecognised, the cluster cost is excluded from the aggregate total and costComplete is set to false in the step response.\n","example":3.2},"pricingModel":{"type":"string","enum":["on-demand","spot"],"description":"Compute and Kubernetes only — the pricing model for worker instances. `on-demand` (default) uses standard pay-as-you-go rates. `spot` applies the provider's spot/preemptible discount to reduce instance cost significantly (AWS/GCP/Azure: ~60% off on-demand; OCI: exactly 50% off) at the cost of potential interruption when the cloud provider needs capacity back. Non-compute resource types (database, storage, network, etc.) and DigitalOcean resources (which have no spot offering) ignore this field.\n","example":"on-demand"},"eksSpotMigration":{"type":"object","description":"AWS EKS Kubernetes Spot interruption model. Opt-in only. When enabled, CWM emits a bounded 120-second interruption notice, workload migration/readiness progress, and a separate deadline hit/miss result. Migration evidence is not the same as sustained service-health recovery.\n","properties":{"enabled":{"type":"boolean","default":false},"deadlineSeconds":{"type":"integer","minimum":1,"maximum":120,"default":120},"workloadCount":{"type":"integer","minimum":1,"maximum":1000,"default":1},"imageSizeMiB":{"type":"number","minimum":0,"maximum":10000000,"default":0},"imageCached":{"type":"boolean","default":true},"pullBandwidthMiBPerSecond":{"type":"number","exclusiveMinimum":0,"maximum":1000000,"default":100},"schedulingCapacity":{"type":"number","exclusiveMinimum":0,"maximum":1000,"default":100},"startupSeconds":{"type":"number","minimum":0,"maximum":120,"default":0},"migrationDurationSeconds":{"type":"number","minimum":0,"maximum":3600,"description":"Optional deterministic scenario override. When omitted, CWM derives migration duration from image pull, scheduling, and startup inputs.\n"},"triggerAtSeconds":{"type":"number","minimum":0,"default":0},"handling":{"type":"string","enum":["reschedule","no_reschedule"],"default":"reschedule"}}},"nodePools":{"type":"array","description":"Kubernetes only — multiple node pools per cluster. When present, each pool scales independently within its own min/max bounds and is billed at its own per-node hourly rate. The legacy single-pool fields (`nodeCount`, `minNodes`, `maxNodes`) are used only when `nodePools` is absent.\n","items":{"type":"object","properties":{"name":{"type":"string","description":"Human-readable node pool name.","example":"general"},"nodeCount":{"type":"number","description":"Current number of worker nodes in this pool. Scales proportionally with the pool's serving capacity and cost as autoscaling adds/removes nodes.\n","example":2},"workerVcpu":{"type":"number","exclusiveMinimum":0,"description":"Kubernetes OpenShift only — vCPU capacity of each worker node in this pool for published per-4-vCPU platform or license fee proration. Falls back to the cluster-level workerVcpu, recognized size, or 2 vCPU.\n","example":8},"minNodes":{"type":"number","description":"Minimum number of worker nodes this pool will scale in to. Defaults to the provider autoscaling profile minimum when omitted.\n","example":2},"maxNodes":{"type":"number","description":"Maximum number of worker nodes this pool will scale out to. Defaults to the provider autoscaling profile maximum when omitted.\n","example":10},"perNodeRate":{"type":"number","exclusiveMinimum":0,"description":"Hourly cost per node for this pool, USD/hr. Must be strictly positive (zero and negative values are rejected). Falls back to the provider default rate when omitted.\n","example":0.096},"maxThroughput":{"type":"number","description":"Per-node serving capacity (requests/second) for this pool. When pools carry different instance sizes, the engine uses these values to compute the correct CPU credit on scale events — a pool of large nodes relieves more load per node added or removed than a pool of small nodes. Falls back to a uniform distribution of the cluster's total `maxThroughput` across all nodes when omitted (preserving backwards-compatible behaviour for homogeneous fleets).\n","example":10000}}}},"streamShardsCount":{"type":"number","description":"DynamoDB only — number of shards provisioned for the DynamoDB Stream attached to this table. Each shard processes up to 1 000 records/s. When write throughput (see `streamWriteRps`) exceeds the total shard capacity, the simulation engine accumulates iterator age (seconds) and emits a `stream_lag` warning event. The iterator age is drained proportionally when traffic falls back within shard capacity. Ignored on non-DynamoDB resources.\n","example":4},"streamWriteRps":{"type":"number","description":"DynamoDB only — explicit write throughput (records/s) for the DynamoDB Stream. When omitted the engine estimates write load as 10 % of the current simulation traffic. Providing an explicit value is useful when the stream write pattern is decoupled from the simulated request traffic (e.g. bulk imports or batch pipelines that run independently). Ignored when `streamShardsCount` is 0 or absent.\n","example":3500},"eventNotificationsEnabled":{"type":"boolean","description":"Storage event notifications — when true the engine models event notification delivery latency: baseline 1 500 ms, rising by 1 ms per 10 RPS above 5 000 RPS, capped at 30 000 ms. The latency penalty is propagated to downstream serverless compute resources (Lambda for S3, Cloud Functions for GCS, Azure Functions for Azure Blob via Event Grid, OCI Functions for OCI Object Storage). A `notification_delayed` warning event is emitted once when simulation traffic exceeds 20 000 RPS and cleared when traffic drops back below that threshold. Supported providers: `aws` (S3), `gcp` (GCS), `azure` (Blob Storage), `oci` (Object Storage). Ignored on non-storage resources and OCI block-volume resources.\n","example":true},"partitionCount":{"type":"number","description":"Azure Event Hubs only — number of partitions provisioned on the event hub. Each partition processes up to `partitionWriteRps` write records/s (defaults to 1 000 records/s per partition when omitted). When write throughput exceeds total partition capacity the engine accumulates `partitionLag` (seconds) in resource metadata and emits a `partition_lag` warning event when lag exceeds 30 s. Ignored on non-eventhub resources.\n","example":8},"streamPartitions":{"type":"number","description":"OCI Streaming only — number of stream partitions. Uses the same Kafka-style offset lag model as Azure Event Hubs `partitionCount`. When absent the engine falls back to `partitionCount`. A `stream_lag` warning event fires when lag exceeds 30 s. Ignored on non-oci-streaming resources.\n","example":4},"partitionWriteRps":{"type":"number","description":"Azure Event Hubs / OCI Streaming only — total write records/second produced into the event stream by all upstream writers combined. When omitted the engine estimates write load as 10 % of the current simulation traffic. Each partition handles 1 000 records/s; lag accumulates when `partitionWriteRps` exceeds `partitionCount × 1 000` (Event Hubs) or `streamPartitions × 1 000` (OCI Streaming).\n","example":5000},"warmupSteps":{"type":"integer","minimum":1,"description":"Compute and Kubernetes only — length of the JIT/CPU warm-up window in simulation steps. The capacity penalty is steepest on the first step (counter 0) and decays linearly to zero once the counter reaches this value, after which the resource operates at full capacity. Falls back to the engine default of 3 when omitted. A longer window models runtimes that take many steps to reach full throughput (e.g. JVM + JIT compilation, Python ML model loading); a value of 1 produces a single-step burst followed immediately by full capacity (useful for static binaries or pre-warmed containers). Ignored on non-compute resource types.\n","example":5},"warmupSeverity":{"type":"number","minimum":0,"maximum":1,"description":"Compute and Kubernetes only — peak fraction by which effective capacity is reduced on the first warm-up step. For example, `0.8` reduces capacity to 20 % of its normal value on step 0. The penalty decays linearly to 0 across the `warmupSteps` window. Falls back to the engine default of `0.5` (capacity halved) when omitted. Use higher values (closer to 1) to model cold-start-heavy stacks such as JVM applications or services that load large ML models at start; use lower values to model near-instant start-ups; set to `0` to disable the warm-up capacity penalty entirely. Ignored on non-compute resource types.\n","example":0.8}}},"routedRps":{"type":"number","description":"Compute and Kubernetes only — effective requests per second routed to this resource during the most recent simulation step. Only present on compute and kubernetes resources in step responses (`POST /simulations/{id}/step`); absent on GET and on non-compute, non-kubernetes resource types.\nThe value is a float (one decimal place) so per-node values sum exactly to the simulation-level traffic without integer rounding error. For an ALB topology with three nodes at 45 RPS total all three nodes report `routedRps: 15.0`; if one node is killed the two surviving nodes each report `routedRps: 22.5` (the killed node's share is redistributed to the survivors). A node that is failed or parked reports `routedRps: 0.0`. Kubernetes clusters in an ALB topology report the per-cluster share of the routed traffic.\n","example":15},"availabilityState":{"type":"string","enum":["available","degraded","unavailable","scaled_to_zero","cold_start"],"description":"Compute, Kubernetes, and Database only — explicit availability classification set by the step pipeline after traffic redistribution is finalised. Absent on resource types where availability is not tracked (network, storage, cache, queue, security).\n`\"available\"` — the resource is healthy and serving traffic normally.\n`\"degraded\"` — the resource is under stress (`status: \"critical\"` or `\"warning\"`) but still serving positive traffic. `isRoutable` is `true`. A saturated DB with many open connections that is still handling requests correctly shows `\"degraded\"`, NOT `\"unavailable\"` — `status: \"critical\"` alone does not imply unavailability.\n`\"unavailable\"` — the resource is not serving any traffic: it is parked-critical (cpuUsage=0 and/or maxThroughput=0 while status=critical), downed by an AZ outage, or removed by an instance_kill failure. `isRoutable` is `false`.\n`\"scaled_to_zero\"` — an ECS Fargate service has no running tasks after scale-in. `\"cold_start\"` — an ECS Fargate service has no task ready to serve while tasks start. Both states have `isRoutable: false`; neither means a failed or parked resource.\nThe field is optional for backward compatibility with stored snapshots that predate this field; it is always present on resources returned by step endpoints.\n","example":"available"},"isRoutable":{"type":"boolean","description":"Compute, Kubernetes, and Database only — whether this resource is receiving routed traffic during the current simulation step.\n`true` for resources with `availabilityState` of `\"available\"` or `\"degraded\"`. `false` for resources with `availabilityState` of `\"unavailable\"`, `\"scaled_to_zero\"`, or `\"cold_start\"`.\nAbsent on resource types where availability is not tracked (network, storage, cache, queue, security), and absent on stored snapshots that predate this field.\n","example":true},"recoveryBlockedReason":{"type":"string","description":"When the recovery cooldown is not advancing because an engine guard blocked the demotion step, this field names the active guard. Absent when no guard is active and the resource is progressing normally through its cooldown arc.\n`\"failure_park_window\"` — the node was explicitly failed via injectNodeFailure and the minimum park-window has not yet elapsed; cooldownSteps is counting up toward the window boundary, not down toward healthy.\n`\"idle_cpu_floor\"` — the resource is parked-critical but the estimated idle CPU is still above the recovery threshold, so it cannot heal yet.\nThe field is cleared (absent) on every step where neither guard fires.\n"},"failureParkStepsRemaining":{"type":"integer","minimum":0,"description":"Compute and Kubernetes only — number of simulation steps remaining in the forced unavailability window after an `inject-failure` call. Present only on step responses (`POST /simulations/{id}/step`); absent on GET responses, on resources that were not explicitly failed via `inject-failure`, and when the forced window has expired (value would be 0, so the field is omitted instead).\nThe value counts down from `parkSteps - 1` on the first step after failure injection toward 0 on the last step of the park window, where `parkSteps` is `recoveryPolicy.failureParkSteps` when supplied and otherwise `recoveryPolicy.criticalSteps` (default 4, so the initial value is typically 3). Once the field is absent the node enters its organic cooldown-demotion recovery arc: it transitions critical → warning → healthy over subsequent steps.\n","example":3},"recoveryProgress":{"$ref":"#/components/schemas/RecoveryProgress"},"recoveryPolicy":{"type":"object","description":"Per-resource recovery thresholds that control how quickly the simulation transitions a resource from critical → warning → healthy. The four cooldown fields default to the global values (criticalCpuThreshold: 80, criticalSteps: 4, warningCpuThreshold: 70, warningSteps: 3) when omitted. `failureParkSteps`, when supplied, controls only the bounded unavailability window for the quick `inject-failure` flow; after that window the node can rejoin routing and continues through organic critical → warning → healthy cooldown. It must be at least 1. Use the persistent `instance_down` failure plus `recover-resource` when the node must remain unavailable until an explicit recovery action. Stateless microservices can use lower thresholds and fewer steps to heal faster; databases can use higher thresholds and more steps for a stricter recovery window.\n","properties":{"criticalCpuThreshold":{"type":"number","minimum":0,"maximum":100,"default":80,"description":"CPU must drop to or below this percentage before the critical → warning cooldown clock starts.","example":75},"criticalSteps":{"type":"integer","minimum":1,"default":4,"description":"Number of consecutive simulation steps the CPU must stay below criticalCpuThreshold before transitioning from critical to warning.","example":2},"warningCpuThreshold":{"type":"number","minimum":0,"maximum":100,"default":70,"description":"CPU must drop to or below this percentage before the warning → healthy cooldown clock starts.","example":60},"warningSteps":{"type":"integer","minimum":1,"default":3,"description":"Number of consecutive simulation steps the CPU must stay below warningCpuThreshold before transitioning from warning to healthy.","example":1},"failureParkSteps":{"type":"integer","minimum":1,"description":"Number of simulation steps that a node failed through the quick inject-failure flow remains unavailable before it may rejoin routing. When omitted, criticalSteps is used. Zero is rejected.\n","example":2}}}}},"Connection":{"type":"object","required":["sourceId","targetId"],"properties":{"sourceId":{"type":"string","description":"ID of the source resource"},"targetId":{"type":"string","description":"ID of the target resource"}}},"SimulationCostAssumptions":{"type":"object","additionalProperties":false,"description":"Optional network billing assumptions. Omitted legacy settings are resolved by the engine. Byte sizes are finite and bounded to 1 GiB.","properties":{"clientLocation":{"type":"string","enum":["internet","in-cluster","same-zone","cross-zone","cross-region"],"default":"internet"},"responseSizeBytes":{"type":"number","minimum":0,"maximum":1073741824,"default":5000},"errorResponseSizeBytes":{"type":"number","minimum":0,"maximum":1073741824,"default":512},"requestSizeBytes":{"type":"number","minimum":0,"maximum":1073741824,"default":1024},"gcpNetworkTier":{"type":"string","enum":["premium","standard"],"default":"premium"}}},"SimulationCostAssumptionsOverride":{"type":"object","additionalProperties":false,"description":"Partial overrides merged over the benchmark artifact before defaults are resolved. Omitted fields retain artifact settings.","properties":{"clientLocation":{"type":"string","enum":["internet","in-cluster","same-zone","cross-zone","cross-region"]},"responseSizeBytes":{"type":"number","minimum":0,"maximum":1073741824},"errorResponseSizeBytes":{"type":"number","minimum":0,"maximum":1073741824},"requestSizeBytes":{"type":"number","minimum":0,"maximum":1073741824},"gcpNetworkTier":{"type":"string","enum":["premium","standard"]}}},"CostBasis":{"type":"object","additionalProperties":true,"description":"Structured modeled-cost provenance and assumptions; optional for legacy metrics."},"Simulation":{"type":"object","properties":{"costAssumptions":{"$ref":"#/components/schemas/SimulationCostAssumptions"},"appWeight":{"$ref":"#/components/schemas/AppWeight"},"appWeightDefaulted":{"type":"boolean","description":"True only when the create request omitted appWeight. Immutable intake provenance."},"predictionEvidence":{"$ref":"#/components/schemas/PredictionEvidence"},"id":{"type":"string","format":"uuid","description":"Unique simulation identifier"},"name":{"type":"string"},"description":{"type":"string"},"resources":{"type":"array","items":{"$ref":"#/components/schemas/Resource"}},"connections":{"type":"array","items":{"$ref":"#/components/schemas/Connection"}},"traffic":{"type":"number"},"runtimeTrafficOverrideActive":{"type":"boolean","description":"Server-managed runtime control. When true on the SQS Queue Backlog Saturation scenario, steps ignore finalized traffic patterns without changing replay identity. Callers cannot set this field."},"scenarioAttribution":{"type":"object","description":"Trusted server-side attribution copied from the live scenario catalog; absent for explicit resource-graph creates.","required":["id","version","revision","source"],"properties":{"id":{"type":"string","description":"Live scenario catalog ID that supplied the simulation graph"},"version":{"type":"string","nullable":true,"description":"Catalog version, when provided"},"revision":{"type":"string","nullable":true,"description":"Catalog revision, when provided"},"source":{"type":"string","enum":["scenario-catalog"],"description":"Trusted server-side attribution source"}}},"currentTime":{"type":"integer","description":"Current simulation time step"},"scenarioHash":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Canonical SHA-256 of the persisted scenario graph and attached traffic-pattern order."},"effectiveConfigHash":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Versioned prediction SHA-256 over the replay startup-input hash, engine version, and calibration identity. The original replay-only hash remains at replayIdentity.effectiveConfigHash."},"predictionEffectiveConfigHash":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Engine-versioned prediction identity, separate from the replay-input identity."},"engineVersion":{"type":"string","description":"Simulation engine version used to produce the prediction."},"calibrationEvidence":{"$ref":"#/components/schemas/CalibrationEvidence"},"replayIdentity":{"$ref":"#/components/schemas/ReplayIdentity"},"normalizedConfig":{"$ref":"#/components/schemas/NormalizedConfig"},"normalizationReceipt":{"$ref":"#/components/schemas/NormalizationReceipt"},"resilienceConfig":{"allOf":[{"$ref":"#/components/schemas/ResilienceConfig"}],"description":"Optional retry/cascade resilience model. When present, the step engine\nmodels retry amplification, circuit-breaker state, rate limiting, and\ncascading-failure depth across declared dependencies. Absent means the\nresilience model is disabled for this simulation (existing simulations are\nbyte-identical and unaffected).\n"}}},"ReplayIdentity":{"type":"object","required":["scenarioHash","effectiveConfigHash"],"properties":{"scenarioHash":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Canonical SHA-256 of the persisted scenario graph and attached traffic-pattern order."},"effectiveConfigHash":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Canonical SHA-256 of the effective six-control startup configuration."}}},"AppWeight":{"type":"string","enum":["lean","typical","heavy"],"default":"typical","description":"Immutable root-level simulation.create input. Omission selects typical and\nappWeightDefaulted=true; explicit typical has identical numerical predictions.\nIn the generic fallback at 100 total RPS across two m5.large apps (50 per server), typical targets\n20% app-host CPU, P50 18.5 ms and P95 45 ms, not the deliberately lean\nbenchmark's 1.9% CPU. These are placeholder guidance, not AWS measurements;\nthe separately eligible owned typical Node CRUD workload uses its measured\n20/100/200 RPS fit instead.\nHeavy doubles typical load/latency multipliers as an unsupported product assumption.\n"},"PredictionRange":{"type":"object","required":["low","central","high"],"properties":{"low":{"type":"number","minimum":0},"central":{"type":"number","minimum":0},"high":{"type":"number","minimum":0}}},"PredictionQuantityEvidence":{"allOf":[{"$ref":"#/components/schemas/PredictionRange"},{"type":"object","required":["evidenceLevel"],"properties":{"evidenceLevel":{"type":"string","enum":["measured","scaled from measured","reference estimate"]}}}]},"PredictionLatencyAvailability":{"type":"object","additionalProperties":false,"required":["status","reason","resourceIds"],"properties":{"status":{"type":"string","enum":["unavailable"]},"reason":{"type":"string","minLength":1,"description":"Names the unresolved Fargate shape, missing workload-specific latency calibration, and applicable per-edge timeout boundary."},"resourceIds":{"type":"array","items":{"type":"string","minLength":1}}}},"PredictionEvidence":{"type":"object","description":"Versioned provenance of prediction basis, not a claim simulated outputs were\nobserved. CPU is mean serving app-host CPU (not aggregate cpuUsage); no apps\nmeans appCpu=null and an empty per-resource array. `latencyP99Basis`\nindependently identifies the P99 basis: owned in-VPC internal-ALB fit,\nscaled from that fit (not directly measured), or uncalibrated generic P99.\n`predictionEvidence.latencyP99`, when present, carries P99's evidenceLevel\nand low/central/high assumption interval; it is prediction provenance, not\nobserved simulated output, and not a statistical confidence interval.\nWhen `latencyPercentileStatus` is unavailable, public latency percentiles\nare null; a serving resource without a modeled latency distribution does not\nsupport P50/P95/P99. Fargate allocation/billing/capacity facts alone do not\nestablish workload latency. Resilience `timeoutMs` is a per-edge, per-attempt deadline\nover local service time, target base latency, injected latency, and queued wait,\nnot a deadline against aggregate chain percentiles.\nHistorical evidence may omit or null this optional quantity. `latencyBasis`\nnames the general modeled latency path; use `latencyP99Basis` for P99.\nRanges are assumption intervals, not confidence intervals. Measured lean\nuses 0.65–1.35 times the center; lean proportional AWS M5 scaling uses\nCPU 0.40–1.60 and latency 0.50–1.50 times the final reported\nP50/P95 centers (after applicable penalties). The lean scaled\nbaseline uses offered application RPS T: P50 =\nmax(1.45, 2.4833 - 0.0010466*T) ms, P95 =\nmax(P50, 3.10, 4.3697 - 0.0012973*T) ms (full-precision fit).\nOn the exact healthy lean owned ALB → 2× m5.large → MySQL db.r5.large\ngraph at 10–1,000 offered target RPS, P99 uses\nmax(final P95, 7.021919127633514 + 0.00027013891327780484*T) ms,\nfitted by ordinary least squares on the owned campaign's 10/100/500\nRPS P99 measurements. The 1,000-RPS point is a holdout, not a fit point.\nThe owned fit is a prediction, not a new observed sample or a provider\nmeasurement. Exact eligible P99 uses 0.65–1.35× assumption bounds;\nanalogous lean M5 sizes use the fit as scaling (0.50–1.50× within\nrange), not direct measurement. Beyond 1,000 RPS scaling/extrapolation\nuses 0.25–2.00×. Generic typical/heavy and ineligible paths retain the generic,\nunverified P99 formula, with 0.50–2.00× bounds in-range and 0.25–3.00×\nbeyond. Active failures disqualify a healthy measured P99 claim. The\nexact owned typical Node CRUD path instead fits P99 from 20/100/200\nRPS measurements; this is a prediction, not an observed output or a\ngeneral typical-app reference. Its 300 RPS holdout is not fitted.\nMatching one- and three-app-server topologies are explicitly labeled\nowned-scaled and derive app CPU/latency/goodput from equivalent\ntwo-server fit load (2 × total RPS ÷ app-server count); their server\ncounts were not measured. Database CPU and connection demand still use\ntotal offered traffic. One server at 300 total RPS requires a 600 RPS\nequivalent fit extrapolation beyond the holdout, with P50 held at its\n300 RPS projection. Pool-capacity comparisons are warnings only and do\nnot independently imply errors or throughput loss. The\nfinal P99 is floored at final P95; P99 is not scored in Accuracy.\nNo generic 5 ms floor applies to scaled lean latency; 0.05 ms is\nonly a physical/non-finite safety floor. Reference estimates use 0.50–2.\nBeyond 1000 offered RPS, scaled uses 0.25–2 and reference 0.25–3.\nUnknown-vCPU legacy fallback uses the widest reference range and has no\nexternal reference. `legacyGeneric` explicitly labels use of that\nunverified generic branch: true at the top level if any reported app CPU\nor latency quantity used it, and true on each affected appCpuByResource\nitem. The evidence note names each affected resource's shape and reason (missing vCPU,\nflexible shape without resource.characteristics.ocpus, or unknown/\nunrecognized shape). CPU ranges are physically capped at 100%.\nPrediction-only capacity metadata covers GCP E2 standard\ne2-standard-2/4/8/16/32, N1 standard\nn1-standard-1/2/4/8/16/32/64/96, and N2 standard\nn2-standard-2/4/8/16/32/48/64/80/96/128. Google-published vCPU and\nmemory specifications establish hardware dimensions only, not CWM\nperformance, throughput, or price.\nHistorical rows missing evidence are returned non-mutating as\nunavailable/legacy with null weight/load/quantities and empty IDs; they\nare never relabeled measured by today's model.\n","required":["version","evidenceLevel","appWeight","appWeightDefaulted","loadScope","sourceIds","formulaIds","appCpu","appCpuByResource","latencyP50","latencyP95","note"],"properties":{"version":{"type":"integer","enum":[1]},"legacyGeneric":{"type":["boolean","null"],"description":"True when any reported app CPU or latency quantity used the unverified generic fallback; false for newly produced non-generic evidence; null on explicitly unavailable/legacy historical evidence. Predating saved evidence can omit this field entirely; it is never reconstructed.\n"},"evidenceLevel":{"type":"string","enum":["measured","scaled from measured","reference estimate","unavailable/legacy"]},"appWeight":{"type":["string","null"],"enum":["lean","typical","heavy",null]},"appWeightDefaulted":{"type":["boolean","null"]},"loadScope":{"type":["string","null"],"enum":["within measured load","beyond measured load","below measured load","not calibrated",null]},"sourceIds":{"type":"array","items":{"type":"string"}},"formulaIds":{"type":"array","items":{"type":"string"}},"appCpu":{"oneOf":[{"$ref":"#/components/schemas/PredictionRange"},{"type":"null"}]},"appCpuByResource":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/PredictionQuantityEvidence"},{"type":"object","required":["resourceId"],"properties":{"resourceId":{"type":"string"},"legacyGeneric":{"type":"boolean","description":"True when this resource's app CPU used the unverified generic fallback. Required for newly produced evidence; absent on predating saved evidence."}}}]}},"requestServingCapacity":{"type":"array","description":"Per-Fargate-service configured task count, per-task modeled rate, aggregate capacity, and explicit evidence basis. Assumptions are not measured throughput.","items":{"type":"object","required":["resourceId","resourceName","taskCount","perTaskCapacityRps","aggregateCapacityRps","maxTaskCount","maxAggregateCapacityRps","capacityEvidence"],"properties":{"resourceId":{"type":"string"},"resourceName":{"type":"string"},"taskCount":{"type":"integer","minimum":0,"description":"Effective configured desired task count."},"perTaskCapacityRps":{"type":"number","exclusiveMinimum":0},"aggregateCapacityRps":{"type":"number","minimum":0},"maxTaskCount":{"type":"integer","minimum":1},"maxAggregateCapacityRps":{"type":"number","minimum":0},"capacityEvidence":{"type":"object","required":["basis","origin"],"properties":{"basis":{"type":"string","enum":["assumption","documented","measured","catalog"]},"origin":{"type":"string","enum":["caller-provided","fargate-size-heuristic","policy-default","provider-catalog"]},"source":{"type":"string"}}}}}},"latencyAvailability":{"$ref":"#/components/schemas/PredictionLatencyAvailability"},"latencyP50":{"oneOf":[{"$ref":"#/components/schemas/PredictionQuantityEvidence"},{"type":"null"}]},"latencyPercentileStatus":{"type":"string","enum":["modeled","unavailable"],"description":"Whether percentile evidence is available from a modeled serving-tier distribution."},"latencyP95":{"oneOf":[{"$ref":"#/components/schemas/PredictionQuantityEvidence"},{"type":"null"}]},"latencyP99":{"oneOf":[{"$ref":"#/components/schemas/PredictionQuantityEvidence"},{"type":"null"}],"description":"Optional per-percentile P99 evidence, with evidenceLevel and assumption bounds (not confidence intervals). May be null or absent on legacy rows.\n"},"note":{"type":"string"}}},"GenericShapeInventory":{"type":"object","description":"Resolver-derived, read-only inventory of remaining generic prediction\ncases. It includes named catalog gaps and conditional categories with\nreasons; unknown/custom compute is a wildcard rather than an exhaustive\nlist of arbitrary SKU strings. Inventory metadata describes capacity\nresolution only, not performance benchmarks or prices.\n","additionalProperties":true,"properties":{"version":{"type":"integer","description":"Inventory contract version."},"namedCatalogGaps":{"type":"array","description":"Named catalog-backed shape gaps, with their reasons.","items":{"type":"object","additionalProperties":true,"required":["provider","shape","reason"],"properties":{"provider":{"type":"string"},"shape":{"type":"string"},"reason":{"type":"string"}}}},"conditionalCategories":{"type":"array","description":"Conditional fallback classes, including their resolution condition and reason.","items":{"type":"object","additionalProperties":true,"required":["category"],"properties":{"category":{"type":"string"},"examples":{"type":"array","items":{"type":"object","required":["shape","reason"],"properties":{"shape":{"type":"string"},"reason":{"type":"string"}}}},"reason":{"type":"string"}}}}}},"CalibrationEvidence":{"type":"object","required":["kind","latencyBasis","note"],"properties":{"kind":{"type":"string","enum":["owned","owned-scaled","modeled"],"description":"Whether the applicable workload calibration is owned, explicitly scaled from an owned fit (not measured at that topology), or remains modeled output. The owned CRUD fit uses calibrationId aws-crud/v1:473f1339-f712-4096-96d6-3d4fc07cb427:7416cb63ace3a7ab2e3486bb6f132a2dcb574c34."},"latencyBasis":{"type":"string","description":"General modeled latency path/boundary; use latencyP99Basis for percentile-specific P99 provenance."},"latencyP99Basis":{"type":"string","description":"P99-specific basis: owned in-VPC internal-ALB fit, scaled from that fit (not directly measured), or uncalibrated generic model. Optional on older persisted calibration evidence.\n"},"calibrationId":{"type":"string","description":"Versioned identifier of the owned calibration, present only when it applies."},"note":{"type":"string","description":"Human-readable calibration scope and limitations. For eligible canonical CRUD graphs, identify omitted workload inference as a modeling assumption when used. For modeled fallbacks, name all actual failed eligibility checks in stable order with resource IDs and safe expected/actual values; do not return only a generic \"not applied\" message. The owned-scaled typical fit identifies its two-server source, equivalent-load derivation, unmeasured server count, pool-to-database ceiling warnings, and any extrapolation.\n"}}},"RequestFlowMetrics":{"type":"object","description":"Per-step flow for explicitly configured cacheable and asynchronous request shares. Rates are requests or messages per second; queueDepth and capacityMessages are messages. These are model assumptions, not production measurements, and do not model partition lag or generic error absorption.\n","required":["bypassRps","synchronousDatabaseRps","queues","databases"],"properties":{"cache":{"type":"object","description":"Present only when a cacheable request share is explicitly configured.","required":["resourceId","name","cacheableShare","requestsToCacheRps","hitRatePct","hitRps","missRps"],"properties":{"resourceId":{"type":"string"},"name":{"type":"string"},"cacheableShare":{"type":"number","minimum":0,"maximum":1,"description":"Declared fraction of offered requests sent to the cache."},"configuredHitRatePct":{"type":"number","minimum":0,"maximum":100,"description":"Configured cache-hit assumption before resource health and recovery warming are applied."},"requestsToCacheRps":{"type":"number","minimum":0},"hitRatePct":{"type":"number","minimum":0,"maximum":100,"description":"Actual modeled hit rate as a percentage","not an RPS count.":null},"hitRps":{"type":"number","minimum":0},"missRps":{"type":"number","minimum":0,"description":"Cache misses forwarded to synchronous downstream work."}}},"bypassRps":{"type":"number","minimum":0,"description":"Offered requests that bypass the cache and queue and reach synchronous work."},"synchronousDatabaseRps":{"type":"number","minimum":0,"description":"Cache misses plus bypass requests routed to relational databases."},"queues":{"type":"array","description":"Per-queue enqueue, consumer drain, and retained backlog for the step.","items":{"type":"object","required":["resourceId","name","asynchronousShare","enqueueRps","drainRps","queueDepth","queueFillPct","consumerCount","drainRatePerConsumerRps","totalDrainCapacityRps","capacityMessages","simulationStepSeconds","rejectedRps"],"properties":{"resourceId":{"type":"string"},"name":{"type":"string"},"asynchronousShare":{"type":"number","minimum":0,"maximum":1},"enqueueRps":{"type":"number","minimum":0,"description":"Accepted messages enqueued after full-queue rejection is applied."},"drainRps":{"type":"number","minimum":0,"description":"Messages consumed and routed to database work during this step."},"ackFailureRps":{"type":"number","minimum":0,"description":"Failed delivery attempts during this step","including retries from messages already in the backlog.":null},"clientAckFailureRps":{"type":"number","minimum":0,"description":"Estimated failed acknowledgements for messages accepted from client traffic during this step; excludes prior-backlog retries."},"queueDepth":{"type":"number","minimum":0,"description":"Total waiting and in-flight messages after this step."},"queueFillPct":{"type":"number","minimum":0,"maximum":100,"description":"Queue depth divided by its explicit message capacity."},"oldestMessageAgeSeconds":{"type":"number","minimum":0,"description":"Age of the oldest waiting or in-flight message."},"inFlightMessages":{"type":"number","minimum":0,"description":"Messages actually delivered to consumers but not yet acknowledged."},"consumerAckFailureRatePct":{"type":"number","minimum":0,"maximum":100,"description":"Declared consumer acknowledgment failure assumption."},"redeliveredMessages":{"type":"number","minimum":0,"description":"Previously delivered messages returned to the visible queue after visibility timeout."},"expiredMessages":{"type":"number","minimum":0,"description":"Messages deleted after exceeding retention; they are not sent to the DLQ."},"deadLetterCount":{"type":"number","minimum":0,"description":"Cumulative messages moved to the configured DLQ after reaching the receive limit."},"deadLetterQueueResourceId":{"type":"string"},"consumerCount":{"type":"integer","minimum":1},"drainRatePerConsumerRps":{"type":"number","minimum":0},"totalDrainCapacityRps":{"type":"number","minimum":0},"capacityMessages":{"type":"integer","minimum":1},"simulationStepSeconds":{"type":"number","exclusiveMinimum":0},"rejectedRps":{"type":"number","minimum":0}}}},"databases":{"type":"array","description":"Per-database offered work after cache misses, bypass, and queue drain are applied.","items":{"type":"object","required":["resourceId","name","offeredRps"],"properties":{"resourceId":{"type":"string"},"name":{"type":"string"},"offeredRps":{"type":"number","minimum":0}}}}}},"NormalizedConfig":{"type":"object","description":"Read-only introspection of what CWM actually modelled from the creation\npayload. Returned on `POST /api/simulations` (201) and\n`GET /api/simulations/{simulationId}` so a consumer can verify the\nresolved billing and capacity values per resource — e.g. why two\npayloads that look identical produce different costs (different\nresolved `size`, GPU SKU, or cost multiplier). Every value is derived\nwith the same resolution functions the billing/autoscale engine uses.\n","properties":{"resources":{"type":"array","items":{"$ref":"#/components/schemas/NormalizedResourceConfig"}},"requestFlow":{"type":"object","description":"Graph-level assumptions for the explicitly configured cache and queue flow. Values remain assumptions; this summary does not claim measured production behavior, partition lag, or error absorption.\n","properties":{"cacheResourceId":{"type":"string"},"cacheableRequestShare":{"type":"number","minimum":0,"maximum":1},"configuredCacheHitRatePct":{"type":"number","minimum":0,"maximum":100},"queueResourceId":{"type":"string"},"asynchronousRequestShare":{"type":"number","minimum":0,"maximum":1},"consumerCount":{"type":"integer","minimum":1},"totalDrainCapacityRps":{"type":"number","minimum":0},"queueCapacityMessages":{"type":"integer","minimum":1},"queueVisibilityTimeoutSeconds":{"type":"number","exclusiveMinimum":0},"queueRetentionSeconds":{"type":"number","exclusiveMinimum":0},"queueMaxReceiveCount":{"type":"integer","minimum":1},"deadLetterQueueResourceId":{"type":"string"},"queueConsumerAckFailureRatePct":{"type":"number","minimum":0,"maximum":100},"simulationStepSeconds":{"type":"number","exclusiveMinimum":0},"databaseResourceIds":{"type":"array","items":{"type":"string"}},"note":{"type":"string"}}}}},"NormalizationReceipt":{"type":"object","description":"Immutable, versioned receipt stored at creation and returned unchanged by\ncreate, get, and step responses. It records each material assumption's\nsource: human/API input, MCP/agent input, platform default, catalog\nresolution, engine inference, or unsupported fallback. It makes\nnormalized Kubernetes inputs comparable without requiring equivalent\nnatural-language prompts.\n","required":["version","creationPolicy","modelingScope","kubernetes"],"properties":{"version":{"type":"string","enum":["gke-normalization-receipt/v2"]},"creationPolicy":{"type":"object","properties":{"canonicalOrdering":{"type":"string"},"warmup":{"type":"object","properties":{"applied":{"type":"boolean"},"steps":{"type":"integer"},"reason":{"type":"string"}}}}},"modelingScope":{"type":"object","description":"Explicit generic-Kubernetes limitations. CWM models worker-node capacity, CPU/utilization, autoscaling, and recovery only. The control-plane management fee is cost-only; CWM does not simulate or report control-plane CPU, master/API throttling, or control-plane cooldown. memoryUsage is synthetic aggregate utilization and cannot establish per-pod GiB, working-set demand, OOM timing, cgroup limits, eviction, or OOMKill behavior.\n","properties":{"kubernetesWorkerCapacity":{"type":"string"},"controlPlane":{"type":"string"},"memoryUsage":{"type":"string"}}},"kubernetes":{"type":"array","items":{"type":"object","required":["resourceId","requested","effective","fields","pricing"],"properties":{"resourceId":{"type":"string"},"requested":{"type":"object","additionalProperties":true},"effective":{"type":"object","additionalProperties":true,"properties":{"capacity":{"type":"object","description":"Resolved Kubernetes worker serving capacity. For a multi-pool cluster, sum `pools[].capacityRps` to reconstruct `clusterMaxThroughputRps`; each `pools[].capacityShare` is the pool's share of that cluster ceiling and sums to approximately 1.0.","required":["perNodeRps","clusterMaxThroughputRps","representation"],"properties":{"perNodeRps":{"type":"number","minimum":0,"description":"Uniform-equivalent per-node capacity in requests per second. For pool-aware capacity, use the pool-level `perNodeRps` values instead."},"clusterMaxThroughputRps":{"type":"number","minimum":0,"description":"Immutable pre-effect Kubernetes cluster serving ceiling in requests per second. This is the reconstruction target for the sum of `pools[].capacityRps`."},"representation":{"type":"string","enum":["pool-aware","uniform-equivalent"],"description":"Whether the capacity is broken down using heterogeneous node-pool weights or represented as a uniform per-node equivalent."},"source":{"type":"string","description":"How the cluster ceiling was resolved."},"pools":{"type":"array","description":"Per-pool capacity contributions. Present only for multi-pool Kubernetes resources. `capacityWeight` is the pool's node-count-weighted capacity, `capacityShare` is `capacityWeight` divided by the sum of all pool weights, and `capacityRps` is `clusterMaxThroughputRps × capacityShare`.","items":{"type":"object","required":["poolId","name","nodeCount","capacityWeight","capacityShare","capacityRps","perNodeRps"],"properties":{"poolId":{"type":"string","description":"Stable unique identifier for this node pool."},"name":{"type":"string","description":"Resolved node-pool name."},"nodeCount":{"type":"integer","minimum":1,"description":"Resolved current worker-node count for this pool."},"capacityWeight":{"type":"number","minimum":0,"description":"Capacity weight used to calculate this pool's share. It is `nodeCount × per-node maxThroughput` when declared, otherwise the node count for the uniform fallback."},"capacityShare":{"type":"number","minimum":0,"maximum":1,"description":"Fraction of the cluster ceiling assigned to this pool."},"capacityRps":{"type":"number","minimum":0,"description":"This pool's serving-capacity contribution in requests per second; sum this field across pools to reconstruct the cluster ceiling."},"perNodeRps":{"type":"number","minimum":0,"description":"This pool's capacity contribution divided by its node count."}}}}}}}},"fields":{"type":"object","additionalProperties":{"type":"object","required":["effective","provenance","detail"],"properties":{"requested":{},"effective":{},"provenance":{"type":"string","enum":["human-provided","agent-supplied","platform-defaulted","catalog-sourced","engine-inferred","unsupported/fallback"]},"detail":{"type":"string"}}}},"pricing":{"type":"object","required":["fidelity","costExcludedFromTotals"],"properties":{"fidelity":{"type":"string","enum":["catalog","estimated","excluded"]},"costExcludedFromTotals":{"type":"boolean"},"warning":{"type":"string"}}}}}}}},"NormalizedResourceConfig":{"type":"object","description":"Resolved billing and capacity values for one resource.","required":["id","name","type","provider"],"properties":{"id":{"type":"string","description":"Resource id (matches the entry in `resources`)"},"name":{"type":"string"},"type":{"type":"string"},"provider":{"type":"string"},"requestFlowAssumptions":{"type":"object","description":"Per-resource opt-in cache or queue configuration. Drain rates, request shares, hit rate, and queue capacity are declared model assumptions rather than production observations.\n","required":["role","note"],"properties":{"role":{"type":"string","enum":["cache","queue"]},"cacheableRequestShare":{"type":"number","minimum":0,"maximum":1},"cacheHitRatePct":{"type":"number","minimum":0,"maximum":100},"asynchronousRequestShare":{"type":"number","minimum":0,"maximum":1},"consumerCount":{"type":"integer","minimum":1},"drainRatePerConsumerRps":{"type":"number","minimum":0},"totalDrainCapacityRps":{"type":"number","minimum":0},"queueCapacityMessages":{"type":"integer","minimum":1},"queueVisibilityTimeoutSeconds":{"type":"number","exclusiveMinimum":0},"queueRetentionSeconds":{"type":"number","exclusiveMinimum":0},"queueMaxReceiveCount":{"type":"integer","minimum":1},"deadLetterQueueResourceId":{"type":"string"},"queueConsumerAckFailureRatePct":{"type":"number","minimum":0,"maximum":100},"simulationStepSeconds":{"type":"number","exclusiveMinimum":0},"note":{"type":"string"}}},"resolvedSku":{"type":"string","description":"Effective resource SKU after applying the size/instanceType compatibility alias. When both are supplied they must match; an unknown explicit SKU remains visible and is reported through the normal estimated/unsupported coverage path."},"openshiftOffering":{"type":"string","enum":["rosa-hcp","rosa-classic","aro","openshift-dedicated","self-managed"],"description":"Resolved OpenShift distribution overlay, when present."},"openshiftPlatformFeePerHour":{"type":"number","description":"Additive OpenShift platform or license fee in USD/hr. For per-4-vCPU offerings this is prorated across the resolved worker vCPU capacity."},"openshiftPricingStatus":{"type":"string","enum":["official","estimated"],"description":"Provenance of the resolved OpenShift platform/license amount. ARO is official only for a resource explicitly located in Azure East US; other or missing regions are estimated reference use of the East US published line."},"openshiftControlPlanePricingStatus":{"type":"string","enum":["official","estimated"],"description":"Provenance of the backing-provider or OpenShift-specific control-plane charge. For ARO this is estimated because the control-plane charge is modeled from Azure Kubernetes pricing and is included in the aggregate rather than represented as an excluded aro/managed resource."},"resolvedCostMultiplier":{"type":"number","description":"Effective cost multiplier the engine applies (explicit characteristics.costMultiplier, size-tier backfill, or pricing benchmark backfill — whichever the billing path resolves)."},"resolvedHourlyRate":{"type":"number","description":"Resolved generic instance hourly rate (base rate × multiplier), USD/hr. Applies to compute and fixed-rate relational database resources; request-priced DynamoDB is described by dynamoDbBilling instead."},"rdsSqlServerBilling":{"$ref":"#/components/schemas/RdsSqlServerBillingResult"},"rateProvenance":{"type":"object","description":"Provenance of the resolved rate — present whenever `resolvedHourlyRate` or `resolvedGpuRatePerNode` is emitted. Tells consumers what pricing basis the constant reflects, which lookup path resolved it, and when it was last cross-checked against the provider's published pricing page (static metadata from the pricing constants, not fetched live). AWS M7, M8, and M9 entries such as `m7g.large`, `m8g.large`, and `m9g.large` use `basis: on-demand` and `resolution: exact-catalog` when their resolved hourly value matches the canonical AWS EC2 Price List catalog. This certifies price provenance only; EC2 performance characteristics remain CWM-modeled unless separately marked by a performance source.","required":["basis","resolution","lastVerified","region"],"properties":{"basis":{"type":"string","enum":["on-demand","spot","estimated"],"description":"Pricing basis — `on-demand` for published list-price constants, `estimated` for fallback/approximation constants."},"resolution":{"type":"string","enum":["exact-catalog","large-sku","entry-level-fallback","base-rate-multiplier","fargate-task-vcpu-memory","aurora-acu-hour","cloud-run-request-based","request-serving-usage-based"],"description":"Lookup path taken — exact first-party catalog SKU (for example AWS M7, M8, or M9), exact premium GPU SKU, provider entry-level GPU fallback, or the general base-rate × multiplier path."},"lastVerified":{"type":["string","null"],"format":"date","description":"ISO-8601 date the constant was last verified against the provider's public pricing page; null when unknown."},"region":{"type":["string","null"],"description":"Region the verified rate applies to, from the constant's pricing source (e.g. `us-east-1`, `us-central1`, `eastus`, `us-ashburn-1`); null only for genuinely region-uniform pricing (e.g. DigitalOcean)."},"sku":{"type":"string","description":"Exact provider catalog SKU when resolution is exact-catalog (for example `m7g.large`); absent for generic and GPU fallback paths.","example":"m7g.large"},"architecture":{"type":"string","description":"Fargate Linux task architecture (X86_64 or ARM64)."},"source":{"type":"string","description":"Official pricing source URL for verified billing-unit rates, or price-list source for an exact catalog rate; absent for generic and fallback paths.","example":"AWS EC2 Price List API (Linux, shared tenancy, on-demand)"}}},"resolvedMaxThroughputRps":{"type":"number","description":"Effective aggregate throughput (RPS) for this resource. For fixed compute fleets, per-VM resolved throughput is multiplied by effectiveVmInstanceCount; independently materialized autoscaling members each count as one VM. When compute capacity is not declared, the resolved CPU/capacity model default is reported rather than an enforced serving ceiling. For Kubernetes, this is the enforced cluster serving ceiling; 0 means no serving ceiling is configured (the autoscaler's separate 10,000-RPS heuristic is not a ceiling)."},"effectiveVmInstanceCount":{"type":"integer","minimum":1,"description":"Effective physical VM count represented by this compute resource during simulation. A fixed logical fleet can represent multiple VMs; materialized autoscaling members each represent one. Absent for non-compute resources."},"effectiveCpuCapacityRps":{"type":"number","description":"Aggregate CPU-model capacity denominator (RPS) for this compute resource, calculated as the resolved per-VM CPU denominator times effectiveVmInstanceCount. This model denominator can differ from the declared serving capacity and is not itself a throughput ceiling."},"nodeCount":{"type":"integer","description":"Resolved worker node count (kubernetes)."},"minNodes":{"type":"integer","description":"Effective autoscale floor (explicit minNodes or the provider autoscaling profile default the engine applies). Single-pool kubernetes."},"maxNodes":{"type":"integer","description":"Effective autoscale ceiling (explicit maxNodes or the provider profile default, floored to the current node count). Single-pool kubernetes."},"perNodeRatePerHour":{"type":"number","description":"Per-worker-node hourly rate the cluster bills at, USD/hr (single-pool kubernetes)."},"controlPlaneFeePerHour":{"type":"number","description":"Cost-only cluster control-plane management fee, USD/hr. It is not control-plane CPU, API-throttling, or cooldown telemetry."},"spotFactor":{"type":"number","description":"Spot-pricing discount factor applied to node billing (present only when pricingModel is spot)."},"currentNodesCostPerHour":{"type":"number","description":"Hourly cost at the CURRENT node count — controlPlaneFee + Σ(rate × nodes), spot factor applied, USD/hr."},"nodePools":{"type":"array","description":"Per-pool resolved billing/bounds for multi-pool clusters. When present, nodeCount/billingFloor values aggregate across pools and the pool entries carry the authoritative per-pool rates.","items":{"type":"object","properties":{"name":{"type":"string"},"nodeCount":{"type":"integer"},"minNodes":{"type":"integer","description":"Effective per-pool scale-in floor (explicit or profile default)."},"maxNodes":{"type":"integer","description":"Effective per-pool scale-out ceiling (explicit or profile default)."},"perNodeRatePerHour":{"type":"number","description":"The rate this pool bills each node at (explicit perNodeRate or the cluster default), USD/hr."},"perNodeMaxThroughputRps":{"type":"number","description":"Per-node serving capacity for this pool, when explicitly configured."}}}},"autoscaleThreshold":{"type":"object","description":"Effective CPU autoscale thresholds (provider profile merged with any simulation-level override; GPU inference clusters include the provider GPU overlay).","properties":{"scaleOutCpuPercent":{"type":"number"},"scaleInCpuPercent":{"type":"number"}}},"resolvedGpuRatePerNode":{"type":"number","description":"Resolved GPU node hourly rate (inference-mode kubernetes clusters), USD/hr."},"resolvedSkuLabel":{"type":"string","description":"The exact GPU SKU (`size`) that matched the premium GPU catalogue, or a fallback indicator naming the provider-level default GPU rate used instead."},"perNodeTokensPerSec":{"type":"number","description":"Per-node token serving capacity (tokens/s) resolved from the accelerator type."},"billingFloorNodes":{"type":"integer","description":"Smallest node count autoscaling can ever shrink the cluster to — min(nodeCount, effective minNodes), summed across pools for multi-pool clusters."},"billingFloorCostPerHour":{"type":"number","description":"Minimum billable hourly cost — controlPlaneFee + rate × billingFloorNodes (per-pool rates for multi-pool clusters), spot factor applied, USD/hr."},"resolvedStorageTier":{"type":"string","description":"Resolved storage/instance tier (databases; the `size` characteristic)."},"resolvedConnectionLimit":{"type":"integer","description":"Effective connection limit — the exact value the engine's connection-pressure model uses (serverless ACU-based limit, else maxConnections, connections, or the engine default of 100)."},"acuBilling":{"type":"object","description":"Aurora Standard Serverless v2 billing units. Hourly compute is currentAcu multiplied by ratePerAcuHour per billable DB instance; not a flat instance-hour rate.","properties":{"currentAcu":{"type":"number"},"minAcu":{"type":"number"},"maxAcu":{"type":"number"},"ratePerAcuHour":{"type":"number"}}},"dynamoDbBilling":{"type":"object","description":"DynamoDB's modeled cost is request-volume based, not a fixed instance-hour price. For each DynamoDB resource, the CWM modeled hourly cost is referenceModeledCostPerHour multiplied by that resource's allocated request rate divided by referenceRequestRateRps. Simulation traffic is split evenly across DynamoDB resources. The reference cost is derived from CWM's generic database base-rate anchor and effective cost multiplier; it is not AWS-published DynamoDB pricing or a provider quote.","required":["billingModel","referenceRequestRateRps","referenceModeledCostPerHour","referenceCostBasis","providerPublishedPrice"],"properties":{"billingModel":{"type":"string","enum":["request-volume"],"description":"DynamoDB's modeled hourly cost scales linearly with allocated request rate."},"referenceRequestRateRps":{"type":"number","description":"Per-resource request-rate reference at which the CWM modeled hourly cost anchor applies.","example":50000},"referenceModeledCostPerHour":{"type":"number","description":"CWM-modeled USD/hour anchor at referenceRequestRateRps; not an AWS DynamoDB price."},"referenceCostBasis":{"type":"string","enum":["cwm-modeled-estimate"],"description":"Identifies the reference cost as an internal CWM model estimate rather than provider-published pricing."},"providerPublishedPrice":{"type":"boolean","enum":[false],"description":"Always false; the CWM reference anchor is not a provider-published price or quote."}}},"auroraInstanceRole":{"type":"string","enum":["writer","reader"]},"auroraReaderScalingMode":{"type":"string","enum":["writer-mirror","independent"]},"auroraScalingNote":{"type":"string"},"requestServing":{"type":"object","description":"Provider request-serving configuration (Cloud Run, functions, Azure Container Apps, or ECS/Fargate) derived from the same characteristics used by\n capacity and billing. Usage-based cost excludes provider free tiers,\n networking, logging, and discounts unless noted by the service policy.\n","properties":{"serviceFamily":{"type":"string","example":"lambda"},"serviceKind":{"type":"string","enum":["function","container"]},"containerCpu":{"type":"number"},"containerMemoryGiB":{"type":"number"},"cpuArchitecture":{"type":"string","enum":["X86_64","ARM64"]},"concurrency":{"type":"integer"},"requestDurationSeconds":{"type":"number"},"minInstances":{"type":"integer"},"maxInstances":{"type":"integer"},"provisionedInstances":{"type":"integer"},"perInstanceCapacityRps":{"type":"number"},"idleMinimumCostPerHour":{"type":"number"},"desiredTaskCount":{"type":"integer","minimum":0},"minTaskCount":{"type":"integer","minimum":0},"maxTaskCount":{"type":"integer","minimum":1},"perTaskCapacityRps":{"type":"number"},"perTaskCapacityEvidence":{"type":"object","description":"Resolved trust label. CWM size heuristics and unverified caller values are assumptions, never measured throughput or provider catalog facts.","required":["basis","origin"],"properties":{"basis":{"type":"string","enum":["assumption","documented","measured","catalog"]},"origin":{"type":"string","enum":["caller-provided","fargate-size-heuristic","policy-default","provider-catalog"]},"source":{"type":"string"}}},"aggregateCapacityRps":{"type":"number","description":"Desired task count multiplied by this service's resolved per-task rate."},"maxAggregateCapacityRps":{"type":"number","description":"Maximum task count multiplied by this service's resolved per-task rate."},"taskStartupSeconds":{"type":"number"},"taskScaleDownSeconds":{"type":"number"},"taskVcpuHourRate":{"type":"number","description":"ECS/Fargate Linux on-demand us-east-1 vCPU-hour rate."},"taskMemoryGibHourRate":{"type":"number","description":"ECS/Fargate Linux on-demand us-east-1 GB-hour rate."},"billingGranularitySeconds":{"type":"number","description":"ECS/Fargate per-second billing granularity."},"minimumBillableSeconds":{"type":"number","description":"ECS/Fargate minimum billable task start duration."}}},"behaviorModel":{"type":"object","description":"Machine-readable identification of the latency simulation path applied to this resource at step time. Present on all resource types. Use `latencyPath` to programmatically determine which model will run — e.g. to verify a GPU inference cluster is using `gpu-inference/ttft-decode` rather than the generic K8s path.","required":["latencyPath","description"],"properties":{"latencyPath":{"type":"string","description":"Canonical latency-path identifier. One of: `gpu-inference/ttft-decode` (K8s cluster with inferenceMode: true — token-economics TTFT+decode model), `generic-kubernetes/load-saturation` (K8s cluster without inferenceMode — generic saturation model), `generic-compute/load-saturation` (compute/VM resources), `database/connection-saturation` (database resources — connection-pool pressure model), `database/request-capacity` (DynamoDB — request/capacity model), `storage/iops-saturation` (OCI Block Volume only — IOPS ceiling tracked; penalties propagated to co-located DBs), `generic/flat-rate` (all other storage, network, cache, queue, security — cost-only, no latency simulation).","example":"gpu-inference/ttft-decode"},"description":{"type":"string","description":"Human-readable description of the latency model.","example":"Token-economics model: TTFT + decode, weighted by throughput and batch pressure."},"modelingNote":{"type":"string","description":"Optional provenance note. Present on GPU inference resources (`latencyPath: gpu-inference/ttft-decode`) to remind API consumers that TTFT and decode-throughput figures are produced by the CWM simulation model, not by externally measured hardware benchmarks. When topology is omitted, includes an explicit legacy-baseline note stating that factor 1.0 reflects the pre-topology assumption, not a measurement of a specific interconnect fabric. Absent on non-inference resources.","example":"TTFT and decode-throughput figures are CWM simulation-model estimates, not externally measured hardware benchmarks."},"topologyThroughputFactor":{"type":"number","description":"The exact throughput scaling coefficient that `resolveTopologyScalingFactor` applied to this resource's token capacity (`nodes × perNodeTokens × topoFactor × gpuUtil/100`). 1.0 when `topology.intraNode` is absent or unrecognised (legacy baseline). Present only on inferenceMode Kubernetes resources. Entirely separate from `topologyTtftFactor` and `topologyDecodeFactor`, which describe the TTFT+decode latency model.","example":0.92},"topologyThroughputCalibrationStatus":{"type":"string","enum":["calibrated","estimated","not_applicable"],"description":"Calibration status of the throughput scaling factor (`resolveTopologyScalingFactor`) that bounds effective token capacity — entirely separate from the TTFT and decode latency factors (`resolveTopologyLatencyFactors`) described by `topologyCalibrationStatus`. \"estimated\" for all current inference-mode intraNode coefficients (0.70 pcie / 0.85 nvlink / 0.92 nvlink-nvswitch), which are engineering assumptions based on bandwidth arithmetic with no directly measured controlled per-request token-throughput ratio attributable to intraNode fabric type alone. \"not_applicable\" on non-inferenceMode resources. Reserve \"calibrated\" for when a directly measured source is added.","example":"estimated"},"topologyIsLegacyBaseline":{"type":"boolean","description":"`true` when `topology.intraNode` was absent or unrecognised, meaning `topologyThroughputFactor: 1.0` reflects the pre-topology legacy baseline — not a measurement of a specific interconnect fabric. `false` when an explicit, known `intraNode` value was supplied and the throughput factor is topology-modeled. Present only on inferenceMode Kubernetes resources. Agents should check this field before interpreting a factor of 1.0 as a best-fabric result.","example":false},"ibNetworkingCostPerNodePerHour":{"type":"number","description":"Per-node InfiniBand networking cost premium (USD/hr) added on top of the base GPU node rate when `topologyInterNode === \"infiniband\"` and `topologyNodeCount > 1`. Sourced from `IB_NETWORKING_COST_PER_NODE_PER_HOUR` in `shared/provider-pricing.ts` (calibration status: estimated — derived from the Azure NC24rs_v3 vs NC24s_v3 IB delta lower bound and NVIDIA DGX Cloud networking pricing upper bound; will be updated to on-demand status when a provider publishes a discrete IB fabric line-item at this granularity). Total cluster IB cost = `ibNetworkingCostPerNodePerHour × nodeCount`, surfaced as a separate `\"infiniband_networking\"` entry in the simulation-level `costBreakdown`. Absent when IB networking is not active (no `interNode` topology, single-node cluster, or non-IB fabric type).","example":2}}},"inferenceWarnings":{"type":"array","description":"Non-fatal configuration warnings. Populated when a Kubernetes resource carries inference/GPU characteristics (`accelerator`, `tokensPerRequest`, `inputTokensPerRequest`) but `inferenceMode` is absent or false — meaning the `generic-kubernetes/load-saturation` model will be used instead of the GPU inference model. Empty or absent when no warnings apply. To silence these warnings and activate the GPU inference path, set `characteristics.inferenceMode: true` on the resource.","items":{"type":"string"},"example":["Resource has inference/GPU characteristics (accelerator: h100, tokensPerRequest: 512) but inferenceMode is not enabled. The generic-kubernetes/load-saturation latency model will be used. Set characteristics.inferenceMode: true to activate gpu-inference/ttft-decode."]},"auroraTopologyWarnings":{"type":"array","description":"Non-fatal warning on a legacy AWS Aurora database with multiAz:true but no instanceCount and no explicit standby. No reader is inferred from that flag alone; specify instanceCount:2 on Aurora Serverless v2 to create a separately billed reader in another AZ.\n","items":{"type":"string"}}}},"EpisodeConfig":{"type":"object","required":["maxSteps","targetSLA"],"properties":{"maxSteps":{"type":"integer","description":"Maximum steps per episode","minimum":1,"example":300},"targetTrafficPattern":{"type":"string","enum":["constant","ramp","burst","step","wave","custom"],"description":"Traffic pattern to simulate","default":"constant","example":"ramp"},"initialTraffic":{"type":"number","description":"Starting traffic load (requests/sec)","default":1000,"example":5000},"targetSLA":{"type":"object","description":"Service-level objectives used to calculate the episode reward.","required":["maxLatencyP95","maxErrorRate"],"properties":{"maxLatencyP95":{"type":"number","description":"Target P95 latency threshold (ms)","example":200},"maxErrorRate":{"type":"number","description":"Target maximum error rate (%)","example":1}}},"costBudgetPerHour":{"type":"number","description":"Target cost budget per hour (USD)","default":10,"example":5},"enableFailures":{"type":"boolean","description":"Whether to inject random failures","default":false},"tick_seconds":{"type":"integer","minimum":1,"maximum":3600,"default":60,"description":"Number of simulated seconds each step advances the simulation clock.\nDefault is 60 (one simulated minute per step). Set higher values to\nmodel longer time horizons (e.g. 300 for 5-minute ticks, 3600 for\nhourly ticks). The value is fixed for the lifetime of the episode.\n","example":60},"unmodeled_cost_penalty_per_dimension":{"type":"number","minimum":0,"default":0.1,"description":"Reward penalty applied per active unmodeled cost dimension per step. The total penalty is -(count × this value) and is reported in `reward_components.unmodeled_cost` and `unmodeled_cost_penalty` in the step response. Default is 0.1.\n","example":0.1},"evalCostOverrides":{"type":"object","additionalProperties":{"type":"number","minimum":0},"description":"Per-dimension cost override rates in USD per 1 000 RPS per hour. Supported keys: `egress` (outbound data-transfer fee charged whenever traffic > 0) and `cross_az_traffic` (inter-AZ data-transfer fee charged when resources in different Availability Zones are connected). When present, these rates are added to the simulated cost before computing the reward cost score, making previously unpriced dimensions visible in the reward signal. Primarily used by the `eval-episodes` endpoint to stress-test trained policies against real-world cost blind spots.\n","example":{"egress":0.5,"cross_az_traffic":0.3}}}},"RLTradeoffSummary":{"type":"object","description":"Per-metric deltas between the episode-start config and the episode-end config.\nReturned only on the final step response (`done: true`). Use this to evaluate\nwhether the agent's actions improved cost, latency, and resilience over the course\nof the episode.\n","required":["costDeltaPercent","costDirection","latencyP95DeltaMs","latencyP95Direction","resilienceDeltaScore","resilienceDirection"],"properties":{"costDeltaPercent":{"type":"number","description":"Cost change as a percentage of the episode-start cost. Negative = cheaper.","example":-18.5},"costDirection":{"type":"string","enum":["improved","degraded","unchanged"],"description":"improved = lower cost, degraded = higher cost. Unchanged when |delta| < 0.5%.","example":"improved"},"latencyP95DeltaMs":{"type":"number","description":"P95 latency change in milliseconds relative to episode start. Negative = faster.","example":-22},"latencyP95Direction":{"type":"string","enum":["improved","degraded","unchanged"],"description":"improved = lower latency, degraded = higher latency. Unchanged when |delta| < 1 ms.","example":"improved"},"resilienceDeltaScore":{"type":"number","description":"Change in resilience score (uptime = 1 − error_rate) between episode start and end. Range −1 to 1. Positive = more resilient (fewer errors).\n","example":0.0015},"resilienceDirection":{"type":"string","enum":["improved","degraded","unchanged"],"description":"improved = higher uptime, degraded = lower uptime. Unchanged when |delta| < 0.001.","example":"improved"}}},"RLEvalJobAccepted":{"type":"object","required":["jobId","status","createdAt"],"properties":{"jobId":{"type":"string","format":"uuid","description":"Unique identifier for the eval job. Use this to poll the GET endpoint.","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"},"status":{"type":"string","enum":["pending","running"],"description":"Current job status","example":"pending"},"createdAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp when the job was created","example":"2026-01-01T00:00:00.000Z"}}},"RLEvalJobResult":{"type":"object","required":["episodes","meanEvalReward","trainingTotalReward","collapseThreshold","reward_collapse"],"properties":{"episodes":{"type":"array","description":"Per-episode results in the same order as the request `actions` array","items":{"type":"object","required":["episodeIndex","stepsExecuted","totalReward"],"properties":{"episodeIndex":{"type":"integer","description":"Zero-based index of this episode in the request array","example":0},"stepsExecuted":{"type":"integer","description":"Number of actions executed (may be less than the action array length if the episode ended early)","example":3},"totalReward":{"type":"number","description":"Cumulative reward across all steps in this eval episode","example":1.82},"steps":{"type":"array","description":"Per-step error evidence from the replay; database pressure details omit zero-contribution databases.","items":{"type":"object","required":["step"],"properties":{"step":{"type":"integer","minimum":1,"description":"One-based step number within this evaluation episode","example":1},"error_breakdown":{"$ref":"#/components/schemas/RLMetrics/properties/error_breakdown"}}}}}}},"meanEvalReward":{"type":"number","description":"Mean of `totalReward` across all eval episodes","example":1.82},"trainingTotalReward":{"type":"number","description":"Stored cumulative training reward from the environment (the baseline being compared against)","example":4.5},"collapseThreshold":{"type":"number","description":"The collapse threshold used for this evaluation (echoed from request)","example":0.2},"reward_collapse":{"type":"boolean","description":"`true` when `meanEvalReward` falls more than `collapseThreshold × |trainingTotalReward|` below `trainingTotalReward`. Indicates the policy exploits cost dimensions unpriced during training.\n","example":true}}},"RLEvalJobCompleted":{"allOf":[{"$ref":"#/components/schemas/RLEvalJobResult"},{"type":"object","required":["jobId","status","createdAt","completedAt"],"properties":{"jobId":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"},"status":{"type":"string","enum":["completed"],"example":"completed"},"createdAt":{"type":"string","format":"date-time","example":"2026-01-01T00:00:00.000Z"},"completedAt":{"type":"string","format":"date-time","example":"2026-01-01T00:00:01.234Z"}}}]},"RLEvalJobFailed":{"type":"object","required":["jobId","status","createdAt"],"properties":{"jobId":{"type":"string","format":"uuid","example":"3fa85f64-5717-4562-b3fc-2c963f66afa6"},"status":{"type":"string","enum":["failed"],"example":"failed"},"error":{"type":"string","description":"Error message describing why the job failed","example":"Environment or simulation no longer exists"},"createdAt":{"type":"string","format":"date-time","example":"2026-01-01T00:00:00.000Z"},"completedAt":{"type":"string","format":"date-time","example":"2026-01-01T00:00:00.100Z"}}},"RLEnvironment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"simulationId":{"type":"string","format":"uuid"},"episodeConfig":{"$ref":"#/components/schemas/EpisodeConfig"},"currentStep":{"type":"integer","description":"Current step number in episode","example":42},"totalReward":{"type":"number","description":"Cumulative reward so far","example":12.45},"isActive":{"type":"boolean","description":"Whether episode is still running","example":true},"lastSimTimeHuman":{"type":"string","description":"Human-readable simulated elapsed time as of the last step or reset (e.g. \"1h 30m\"). Absent before the first step.","example":"1h 30m"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"webhookDeliveryStatus":{"type":"string","enum":["pending","delivered","failed"],"description":"Webhook delivery status (for episode completion)","example":"delivered"},"webhookDeliveryAttempts":{"type":"integer","description":"Number of webhook delivery attempts made","example":1},"webhookDeliveryError":{"type":"string","description":"Error message if webhook delivery failed"},"webhookDeliveredAt":{"type":"string","format":"date-time","description":"Timestamp when webhook was successfully delivered"},"idleExpiresAt":{"type":"string","format":"date-time","description":"ISO 8601 timestamp at which this environment will be automatically expired\ndue to inactivity. Computed as `lastActivityAt` (or `createdAt` if no step\nor reset has been called yet) plus the 2-hour idle TTL. Agents can use\nthis value to schedule keep-alive calls (`POST /rl/environments/{environmentId}/step`\nor `POST /rl/environments/{environmentId}/reset`) before expiry.\n","example":"2024-01-15T14:00:00.000Z"},"rewardWeights":{"$ref":"#/components/schemas/RewardWeights"}}},"RewardWeights":{"type":"object","description":"User-defined weights controlling how cost, resilience, latency, performance,\nand stability each contribute to the scalar reward returned on each RL step.\n\nValid create-time keys: `cost`, `resilience`, `latency`, `performance`, `stability`.\nThese are the weights you set when creating or resetting an environment — they\nare distinct from the step-response `reward_components` fields\n(`connection_pressure`, `unmodeled_cost`, `sla`) which are computed automatically.\n\nAll values must be non-negative. They are automatically normalized to sum to 1.0\non the server side, so you may supply raw priority scores\n(e.g. `{ cost: 3, resilience: 1, latency: 1 }`) or pre-normalized fractions\n(e.g. `{ cost: 0.6, resilience: 0.2, latency: 0.2 }`).\n\nWhen omitted (the default), the agent uses the built-in weighting of\nlatency: 0.35 / cost: 0.25 / resilience: 0.40 — optimized for\nbalanced cloud workloads.\n","properties":{"cost":{"type":"number","minimum":0,"maximum":1,"description":"Weight assigned to cost optimization (staying within cost budget).","example":0.4},"resilience":{"type":"number","minimum":0,"maximum":1,"description":"Weight assigned to resilience (low error rates, stable resources, no SLA violations).","example":0.2},"latency":{"type":"number","minimum":0,"maximum":1,"description":"Weight assigned to latency performance (p95 latency relative to SLA target).","example":0.2},"performance":{"type":"number","minimum":0,"maximum":1,"description":"Weight assigned to throughput and capacity performance.","example":0.1},"stability":{"type":"number","minimum":0,"maximum":1,"description":"Weight assigned to autoscaling stability (penalizes thrashing and rapid oscillation).","example":0.1}},"required":["cost","resilience","latency"],"example":{"cost":0.4,"resilience":0.2,"latency":0.2,"performance":0.1,"stability":0.1}},"Action":{"type":"object","description":"Action selected by the RL agent for one simulation step.","required":["type","parameters"],"properties":{"type":{"type":"string","enum":["adjust_threshold","scale_out","scale_in","add_resource","remove_resource","no_op","set_recovery_policy"],"description":"Type of action to execute"},"parameters":{"description":"Type-specific values for the selected action; pass an empty object when the action has no parameters.","oneOf":[{"$ref":"#/components/schemas/AdjustThresholdParams"},{"$ref":"#/components/schemas/ScaleParams"},{"$ref":"#/components/schemas/ResourceParams"},{"$ref":"#/components/schemas/SetRecoveryPolicyParams"}]}}},"AdjustThresholdParams":{"type":"object","description":"Parameters for adjust_threshold action","properties":{"cpuThreshold":{"type":"number","minimum":0,"maximum":100,"description":"CPU utilization threshold for scaling (%)","example":70},"throughputThreshold":{"type":"number","minimum":0,"maximum":100,"description":"Throughput utilization threshold (%)","example":75},"latencyThreshold":{"type":"number","minimum":0,"description":"Latency threshold for scaling (ms)","example":180}}},"ScaleParams":{"type":"object","description":"Parameters for scaling the compute fleet in or out.","required":["instanceCount"],"properties":{"instanceCount":{"type":"integer","minimum":1,"description":"Number of instances to add (`scale_out`) or remove (`scale_in`) — a delta, not a target count. The effective change is clamped to the per-resource instance bounds: the ceiling is `min(autoscalingConfig.maxInstances, characteristics.maxInstances)` for `scale_out`, and the floor is `max(autoscalingConfig.minInstances, characteristics.minInstances)` for `scale_in`. When clamping occurs the step response `info` object contains the flat keys `scale_clamped: true`, `requested` (this parameter's value), `actual` (delta applied, 0 if blocked), and `limit` (effective bound).\n","example":2}}},"ResourceParams":{"type":"object","description":"Resource details for adding a resource, or its identifier when removing one.","properties":{"resource":{"$ref":"#/components/schemas/Resource"},"resourceId":{"type":"string","description":"For remove_resource action, ID of resource to remove"},"resourceType":{"type":"string","enum":["compute","database","storage","network","security"],"description":"For add_resource action, type of resource to add"},"provider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"],"description":"For add_resource action, cloud provider for the new resource"},"targetSize":{"type":"string","description":"For add_resource action, size label for the new resource (e.g. \"t3.micro\" for aws)"},"regionKey":{"type":"string","description":"For add_resource action, region key to assign to the new resource (e.g. \"us-east-1\" for aws). Must be a valid region for the specified provider. Returns UNKNOWN_REGION if invalid."}}},"SetRecoveryPolicyParams":{"type":"object","description":"Parameters for set_recovery_policy action. Supports two call forms:\n\n**Per-resource form** — provide `resourceId` and the nested `recoveryPolicy`\nobject to override healing thresholds on one specific resource.\n\n**Global flat form** — omit `resourceId` and pass the four threshold fields\n(`criticalCpuThreshold`, `criticalSteps`, `warningCpuThreshold`, `warningSteps`)\ndirectly at the top level to apply the same policy to every resource in the\nsimulation at once. The nested `recoveryPolicy` object takes precedence over the\nflat fields if both are supplied.","properties":{"resourceId":{"type":"string","description":"ID of the resource to update. Omit to apply the policy globally to all resources (global flat form).","example":"res-abc123"},"recoveryPolicy":{"type":"object","description":"Recovery policy thresholds to apply (per-resource form). Takes precedence over flat threshold fields when both are provided.","required":["criticalCpuThreshold","criticalSteps","warningCpuThreshold","warningSteps"],"properties":{"criticalCpuThreshold":{"type":"number","minimum":0,"maximum":100,"default":80,"description":"CPU % above which a resource is considered critical","example":90},"criticalSteps":{"type":"integer","minimum":1,"default":4,"description":"Steps the resource must stay at critical CPU before recovery triggers","example":2},"warningCpuThreshold":{"type":"number","minimum":0,"maximum":100,"default":70,"description":"CPU % above which a resource is considered in warning state","example":75},"warningSteps":{"type":"integer","minimum":1,"default":3,"description":"Steps the resource must stay at warning CPU before recovery triggers","example":2}}},"criticalCpuThreshold":{"type":"number","minimum":0,"maximum":100,"default":80,"description":"CPU % above which a resource is considered critical (global flat form — applies to all resources when resourceId is omitted)","example":60},"criticalSteps":{"type":"integer","minimum":1,"default":4,"description":"Steps a resource must stay at critical CPU before recovery triggers (global flat form)","example":2},"warningCpuThreshold":{"type":"number","minimum":0,"maximum":100,"default":70,"description":"CPU % above which a resource is considered in warning state (global flat form)","example":50},"warningSteps":{"type":"integer","minimum":1,"default":3,"description":"Steps a resource must stay at warning CPU before recovery triggers (global flat form)","example":1}}},"Observation":{"type":"object","required":["metrics","resources","traffic","currentTime"],"properties":{"metrics":{"type":"object","description":"Current system metrics","properties":{"cpuUsage":{"type":"number","description":"Average CPU utilization (%)","example":65.3},"latencyP50":{"type":"number","nullable":true,"description":"P50 latency (ms)","example":45},"latencyP95":{"type":"number","nullable":true,"description":"P95 latency (ms)","example":91},"latencyP99":{"type":"number","nullable":true,"description":"P99 latency (ms)"},"latencyPercentileStatus":{"type":"string","enum":["modeled","unavailable"],"description":"Whether P50/P95/P99 are supported by a modeled serving-tier latency distribution."},"errorRate":{"type":"number","description":"Error rate (%)","example":0.5},"throughput":{"type":"number","description":"Current successful throughput/goodput (requests/sec)","example":4500},"costPerHour":{"type":"number","description":"Current hourly cost (USD)","example":0.31},"connectionPressure":{"type":"number","description":"Database connection-pool pressure — the ratio of estimated active connections to total pool capacity (capped at 3.0). Only present when the simulation contains a database resource; absent otherwise.\n","example":0.42}}},"resources":{"type":"array","description":"Current resources in simulation","items":{"$ref":"#/components/schemas/Resource"}},"traffic":{"type":"number","description":"Current traffic load","example":5000},"currentTime":{"type":"integer","description":"Current simulation time (total elapsed simulated seconds)","example":2520},"sim_time_seconds":{"type":"integer","description":"Total elapsed simulated seconds since the episode started.\nEquals `currentTime` (which is now expressed in seconds).\nIncluded so agents can compute real-world time durations without\nre-reading the environment config.\n","example":2520},"tick_seconds":{"type":"integer","minimum":1,"maximum":3600,"description":"Number of simulated seconds this step advanced the clock.\nReflects the per-step `tick_seconds` override if one was supplied in\nthe step request body; otherwise matches the `tick_seconds` value set\nin `episodeConfig` at environment creation time.\n","example":60},"autoscalingConfig":{"type":"object","description":"Current autoscaling configuration","properties":{"scaleOutCpuThreshold":{"type":"number"},"scaleInCpuThreshold":{"type":"number"},"maxInstances":{"type":"number"},"minInstances":{"type":"number"}}},"scalingHistory":{"type":"array","description":"Recent scaling actions","items":{"type":"object","properties":{"timestamp":{"type":"integer"},"action":{"type":"string"},"reason":{"type":"string"}}}},"recentEvents":{"type":"array","description":"Recent system events","items":{"type":"object"}}}},"Reward":{"type":"object","required":["total","components"],"properties":{"total":{"type":"number","description":"Total reward for this step (weighted sum of components)","example":0.312},"components":{"type":"object","description":"Individual reward components before weighting","properties":{"performance":{"type":"number","description":"Performance score (0-1, based on latency and errors)","example":0.578},"cost":{"type":"number","description":"Cost efficiency score (0-1, based on budget)","example":1},"stability":{"type":"number","description":"Stability score (-1 to 1, penalizes excessive changes)","example":-0.2},"sla":{"type":"number","description":"SLA compliance score (-1 to 0, penalizes violations)","example":-0.5}}},"metrics":{"type":"object","description":"Raw metrics used for reward calculation","properties":{"avgLatency":{"type":"number"},"errorRate":{"type":"number"},"costPerHour":{"type":"number"},"slaViolations":{"type":"integer"}}}}},"RLObs":{"type":"object","description":"Agent-facing decision state returned by the step and observation endpoints. Contains only the signals an RL agent needs to choose its next action. Fractions (cpu_util, error_rate, uptime) are in the range [0, 1].\n","required":["rps","cpu_util","instances","traffic","currentTime"],"properties":{"rps":{"type":"number","description":"Throughput in requests per second","example":1620},"cpu_util":{"type":"number","description":"CPU utilization as a fraction (0–1)","example":0.582},"instances":{"type":"integer","description":"Total number of active compute instances","example":3},"traffic":{"type":"number","description":"Current traffic load (requests per second)","example":1620},"currentTime":{"type":"integer","description":"Current simulation time step","example":15},"tick_seconds":{"type":"integer","minimum":1,"maximum":3600,"description":"Number of simulated seconds this step advanced the clock.\nReflects the per-step `tick_seconds` override supplied in the step\nrequest body, or the episode-level `episodeConfig.tick_seconds` when\nno per-step override was given.\n","example":60},"unmodeled_dimensions_active":{"type":"boolean","description":"True when at least one cost dimension is active but not modeled in the reward function (e.g. egress or cross-AZ traffic). See `unmodeled_cost_warning` at the step-response top level for the list of active dimensions. Absent (falsy) when all dimensions are modeled or no unmodeled dimensions are detected.\n","example":true},"warmup_factor":{"type":"number","description":"Minimum warmup capacity factor across all compute/Kubernetes resources that carry a warm-up counter, in (0, 1]. Value is 1.0 when all instances are fully warmed; less than 1.0 while any instance is still in its warm-up window.\n\n**Mid-episode transition:** This field is absent at episode start when no compute resource carries a warm-up counter. It appears dynamically once a cold compute instance is provisioned — either via an `add_resource` action or via canvas autoscaling triggered by high CPU or traffic. After the warm-up window completes (typically 3 steps), the field returns to 1.0 and may become absent again if no further cold instances exist.\n\n**Safe access pattern:** Agents building a fixed-width observation vector must guard against its absence by defaulting to 1.0 when the field is not present. In Python: `wf = obs.get(\"warmup_factor\", 1.0)`. In JavaScript/TypeScript: `const wf = obs.warmup_factor ?? 1.0`. Never assume the field is present across all steps of an episode.\n\nUse this as a direct observation feature in your reward function — low values indicate recently-provisioned instances are not yet at full throughput. For per-resource detail see metrics.compute[].warmup_factor.\n","example":0.5},"routedRps_per_node":{"type":"number","description":"Average requests per second routed to each active compute node this step. Computed as the instance-count-weighted mean of per-node `routedRps` across all compute resources: `sum(perNodeRps × instances) / totalInstances`.\n\nUnlike `rps` (the simulation-wide throughput total), this field expresses per-node load directly so agents can make scaling decisions without dividing by instance count themselves. When nodes fail their share of traffic is redistributed to surviving nodes — `routedRps_per_node` captures this redistribution effect accurately, eliminating the approximation error that arises when computing `rps / instances` after a partial failure.\n\n**Availability:** Present on step responses whenever at least one compute resource exists and the simulation engine has produced a routedRps map for the step. Absent on reset observations (which are pre-step snapshots) and on simulations that contain no compute resources.\n\n**Safe access pattern:** Default to `obs.get(\"rps\", 0) / max(obs.get(\"instances\", 1), 1)` when the field is absent for backward compatibility.\n","example":583.3},"unavailable_node_count":{"type":"integer","description":"Number of compute, Kubernetes, and database resources whose `availabilityState` is `\"unavailable\"` at this step. A value of zero means every tracked resource is serving traffic normally.\n\nUse this as a compact policy signal — when greater than zero, at least one node has failed (parked-critical, AZ-outage, or instance_kill) and surviving nodes are absorbing its redirected traffic share. Pair with `observation.resources[].availabilityState` for per-node detail when you need to identify which specific resources are unavailable.\n\n**Zero on reset observations** (all resources default to `\"available\"` before the first step, so this value is always 0 on fresh episode start).\n\n**Safe access pattern:** `unavailable = obs.get(\"unavailable_node_count\", 0)` — treat absence as zero.\n","example":1}}},"RLMetrics":{"type":"object","description":"Evaluation-oriented metrics returned alongside obs by the step and observation endpoints. These are the outputs your reward function and monitoring dashboards should read. Fractions (error_rate, uptime) are in the range [0, 1]. The `connection_pressure` field is optional — it is only present when the simulation contains at least one database resource (e.g. RDS, Cloud SQL, Azure SQL, Autonomous DB, Managed PostgreSQL). Agents should guard against its absence when no database resource is configured.\n","required":["cost_usd_hr","latency_p95","error_rate","uptime","sla_violations"],"properties":{"cost_usd_hr":{"type":"number","description":"Current hourly cost in USD","example":1.08},"latency_p95":{"type":"number","description":"P95 latency in milliseconds","example":104},"error_rate":{"type":"number","description":"Error rate as a fraction (0–1)","example":0.003},"uptime":{"type":"number","description":"Uptime fraction (0–1); equals 1 − error_rate","example":0.997},"sla_violations":{"type":"integer","description":"Number of SLA dimension violations at this step (0 = fully compliant)","example":0},"connection_pressure":{"type":"number","description":"DB connection-pool pressure ratio (activeConnections / maxConnections), capped at 3.0. Only present when the simulation contains database resources. Values > 1.0 indicate pool exhaustion; values > 1.5 indicate severe saturation. Use this field to build reward functions that penalise connection-pool exhaustion independently of the aggregate error_rate signal.\n","example":1.25},"compute":{"type":"array","description":"Per-resource warm-up state for compute and Kubernetes resources that carry a warm-up counter. Only present when at least one such resource exists in the simulation. Absent when no resources have a warm-up counter.\n\n**Mid-episode transition:** Like `obs.warmup_factor`, this array is absent at episode start when no compute resource carries a warm-up counter. It appears dynamically once a cold compute instance is provisioned mid-episode (via `add_resource` or canvas autoscaling) and disappears again once all warm-up windows complete. Guard against its absence with `metrics.compute ?? []` or `metrics.get(\"compute\", [])`.\n\nAgents should read `obs.warmup_factor` as the primary scalar observation feature — do not re-derive it from step counters or this array.\n","items":{"type":"object","properties":{"resource_id":{"type":"string","description":"Resource id of this compute or Kubernetes resource"},"name":{"type":"string","description":"Human-readable resource name"},"warmup_factor":{"type":"number","description":"Normalized capacity factor in (0, 1]. Value is 1.0 when fully warmed; less than 1.0 while warming (steepest at step 0). Use this directly as an RL observation feature instead of inferring it from step counters.\n","example":0.5},"warming":{"type":"boolean","description":"True while the resource is still in its warm-up window"},"warmup_steps_remaining":{"type":"integer","description":"Steps remaining until the resource is fully warmed (0 when warmed)","example":2}}}},"error_breakdown":{"type":"object","description":"Per-contributor breakdown of the error rate. Each field is the pre-clamp additive contribution in percentage-point units (same scale as `error_rate × 100`). `queue_absorption` is the amount removed by queue buffering (positive value = reduction). `dependency_failure` is the client-level contribution from failed downstream dependency paths. Only present when the simulation engine produces an errorBreakdown for the step. Absent on early steps or simulations without failure sources. Use these fields to build reward functions that penalise specific failure modes — for example, penalise `pool_saturation > 0` for modeled connection demand beyond a usable database limit, or reward reducing `capacity_overload` after a scale-out. `database_pressure` reports the generic contribution once a database's connection or CPU utilization exceeds its effective threshold (80% by default); benchmark-compatibility runs retain their provider reference fit. It shares a combined 5 percentage-point database-error cap with `pool_saturation`. The accompanying details name the database and whether connection use, CPU, or both drove the pressure. Pool-saturation details identify databases whose modeled demand exceeds their own usable connection limits. `cpu_overload` is serving-CPU telemetry, not a compute-failure signal. Only active, request-serving compute and Kubernetes resources that are actually serving traffic contribute; parked or unavailable nodes are excluded from the numerator and denominator. Database and network resource CPU is separate and never contributes to `cpu_overload`. For each eligible serving resource, the engine computes `max(0, (cpuUsagePercent - 80) / 20)`, averages those per-resource excesses, and multiplies by the provider slope. Ordinary/custom AWS uses a 10 percentage-point slope: one eligible AWS node at 100% CPU contributes 10 points, while eligible nodes at 100% and 70% contribute 5 points. Adding a parked 100% node, a database at 100%, or a network resource at 100% leaves that value unchanged. Other providers currently use slope 0. The owned AWS CRUD benchmark may intentionally report zero here as an internal calibration policy; that is not a generic API rule and should not be applied to ordinary AWS responses.\n","properties":{"database_pressure":{"type":"number","description":"Generic database pressure contribution after per-database connection utilization or CPU utilization exceeds its effective threshold (80% by default); benchmark-compatibility runs retain their provider reference fit.","example":0},"database_pressure_details":{"type":"array","description":"Per-database attribution for the under-limit pressure contribution.","items":{"type":"object","required":["resource_id","name","cause","modeled_connections","usable_connection_limit","connection_utilization","cpu_utilization","contribution_pct"],"properties":{"resource_id":{"type":"string"},"name":{"type":"string"},"cause":{"type":"string","enum":["connection_pressure","cpu_pressure","connection_and_cpu_pressure","provider_reference_fit"]},"modeled_connections":{"type":"number","minimum":0},"usable_connection_limit":{"type":"number","minimum":0},"connection_utilization":{"type":"number","minimum":0},"cpu_utilization":{"type":"number","minimum":0},"contribution_pct":{"type":"number","minimum":0}}}},"pool_saturation":{"type":"number","description":"Error contribution from connection-pool exhaustion (percentage points)","example":0},"pool_saturation_details":{"type":"array","description":"Databases whose modeled connection demand exceeds their own usable pool limits.","items":{"type":"object","required":["resource_id","name","cause","modeled_connections","usable_connection_limit","demand_basis"],"properties":{"resource_id":{"type":"string"},"name":{"type":"string"},"cause":{"type":"string","enum":["connection_limit_exceeded"]},"modeled_connections":{"type":"number","minimum":0},"usable_connection_limit":{"type":"number","minimum":0},"demand_basis":{"type":"string","enum":["modeled connection demand; not observed live sessions"]}}}},"db_failure":{"type":"number","description":"Error contribution from failed or overloaded database resources (percentage points)","example":24},"compute_failure":{"type":"number","description":"Error contribution from failed compute nodes (percentage points)","example":0},"capacity_overload":{"type":"number","description":"Error contribution from traffic exceeding compute capacity (percentage points)","example":0},"cpu_overload":{"type":"number","description":"Serving CPU overload contribution in percentage points. Uses the mean per-resource CPU excess above 80% among active serving compute/Kubernetes resources, excluding parked or unavailable nodes and excluding database/network CPU. Ordinary/custom AWS uses a 10-point slope, so 100% on one eligible node contributes 10 points.\n","example":0},"oci_storage":{"type":"number","description":"Error contribution from OCI storage I/O issues (percentage points)","example":0},"queue_absorption":{"type":"number","description":"Amount of error rate absorbed (buffered) by queue resources (percentage points, positive = reduction in effective error rate). Subtract this from the sum of the other contributors to get the net error rate before clamping.\n","example":0},"dependency_failure":{"type":"number","description":"Client-level error contribution from unavailable or failing downstream dependency paths (percentage points)","example":0}}},"ttft_p50_ms":{"type":"number","description":"Weighted-average time-to-first-token (TTFT) in milliseconds across all `inferenceMode` GPU Kubernetes clusters in the simulation. Accounts for the accelerator catalog TTFT base, the prompt-token count (`inputTokensPerRequest`), the topology TTFT factor, and batch pressure at the current GPU utilization. Only present when the simulation contains at least one active `inferenceMode` cluster with positive throughput weight. Absent on non-inference simulations. Use this field to reward inference-serving agents that minimize time-to-first-token.\n","example":245},"decode_tokens_per_sec":{"type":"number","description":"Weighted-average effective decode throughput (tokens/second) across all `inferenceMode` GPU Kubernetes clusters. Derived from the accelerator catalog decode rate, the topology decode factor, and batch pressure at the current GPU utilization. Higher values indicate the cluster is decoding tokens faster. Only present when at least one active `inferenceMode` cluster exists with positive throughput weight. Absent on non-inference simulations. Convert to per-request decode latency: `decodeMs = (outputTokensPerRequest / decode_tokens_per_sec) * 1000`.\n","example":3840}}},"AutoscalingConfig":{"type":"object","properties":{"scaleOutCpuThreshold":{"type":"number"},"scaleInCpuThreshold":{"type":"number"},"scaleOutThroughputThreshold":{"type":"number"},"scaleInThroughputThreshold":{"type":"number"},"scaleOutLatencyThreshold":{"type":"number"},"cooldownSeconds":{"type":"number","description":"Simulated seconds between ordinary scale-out decisions (ordinary simulation steps advance one simulated second each). Creation warm-up sizing with declared compute capacityRps does not arm this cooldown; a later offered load exceeding the entire declared serving fleet can override an active scale-out cooldown."},"ecsCpuTargetTracking":{"type":"boolean","description":"When true, the ECS Fargate fleet uses CPU-only target tracking; latency/throughput triggers in this config do not apply to ECS."},"scaleOutCooldownSeconds":{"type":"integer","minimum":0,"maximum":86400},"scaleInCooldownSeconds":{"type":"integer","minimum":0,"maximum":86400},"simulationSecondsPerStep":{"type":"number","exclusiveMinimum":0,"maximum":60},"minInstances":{"type":"number"},"maxInstances":{"type":"number"}}},"OptimizationGoals":{"type":"object","required":["primary"],"properties":{"primary":{"type":"string","enum":["minimize_cost","maximize_performance","balance"],"description":"Primary optimization objective","example":"minimize_cost"},"constraints":{"type":"object","properties":{"max_cost_per_hour":{"type":"number","description":"Maximum acceptable cost per hour (USD)","example":10},"min_throughput":{"type":"number","description":"Minimum required throughput (requests/second)","example":5000},"max_latency_p95":{"type":"number","description":"Maximum acceptable P95 latency (milliseconds)","example":200}}},"weights":{"type":"object","description":"Custom weights for multi-objective optimization","properties":{"cost":{"type":"number","example":0.4},"performance":{"type":"number","example":0.4},"stability":{"type":"number","example":0.2}}}}},"OptimizationRecommendation":{"type":"object","properties":{"rank":{"type":"integer","description":"Recommendation ranking (1 is best)","example":1},"name":{"type":"string","description":"Descriptive name","example":"Cost-Optimized Configuration"},"description":{"type":"string","example":"Reduces costs by 38% while maintaining performance"},"simulationSnapshot":{"type":"object","description":"Modified simulation configuration","properties":{"resources":{"type":"array","items":{"$ref":"#/components/schemas/Resource"}},"connections":{"type":"array","items":{"$ref":"#/components/schemas/Connection"}},"autoscalingConfig":{"$ref":"#/components/schemas/AutoscalingConfig"}}},"metrics":{"type":"object","properties":{"cost_per_hour":{"type":"number"},"latency_p95":{"type":"number"},"throughput":{"type":"number"},"error_rate":{"type":"number"}}},"improvements":{"type":"array","items":{"type":"string"},"example":["Reduced cost by 38%","Resolved CPU saturation"]},"changes":{"type":"array","items":{"type":"string"},"example":["Changed web-server-1 from m5.large to t3.medium"]},"score":{"type":"number","description":"Overall score based on goals","example":87.5},"costSavingsPercent":{"type":["number","null"],"description":"Cost savings vs baseline. `null` when `coverageState` is `insufficient_cost_coverage` — the baseline aggregate cost excluded at least one unknown-rate resource, so no real savings percentage can be computed.\n","example":38},"coverageState":{"type":"string","enum":["cost_complete","insufficient_cost_coverage"],"description":"Cost-coverage trust signal. `cost_complete` — the baseline cost covered every resource and costSavingsPercent is a real figure. `insufficient_cost_coverage` — at least one resource with an unknown rate (e.g. an inferenceMode Kubernetes cluster with an uncatalogued GPU SKU and no characteristics.perNodeRate) was excluded from the baseline aggregate; costSavingsPercent is null. Behavioral and latency metrics remain valid. Absent on jobs completed before this field existed (treat as cost_complete).\n","example":"cost_complete"},"excludedRateResources":{"type":"array","items":{"type":"string"},"description":"Names of the resources excluded from the baseline cost aggregate because their rate is unknown. Empty when coverage is complete.\n","example":[]},"reversibility":{"type":"object","description":"Rule-based classification of how hard this recommendation is to reverse. Derived deterministically from the changes array (never from real cloud APIs or an LLM — basis is always rule_based). The least-reversible change sets the tier. Absent on jobs completed before this field existed.\n","properties":{"tier":{"type":"string","enum":["instant","minutes","hours","days"],"example":"minutes"},"estimatedMinutesMin":{"type":"number","description":"Lower bound of the estimated rollback time in minutes","example":3},"estimatedMinutesMax":{"type":"number","description":"Upper bound of the estimated rollback time in minutes","example":10},"reason":{"type":"string","description":"Names the dominant (least-reversible) action that set the tier","example":"Same-region compute resize"},"basis":{"type":"string","enum":["rule_based"],"description":"Always rule_based — classification comes from deterministic rules","example":"rule_based"}}}}},"OptimizationJob":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","running","completed","failed"],"example":"completed"},"variantsGenerated":{"type":"integer","example":57},"variantsCompleted":{"type":"integer","example":57},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"error":{"type":"string","description":"Error message if failed"},"webhookDeliveryStatus":{"type":"string","enum":["pending","delivered","failed"],"description":"Webhook delivery status","example":"delivered"},"webhookDeliveryAttempts":{"type":"integer","description":"Number of webhook delivery attempts made","example":1},"webhookDeliveryError":{"type":"string","description":"Error message if webhook delivery failed"},"webhookDeliveredAt":{"type":"string","format":"date-time","description":"Timestamp when webhook was successfully delivered"}}},"TrafficForecastPoint":{"type":"object","required":["timestamp","rps"],"properties":{"timestamp":{"type":"number","description":"Time offset from the start of the forecast, in simulation steps; points must be ordered by increasing timestamps.","example":30},"rps":{"type":"number","description":"Forecast request rate in requests per second at this timestamp.","example":12000},"label":{"type":"string","description":"Optional label for this data point","example":"Peak - Noon"}}},"TrafficForecast":{"type":"object","required":["name","dataPoints"],"properties":{"name":{"type":"string","description":"Name of the traffic forecast","example":"Black Friday 2025"},"description":{"type":"string","description":"Optional description","example":"Predicted traffic spike for sale event"},"dataPoints":{"type":"array","description":"Traffic data points over time","items":{"$ref":"#/components/schemas/TrafficForecastPoint"}},"peakRPS":{"type":"number","description":"Peak requests per second (calculated)","example":12000},"avgRPS":{"type":"number","description":"Average requests per second (calculated)","example":5200}}},"ValidationResult":{"type":"object","properties":{"passed":{"type":"boolean","description":"Whether infrastructure can handle the forecast","example":false},"summary":{"type":"string","description":"Summary of validation results","example":"Infrastructure will fail under peak load due to CPU saturation"},"peakMetrics":{"type":"object","description":"Metrics at peak traffic","properties":{"timestamp":{"type":"number"},"traffic":{"type":"number"},"cpuUsage":{"type":"number"},"latencyP95":{"type":"number"},"errorRate":{"type":"number"},"costPerHour":{"type":"number"}}},"bottlenecksDetected":{"type":"array","description":"List of bottlenecks found","items":{"type":"string"},"example":["CPU saturation at 98%","Error rate exceeds 5%"]},"failurePoints":{"type":"array","description":"Time points where infrastructure fails","items":{"type":"object","properties":{"timestamp":{"type":"number"},"traffic":{"type":"number"},"reason":{"type":"string"}}}},"recommendations":{"type":"array","description":"Recommended fixes","items":{"type":"string"},"example":["Scale out to 5 instances before peak","Increase CPU threshold to 75%"]}}},"ThresholdTestResult":{"type":"object","properties":{"scaleOutCpuThreshold":{"type":"number","description":"CPU threshold for scaling out","example":70},"scaleInCpuThreshold":{"type":"number","description":"CPU threshold for scaling in","example":30},"scaleOutThroughputThreshold":{"type":"number","example":80},"scaleInThroughputThreshold":{"type":"number","example":40},"metrics":{"type":"object","description":"Performance metrics for this configuration","properties":{"cost_per_hour":{"type":"number","example":4.5},"latency_p95":{"type":"number","example":142},"error_rate":{"type":"number","example":0.8},"throughput":{"type":"number","example":11500},"scaling_events":{"type":"integer","example":12}}},"bottlenecks":{"type":"array","items":{"type":"string"},"example":["CPU spike during rapid scale-out"]},"score":{"type":"number","description":"Overall score (0-100)","example":87.5},"passed":{"type":"boolean","description":"Whether this configuration meets requirements","example":true}}},"PredictionRecommendation":{"type":"object","properties":{"rank":{"type":"integer","description":"Recommendation ranking (1 is best)","example":1},"title":{"type":"string","description":"Short title for recommendation","example":"Proactive scaling before peak"},"description":{"type":"string","example":"Scale out 2 hours before predicted peak to avoid saturation"},"priority":{"type":"string","enum":["critical","high","medium","low"],"example":"critical"},"action":{"type":"string","description":"Specific action to take","example":"Set CPU threshold to 65% and enable predictive scaling"},"expectedImpact":{"type":"string","example":"Prevents 98% CPU saturation, reduces error rate to <1%"},"autoscalingConfig":{"$ref":"#/components/schemas/AutoscalingConfig"},"resourceChanges":{"type":"array","description":"Specific resource modifications","items":{"type":"object","properties":{"resourceId":{"type":"string"},"change":{"type":"string"},"reason":{"type":"string"}}}}}},"PredictionJob":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["validation","threshold_optimization"],"description":"Type of prediction job","example":"validation"},"status":{"type":"string","enum":["pending","running","completed","failed"],"example":"completed"},"baseSimulationId":{"type":"string","description":"ID of simulation being tested"},"trafficForecast":{"$ref":"#/components/schemas/TrafficForecast"},"validationResult":{"$ref":"#/components/schemas/ValidationResult"},"thresholdTests":{"type":"array","description":"Results from testing different thresholds","items":{"$ref":"#/components/schemas/ThresholdTestResult"}},"bestThresholds":{"$ref":"#/components/schemas/AutoscalingConfig"},"recommendations":{"type":"array","items":{"$ref":"#/components/schemas/PredictionRecommendation"}},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"error":{"type":"string","description":"Error message if failed"},"webhookDeliveryStatus":{"type":"string","enum":["pending","delivered","failed"],"description":"Webhook delivery status","example":"delivered"},"webhookDeliveryAttempts":{"type":"integer","description":"Number of webhook delivery attempts made","example":1},"webhookDeliveryError":{"type":"string","description":"Error message if webhook delivery failed"},"webhookDeliveredAt":{"type":"string","format":"date-time","description":"Timestamp when webhook was successfully delivered"}}},"ChaosInjectionConfig":{"type":"object","required":["type","targetId","injectionTime"],"properties":{"type":{"type":"string","enum":["kill_instance","network_delay","database_slowdown","database_overload","cpu_spike","memory_pressure","zone_outage","eks_spot_interruption"],"description":"Type of failure to inject","example":"kill_instance"},"targetId":{"type":"string","description":"ID of the resource to target (or zone ID for zone_outage)","example":"web-1"},"injectionTime":{"type":"integer","description":"Simulation step when failure should be injected","example":50},"duration":{"type":"integer","description":"How long the failure lasts (in steps, optional)","example":20},"severity":{"type":"number","description":"Severity multiplier (0.0-1.0, optional)","example":0.8}}},"ResilienceScore":{"type":"object","description":"Composite resilience result. For canonical outcome-backed results, `overall` is reproducible from the displayed breakdown components using weights recovery 0.25, availability 0.30, dataIntegrity 0.25, and gracefulDegradation 0.20 (rounded displayed components are used). A critical no-failover/no-redundancy/single-point finding on the crashed resource sets the recovery component to zero, even if a later restart succeeds. The batch applies this rule if any child has the target-specific risk. `scoringReason` explains the policy. Legacy payloads may omit newer breakdown components and metrics. `grade` is derived from the live weighted score when canonical evidence is present, not from static vulnerability labels. It is not a deadline verdict or a statement that an unrecovered run is production-ready; inspect `metrics.outcome` for authoritative evidence and final health state when present.\n","required":["overall","grade"],"properties":{"scoringReason":{"type":"string","description":"Explanation of a target-specific recovery score adjustment."},"overall":{"type":"number","description":"Overall resilience score (0-100)","example":72.5},"breakdown":{"type":"object","description":"Score components on a 0-100 scale. The composite uses `recovery * 0.25 + availability * 0.30 + dataIntegrity * 0.25 + gracefulDegradation * 0.20`, rounded to the reported overall score. Canonical outcome-backed scores provide all four components. Legacy payloads may retain historical breakdown keys or omit newer components; treat those payloads as compatibility data.\n","properties":{"recovery":{"type":"number","description":"Recovery component (0-100)","example":75},"availability":{"type":"number","description":"Availability component (0-100)","example":85},"dataIntegrity":{"type":"number","description":"Data-integrity component (0-100)","example":100},"gracefulDegradation":{"type":"number","description":"Graceful-degradation component (0-100)","example":90}}},"grade":{"type":"string","enum":["A","B","C","D","F"],"description":"Letter grade for resilience","example":"C"},"metrics":{"$ref":"#/components/schemas/ResilienceMetrics"}}},"EndpointHealthCheck":{"type":"object","additionalProperties":false,"properties":{"path":{"type":"string","minLength":1,"maxLength":256,"default":"/health"},"intervalSeconds":{"type":"integer","minimum":1,"maximum":300,"default":15},"timeoutSeconds":{"type":"integer","minimum":1,"maximum":120,"default":5},"unhealthyThreshold":{"type":"integer","minimum":1,"maximum":20,"default":3},"healthyThreshold":{"type":"integer","minimum":1,"maximum":20,"default":2},"dependencyResourceIds":{"type":"array","maxItems":32,"items":{"type":"string","minLength":1,"maxLength":128},"default":[]},"livenessOnly":{"type":"boolean","default":false,"description":"If true, dependencyResourceIds must be empty and downstream outages do not fail this check."}}},"HealthCheckLifecycle":{"type":"object","required":["resourceId","resourceName","platform","checkType","desiredReplicas","unhealthyReplicas","deregisteredReplicas","replacementReplicas","readyReplicas","unhealthyAtSeconds","deregisteredAtSeconds","replacementStartedAtSeconds","readyAtSeconds","albFailOpen"],"properties":{"resourceId":{"type":"string"},"resourceName":{"type":"string"},"platform":{"type":"string","enum":["ecs","kubernetes"]},"checkType":{"type":"string","enum":["load_balancer_readiness","liveness","readiness"]},"desiredReplicas":{"type":"integer","minimum":0},"unhealthyReplicas":{"type":"integer","minimum":0},"deregisteredReplicas":{"type":"integer","minimum":0},"replacementReplicas":{"type":"integer","minimum":0},"readyReplicas":{"type":"integer","minimum":0},"unhealthyAtSeconds":{"type":"number","nullable":true},"deregisteredAtSeconds":{"type":"number","nullable":true},"replacementStartedAtSeconds":{"type":"number","nullable":true},"scheduledReadyAtSeconds":{"type":"number","nullable":true,"description":"Estimated minimum cold-start completion; readyAtSeconds is set only when readiness is observed."},"readyAtSeconds":{"type":"number","nullable":true,"description":"First simulation-clock sample at which readiness is actually observed."},"albFailOpen":{"type":"boolean"}}},"ResilienceMetrics":{"type":"object","description":"Resilience measurements over the same simulation-time request window. `availabilityPercent` is successful service over offered requests (goodput divided by offered load), not the percentage of samples below the 5% recovery-health threshold. When offered-load evidence is absent, the authoritative basis and explicit estimate are in `outcome`; no request totals are invented. `recoveryTimeSeconds` and `meanTimeToRecovery` are null when no sustained recovery was observed; older stored reports may contain 0 as a legacy sentinel, not proof of immediate recovery. Use recovery milestones and `sustainPeriodCompleted` in `outcome` to distinguish\n unrecovered, recovered, and recovered-then-regressed runs. A 15%\n error rate with zero goodput and no offered load is unavailable, not\n 85% availability. A healthy history with no detected incident has\n baseline evidence only and no recovery milestone. In a batch, mixing\n request-backed children with a rate-only `estimated_from_error_rate`\n child makes the offered denominator unavailable; independent goodput,\n request-window duration, and duration-weighted error-rate evidence remain\n reported. Graceful degradation is the percentage of offered requests\n successfully served in the same window; without a complete positive\n denominator its score is 0, not evidence of successful service. Batch\n recovery duration is null if any child incident did not recover.\n","required":["recoveryTimeSeconds","availabilityPercent","dataLossIncidents","gracefulDegradationScore","meanTimeToRecovery","errorRateDuringChaos"],"properties":{"healthCheckLifecycle":{"type":"array","maxItems":64,"description":"Per-resource unhealthy, deregistration, replacement/restart, readiness, and ALB fail-open evidence.","items":{"$ref":"#/components/schemas/HealthCheckLifecycle"}},"databaseCrashAssumptions":{"type":"array","maxItems":32,"description":"Effective simulation-clock assumptions for each database_crash target, including defaults, in injection order. Absent on older reports and on runs with no database crash. Both delays are reported even though only the delay selected by recoveryMode applies to a given target. Neither value is a provider SLA. Available on single-job results and on each childJobs[].resilienceScore in batch results.\n","items":{"type":"object","required":["targetResourceId","recoveryMode","promotionDelaySeconds","restartDelaySeconds"],"properties":{"targetResourceId":{"type":"string"},"recoveryMode":{"type":"string","enum":["explicit_replica_promotion","sole_writer_restart"]},"promotionUnavailableReason":{"type":"string","enum":["related_replica_unavailable_at_injection"],"description":"Present only when an explicitly related replica exists but is unavailable at injection time (including a simultaneous crash). The writer uses sole_writer_restart; promotionDelaySeconds is reported but does not apply."},"promotionDelaySeconds":{"type":"integer","minimum":0,"maximum":86400},"restartDelaySeconds":{"type":"integer","minimum":0,"maximum":86400}}}},"latencyP95Ms":{"type":"number","nullable":true,"description":"Maximum sampled per-interval p95 latency in milliseconds; null when no serving interval has latency evidence. Not a pooled request percentile."},"chaosIntervals":{"type":"array","maxItems":100,"description":"Bounded ten-second [startSeconds,endSeconds) simulation-clock intervals (last interval clipped at job duration). Rates integrate by interval duration into outcome request totals. Null means unavailable, not zero; targetStatus remains critical during the crash. Connected writers are hard dependencies: any required writer without its own serving replacement causes 100% errors and zero goodput, including when another independent writer is healthy or crashes concurrently. Without connection edges each writer is considered required; crashing a non-serving reader does not cause this outage. servingResourceId identifies only the first crash target (or its explicitly related promoted replica) when aggregate service is available, otherwise null; it never identifies an unrelated writer. Database crashes honor startTime on the ten-second sampling clock. No task, ACU, or connection-usage values are inferred. databaseTargets reports each distinct crashed database separately; rates and request totals remain aggregate interval values, never duplicated per target. Absent on older persisted reports.\n","items":{"type":"object","required":["startSeconds","endSeconds","offeredRps","errorRatePercent","successfulThroughputRps","latencyP95Ms","targetResourceId","servingResourceId","targetStatus","servingStatus","servingMaxConnections"],"properties":{"startSeconds":{"type":"number"},"endSeconds":{"type":"number"},"offeredRps":{"type":"number","nullable":true},"errorRatePercent":{"type":"number","nullable":true},"successfulThroughputRps":{"type":"number","nullable":true},"latencyP95Ms":{"type":"number","nullable":true},"targetResourceId":{"type":"string","nullable":true},"servingResourceId":{"type":"string","nullable":true},"targetStatus":{"type":"string","nullable":true,"enum":["healthy","warning","critical"]},"servingStatus":{"type":"string","nullable":true,"enum":["healthy","warning","critical"]},"servingMaxConnections":{"type":"number","nullable":true,"description":"Configured connection limit, not measured live connections."},"databaseTargets":{"type":"array","maxItems":32,"description":"One entry per distinct database crash target, in injection order. A null targetStatus means the target was removed after explicit replica promotion. Replacement is null while promotion/restart is pending; for sole-writer restart the replacement is the original target. Connection limits are configured capacity, not observed usage. No per-target request counts are implied.\n","items":{"type":"object","required":["targetResourceId","targetStatus","servingReplacementResourceId","servingReplacementStatus","servingReplacementMaxConnections"],"properties":{"targetResourceId":{"type":"string"},"targetStatus":{"type":"string","nullable":true,"enum":["healthy","warning","critical"]},"servingReplacementResourceId":{"type":"string","nullable":true},"servingReplacementStatus":{"type":"string","nullable":true,"enum":["healthy","warning","critical"]},"servingReplacementMaxConnections":{"type":"number","nullable":true,"description":"Configured connection limit, not measured live connections."}}}},"healthChecks":{"type":"array","maxItems":64,"items":{"$ref":"#/components/schemas/HealthCheckLifecycle"}}}}},"recoveryTimeSeconds":{"type":"number","nullable":true,"description":"Observed sustained recovery duration in simulation seconds, null when unobserved. Older reports may contain a legacy zero sentinel; consult outcome recovery milestones for authoritative status.\n","example":45.2},"availabilityPercent":{"type":"number","description":"Successful service percentage over the measured request window, clamped to 0-100. With observed offered load this is successfulRequests / observedOfferedRequests × 100 (equivalently duration-weighted goodput / offered RPS); 15% error with valid offered load is approximately 85%, even when no sample is below the recovery threshold.\n","minimum":0,"maximum":100,"example":85},"dataLossIncidents":{"type":"number","description":"Count of data-loss or corruption incidents observed during the run.","example":0},"gracefulDegradationScore":{"type":"number","description":"Successful requests divided by offered requests over the bounded chaos window (0-100). Zero when no complete positive denominator establishes partial service; scoringReason explains the basis.\n","example":90},"meanTimeToDetect":{"type":"number","description":"Average time to detect failures (seconds)","example":3.5},"meanTimeToRecovery":{"type":"number","nullable":true,"description":"Observed sustained recovery duration in simulation seconds; null if unobserved. Legacy stored zero is not proof of recovery.\n","example":12.8},"errorRateDuringChaos":{"type":"number","description":"Error rate during chaos in percentage points.","example":15},"eksSpotMigration":{"$ref":"#/components/schemas/EksSpotMigrationState"},"eksSpotMigrations":{"type":"array","description":"Per-resource EKS Spot migration evidence for concurrent active migrations.","items":{"$ref":"#/components/schemas/EksSpotMigrationState"}},"eksSpotMigrationEvents":{"type":"array","description":"EKS Spot interruption lifecycle events; intentionally separate from sustained recovery evidence.","items":{"$ref":"#/components/schemas/EksSpotMigrationEvent"}},"errorRateDuringFailure":{"type":"number","description":"Legacy alias for the error rate during failures (percentage points).","example":15.7},"outcome":{"type":"object","description":"Optional authoritative outcome evidence. The object is omitted for legacy results that predate this contract; when present, all fields below describe one common simulation-time request window. RPS fields are rates and request fields are totals over that window. A positive modeled shedding value means offered work exceeded modeled serving\n capacity; it is not silently counted as an application error and is\n never fabricated from goodput or error rate. For batch results,\n request-backed evidence mixed with a rate-only\n `estimated_from_error_rate` child yields\n `availabilityBasis: \"unavailable\"` rather than combining an\n observed subset into a denominator; goodput, duration, and error-rate\n evidence remain independent.\n","required":["availabilityBasis","requestWindowSeconds","observedOfferedRequests","estimatedOfferedRequests","successfulRequests","modeledShedRequests","observedOfferedLoadRps","estimatedOfferedLoadRps","successfulThroughputRps","modeledSheddingRps","errorRatePercent","recovery"],"properties":{"availabilityBasis":{"type":"string","enum":["observed_offered_load","estimated_from_goodput_and_error_rate","estimated_from_error_rate","mixed_observed_and_estimated","unavailable"],"description":"Evidence basis for availability and offered-load values. `mixed_observed_and_estimated` combines observed offered load with goodput-and-error-rate modeled demand only. A batch that mixes request-backed evidence with a rate-only `estimated_from_error_rate` child is `unavailable`, because no relative request weight exists for that child.\n"},"requestWindowSeconds":{"type":"number","nullable":true,"minimum":0,"description":"Exact duration of the normalized simulation-time request window, including the final sample duration. The analyzer's optional windowEndSeconds input clips the final interval to an exact exclusive boundary; this public field reports the resulting duration.\n","example":60},"observedOfferedRequests":{"type":"number","nullable":true,"minimum":0,"description":"Observed original-client offered request total over the window.","example":60000},"estimatedOfferedRequests":{"type":"number","nullable":true,"minimum":0,"deprecated":true,"description":"Deprecated compatibility alias for modeledOfferedRequests. This value is an estimate, never an observed request count.\n","example":60000},"modeledOfferedRequests":{"type":"number","nullable":true,"minimum":0,"description":"Offered request total modeled from goodput and error rate when no aggregate original-client denominator exists. This is an estimate, not an observed request count.\n","example":60000},"successfulRequests":{"type":"number","nullable":true,"minimum":0,"description":"Successful request total (goodput × duration) over the window.","example":51000},"modeledShedRequests":{"type":"number","nullable":true,"minimum":0,"description":"Requests explicitly shed by modeled capacity limits over the window; null when not evidenced.","example":0},"observedOfferedLoadRps":{"type":"number","nullable":true,"minimum":0,"description":"Observed original-client offered load rate for the window.","example":1000},"estimatedOfferedLoadRps":{"type":"number","nullable":true,"minimum":0,"deprecated":true,"description":"Deprecated compatibility alias for modeledOfferedLoadRps. This value is an estimate, never observed offered load.\n","example":1000},"modeledOfferedLoadRps":{"type":"number","nullable":true,"minimum":0,"description":"Offered load rate modeled from goodput and error rate when no aggregate original-client denominator exists.\n","example":1000},"successfulThroughputRps":{"type":"number","nullable":true,"minimum":0,"description":"Duration-weighted successful throughput/goodput rate over the window.","example":850},"modeledSheddingRps":{"type":"number","nullable":true,"minimum":0,"description":"Duration-weighted request rate explicitly shed by modeled capacity limits.","example":0},"errorRatePercent":{"type":"number","nullable":true,"minimum":0,"maximum":100,"description":"Error rate in percentage points over the same request window.","example":15},"recovery":{"type":"object","description":"Sustained service-health recovery evidence. Health requires positive successful service and error rate below 5% for 30 simulation seconds. A healthy blip shorter than the sustain period does not complete recovery. A later regression keeps the first recovery milestone but clears the current healthy-streak milestone until another sustained recovery; finalState becomes unhealthy and regressedAfterRecovery is true. If no incident is detected, healthy samples are baseline evidence and do not create recovery milestones.\n","required":["healthErrorRateThresholdPercent","sustainPeriodSeconds","incidentStartSeconds","firstHealthyStartSeconds","firstRecoveryCompletedSeconds","finalSustainedRecoveryCompletedSeconds","sustainPeriodCompleted","regressedAfterRecovery","finalState"],"properties":{"healthErrorRateThresholdPercent":{"type":"number","enum":[5],"description":"Strict healthy-state error-rate threshold in percent."},"sustainPeriodSeconds":{"type":"number","enum":[30],"description":"Simulation-time duration below the health threshold required to complete recovery."},"incidentStartSeconds":{"type":"number","nullable":true,"description":"Simulation-time start of the detected incident."},"firstHealthyStartSeconds":{"type":"number","nullable":true,"description":"First simulation-time instant at which the run entered the healthy threshold."},"firstRecoveryCompletedSeconds":{"type":"number","nullable":true,"description":"First sustained-recovery completion time, or null when not completed."},"finalSustainedRecoveryCompletedSeconds":{"type":"number","nullable":true,"description":"Most recent completed sustain milestone for the current healthy streak; null after a relapse until another sustain period completes.\n"},"sustainPeriodCompleted":{"type":"boolean","description":"Whether the 30-second healthy sustain period completed."},"regressedAfterRecovery":{"type":"boolean","description":"Whether the run became unhealthy after a sustained recovery milestone."},"finalState":{"type":"string","enum":["healthy","unhealthy","unavailable"],"description":"Final observed health state at the end of the common request window."},"recoveryTimeLowerBoundSeconds":{"type":"number","nullable":true,"description":"When recovery is unobserved, at least this many simulation seconds have elapsed since the incident without sustained recovery."},"estimatedServiceRestoreSeconds":{"type":"number","nullable":true,"description":"Modeled earliest return of serving capacity (not a successful recovery or provider SLA)."},"assumption":{"type":"string","description":"Explains the clock and restart/promotion assumptions, not a universal cloud-provider guarantee."}}}}}}},"EksSpotMigrationState":{"type":"object","description":"Current bounded EKS Spot interruption migration state. This evidence describes migration completion against the 120-second deadline and is separate from sustained service-health recovery.\n","required":["resourceId","resourceName","status","noticeAtSeconds","deadlineAtSeconds","migrationStartedAtSeconds","migrationDurationSeconds","workloadCount","readyWorkloadCount","remainingDeadlineSeconds","unavailableCapacity"],"properties":{"resourceId":{"type":"string"},"resourceName":{"type":"string"},"status":{"type":"string","enum":["not_started","notice","migrating","ready","deadline_hit","deadline_missed"]},"noticeAtSeconds":{"type":"number","minimum":0},"deadlineAtSeconds":{"type":"number","minimum":0},"migrationStartedAtSeconds":{"type":"number","minimum":0},"migrationDurationSeconds":{"type":"number","minimum":0},"workloadCount":{"type":"integer","minimum":0},"readyWorkloadCount":{"type":"integer","minimum":0},"remainingDeadlineSeconds":{"type":"number"},"unavailableCapacity":{"type":"number","minimum":0},"replacementNodeCount":{"type":"integer","minimum":0},"outcome":{"type":"string","enum":["hit","miss"]}}},"EksSpotMigrationEvent":{"type":"object","required":["type","timestampSeconds","resourceId","resourceName","deadlineSeconds","workloadCount","readyWorkloadCount","migrationDurationSeconds","remainingDeadlineSeconds"],"properties":{"type":{"type":"string","enum":["interruption_notice","migration_deadline","workload_migration_started","workload_ready","deadline_outcome"]},"timestampSeconds":{"type":"number","minimum":0},"resourceId":{"type":"string"},"resourceName":{"type":"string"},"deadlineSeconds":{"type":"number","exclusiveMinimum":0},"workloadCount":{"type":"integer","minimum":0},"readyWorkloadCount":{"type":"integer","minimum":0},"migrationDurationSeconds":{"type":"number","minimum":0},"remainingDeadlineSeconds":{"type":"number"},"replacementNodeCount":{"type":"integer","minimum":0},"outcome":{"type":"string","enum":["hit","miss"]}}},"Vulnerability":{"type":"object","properties":{"type":{"type":"string","enum":["single_point_of_failure","no_redundancy","insufficient_capacity","slow_recovery","cascading_risk","no_failover","zone_dependency","health_check_replacement"],"description":"Machine-readable vulnerability category."},"id":{"type":"string","description":"Unique vulnerability identifier","example":"zone_dependency"},"severity":{"type":"string","enum":["critical","high","medium","low"],"description":"Severity level","example":"high"},"title":{"type":"string","description":"Short vulnerability title","example":"Single Availability Zone Dependency"},"description":{"type":"string","description":"Detailed description of the vulnerability","example":"All web servers are in the same availability zone. A zone failure causes complete service outage."},"impact":{"type":"string","description":"Impact on the system","example":"100% service downtime if us-east-1a fails"},"recommendation":{"type":"string","description":"How to fix the vulnerability","example":"Distribute web servers across at least 2 availability zones"},"detectedAt":{"type":"integer","description":"Simulation step when vulnerability was detected","example":52},"affectedResources":{"type":"array","description":"Resources affected by this vulnerability","items":{"type":"string"},"example":["web-1","web-2","web-3"]}}},"ChaosScenario":{"type":"object","properties":{"id":{"type":"string","description":"Unique scenario identifier","example":"zone_failure"},"name":{"type":"string","description":"Human-readable scenario name","example":"Availability Zone Failure"},"description":{"type":"string","description":"What this scenario tests","example":"Tests resilience to complete availability zone failure"},"injections":{"type":"array","description":"Failure injections in this scenario","items":{"$ref":"#/components/schemas/ChaosInjectionConfig"}},"expectedVulnerabilities":{"type":"array","description":"Vulnerabilities this scenario typically detects","items":{"type":"string"},"example":["zone_dependency","insufficient_capacity"]}}},"ChaosJob":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique job identifier"},"type":{"type":"string","enum":["chaos_test"],"description":"Job type"},"status":{"type":"string","enum":["pending","running","completed","failed","cancelled","partial_failed"],"description":"Current job status"},"simulationId":{"type":"string","format":"uuid","description":"Base simulation being tested"},"scenarioId":{"type":"string","description":"Scenario used (if applicable)"},"scenarioRecoveryDelays":{"type":"object","description":"Requested prebuilt database_crash delay overrides for this job; effective values including defaults are in resilienceScore.metrics.databaseCrashAssumptions.","properties":{"promotionDelaySeconds":{"type":"integer","minimum":0,"maximum":86400},"restartDelaySeconds":{"type":"integer","minimum":0,"maximum":86400}}},"customInjections":{"type":"array","description":"Custom injections (if applicable)","items":{"$ref":"#/components/schemas/ChaosInjectionConfig"}},"duration":{"type":"integer","description":"Test duration in steps"},"createdAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"webhookDeliveryStatus":{"type":"string","enum":["pending","delivered","failed"],"description":"Webhook delivery status","example":"delivered"},"webhookDeliveryAttempts":{"type":"integer","description":"Number of webhook delivery attempts made","example":1},"webhookDeliveryError":{"type":"string","description":"Error message if webhook delivery failed"},"webhookDeliveredAt":{"type":"string","format":"date-time","description":"Timestamp when webhook was successfully delivered"},"error":{"type":"string","description":"Error message if failed"}}},"BatchChaosRequest":{"type":"object","required":["simulationId","scenarios"],"properties":{"simulationId":{"type":"string","format":"uuid","description":"ID of the base simulation to test","example":"sim_abc123"},"scenarios":{"type":"array","description":"Array of chaos test scenarios to execute in parallel","minItems":1,"maxItems":10,"items":{"type":"object","properties":{"scenarioId":{"type":"string","description":"Pre-built scenario ID (optional, mutually exclusive with customInjections)","example":"zone_failure","enum":["zone_failure","database_crash","network_partition","cascading_failure","random_instance_failure","database_slowdown"]},"promotionDelaySeconds":{"type":"integer","minimum":0,"maximum":86400,"description":"Prebuilt database_crash scenario only: simulated replica promotion delay (default 30 seconds); not a provider SLA. Cannot be combined with customInjections."},"restartDelaySeconds":{"type":"integer","minimum":0,"maximum":86400,"description":"Prebuilt database_crash scenario only: simulated sole-writer restart delay (default 1800 seconds); not a provider SLA. Cannot be combined with customInjections."},"customInjections":{"type":"array","description":"Custom failure injections (optional, mutually exclusive with scenarioId)","items":{"$ref":"#/components/schemas/ChaosInjectionConfig"}},"duration":{"type":"integer","description":"Test duration in simulation steps","minimum":10,"maximum":300,"default":300,"example":120}}}},"webhookUrl":{"type":"string","format":"uri","description":"Optional HTTPS URL to receive webhook notification when batch completes","example":"https://example.com/webhook"},"webhookSecret":{"type":"string","description":"Optional secret for HMAC-SHA256 webhook signature verification","example":"secret123"}}},"BatchChaosJob":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique batch job identifier","example":"batch_xyz789"},"status":{"type":"string","enum":["pending","running","completed","failed","cancelled","partial_failed"],"description":"Current batch status. 'partial_failed' indicates some child jobs failed but others succeeded.\n","example":"completed"},"childJobIds":{"type":"array","description":"IDs of all child chaos jobs in this batch","items":{"type":"string","format":"uuid"},"example":["job_1","job_2","job_3"]},"totalJobs":{"type":"integer","description":"Total number of child jobs in this batch","example":3},"completedJobs":{"type":"integer","description":"Number of child jobs that completed successfully","example":2},"failedJobs":{"type":"integer","description":"Number of child jobs that failed","example":1},"cancelledJobs":{"type":"integer","description":"Number of child jobs that were cancelled","example":0},"childJobProgress":{"type":"array","description":"Lightweight child progress in submission order on GET /chaos/batch/{batchId}, available while pending or running and after completion. Does not include child results or unrelated child data.\n","items":{"type":"object","required":["jobId","status"],"properties":{"jobId":{"type":"string","description":"Child chaos job ID"},"status":{"type":"string","enum":["pending","running","completed","failed","cancelled"]},"scenarioId":{"type":"string","description":"Prebuilt scenario ID, omitted for custom injections"},"scenarioRecoveryDelays":{"type":"object","description":"Requested overrides for a prebuilt database_crash scenario only. Missing delay fields mean simulator defaults (promotion 30 seconds, restart 1800 seconds); these are not the effective per-target delays reported in completed child results.\n","properties":{"promotionDelaySeconds":{"type":"integer","minimum":0,"maximum":86400},"restartDelaySeconds":{"type":"integer","minimum":0,"maximum":86400}}}}}},"aggregatedResilienceScore":{"allOf":[{"$ref":"#/components/schemas/ResilienceScore"}],"description":"Aggregated resilience score across all completed child jobs. Only present when status is \"completed\"."},"aggregatedVulnerabilities":{"type":"array","description":"Aggregated vulnerabilities from all child jobs (deduplicated with occurrence counts). Only present when status is \"completed\".","items":{"type":"object","properties":{"id":{"type":"string","description":"Vulnerability identifier","example":"zone_dependency"},"severity":{"type":"string","enum":["critical","high","medium","low"],"description":"Severity level","example":"high"},"title":{"type":"string","description":"Vulnerability title","example":"Single Availability Zone Dependency"},"description":{"type":"string","description":"Detailed description","example":"Resources are not distributed across availability zones"},"occurrences":{"type":"integer","description":"Number of child jobs where this vulnerability was detected","example":2}}}},"aggregatedRecommendations":{"type":"array","description":"Aggregated recommendations from all child jobs (deduplicated). Only present when status is \"completed\".","items":{"type":"string"},"example":["Distribute resources across multiple availability zones","Implement database connection pooling","Add circuit breakers for external dependencies"]},"childJobResults":{"type":"array","description":"Summary results for each child job (lightweight version for status endpoint)","items":{"type":"object","properties":{"jobId":{"type":"string","format":"uuid","description":"Child job ID","example":"job_1"},"status":{"type":"string","enum":["pending","running","completed","failed","cancelled"],"description":"Child job status","example":"completed"},"resilienceScore":{"allOf":[{"$ref":"#/components/schemas/ResilienceScore"}],"description":"Resilience score (only present if completed)"},"vulnerabilities":{"type":"array","description":"Vulnerabilities detected (only present if completed)","items":{"type":"string"},"example":["zone_dependency","insufficient_capacity"]},"error":{"type":"string","description":"Error message if failed","example":"Simulation failed to initialize"}}}},"webhookUrl":{"type":"string","format":"uri","description":"Webhook URL for batch completion notification"},"webhookDeliveryStatus":{"type":"string","enum":["pending","delivered","failed"],"description":"Webhook delivery status","example":"delivered"},"webhookDeliveryAttempts":{"type":"integer","description":"Number of webhook delivery attempts made","example":1},"webhookDeliveryError":{"type":"string","description":"Error message if webhook delivery failed"},"webhookDeliveredAt":{"type":"string","format":"date-time","description":"Timestamp when webhook was successfully delivered"},"createdAt":{"type":"string","format":"date-time","description":"When the batch was created","example":"2025-11-23T10:00:00Z"},"updatedAt":{"type":"string","format":"date-time","description":"When the batch was last updated","example":"2025-11-23T10:15:00Z"},"completedAt":{"type":"string","format":"date-time","description":"When the batch completed (or failed)","example":"2025-11-23T10:15:00Z"},"cancelledAt":{"type":"string","format":"date-time","description":"When the batch was cancelled"},"error":{"type":"string","description":"Error message if batch failed"}}},"WorkloadProfile":{"type":"object","required":["computeInstances","databaseInstances","storageGB","trafficRPS","latencyRequirementMs","primaryRegion"],"properties":{"computeInstances":{"type":"integer","minimum":1,"description":"Number of compute instances required","example":10},"databaseInstances":{"type":"integer","minimum":1,"description":"Number of database instances required","example":2},"storageGB":{"type":"integer","minimum":1,"description":"Storage capacity in gigabytes","example":500},"trafficRPS":{"type":"integer","minimum":1,"description":"Expected traffic in requests per second","example":5000},"latencyRequirementMs":{"type":"integer","minimum":1,"description":"Maximum acceptable latency in milliseconds","example":100},"primaryRegion":{"type":"string","description":"Primary deployment region","example":"us-east-1"},"secondaryRegions":{"type":"array","description":"Optional secondary regions for multi-region deployment","items":{"type":"string"},"example":["eu-west-1","ap-southeast-1"]},"requiresMultiRegion":{"type":"boolean","description":"Whether multi-region deployment is required","default":false},"dataResidencyRequirements":{"type":"array","description":"Data residency constraints (e.g., GDPR regions)","items":{"type":"string"},"example":["eu","us"]},"sourceProvider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"],"description":"The cloud provider you are currently on and migrating from. Used to calculate one-time egress costs. Defaults to \"aws\".","default":"aws","example":"aws"}}},"ProviderAllocation":{"type":"object","required":["skuClass"],"properties":{"provider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"],"description":"Cloud provider name","example":"aws"},"computeInstances":{"type":"integer","description":"Number of compute instances on this provider","example":6},"databaseInstances":{"type":"integer","description":"Number of database instances on this provider","example":1},"storageGB":{"type":"integer","description":"Storage allocated to this provider (GB)","example":300},"trafficPercentage":{"type":"number","description":"Percentage of traffic routed to this provider","example":60},"regions":{"type":"array","description":"Regions used on this provider","items":{"type":"string"},"example":["us-east-1","us-west-2"]},"skuClass":{"type":"string","description":"Primary product-class label for this allocation. Derived from the two per-dimension fields below (`computeSkuClass`, `databaseSkuClass`) using the following precedence: if `computeSkuClass` is non-default (i.e. `do-droplet`, `bare-metal-compute`, or `free-tier-compute`), that compute class is used even when a `databaseSkuClass` is also present. Otherwise the `databaseSkuClass` is used when present; the final fallback is `general-compute`.\nAllocations that share the same `skuClass` are cost-comparable at the class level; allocations with different values are not directly comparable — see the parent Strategy's `crossClassNote` when present.\n","example":"bare-metal-compute"},"computeSkuClass":{"type":"string","description":"Compute-dimension SKU class for this allocation.\n`general-compute` — standard dedicated-vCPU VMs (EC2, Compute Engine, Azure VMs, OCI VM.Standard/Flex; the default when no specific compute family is set), `do-droplet` — DigitalOcean Droplets (shared-vCPU cloud VMs; different resource model and performance guarantees from dedicated-vCPU instances), `free-tier-compute` — always-free / zero-cost compute (OCI Always Free AMD micro VMs; costs excluded from estimate totals), `bare-metal-compute` — dedicated bare-metal servers (OCI BM.Standard3.64, BM.Optimized3.36; no hypervisor overhead, full core isolation).\nAlways present after annotation; defaults to `general-compute`.\n","example":"bare-metal-compute"},"databaseSkuClass":{"type":"string","description":"Database-dimension SKU class for this allocation. Absent when no database service family is set (allocation carries no bespoke database tier).\n`managed-rdbms` — provisioned relational databases (RDS, Cloud SQL, Azure SQL, …), `oci-heatwave` — OCI MySQL HeatWave DB System and HeatWave Analytics Cluster (in-memory analytics accelerator; distinct billing model and capability profile from standard managed-rdbms — not directly cost-comparable), `serverless-db` — pay-per-use / auto-scaling databases (Aurora Serverless, Cloud Spanner, Autonomous DB, …), `managed-nosql` — wide-column or key-value NoSQL (Bigtable SSD/HDD, …).\n","example":"serverless-db"}}},"ProviderCostBreakdown":{"type":"object","required":["provider","computeCostPerHour","databaseCostPerHour","storageCostPerHour","egressCostPerHour","totalCostPerHour","computeInstances","databaseInstances","storageGB"],"properties":{"provider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"],"description":"Cloud provider for this allocation slice","example":"aws"},"computeCostPerHour":{"type":"number","description":"Hourly compute cost for this provider slice (USD)","example":0.384},"databaseCostPerHour":{"type":"number","description":"Hourly database cost for this provider slice (USD)","example":0.54},"storageCostPerHour":{"type":"number","description":"Hourly storage cost for this provider slice (USD)","example":0.0115},"egressCostPerHour":{"type":"number","description":"Hourly internet egress cost attributed to this provider slice (USD), after deducting the provider's free monthly egress allowance (converted to an hourly GB budget — ÷ 730). Free allowances: OCI 10,000 GB/month (Always Free, flat), DigitalOcean 1,000 GB/instance/month (scales with computeInstances), AWS 100 GB/month, GCP 200 GB/month (Americas), Azure 5 GB/month. Volume within the free tier produces zero. Zero also when trafficRPS is 0.\n","example":0.0162},"totalCostPerHour":{"type":"number","description":"Sum of compute + database + storage + egress costs for this slice (USD)","example":0.9517},"computeInstances":{"type":"number","description":"Number of compute instances in this provider slice","example":4},"databaseInstances":{"type":"number","description":"Number of database instances in this provider slice","example":2},"storageGB":{"type":"number","description":"Total storage provisioned in this provider slice (GB)","example":500},"computeUnitRate":{"type":"number","description":"Per-instance hourly compute rate used for this slice (USD/hr)","example":0.096},"databaseUnitRate":{"type":"number","description":"Per-instance hourly database rate used for this slice (USD/hr)","example":0.27},"storageUnitRate":{"type":"number","description":"Per-GB storage rate used (USD/GB/hr, on a per-1000-GB basis)","example":0.023},"egressFreeTierExhaustedAtRPS":{"type":"number","description":"The requests-per-second (RPS) threshold at which this provider's hourly egress free-tier allowance is fully consumed, given the current traffic share and instance count. Below this value egressCostPerHour is $0; above it the overage rate applies. Computed as Math.round(freeHourlyGB / (RPS_TO_GB_PER_HOUR × share)), where RPS_TO_GB_PER_HOUR = 0.0072 and freeHourlyGB = monthlyFreeGB / 730 (DigitalOcean scales by computeInstances). Omitted when trafficPercentage is 0 or when the provider has no free egress allowance.\n","example":19},"databaseServiceFamily":{"type":"string","description":"Database product sub-family override applied to this slice (e.g. \"cloud-sql\", \"aurora-serverless\", \"autonomous-db\"). Absent when the provider's default managed database rate is used.\n","example":"aurora-serverless"}}},"StrategyMetrics":{"type":"object","properties":{"totalCostPerHour":{"type":"number","description":"Total hourly cost across all providers","example":12.5},"avgLatencyMs":{"type":"number","description":"Average latency (P50 estimate) in milliseconds","example":45.2},"p95LatencyMs":{"type":"number","description":"Estimated P95 latency in milliseconds (avgLatencyMs × 1.5)","example":67.8},"vendorLockInScore":{"type":"number","description":"Vendor lock-in score (0-100, higher = more locked in / harder to migrate away). Single-provider strategies score 55-84 depending on service proprietary-ness (Aurora Serverless scores higher than standard RDS); multi-provider strategies score 20-50 depending on traffic distribution and provider count.","example":72},"dataPortabilityScore":{"type":"number","description":"Data portability score (0-100, higher = more portable)","example":75},"geographicCoverage":{"type":"number","description":"Geographic coverage score (0-100)","example":85},"compositeScore":{"type":"number","description":"Overall weighted composite score (0-10)","example":8.5},"migrationEgressCost":{"type":"number","description":"One-time data-migration egress cost estimate (USD) — the cost of moving the workload's stored data out of the source provider. Computed as `EGRESS_PER_GB_RATES[sourceProvider] × storageGB`. Always present; zero-valued strategies have zero storage in the workload profile.\n","example":18},"egressCostPerHour":{"type":"number","description":"Estimated ongoing internet egress cost per hour (USD) — the recurring cost of serving outbound responses to external users. Derived from `trafficRPS` using a 2 KB average response payload (RPS × 3600 s/hr × 0.000002 GB/request) and each provider's `INTERNET_EGRESS_RATES` overage rate (USD/GB), after deducting each provider's free monthly egress allowance (converted to an hourly GB budget): OCI 10,000 GB/month (Always Free, flat), DigitalOcean 1,000 GB/instance/month (scales with computeInstances), AWS 100 GB/month, GCP 200 GB/month (Americas), Azure 5 GB/month. For multi-provider strategies the cost is traffic-share-weighted across all allocations. This estimate is added into `totalCostPerHour` so rankings reflect the full ongoing cost including egress. Absent only on strategies with zero traffic. Volume within a provider's free tier produces `egressCostPerHour: 0`.\n","example":0.162},"latencyHeadroomPct":{"type":"number","description":"How far below the user's latency SLO this strategy sits, expressed as a percentage of the SLO limit. Computed as max(0, (SLO − p95LatencyMs) / SLO × 100). A value of 13.75 means p95 latency is 13.75% below the SLO limit — the strategy has that much buffer before it would breach the target. 0 means the strategy is at or above the SLO limit. Absent for strategies that exceed the SLO.\n","example":13.75}}},"Strategy":{"type":"object","description":"A flight-simulator-style planning estimate for a specific multi-cloud deployment configuration. Cost and latency figures reflect baseline on-demand rates under typical load and are **not actual billing data**. Internet egress is estimated and included in `totalCostPerHour` via the `egressCostPerHour` field in each `costBreakdown` entry, after deducting each provider's free monthly egress allowance (OCI 10,000 GB/month, DigitalOcean 1,000 GB/instance/month, AWS 100 GB/month, GCP 200 GB/month, Azure 5 GB/month). Still excluded: monitoring/support surcharges, non-egress free-tier credits, reserved-instance discounts, and spot/preemptible pricing. Allocations with different `skuClass` values are not directly cost-comparable; the `crossClassNote` field explains any such mix. Validate top candidates by materialising them as owned simulations before making infrastructure commitments.\n","properties":{"id":{"type":"string","description":"Unique strategy identifier","example":"strategy-1"},"name":{"type":"string","description":"Strategy name","example":"AWS-Primary Multi-Region"},"description":{"type":"string","description":"Strategy description","example":"AWS-heavy deployment with GCP for data redundancy and reduced vendor lock-in"},"allocations":{"type":"array","description":"Provider allocations in this strategy","items":{"$ref":"#/components/schemas/ProviderAllocation"}},"metrics":{"$ref":"#/components/schemas/StrategyMetrics"},"tradeoffs":{"type":"array","description":"Key tradeoffs of this strategy","items":{"type":"string"},"example":["Higher cost for improved redundancy","Lower vendor lock-in at expense of complexity"]},"recommendations":{"type":"array","description":"Recommendations for this strategy","items":{"type":"string"},"example":["Best for workloads requiring high availability","Consider multi-region replication for databases"]},"suggestedResources":{"type":"array","description":"Materializable SKU objects for each resource type in this strategy. Each entry identifies the canonical instance size, unit count, and per-unit hourly rate so consumers can pre-populate infrastructure manifests or compare costs directly.\n","items":{"type":"object","required":["provider","resourceType","size","count","hourlyRate"],"properties":{"provider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"],"example":"aws"},"resourceType":{"type":"string","enum":["compute","database","storage"],"example":"compute"},"size":{"type":"string","description":"Canonical instance/SKU identifier (e.g. \"t3.medium\", \"db.t3.medium\", \"s3-standard\")","example":"t3.medium"},"count":{"type":"number","description":"Number of instances (or GB for storage)","example":4},"hourlyRate":{"type":"number","description":"Per-unit hourly cost in USD","example":0.096}}},"example":[{"provider":"aws","resourceType":"compute","size":"t3.medium","count":4,"hourlyRate":0.096},{"provider":"aws","resourceType":"database","size":"db.t3.medium","count":1,"hourlyRate":0.27}]},"securityRecommendations":{"type":"array","description":"Security product identifiers recommended for this strategy (e.g. `aws-waf`, `gcp-cloud-armor`, `azure-waf`, `oci-waf`). These are WAF/firewall palette IDs that operators can add to protect each provider footprint.\n","items":{"type":"string"},"example":["aws-waf"]},"crossClassNote":{"type":"string","description":"Present only when this strategy's allocations span more than one class in either the compute dimension (comparing `computeSkuClass` values) or the database dimension (comparing `databaseSkuClass` values). Each dimension is evaluated independently so a compute-tier mismatch is never masked by a shared database tier, and vice-versa. The note names the mismatched dimension(s) and warns that cost and latency figures across those allocations may not be directly comparable because they model different product tiers. Absent when all allocations share the same class in both dimensions.\n","example":"This strategy mixes non-equivalent compute product tiers (bare-metal-compute, general-compute). Cost and latency estimates may not be directly comparable across allocations; validate each provider's tier independently before committing.\n"},"costBreakdown":{"type":"array","description":"Per-provider cost breakdown for this strategy. One entry per provider allocation, each showing compute, database, storage, and egress costs attributed to that provider's slice of the workload. `egressCostPerHour` in each entry reflects the overage cost after deducting the provider's free monthly egress allowance (see `ProviderCostBreakdown`). `totalCostPerHour` in each entry is the sum of all four line items for that slice.\n","items":{"$ref":"#/components/schemas/ProviderCostBreakdown"}}},"required":["id","name","description","allocations","metrics","tradeoffs","recommendations","costBreakdown"]},"CompletedResilienceEvidence":{"type":"object","additionalProperties":false,"description":"Caller-attested completed test report for the exact candidateFingerprint. Keep the fingerprint with the actual test inputs; never relabel a report from another architecture or workload. Recorded means observations recorded during the test, not externally verified production evidence. Estimated reports remain unevaluated for deployment gating.","required":["candidateFingerprint","reportRef","status","completedAt","provenance","failureInjected","recoveryObserved","finalHealthy","resilienceCheck","scope"],"properties":{"candidateFingerprint":{"type":"string","pattern":"^[a-f0-9]{64}$"},"reportRef":{"type":"string","minLength":1,"maxLength":500},"status":{"type":"string","enum":["completed"]},"completedAt":{"type":"string","format":"date-time","description":"Actual completion timestamp, not in the future."},"provenance":{"type":"string","enum":["recorded","estimated"]},"failureInjected":{"type":"boolean"},"recoveryObserved":{"type":"boolean"},"finalHealthy":{"type":"boolean"},"resilienceCheck":{"type":"string","enum":["pass","fail","not_evaluated"]},"scope":{"type":"string","minLength":1,"maxLength":2000,"description":"Tested failures, test coverage, and pass criteria."}}},"PreProvisionFinalizeRequest":{"type":"object","additionalProperties":false,"required":["strategyId"],"properties":{"strategyId":{"type":"string","minLength":1,"maxLength":200},"maxCostPerHour":{"type":"number","exclusiveMinimum":0,"description":"Optional explicit hourly cost budget, replacing the exploration budget check for this evaluation only."},"errorBudgetPct":{"type":"number","minimum":0,"maximum":100,"description":"Optional explicit error budget percentage, replacing the exploration error-budget check for this evaluation only."},"resilienceEvidence":{"$ref":"#/components/schemas/CompletedResilienceEvidence"}}},"PreProvisionVerdict":{"type":"object","description":"Conservative machine-readable verdict for the highest-ranked returned strategy. Exploration alone cannot return ship. The verdict endpoint can return ship when cost, latency, error-budget and recorded caller-attested resilience checks all pass. This is a planning decision, not independent verification or a production guarantee.","required":["decision","strategy","candidateFingerprint","reasons","evidence","uncertainty"],"properties":{"decision":{"type":"string","enum":["ship","hold","ship_with_changes"],"description":"hold means a known supplied budget or latency requirement failed, or no candidate is available. ship_with_changes means the visible checks have no known failure but evidence or explicit budgets are missing. ship requires every required check to pass."},"candidateFingerprint":{"type":"string","nullable":true,"description":"SHA-256 identity of the candidate ID, allocations, suggested resources and workload. Retain with test inputs; null when no candidate exists."},"strategy":{"oneOf":[{"$ref":"#/components/schemas/PreProvisionVerdictStrategy"},{"type":"null"}],"description":"Strategy evaluated by this verdict; null when no candidate is available."},"reasons":{"type":"array","items":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["no_candidate_strategy","cost_budget_exceeded","latency_slo_missed","error_budget_exceeded","resilience_validation_required","resilience_check_failed","resilience_evidence_insufficient","required_checks_incomplete","required_checks_passed"]},"message":{"type":"string"}}}},"evidence":{"type":"object","required":["cost","latency","errorRate","resilience"],"properties":{"cost":{"type":"object","required":["estimatedPerHour","budgetCheck"],"properties":{"estimatedPerHour":{"type":"number","nullable":true,"description":"Modeled total cost per hour, including modeled egress."},"budgetCheck":{"type":"string","enum":["pass","fail","not_provided"]}}},"latency":{"type":"object","required":["estimatedP95Ms","requirementMs","sloCheck","headroomPct"],"properties":{"estimatedP95Ms":{"type":"number","nullable":true},"requirementMs":{"type":"number"},"sloCheck":{"type":"string","enum":["pass","fail","not_evaluated"]},"headroomPct":{"type":"number","nullable":true}}},"errorRate":{"type":"object","required":["modeledFraction","budgetCheck"],"properties":{"modeledFraction":{"type":"number","nullable":true,"description":"Allocation-count-based modeled proxy, not an observed failure rate."},"budgetCheck":{"type":"string","enum":["pass","fail","not_provided"]}}},"resilience":{"type":"object","required":["status","reason"],"properties":{"status":{"type":"string","enum":["not_evaluated","pass","fail"]},"reason":{"type":"string"},"provenance":{"type":"string","enum":["recorded","estimated"]},"verification":{"type":"string","enum":["caller_attested"]},"report":{"$ref":"#/components/schemas/CompletedResilienceEvidence"}}}}},"uncertainty":{"type":"object","required":["dataSource","externallyValidated","providerRateTrust","limitations"],"properties":{"dataSource":{"type":"string","enum":["simulation-model"]},"externallyValidated":{"type":"boolean","enum":[false]},"providerRateTrust":{"type":"object","additionalProperties":{"type":"string","enum":["on-demand","spot","estimated"]}},"limitations":{"type":"array","items":{"type":"string"}}}}}},"PreProvisionVerdictStrategy":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"MultiCloudJob":{"type":"object","description":"A multi-cloud exploration job. All cost and latency figures in the job results are flight-simulator-style planning estimates reflecting baseline on-demand rates under typical load — **not actual billing data**. Internet egress is estimated and included in each strategy's `totalCostPerHour` via `egressCostPerHour`, after deducting each provider's free monthly egress allowance (OCI 10,000 GB/month, DigitalOcean 1,000 GB/instance/month, AWS 100 GB/month, GCP 200 GB/month, Azure 5 GB/month). Still excluded: monitoring/support surcharges, non-egress free-tier credits, reserved-instance discounts, and spot/preemptible pricing. Validate top-ranked strategies by materialising them as owned simulations before making infrastructure commitments.\n","properties":{"id":{"type":"string","format":"uuid","description":"Unique job identifier","example":"job-abc123"},"workloadProfile":{"$ref":"#/components/schemas/WorkloadProfile"},"optimizationWeights":{"type":"object","description":"Optimization weights used","properties":{"cost":{"type":"number","example":0.4},"latency":{"type":"number","example":0.4},"vendorLockIn":{"type":"number","example":0.2}}},"status":{"type":"string","enum":["pending","running","completed","failed"],"description":"Current job status","example":"completed"},"progress":{"type":"number","description":"Progress percentage (0-100)","example":100},"strategiesGenerated":{"type":"integer","description":"Number of strategies generated","example":15},"topStrategies":{"type":"array","description":"Top-ranked strategies (available when completed)","items":{"$ref":"#/components/schemas/Strategy"}},"comparisonReport":{"type":"string","description":"Detailed comparison report (available when completed)","example":"Multi-Cloud Strategy Analysis Report..."},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"error":{"type":"string","description":"Error message if failed"},"webhookDeliveryStatus":{"type":"string","enum":["pending","delivered","failed"],"description":"Webhook delivery status","example":"delivered"},"webhookDeliveryAttempts":{"type":"integer","description":"Number of webhook delivery attempts made","example":1},"webhookDeliveryError":{"type":"string","description":"Error message if webhook delivery failed"},"webhookDeliveredAt":{"type":"string","format":"date-time","description":"Timestamp when webhook was successfully delivered"}}},"OptimizationWebhookPayload":{"type":"object","description":"Webhook payload sent when an optimization job completes","properties":{"event":{"type":"string","enum":["optimization.completed","optimization.failed"],"description":"Event type","example":"optimization.completed"},"jobId":{"type":"string","format":"uuid","description":"Job identifier","example":"abc-123-def"},"status":{"type":"string","enum":["completed","failed"],"description":"Final job status","example":"completed"},"timestamp":{"type":"string","format":"date-time","description":"When the webhook was sent","example":"2025-11-23T10:30:00Z"},"data":{"type":"object","description":"Optimization job results","properties":{"variantsGenerated":{"type":"integer","example":57},"recommendations":{"type":"array","items":{"$ref":"#/components/schemas/OptimizationRecommendation"}},"error":{"type":"string","description":"Error message if status is failed"}}}}},"ChaosWebhookPayload":{"type":"object","description":"Webhook payload sent when a chaos engineering test completes","properties":{"event":{"type":"string","enum":["chaos.completed","chaos.failed"],"description":"Event type","example":"chaos.completed"},"jobId":{"type":"string","format":"uuid","description":"Job identifier"},"status":{"type":"string","enum":["completed","failed"],"description":"Final job status","example":"completed"},"timestamp":{"type":"string","format":"date-time","description":"When the webhook was sent"},"data":{"type":"object","description":"Chaos test results","properties":{"resilienceScore":{"$ref":"#/components/schemas/ResilienceScore"},"vulnerabilities":{"type":"array","items":{"$ref":"#/components/schemas/Vulnerability"}},"error":{"type":"string","description":"Error message if status is failed"}}}}},"PredictionWebhookPayload":{"type":"object","description":"Webhook payload sent when a prediction job (validation or threshold optimization) completes. When `status` is `\"failed\"`, inspect `data.error` to determine the recovery action. See the **Handling Failures** section in the API description for a full classification of retryable vs non-retryable error conditions and per-error recovery steps.\n","properties":{"event":{"type":"string","enum":["prediction.validation.completed","prediction.validation.failed","prediction.threshold_optimization.completed","prediction.threshold_optimization.failed"],"description":"Event type","example":"prediction.validation.completed"},"jobId":{"type":"string","format":"uuid","description":"Job identifier"},"status":{"type":"string","enum":["completed","failed"],"description":"Final job status","example":"completed"},"timestamp":{"type":"string","format":"date-time","description":"When the webhook was sent"},"data":{"type":"object","description":"Prediction job results","properties":{"type":{"type":"string","enum":["validation","threshold_optimization"],"description":"Type of prediction job"},"validationResult":{"$ref":"#/components/schemas/ValidationResult"},"bestThresholds":{"$ref":"#/components/schemas/AutoscalingConfig"},"recommendations":{"type":"array","items":{"$ref":"#/components/schemas/PredictionRecommendation"}},"error":{"type":"string","description":"Error message if status is failed"}}}},"examples":{"awsThresholdOptimizationWebhook":{"summary":"AWS — EC2 Auto Scaling threshold optimization completed (Black Friday)","value":{"event":"prediction.threshold_optimization.completed","jobId":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","status":"completed","timestamp":"2025-11-23T10:35:00Z","data":{"type":"threshold_optimization","bestThresholds":{"scaleOutCpuThreshold":70,"scaleInCpuThreshold":30,"scaleOutThroughputThreshold":75,"scaleInThroughputThreshold":35,"scaleOutLatencyThreshold":120,"cooldownSeconds":180,"minInstances":3,"maxInstances":15},"recommendations":[{"rank":1,"title":"Lower CPU scale-out threshold to 70%","description":"Triggering scale-out at 70% CPU instead of 80% gives 60–90 seconds of lead time before saturation under the Black Friday ramp","priority":"high","action":"Set scaleOutCpuThreshold to 70 in the EC2 Auto Scaling policy","expectedImpact":"Reduces peak CPU from 81% to ~68%, drops error rate from 0.4% to <0.1%"},{"rank":2,"title":"Keep scale-in threshold at 30% to avoid flapping","description":"A conservative scale-in threshold prevents the Auto Scaling group from terminating instances too quickly after the Black Friday peak, avoiding a secondary spike during wind-down","priority":"medium","action":"Set scaleInCpuThreshold to 30 in the EC2 Auto Scaling policy","expectedImpact":"Eliminates post-peak scale-in flap; saves one unnecessary scale-out cycle during wind-down"}]}}},"gcpThresholdOptimizationWebhook":{"summary":"GCP — Cloud Run threshold optimization completed (Holiday seasonal burst)","value":{"event":"prediction.threshold_optimization.completed","jobId":"b2c3d4e5-f6a7-8901-bcde-f12345678901","status":"completed","timestamp":"2025-12-15T08:20:00Z","data":{"type":"threshold_optimization","bestThresholds":{"scaleOutCpuThreshold":60,"scaleInCpuThreshold":25,"scaleOutThroughputThreshold":65,"scaleInThroughputThreshold":30,"scaleOutLatencyThreshold":150,"cooldownSeconds":60,"minInstances":3,"maxInstances":25},"recommendations":[{"rank":1,"title":"Set CPU scale-out threshold to 60% to prevent concurrency saturation","description":"Cloud Run saturates when per-instance concurrency fills before CPU-based scale-out fires. Triggering at 60% CPU ensures new instances are warm before the holiday ramp overwhelms the active pool","priority":"critical","action":"Configure Cloud Run --cpu-throttling and set Knative autoscaling target annotation to 60","expectedImpact":"Peak error rate drops from 12.1% to <0.5%; p95 latency drops from 310 ms to ~58 ms"},{"rank":2,"title":"Set minimum instances to 3 to eliminate cold-start lag","description":"Keeping 3 instances warm prevents the 45-second scale-out delay at ramp start that causes early-stage errors before the optimizer thresholds can take effect","priority":"high","action":"Set --min-instances=3 on the Cloud Run service revision","expectedImpact":"Removes cold-start lag; threshold optimizer can respond within 5 s instead of 45 s"}]}}},"azureThresholdOptimizationWebhook":{"summary":"Azure — AKS HPA threshold optimization completed (product launch spike)","value":{"event":"prediction.threshold_optimization.completed","jobId":"c3d4e5f6-a7b8-9012-cdef-123456789012","status":"completed","timestamp":"2025-09-10T14:05:00Z","data":{"type":"threshold_optimization","bestThresholds":{"scaleOutCpuThreshold":60,"scaleInCpuThreshold":25,"scaleOutThroughputThreshold":70,"scaleInThroughputThreshold":35,"scaleOutLatencyThreshold":80,"cooldownSeconds":120,"minInstances":3,"maxInstances":12},"recommendations":[{"rank":1,"title":"Lower HPA CPU target to 60%","description":"Scaling out at 60% CPU instead of 75% gives the AKS node pool an extra 45 seconds of lead time for the product launch spike, eliminating the brief 110 ms latency overshoot at step 21","priority":"high","action":"Update HorizontalPodAutoscaler targetCPUUtilizationPercentage to 60","expectedImpact":"Eliminates p95 latency spike at launch; steady-state p95 drops from 62 ms to 48 ms"},{"rank":2,"title":"Set cooldown to 120 s to prevent HPA thrashing during sustained load","description":"The product launch pattern holds elevated traffic for 50 steps — a 120-second cooldown prevents the HPA from scale-in oscillations during the sustained period","priority":"medium","action":"Set HorizontalPodAutoscaler spec.behavior.scaleDown.stabilizationWindowSeconds to 120","expectedImpact":"Eliminates 3 unnecessary scale-in/scale-out cycles during the sustained traffic window"}]}}},"ociThresholdOptimizationWebhook":{"summary":"OCI — VM.Standard.E4.Flex threshold optimization completed (month-end batch)","value":{"event":"prediction.threshold_optimization.completed","jobId":"d4e5f6a7-b8c9-0123-defa-234567890123","status":"completed","timestamp":"2025-10-25T02:00:00Z","data":{"type":"threshold_optimization","bestThresholds":{"scaleOutCpuThreshold":65,"scaleInCpuThreshold":30,"scaleOutThroughputThreshold":70,"scaleInThroughputThreshold":35,"scaleOutLatencyThreshold":200,"cooldownSeconds":300,"minInstances":2,"maxInstances":8},"recommendations":[{"rank":1,"title":"Set scale-out CPU threshold to 65% for the batch window","description":"Month-end batch load ramps gradually over 35 steps — triggering at 65% CPU provides a 2-instance buffer before peak query load hits, keeping ATP connection pool below 60%","priority":"medium","action":"Update OCI Autoscaling policy CPU threshold to 65% for the VM.Standard.E4.Flex instance pool","expectedImpact":"Peak CPU drops from 72% to ~63%; ATP connection pool pressure drops from 74% to ~58%"},{"rank":2,"title":"Use a 300-second cooldown to prevent premature scale-in mid-batch","description":"Month-end batch jobs run for 55 steps — a short cooldown causes the autoscaler to prematurely scale in between reporting sub-jobs, then immediately scale out again","priority":"low","action":"Set OCI Autoscaling policy cooldown period to 300 seconds","expectedImpact":"Eliminates 2 mid-batch scale-in/out cycles; reduces ATP reconnect overhead"}]}}},"digitalOceanThresholdOptimizationWebhook":{"summary":"DigitalOcean — Droplet autoscaling threshold optimization completed (viral traffic spike)","value":{"event":"prediction.threshold_optimization.completed","jobId":"e5f6a7b8-c9d0-1234-efab-345678901234","status":"completed","timestamp":"2025-08-03T19:45:00Z","data":{"type":"threshold_optimization","bestThresholds":{"scaleOutCpuThreshold":55,"scaleInCpuThreshold":20,"scaleOutThroughputThreshold":60,"scaleInThroughputThreshold":25,"scaleOutLatencyThreshold":100,"cooldownSeconds":90,"minInstances":6,"maxInstances":20},"recommendations":[{"rank":1,"title":"Lower scale-out CPU threshold to 55% to react before viral saturation","description":"The viral spike reaches full intensity within 15 steps — the default 75% threshold fires too late for DigitalOcean App Platform to provision new Droplets in time. 55% gives a 10-step head start","priority":"critical","action":"Update DigitalOcean App Platform autoscaling CPU threshold to 55%","expectedImpact":"Peak CPU drops from 98% to ~58%; error rate drops from 18% to <0.5%"},{"rank":2,"title":"Set minimum Droplet pool to 6 as a prerequisite for the threshold to take effect","description":"Even the optimized 55% threshold cannot compensate if the starting pool is too small — the 15-step viral ramp outpaces Droplet provisioning speed from a pool of 3","priority":"critical","action":"Set DigitalOcean App Platform min_instance_count to 6","expectedImpact":"Ensures the threshold optimizer has sufficient baseline capacity; CPU peak drops to ~52% when combined with the 55% scale-out trigger"}]}}},"awsThresholdOptimizationFailedInvalidSimulation":{"summary":"AWS — threshold optimization failed (invalid simulation ID)","value":{"event":"prediction.threshold_optimization.failed","jobId":"f6a7b8c9-d0e1-2345-fabc-456789012345","status":"failed","timestamp":"2025-11-23T10:36:42Z","data":{"type":"threshold_optimization","error":"Simulation 'sim_nonexistent_abc123' not found. Verify the simulationId references an existing simulation before submitting a threshold optimization job."}}},"awsThresholdOptimizationFailedInsufficientTraffic":{"summary":"AWS — EC2 threshold optimization failed (insufficient traffic data)","value":{"event":"prediction.threshold_optimization.failed","jobId":"a7b8c9d0-e1f2-3456-abcd-567890123456","status":"failed","timestamp":"2025-11-24T14:22:10Z","data":{"type":"threshold_optimization","error":"No valid threshold combinations found for the provided traffic forecast on AWS EC2 Auto Scaling. The forecast contains fewer than 3 distinct traffic levels, which is insufficient to evaluate scale-out and scale-in thresholds independently. Supply a traffic pattern with at least 3 discrete load steps (e.g. low, medium, high) and resubmit."}}},"gcpValidationFailedNoTrafficForecast":{"summary":"GCP — Cloud Run validation failed (no traffic forecast data)","value":{"event":"prediction.validation.failed","jobId":"b8c9d0e1-f2a3-4567-bcde-678901234567","status":"failed","timestamp":"2025-12-15T09:12:33Z","data":{"type":"validation","error":"Validation job for GCP Cloud Run scenario 'sim_gcp_prod_cr_88f2' failed: no traffic forecast data found. A traffic pattern must be associated with the simulation before validation can run. Attach a trafficPatternId to the simulation and resubmit."}}},"awsValidationFailedSimulationNotFound":{"summary":"AWS — EC2 validation failed (simulation not found)","value":{"event":"prediction.validation.failed","jobId":"c9d0e1f2-a3b4-5678-cdef-789012345678","status":"failed","timestamp":"2025-11-25T16:04:51Z","data":{"type":"validation","error":"Simulation 'sim_nonexistent_xyz999' not found. Verify the simulationId references an existing simulation before submitting a validation job."}}}}},"MultiCloudWebhookPayload":{"type":"object","description":"Webhook payload sent when a multi-cloud exploration job completes","properties":{"event":{"type":"string","enum":["multicloud.completed","multicloud.failed"],"description":"Event type","example":"multicloud.completed"},"jobId":{"type":"string","format":"uuid","description":"Job identifier"},"status":{"type":"string","enum":["completed","failed"],"description":"Final job status","example":"completed"},"timestamp":{"type":"string","format":"date-time","description":"When the webhook was sent"},"data":{"type":"object","description":"Multi-cloud exploration results","properties":{"strategiesGenerated":{"type":"integer","example":15},"topStrategies":{"type":"array","items":{"$ref":"#/components/schemas/Strategy"}},"comparisonReport":{"type":"string","example":"Multi-Cloud Strategy Analysis Report..."},"error":{"type":"string","description":"Error message if status is failed"}}}}},"RLEpisodeWebhookPayload":{"type":"object","description":"Webhook payload sent when an RL training episode completes","properties":{"event":{"type":"string","enum":["rl_episode.completed"],"description":"Event type","example":"rl_episode.completed"},"environmentId":{"type":"string","format":"uuid","description":"RL environment identifier"},"simulationId":{"type":"string","format":"uuid","description":"Simulation identifier"},"timestamp":{"type":"string","format":"date-time","description":"When the webhook was sent"},"data":{"type":"object","description":"Episode completion data","properties":{"totalSteps":{"type":"integer","description":"Total steps in the episode","example":300},"totalReward":{"type":"number","description":"Cumulative reward achieved","example":145.67},"sim_time_human":{"type":"string","description":"Human-readable simulated elapsed time at episode completion (e.g. \"1h 0m\")","example":"1h 0m"},"episodeConfig":{"$ref":"#/components/schemas/EpisodeConfig"},"finalMetrics":{"type":"object","description":"Final system metrics","properties":{"avgCost":{"type":"number"},"avgLatency":{"type":"number"},"avgErrorRate":{"type":"number"}}}}}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Error message","example":"Simulation not found"},"details":{"type":"array","description":"Detailed validation errors (for 400 responses)","items":{"type":"object"}}}},"ResilienceRetryPolicy":{"type":"object","description":"Per-dependency retry policy. Controls how many times a failed request is\nretried, the back-off shape, jitter, and per-call timeout. Retry budgets\n(`retryBudgetRatio` / `retryBudgetRps`) cap the total retry RPS produced\nby this dependency so cascading retry storms are bounded.\n","properties":{"maxRetries":{"type":"integer","minimum":0,"maximum":8,"default":2,"description":"Maximum retry attempts per original request (0 = no retries).","example":3},"backoffMs":{"type":"integer","minimum":0,"maximum":60000,"default":100,"description":"Initial back-off delay in milliseconds before the first retry.","example":100},"backoffMultiplier":{"type":"number","minimum":1,"maximum":10,"default":2,"description":"Exponential multiplier applied to backoffMs on each retry attempt.","example":2},"jitterRatio":{"type":"number","minimum":0,"maximum":1,"default":0.1,"description":"Fraction of the computed delay added as random jitter (0 = no jitter, 1 = full jitter).","example":0.1},"timeoutMs":{"type":"integer","minimum":1,"maximum":120000,"default":2000,"description":"Per-request timeout in milliseconds. Requests that exceed this are counted as failures.","example":2000},"retryBudgetRatio":{"type":"number","minimum":0,"maximum":10,"default":2,"description":"Retry budget as a multiple of the original RPS. Retry RPS is capped at `retryBudgetRatio × originalRps`. Set to 0 to disable retries entirely even when maxRetries > 0.\n","example":2},"retryBudgetRps":{"type":"number","minimum":0,"maximum":500000,"description":"Absolute retry RPS cap (takes precedence over retryBudgetRatio when both are set and the ratio-derived cap would be higher). Use to impose a hard ceiling on retry amplification regardless of traffic scale.\n","example":1000},"retryActorId":{"type":"string","minLength":1,"maxLength":128,"description":"Optional stable identifier for the actor producing this edge's retries (for example, a gateway or client).","example":"gateway_optimistic_retries"}}},"ResilienceCircuitBreaker":{"type":"object","description":"Circuit-breaker configuration for one dependency edge.","properties":{"enabled":{"type":"boolean","default":false,"description":"Whether the circuit breaker is active for this dependency."},"failureRateThreshold":{"type":"number","minimum":0,"maximum":1,"default":0.5,"description":"Fraction of requests that must fail before the breaker opens (0.5 = 50%).","example":0.5},"minimumRequests":{"type":"integer","minimum":1,"maximum":100000,"default":20,"description":"Minimum requests in the measurement window before the failure rate is evaluated.","example":20},"openSteps":{"type":"integer","minimum":1,"maximum":120,"default":3,"description":"Number of simulation steps the breaker stays open before transitioning to half-open.","example":3},"halfOpenMaxRequests":{"type":"integer","minimum":1,"maximum":10000,"default":10,"description":"Max probe requests allowed through while the breaker is half-open.","example":10}}},"ResilienceProtection":{"type":"object","description":"Load-protection mechanisms for a dependency edge.","properties":{"circuitBreaker":{"$ref":"#/components/schemas/ResilienceCircuitBreaker"},"rateLimitRps":{"type":"number","minimum":0,"maximum":500000,"description":"Hard RPS ceiling applied to this dependency. Requests above the limit are shed immediately (counted in `shedRps`). Absent means no rate limit.\n","example":5000},"loadShedding":{"type":"boolean","default":false,"description":"When true, requests beyond the dependency's capacity are shed rather than queued. Enables graceful degradation under overload.\n"}}},"ResilienceCapacityConstraint":{"type":"object","description":"Capacity model for a dependency's target resource endpoint.","properties":{"maxRps":{"type":"number","minimum":0,"maximum":500000,"description":"Peak RPS the target can sustain before requests overflow.","example":10000},"maxConcurrent":{"type":"integer","minimum":1,"maximum":1000000,"description":"Maximum concurrent in-flight requests the target can hold.","example":200},"meanServiceTimeMs":{"type":"number","minimum":0,"maximum":120000,"default":100,"description":"Average time (ms) the target takes to process one request. Drives Little's Law concurrency pressure.","example":100}}},"ResilienceScalingPolicy":{"type":"object","required":["id","dependencyId","constrainedMetric","observationMetric","scaleOutThresholdPercent"],"description":"Generic capacity/autoscaling policy for a dependency. `constrainedMetric`\ndescribes the resource that can saturate while `observationMetric` describes\nwhat the policy actually watches; they may intentionally differ to model an\nautoscaling blind spot.\n","properties":{"id":{"type":"string","minLength":1,"maxLength":128},"dependencyId":{"type":"string","minLength":1},"constrainedMetric":{"type":"string","enum":["rps","concurrency"]},"observationMetric":{"type":"string","enum":["source_cpu","capacity_utilization"]},"observedResourceId":{"type":"string","description":"Optional resource supplying source_cpu; defaults to the dependency source."},"scaleOutThresholdPercent":{"type":"number","minimum":1,"maximum":100},"scaleOutCapacityMultiplier":{"type":"number","minimum":1,"maximum":20,"default":2}}},"Scenario":{"type":"object","description":"Reproducible, pre-built simulation template. Pass resources, connections,\nseed, resilienceConfig, and the initial traffic preset to simulation creation.\nWhen protectedResilienceConfig is present, use it as the mitigated policy in\na deterministic resilience comparison.\n","required":["id","title","description","difficulty","resources","connections","duration","tags","category"],"properties":{"id":{"type":"string","description":"Unique scenario identifier.","example":"github-inspired-cascading-retry-storm"},"title":{"type":"string","description":"Short display title for the reusable simulation scenario.","example":"GitHub-Inspired Cascading Retry Storm (Unofficial)"},"description":{"type":"string","description":"Summary of the scenario setup and the behavior it is intended to demonstrate."},"difficulty":{"type":"string","enum":["beginner","intermediate","advanced"],"description":"Relative experience level recommended for running and interpreting the scenario."},"resources":{"type":"array","description":"Resource graph instantiated when the scenario is loaded.","items":{"$ref":"#/components/schemas/Resource"}},"connections":{"type":"array","description":"Directed links between the scenario resources.","items":{"$ref":"#/components/schemas/Connection"}},"duration":{"type":"string","description":"Approximate time needed to run the scenario.","example":"~25 min"},"tags":{"type":"array","description":"Search and categorization labels for the scenario.","items":{"type":"string"}},"category":{"type":"string","description":"Broad scenario category used for filtering and organization.","example":"reliability"},"primaryPurpose":{"type":"string","nullable":true,"enum":["educational","chaos","predictive","optimization"],"description":"Optional explicit primary purpose. Educational means a guided concept,\nchaos means an injected failure, predictive means a capacity simulation,\nand optimization means a configuration comparison. Legacy rows may omit it.\n"},"autoscalingConfig":{"$ref":"#/components/schemas/AutoscalingConfig"},"seed":{"type":"integer","minimum":0,"description":"Deterministic simulation seed for repeatable scenario replays."},"resilienceConfig":{"$ref":"#/components/schemas/ResilienceConfig"},"protectedResilienceConfig":{"allOf":[{"$ref":"#/components/schemas/ResilienceConfig"}],"description":"Source-aligned protective policy for deterministic A/B replay."},"defaultTrafficPatterns":{"type":"array","items":{"type":"object","required":["name","type","startTime","parameters","isActive"],"properties":{"name":{"type":"string","description":"Name for the scenario's default traffic pattern."},"type":{"type":"string","enum":["ramp","burst","step","wave","custom"],"description":"Shape of the traffic pattern."},"startTime":{"type":"number","default":0,"description":"Simulation step when the pattern begins."},"endTime":{"type":"number"},"parameters":{"type":"object","description":"Pattern-specific traffic values such as start, end, or peak RPS.","properties":{"startTraffic":{"type":"number"},"endTraffic":{"type":"number"},"peakTraffic":{"type":"number"},"duration":{"type":"number"},"period":{"type":"number"},"amplitude":{"type":"number"},"baseline":{"type":"number"}}},"isActive":{"type":"boolean","default":true}}}},"defaultFailureInjections":{"type":"array","items":{"type":"object","required":["name","type","severity","startTime","isActive","parameters"],"properties":{"name":{"type":"string"},"type":{"type":"string","enum":["instance_kill","instance_down","az_outage","region_outage","permanent_data_loss","database_overload","network_latency","spot_interruption"]},"targetResourceId":{"type":"string"},"targetZone":{"type":"string"},"targetRegion":{"type":"string"},"targetProvider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"]},"severity":{"type":"string","enum":["minor","moderate","severe"],"default":"moderate"},"startTime":{"type":"number","default":0},"duration":{"type":"number","description":"Duration in simulation steps, independent of startTime."},"endTime":{"type":"number"},"isActive":{"type":"boolean","default":true},"parameters":{"type":"object","additionalProperties":true}}}},"defaultLifecycleEvents":{"type":"array","description":"Deterministic lifecycle events that are part of the scenario's pre-run plan.","items":{"type":"object","required":["name","type","targetResourceId","startTime","noticeWindowSeconds","affectedWorkloadCount","handling"],"properties":{"name":{"type":"string"},"type":{"type":"string","enum":["spot_interruption"]},"targetResourceId":{"type":"string"},"startTime":{"type":"integer","minimum":0},"noticeWindowSeconds":{"type":"integer","const":120},"affectedWorkloadCount":{"type":"integer","minimum":1},"handling":{"type":"string","enum":["reschedule","drain-only","fail-fast"],"default":"reschedule"}}}},"providerApiLimitScenario":{"type":"object","description":"Optional preset for the separate synchronous provider API-limit simulator.\nThe preset is loaded into the Workspace panel, but it is not executed\nautomatically and does not change the simulation's /step state.\n","required":["question","policyId","scopeDescription","provider","region","service","operation","category","plannedCount","workerCount","maxConcurrency","latency","retryAttempts"],"properties":{"question":{"type":"string","description":"Learning question shown above the provider-limit controls."},"policyId":{"type":"string","description":"Expected provider policy identifier used for provenance checks."},"scopeDescription":{"type":"string","description":"Human-readable scope of the expected provider quota."},"provider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"]},"region":{"type":"string"},"service":{"type":"string"},"operation":{"type":"string"},"category":{"type":"string"},"plannedCount":{"type":"integer","minimum":1,"maximum":100000},"workerCount":{"type":"integer","minimum":1,"maximum":1000},"maxConcurrency":{"type":"integer","minimum":1,"maximum":1000},"latency":{"type":"object","required":["meanMs","p95Ms","timeoutMs"],"properties":{"meanMs":{"type":"number","minimum":0},"p95Ms":{"type":"number","minimum":0},"timeoutMs":{"type":"number","exclusiveMinimum":0}}},"retryAttempts":{"type":"integer","minimum":1,"maximum":10}}},"realWorldIncident":{"type":"object","required":["date","provider"],"properties":{"date":{"type":"string","description":"Incident date as published by the cited source."},"provider":{"type":"string"},"summary":{"type":"string"},"references":{"type":"array","items":{"type":"object","required":["label","url"],"properties":{"label":{"type":"string"},"url":{"type":"string","format":"uri"}}}}}}}},"ResilienceDependency":{"type":"object","required":["id","sourceId","targetId"],"description":"A directed dependency edge between two resources. The engine models retry\namplification, circuit-breaker state, rate-limiting, and load-shedding across\nthis edge on every simulation step. Caller-set retry settings belong in this\nedge's `retryPolicy` object; omitted retry-policy fields are materialized with\nthe documented defaults and returned as the effective configuration.\n","properties":{"id":{"type":"string","minLength":1,"maxLength":128,"description":"Unique identifier for this dependency within the simulation.","example":"web-to-db"},"sourceId":{"type":"string","minLength":1,"description":"Resource ID of the upstream (calling) service.","example":"web-1"},"targetId":{"type":"string","minLength":1,"description":"Resource ID of the downstream (called) service.","example":"db-1"},"requestRatio":{"type":"number","minimum":0,"maximum":20,"default":1,"description":"Requests sent to the target per original client request (fan-out multiplier).","example":1},"authRequestsPerAttempt":{"type":"number","minimum":0,"maximum":10,"default":0,"description":"Auth/token requests added per attempt to an auth service. Models token-refresh overhead.","example":0},"authDependencyId":{"type":"string","minLength":1,"maxLength":128,"description":"Optional dependency edge that receives the auth/token traffic generated\nby this edge. Use it to model gateway retry amplification causally\nsaturating a downstream token service. The referenced dependency must\nexist and cannot be this dependency itself.\n","example":"gateway-to-token-service"},"retryPolicy":{"$ref":"#/components/schemas/ResilienceRetryPolicy","description":"Per-edge retry timeout, retry count, backoff, jitter, retry budget, and retry actor settings."},"capacity":{"$ref":"#/components/schemas/ResilienceCapacityConstraint"},"protection":{"$ref":"#/components/schemas/ResilienceProtection"}}},"ResilienceScheduledFault":{"type":"object","required":["id","type","startStep"],"description":"A fault injected at a specific simulation step range. Must target either a\nresource (`targetResourceId`) or a dependency (`dependencyId`). `endStep` must\nbe greater than `startStep` when provided. Duplicate `id` values within a\nconfig are rejected.\n","properties":{"id":{"type":"string","minLength":1,"maxLength":128,"description":"Unique fault identifier within the resilience config.","example":"db-capacity-fault"},"type":{"type":"string","enum":["capacity_limit","concurrency_limit","latency","error_rate","traffic_surge"],"description":"Fault category:\n- `capacity_limit` — reduce the target's RPS ceiling to `capacityPercent`% of normal.\n- `concurrency_limit` — cap concurrent in-flight requests at `maxConcurrent`.\n- `latency` — add `addedLatencyMs` to every request on this path.\n- `error_rate` — inject synthetic failures at `errorRate` fraction (0–1).\n- `traffic_surge` — multiply original client demand on the targeted root dependency.\n"},"targetResourceId":{"type":"string","description":"Resource to fault (mutually exclusive with dependencyId; one is required).","example":"db-1"},"dependencyId":{"type":"string","description":"Dependency edge to fault (mutually exclusive with targetResourceId; one is required).","example":"web-to-db"},"startStep":{"type":"integer","minimum":0,"description":"Simulation step at which the fault activates (inclusive).","example":5},"endStep":{"type":"integer","minimum":1,"description":"Simulation step at which the fault deactivates (exclusive). Omit for a permanent fault.","example":15},"capacityPercent":{"type":"number","minimum":0,"maximum":100,"description":"Used with type=capacity_limit — target capacity reduced to this percentage of normal.","example":30},"maxConcurrent":{"type":"integer","minimum":1,"maximum":1000000,"description":"Used with type=concurrency_limit — max concurrent requests while the fault is active.","example":5},"addedLatencyMs":{"type":"number","minimum":0,"maximum":120000,"description":"Used with type=latency — milliseconds added to every request.","example":500},"errorRate":{"type":"number","minimum":0,"maximum":1,"description":"Used with type=error_rate — fraction of requests that fail synthetically.","example":0.3},"trafficMultiplier":{"type":"number","exclusiveMinimum":true,"minimum":1,"maximum":20,"description":"Used with type=traffic_surge — multiplier applied to original client demand.","example":2.25}}},"ExternalMetricsConfig":{"type":"object","additionalProperties":false,"required":["errorMode","initialCapacity","triggers"],"description":"Opt-in deterministic external-metric recommendation model, not KEDA controller parity.\nNo network calls, fallback algorithm, cooldowns, provisioning, billing changes, or\nprovider autoscaler changes. Capacity is a separate modeled recommendation track.\nEach trigger recommends ceil(value / targetValue), clamped to the capacity bounds.\nSuccessful triggers combine by maximum, so zero cannot cancel demand.\nSamples start at step 0, have strictly increasing steps, and hold until replaced.\nEvaluation uses simulation.currentTime before step advancement. Normal creation\nwarms up five steps; read returned currentTime before choosing sample steps.\nA supplied positive currentTime restores that clock and skips creation warm-up.\nThe entire declared timeline is replayable: PATCH replaces the timeline and resets\nits derived history; reset/clone require no hidden controller state.\ndrop_failed_trigger excludes errors but blocks scale-in whenever any trigger errors;\nall errors produce no recommendation and retain current modeled capacity.\nfreeze_combined_decision retains the last all-success combined recommendation on any\nerror, or returns null/frozen_without_prior if none exists. initialCapacity is not\na successful recommendation. Only no_ready_endpoints may opt into a successful zero.\n","properties":{"errorMode":{"type":"string","enum":["drop_failed_trigger","freeze_combined_decision"]},"initialCapacity":{"type":"integer","minimum":0,"maximum":10000,"description":"Must lie between minCapacity and maxCapacity."},"minCapacity":{"type":"integer","minimum":0,"maximum":10000,"default":0},"maxCapacity":{"type":"integer","minimum":1,"maximum":10000,"default":100},"triggers":{"type":"array","minItems":1,"maxItems":16,"description":"Independently identified triggers; IDs must be unique.","items":{"type":"object","additionalProperties":false,"required":["id","targetValue","samples"],"properties":{"id":{"type":"string","minLength":1,"maxLength":128},"targetValue":{"type":"number","minimum":0.000001,"maximum":1000000000},"noReadyEndpointsAsZero":{"type":"boolean","default":false,"description":"Converts only no_ready_endpoints to successful zero, never discovery_error or fetch_error."},"samples":{"type":"array","minItems":1,"maxItems":120,"items":{"$ref":"#/components/schemas/ExternalMetricSample"}}}}}}},"ExternalMetricSample":{"oneOf":[{"type":"object","additionalProperties":false,"required":["step","status","value"],"properties":{"step":{"type":"integer","minimum":0,"maximum":100000},"status":{"type":"string","enum":["value"]},"value":{"type":"number","minimum":0,"maximum":1000000000}}},{"type":"object","additionalProperties":false,"required":["step","status"],"properties":{"step":{"type":"integer","minimum":0,"maximum":100000},"status":{"type":"string","enum":["no_ready_endpoints","discovery_error","fetch_error"]}}}]},"ExternalMetricsTelemetry":{"type":"object","required":["step","errorMode","currentCapacity","desiredCapacity","lastSuccessfulRecommendation","decision","scaleInBlocked","triggers"],"properties":{"step":{"type":"integer","minimum":0},"errorMode":{"type":"string","enum":["drop_failed_trigger","freeze_combined_decision"]},"currentCapacity":{"type":"integer","minimum":0,"description":"Modeled capacity before this decision; not a measured resource count."},"desiredCapacity":{"type":["integer","null"],"minimum":0},"lastSuccessfulRecommendation":{"type":["integer","null"],"minimum":0,"description":"Last combined recommendation with all triggers successful, including this step."},"decision":{"type":"string","enum":["scale_out","scale_in","no_scale","no_recommendation","frozen","frozen_without_prior"]},"scaleInBlocked":{"type":"boolean"},"triggers":{"type":"array","items":{"type":"object","required":["id","sampleStatus","outcome","value","desiredCapacity"],"properties":{"id":{"type":"string"},"sampleStatus":{"type":"string","enum":["value","no_ready_endpoints","discovery_error","fetch_error"]},"outcome":{"type":"string","enum":["success","error"]},"value":{"type":["number","null"],"minimum":0},"desiredCapacity":{"type":["integer","null"],"minimum":0}}}}}},"ResilienceConfig":{"type":"object","description":"Dependency-resilience model for a simulation. When attached (via\n`simulation.resilienceConfig`), the step engine traverses declared dependency\nedges, applies retry policies and circuit-breaker state, and emits\n`retryAmplificationFactor` + full `resilience` telemetry in every step response.\n\n**Limits** (shared with the engine to prevent unbounded work):\n- `dependencies`: max 64 edges\n- `scheduledFaults`: max 32 faults\n- `maxRetries` per dependency: max 8\n- `maxCascadeDepth`: max 8\n- `maxGeneratedRps`: max 500,000 RPS\n- `maxStepWork`: max 2,048 work units per step\n\n**Version:** only `version: 1` is accepted.\n","properties":{"enabled":{"type":"boolean","default":true,"description":"Master switch — set false to disable the resilience model without removing the config."},"version":{"type":"integer","enum":[1],"default":1,"description":"Config schema version (must be 1)."},"dependencies":{"type":"array","maxItems":64,"default":[],"description":"Directed dependency edges to model. Duplicate ids are rejected.","items":{"$ref":"#/components/schemas/ResilienceDependency"}},"scheduledFaults":{"type":"array","maxItems":32,"default":[],"description":"Step-ranged faults to inject during the run. Duplicate ids are rejected.","items":{"$ref":"#/components/schemas/ResilienceScheduledFault"}},"scalingPolicies":{"type":"array","maxItems":32,"default":[],"description":"Capacity/autoscaling policies, including intentionally mismatched observed and constrained metrics.","items":{"$ref":"#/components/schemas/ResilienceScalingPolicy"}},"externalMetrics":{"$ref":"#/components/schemas/ExternalMetricsConfig"},"podServices":{"type":"array","minItems":1,"maxItems":32,"description":"Opt-in generic pod lifecycle, keyed by unique compute or Kubernetes resourceId.\nOmission preserves legacy behavior. This models workload pods, not billed VM nodes.\nOrdinary steps are one simulated second. Full step/metrics output is in\nresilience.podServices; compact MCP output is in resilienceDiagnostics.podServices.\nInitial pods are Ready by default; scale-out pods pass startup and probes.\nNo controller, probe HTTP, or vendor-proxy parity is claimed.\n","items":{"$ref":"#/components/schemas/PodService"}},"maxCascadeDepth":{"type":"integer","minimum":1,"maximum":8,"default":4,"description":"Maximum dependency depth the engine traverses per step (prevents O(n) blow-up on deep graphs).","example":4},"maxGeneratedRps":{"type":"number","minimum":0,"maximum":500000,"default":100000,"description":"Hard ceiling on combined retry-generated and auth/token RPS across all dependency edges per step.","example":100000},"maxStepWork":{"type":"integer","minimum":1,"maximum":2048,"default":512,"description":"Maximum work-unit budget consumed per step across all dependency paths.","example":512},"retryGeneratedTrafficAffectsCost":{"type":"boolean","default":false,"description":"When true, retry-amplified RPS contributes to the cost model (more compute utilization = higher cost)."}}},"PodService":{"type":"object","additionalProperties":false,"required":["resourceId","replicas","perPodCapacityRps"],"properties":{"resourceId":{"type":"string","minLength":1,"maxLength":128},"replicas":{"type":"integer","minimum":0,"maximum":1000,"description":"Initial workload pod count; must match kubernetesCpuHpa.replicas when present."},"perPodCapacityRps":{"type":"number","exclusiveMinimum":0,"maximum":1000000,"description":"Declared modeled capacity of one serving pod; not measured capacity."},"initialReady":{"type":"boolean","default":true},"readiness":{"type":"object","additionalProperties":false,"properties":{"startupDelaySeconds":{"type":"number","minimum":0,"maximum":3600,"default":0},"readinessProbePeriodSeconds":{"type":"integer","minimum":1,"maximum":300,"default":1},"failureThreshold":{"type":"integer","minimum":1,"maximum":20,"default":1},"successThreshold":{"type":"integer","minimum":1,"maximum":20,"default":1}}},"endpointPropagation":{"type":"object","additionalProperties":false,"properties":{"addDelaySeconds":{"type":"number","minimum":0,"maximum":3600,"default":0},"removeDelaySeconds":{"type":"number","minimum":0,"maximum":3600,"default":0}}},"termination":{"type":"object","additionalProperties":false,"description":"New requests are accepted during preStop, bounded by the grace period.\nAfterwards a stale registered endpoint produces connection errors until removal.\npreStopSeconds cannot exceed terminationGracePeriodSeconds.\nIn-flight work is modeled from prior per-endpoint RPS times declared duration.\nfinish allows already accepted work to finish; drop-at-sigkill drops unfinished\nwork at the grace deadline. Drops are reported separately as prior-request counts,\nnot falsely attributed to the current step's offered requests.\n","properties":{"preStopSeconds":{"type":"number","minimum":0,"maximum":3600,"default":0},"terminationGracePeriodSeconds":{"type":"number","minimum":0,"maximum":3600,"default":30},"inflightDrainBehavior":{"type":"string","enum":["finish","drop-at-sigkill"],"default":"finish"},"requestDurationSeconds":{"type":"number","minimum":0,"maximum":3600,"default":0}}},"hpaUnreadyPodPolicy":{"type":"string","enum":["excluded","zero","target"],"default":"excluded","description":"CPU-request HPA averages Ready-pod demand only (excluded), includes unready pods\nat zero (zero), or includes them at each metric target (target). Recommendations\nuse the existing nominal-replica formula and stabilization. With no Ready pods,\nexcluded holds the current count. This is an exploration model, not controller parity.\n"},"replicaChanges":{"type":"array","maxItems":100,"description":"Unique ascending absolute engine steps; overrides HPA desired counts when supplied.","items":{"type":"object","additionalProperties":false,"required":["step","replicas"],"properties":{"step":{"type":"integer","minimum":0,"maximum":100000},"replicas":{"type":"integer","minimum":0,"maximum":1000}}}},"readinessChanges":{"type":"array","maxItems":100,"description":"Unique ascending steps providing a synthetic service-wide probe signal; default passing.","items":{"type":"object","additionalProperties":false,"required":["step","passing"],"properties":{"step":{"type":"integer","minimum":0,"maximum":100000},"passing":{"type":"boolean"}}}},"rollingUpdate":{"type":"object","additionalProperties":false,"required":["startStep"],"description":"One deterministic rollout; absolute pod-count surge/unavailable bounds cannot both be zero.","properties":{"startStep":{"type":"integer","minimum":0,"maximum":100000},"maxSurge":{"type":"integer","minimum":0,"maximum":1000,"default":1},"maxUnavailable":{"type":"integer","minimum":0,"maximum":1000,"default":0}}}}},"PodServiceTelemetry":{"type":"object","description":"Per-step modeled lifecycle, routing and capacity; RPS fields are rates, inflight fields are request counts.","properties":{"resourceId":{"type":"string"},"simulationStep":{"type":"number"},"simulationSeconds":{"type":"number"},"nominalReplicas":{"type":"number"},"ready":{"type":"number"},"notReady":{"type":"number"},"terminating":{"type":"number"},"registeredEndpoints":{"type":"number"},"effectiveServingPods":{"type":"number"},"effectiveCapacityRps":{"type":"number"},"nominalCapacityRps":{"type":"number"},"offeredRps":{"type":"number"},"requestsToTerminatingRps":{"type":"number"},"requestsToRemovedEndpointsRps":{"type":"number"},"propagationErrorRps":{"type":"number"},"overloadErrorRps":{"type":"number"},"droppedInflightRequests":{"type":"number"},"inflightRequests":{"type":"number"},"hpaUnreadyPodPolicy":{"type":"string","enum":["excluded","zero","target"]},"rolloutInProgress":{"type":"boolean"},"modelScope":{"type":"string","enum":["generic-pod-lifecycle; modeled estimates, not controller or proxy parity"]}}},"ResiliencePathMetrics":{"type":"object","description":"Per-dependency-edge metrics for one simulation step.","required":["dependencyId","sourceId","targetId","depth","originalRps","attemptedRps","retryRps","servedRps","shedRps","failedRps","timedOutRps","authTokenRps","authTokenFailedRps","retryAmplificationFactor","queueDepth","connectionPressure","availability","circuitState","saturated","activeFaultIds"],"properties":{"dependencyId":{"type":"string","description":"ID of the dependency edge."},"sourceId":{"type":"string","description":"Source resource ID."},"targetId":{"type":"string","description":"Target resource ID."},"depth":{"type":"integer","minimum":0,"description":"Cascade depth (0 = direct client call)."},"originalRps":{"type":"number","minimum":0,"description":"Original (pre-retry) RPS arriving at this edge."},"attemptedRps":{"type":"number","minimum":0,"description":"Total attempted RPS including retries."},"retryRps":{"type":"number","minimum":0,"description":"Retry-generated RPS (attemptedRps − originalRps)."},"servedRps":{"type":"number","minimum":0,"description":"RPS successfully served by the target."},"shedRps":{"type":"number","minimum":0,"description":"RPS dropped by rate-limiting or load-shedding."},"failedRps":{"type":"number","minimum":0,"description":"Attempt RPS that failed from capacity/queue pressure, timeout, or an application error. Excludes shed requests."},"timedOutRps":{"type":"number","minimum":0,"description":"RPS that timed out (counted against the retry budget)."},"authTokenRps":{"type":"number","minimum":0,"description":"Auth/token-refresh RPS generated by this edge."},"authTokenFailedRps":{"type":"number","minimum":0,"description":"Auth/token-refresh RPS that failed on this edge."},"retryAmplificationFactor":{"type":"number","minimum":0,"description":"Per-edge retry amplification (attemptedRps / originalRps). 1.0 = no amplification.","example":1.42},"queueDepth":{"type":"number","minimum":0,"description":"Estimated in-flight queue depth at the target."},"connectionPressure":{"type":"number","minimum":0,"description":"Connection pressure ratio at the target (analogous to DB connection-pool pressure)."},"availability":{"type":"number","minimum":0,"maximum":1,"description":"Fraction of requests successfully served (0 = fully unavailable, 1 = fully available).","example":0.97},"circuitState":{"type":"string","enum":["closed","open","half_open"],"description":"Current circuit-breaker state for this edge.","example":"closed"},"saturated":{"type":"boolean","description":"True when the target is operating above its capacity ceiling."},"activeFaultIds":{"type":"array","items":{"type":"string"},"description":"IDs of scheduled faults currently active on this edge or its target."}}},"ResilienceTelemetry":{"type":"object","description":"Aggregated resilience telemetry for one simulation step — produced by the\nengine when `simulation.resilienceConfig.enabled` is true. Contains both\nsimulation-level totals and the per-dependency `paths` array.\n","required":["originalClientRps","totalAttemptedRps","retryRps","servedRps","shedRps","authTokenRps","authTokenFailedRps","retryAmplificationFactor","queueDepth","connectionPressure","incidentOutcome","bounded","truncatedWorkItems","paths","scalingPolicies","retryActors"],"properties":{"originalClientRps":{"type":"number","minimum":0,"description":"Total original client RPS entering the dependency graph this step."},"totalAttemptedRps":{"type":"number","minimum":0,"description":"Original client RPS plus all retry-generated RPS. Normal service hops are not counted again."},"retryRps":{"type":"number","minimum":0,"description":"Total retry-generated RPS (totalAttemptedRps − originalClientRps)."},"servedRps":{"type":"number","minimum":0,"description":"Total RPS successfully served across all edges."},"shedRps":{"type":"number","minimum":0,"description":"Total RPS shed by rate-limiting or load-shedding across all edges."},"authTokenRps":{"type":"number","minimum":0,"description":"Total auth/token-refresh RPS generated across all edges."},"authTokenFailedRps":{"type":"number","minimum":0,"description":"Total auth/token-refresh RPS that failed across all edges."},"retryAmplificationFactor":{"type":"number","nullable":true,"description":"Simulation-level retry amplification factor (totalAttemptedRps / originalClientRps). null when originalClientRps is zero (no traffic). Values > 1.0 indicate net amplification.\n","example":1.42},"queueDepth":{"type":"number","minimum":0,"description":"Peak queue depth across all dependency edges this step."},"connectionPressure":{"type":"number","minimum":0,"description":"Peak connection pressure across all dependency edges this step."},"incidentOutcome":{"type":"string","enum":["stable","degraded","cascading","protected","recovered"],"description":"Aggregate incident outcome for this step:\n- `stable` — all dependencies within normal bounds; no amplification.\n- `degraded` — one or more edges are saturated or failing but traffic still flows.\n- `cascading` — retry amplification is propagating failures across multiple edges.\n- `protected` — circuit breakers or rate limiters absorbed the failure; end-user impact limited.\n- `recovered` — previously failing edges have returned to normal; circuit breakers reset.\n","example":"stable"},"bounded":{"type":"boolean","description":"True when a generated-traffic, traversal-work, or cascade-depth bound truncated model work."},"truncatedWorkItems":{"type":"integer","minimum":0,"description":"Number of dependency-path work items skipped due to the maxStepWork budget ceiling."},"paths":{"type":"array","maxItems":2048,"description":"Per-dependency-edge metrics for this step. Bounded by maxStepWork.","items":{"$ref":"#/components/schemas/ResiliencePathMetrics"}},"capacityByResource":{"type":"array","maxItems":128,"description":"Bounded aggregate capacity attribution, with one entry per logical dependency\nresource. When values are finite, nominalCapacityRps minus\nwarmupCapacityLossRps (capacity reduced while members are warming) and\nhealthCapacityLossRps (capacity reduced by unavailable or degraded members)\nequals routableCapacityRps, within rounding. Null values mean no finite\ncapacity ceiling is known.\n","items":{"$ref":"#/components/schemas/ResilienceCapacityAttribution"}},"scalingPolicies":{"type":"array","maxItems":32,"description":"Per-step constrained-capacity versus observed-autoscaling-metric decisions.","items":{"$ref":"#/components/schemas/ResilienceScalingPolicyTelemetry"}},"externalMetrics":{"$ref":"#/components/schemas/ExternalMetricsTelemetry"},"podServices":{"type":"array","maxItems":32,"items":{"$ref":"#/components/schemas/PodServiceTelemetry"}},"retryActors":{"type":"array","maxItems":64,"description":"Retry RPS grouped by independently identified retry actor.","items":{"$ref":"#/components/schemas/ResilienceRetryActorTelemetry"}}}},"ResilienceCapacityAttribution":{"type":"object","description":"Aggregate capacity details for one logical resilience resource; never a per-member list.","required":["resourceId","role","routableCapacityRps"],"properties":{"resourceId":{"type":"string","description":"Logical resource ID used by one or more resilience dependency paths."},"role":{"type":"string","enum":["source","target","source_and_target"],"description":"Whether this resource is a dependency source, target, or both."},"routableCapacityRps":{"type":"number","nullable":true,"minimum":0,"description":"Aggregate capacity available after modeled warm-up and member health; null when no finite capacity ceiling is known."},"nominalCapacityRps":{"type":"number","nullable":true,"minimum":0,"description":"Aggregate capacity before warm-up and member-health reductions; null when no finite capacity ceiling is known."},"warmupCapacityLossRps":{"type":"number","nullable":true,"minimum":0,"description":"Aggregate capacity lost because fleet members are still warming up; zero means no modeled warm-up loss."},"healthCapacityLossRps":{"type":"number","nullable":true,"minimum":0,"description":"Aggregate capacity lost because members are unavailable or degraded; zero means no modeled member-health loss."}}},"ResilienceScalingPolicyTelemetry":{"type":"object","required":["id","dependencyId","constrainedMetric","observationMetric","constrainedValuePercent","observedValuePercent","scaleOutThresholdPercent","decision"],"properties":{"id":{"type":"string"},"dependencyId":{"type":"string"},"constrainedMetric":{"type":"string","enum":["rps","concurrency"]},"observationMetric":{"type":"string","enum":["source_cpu","capacity_utilization"]},"constrainedValuePercent":{"type":"number","minimum":0},"observedValuePercent":{"type":"number","minimum":0},"scaleOutThresholdPercent":{"type":"number","minimum":1},"decision":{"type":"string","enum":["not_needed","no_scale","scale_out"]}}},"ResilienceRetryActorTelemetry":{"type":"object","required":["id","retryRps"],"properties":{"id":{"type":"string"},"retryRps":{"type":"number","minimum":0}}},"ResilienceIncidentOutcome":{"type":"object","description":"Stable, generic incident result for one bounded replay. It is suitable for\nautomated diagnosis and deterministic A/B comparisons without interpreting\nbrowser-only copy.\n","required":["rootTrigger","retryAmplificationFactor","peakOriginalRps","peakAttemptedRps","peakErrorRate","affectedDependencyIds","protectiveControlsActivated","timeToRecoverySteps","cascadeContained","runMetadata"],"properties":{"rootTrigger":{"type":"object","required":["faultIds","faultTypes","targetResourceIds","dependencyIds","firstActiveStep"],"properties":{"faultIds":{"type":"array","items":{"type":"string"}},"faultTypes":{"type":"array","items":{"type":"string","enum":["capacity_limit","concurrency_limit","latency","error_rate","traffic_surge"]}},"targetResourceIds":{"type":"array","items":{"type":"string"}},"dependencyIds":{"type":"array","items":{"type":"string"}},"firstActiveStep":{"type":"integer","minimum":0,"nullable":true}}},"retryAmplificationFactor":{"type":"number","nullable":true,"description":"Peak attempted/original retry amplification observed during the run."},"peakOriginalRps":{"type":"number","minimum":0},"peakAttemptedRps":{"type":"number","minimum":0},"peakErrorRate":{"type":"number","minimum":0,"maximum":100,"description":"Peak failed-attempt percentage across modeled dependency paths."},"affectedDependencyIds":{"type":"array","items":{"type":"string"}},"protectiveControlsActivated":{"type":"array","description":"Activated controls as type:dependency-id values.","items":{"type":"string"}},"timeToRecoverySteps":{"type":"integer","minimum":0,"nullable":true,"description":"Steps from first active fault to recovery; null when recovery was not observed in the replay window."},"cascadeContained":{"type":"boolean","description":"True when no step in the replay was classified as cascading."},"runMetadata":{"type":"object","required":["seed","startStep","endStep","steps","trafficRps","configVersion","faultIds"],"properties":{"seed":{"type":"integer","minimum":0},"startStep":{"type":"integer","minimum":0},"endStep":{"type":"integer","minimum":0},"steps":{"type":"integer","minimum":1},"trafficRps":{"type":"number","minimum":0},"configVersion":{"type":"integer","minimum":1},"faultIds":{"type":"array","items":{"type":"string"}}}}}},"ResilienceRunSummary":{"type":"object","description":"Aggregate statistics for one resilience replay run (baseline or mitigated).","required":["config","peakRetryAmplificationFactor","peakErrorRate","peakLatencyP95","totalServedRequests","totalShedRequests","finalOutcome","incidentOutcome"],"properties":{"config":{"$ref":"#/components/schemas/ResilienceConfig","description":"The resilience config used for this run."},"peakRetryAmplificationFactor":{"type":"number","nullable":true,"description":"Highest per-step retryAmplificationFactor observed across the replay window. null when no steps produced traffic (zero originalClientRps throughout).\n","example":1.87},"peakErrorRate":{"type":"number","minimum":0,"description":"Peak simulation error rate (%) across all replayed steps.","example":4.2},"peakLatencyP95":{"type":"number","minimum":0,"description":"Peak P95 latency (ms) across all replayed steps.","example":340},"totalServedRequests":{"type":"number","minimum":0,"description":"Cumulative successfully-served requests across all replayed steps.","example":198000},"totalShedRequests":{"type":"number","minimum":0,"description":"Cumulative shed (rate-limited / load-shed) requests across all replayed steps.","example":2000},"finalOutcome":{"type":"string","enum":["stable","degraded","cascading","protected","recovered"],"description":"Incident outcome at the last replayed step.","example":"stable"},"incidentOutcome":{"$ref":"#/components/schemas/ResilienceIncidentOutcome"}}},"ResilienceComparisonRequest":{"type":"object","required":["mitigatedConfig"],"description":"Request body for `POST /simulations/{simulationId}/resilience/compare`.\nThe baseline defaults to the simulation's current `resilienceConfig`; an\nexplicit `baselineConfig` overrides it. Returns 400 when neither is set.\n","properties":{"steps":{"type":"integer","minimum":1,"maximum":120,"default":20,"description":"Number of simulation steps to replay for both runs.","example":20},"baselineConfig":{"allOf":[{"$ref":"#/components/schemas/ResilienceConfig"}],"description":"Explicit baseline resilience config. When omitted, the simulation's current `resilienceConfig` is used. Returns 400 when both are absent.\n"},"mitigatedConfig":{"allOf":[{"$ref":"#/components/schemas/ResilienceConfig"}],"description":"The mitigated resilience config to evaluate against the baseline."},"mitigatedResources":{"type":"array","maxItems":256,"description":"Alternative resource array for the mitigated run. Uses the simulation's resources when omitted.","items":{"$ref":"#/components/schemas/Resource"}},"mitigatedAutoscalingConfig":{"type":"object","description":"Optional autoscaling config override for the mitigated run.","properties":{"scaleOutCpuThreshold":{"type":"number"},"scaleInCpuThreshold":{"type":"number"},"scaleOutThroughputThreshold":{"type":"number"},"scaleInThroughputThreshold":{"type":"number"},"scaleOutLatencyThreshold":{"type":"number"},"cooldownSeconds":{"type":"number"},"minInstances":{"type":"number"},"maxInstances":{"type":"number"}}}}},"ResilienceComparison":{"type":"object","description":"Side-by-side comparison of baseline vs. mitigated resilience runs.\nUse `delta.retryAmplificationFactor` as the headline: a negative value means\nthe mitigated config amplifies retries less (better). `delta.errorRate < 0`\nmeans fewer errors. Both `baseline` and `mitigated` carry peak and cumulative\nmetrics plus the final incident outcome.\n","required":["seed","startStep","traffic","steps","baseline","mitigated","delta"],"properties":{"seed":{"type":"integer","minimum":0,"description":"RNG seed used for both replay runs (deterministic; derived from the simulation's seed and current step).","example":42},"startStep":{"type":"integer","minimum":0,"description":"Simulation step at which both replay runs began.","example":10},"traffic":{"type":"number","minimum":0,"description":"Traffic level (RPS) at the start of the replay window.","example":5000},"steps":{"type":"integer","minimum":1,"description":"Number of steps replayed.","example":20},"baseline":{"$ref":"#/components/schemas/ResilienceRunSummary"},"mitigated":{"$ref":"#/components/schemas/ResilienceRunSummary"},"delta":{"type":"object","description":"Difference (mitigated − baseline) for key metrics. Negative values indicate\nimprovement (less amplification, fewer errors, lower latency). All fields\nare nullable when the corresponding baseline metric is null.\n","required":["retryAmplificationFactor","errorRate","latencyP95","shedRequests"],"properties":{"retryAmplificationFactor":{"type":"number","nullable":true,"description":"Change in peak retry amplification factor (mitigated − baseline). Negative means the mitigated config produces less retry amplification.\n","example":-0.45},"errorRate":{"type":"number","description":"Change in peak error rate in percentage points (mitigated − baseline). Negative = fewer errors.","example":-1.8},"latencyP95":{"type":"number","description":"Change in peak P95 latency in ms (mitigated − baseline). Negative = lower latency.","example":-60},"shedRequests":{"type":"number","description":"Change in total shed requests (mitigated − baseline). Negative = fewer shed requests.","example":-500}}}}},"ProviderApiLimitSimulationRequest":{"type":"object","additionalProperties":false,"required":["provider","region","service","operations","latency"],"description":"Caller-defined workload and client behavior for a deterministic provider API-quota simulation.","properties":{"contractVersion":{"type":"integer","enum":[1],"default":1},"provider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"],"description":"Cloud provider whose modeled API quota policy will be applied."},"region":{"type":"string","minLength":1,"maxLength":128,"description":"Provider region used to resolve regional quota policies."},"service":{"type":"string","minLength":1,"maxLength":128,"description":"Provider service whose API operations will be simulated."},"operations":{"type":"array","minItems":1,"maxItems":256,"description":"Ordered or dependency-linked groups of provider API operations to schedule.","items":{"$ref":"#/components/schemas/ProviderApiOperation"}},"workerCount":{"type":"integer","minimum":1,"maximum":1000,"default":1,"description":"Number of simulated workers issuing the planned operations."},"maxConcurrency":{"type":"integer","minimum":1,"maximum":1000,"default":1,"description":"Maximum simultaneous in-flight operations scheduled across workers."},"concurrencySweep":{"$ref":"#/components/schemas/ProviderApiConcurrencySweep"},"latency":{"$ref":"#/components/schemas/ProviderApiLatency"},"retry":{"$ref":"#/components/schemas/ProviderApiRetry"},"awsSdkRetryProfile":{"$ref":"#/components/schemas/AwsSdkRetryProfile"},"quotaScope":{"$ref":"#/components/schemas/ProviderApiQuotaScope"},"quotaOverrides":{"type":"array","maxItems":32,"description":"Optional caller-supplied provider quota policies for this workload.","items":{"$ref":"#/components/schemas/ProviderApiQuotaPolicy"}},"seed":{"type":"integer","minimum":0,"maximum":4294967295,"default":0,"description":"Seed used to make randomized scheduler outcomes reproducible."},"concurrency":{"type":"integer","minimum":1,"maximum":1000,"description":"Fixed simulated concurrency when a concurrency sweep is not used."},"failureProbability":{"type":"number","minimum":0,"maximum":1,"default":0,"description":"Probability that an operation encounters a modeled transient failure."},"maxThrottleRate":{"type":"number","minimum":0,"maximum":1,"default":1,"description":"Upper bound on the fraction of planned operations the scheduler may throttle."},"maxEvents":{"type":"integer","minimum":1,"maximum":1000000,"default":1000000,"description":"Maximum scheduler events permitted for this request."}}},"ProviderApiOperation":{"type":"object","additionalProperties":false,"required":["id","operation","category","plannedCount"],"description":"One provider operation group included in the simulated workload.","properties":{"id":{"type":"string","minLength":1,"maxLength":128,"description":"Unique identifier for this group and any groups that depend on it."},"operation":{"type":"string","minLength":1,"maxLength":128,"description":"Provider operation class or operation name used to select quota policies."},"category":{"type":"string","enum":["compute","database","storage","network","kubernetes","identity","monitoring","quota","other"],"description":"Service category used to resolve provider API-limit policies."},"plannedCount":{"type":"integer","minimum":1,"maximum":100000,"description":"Number of operations in this group to schedule."},"dependsOn":{"type":"array","maxItems":32,"description":"IDs of operation groups that must complete before this group is scheduled.","items":{"type":"string","minLength":1,"maxLength":128,"description":"Identifier of a prerequisite operation group."}},"resource":{"type":"string","minLength":1,"maxLength":128,"description":"Optional opaque resource label used to match resource-scoped quota policies."},"sdkClientId":{"type":"string","minLength":1,"maxLength":128,"description":"Optional SDK client identity; retry-token budgets are independent for each identity. Defaults to `default`."},"longPolling":{"type":"boolean","description":"Marks a long-poll operation that waits before returning when the selected SDK retry budget is exhausted."},"retryAfterMs":{"type":"number","minimum":0,"maximum":120000,"description":"Simulated AWS `x-amz-retry-after` header delay in milliseconds; requires an explicit AWS SDK profile without a generic retry override and is clamped to AWS's effective delay bounds."}}},"ProviderApiConcurrencySweep":{"type":"object","additionalProperties":false,"required":["values"],"description":"Optional set of concurrency levels at which to repeat the modeled workload.","properties":{"values":{"type":"array","minItems":1,"maxItems":16,"uniqueItems":true,"description":"Distinct concurrency levels to evaluate.","items":{"type":"integer","minimum":1,"maximum":1000,"description":"A number of simultaneous in-flight operations to evaluate."}}}},"ProviderApiLatency":{"type":"object","additionalProperties":false,"required":["meanMs","p95Ms","timeoutMs"],"description":"Required modeled client-side latency inputs. These values represent\ncaller assumptions for the Cloud World Model scheduler, not AWS\nservice defaults or a live provider latency claim. When measurements\nare unavailable, use the canonical baseline meanMs 40, p95Ms 80, and\ntimeoutMs 5000 from the provider-limit operation example.\n","properties":{"meanMs":{"type":"number","minimum":0,"maximum":120000,"description":"Caller-supplied mean operation latency in milliseconds."},"p95Ms":{"type":"number","minimum":0,"maximum":120000,"description":"Caller-supplied P95 operation latency in milliseconds."},"timeoutMs":{"type":"number","exclusiveMinimum":0,"maximum":120000,"description":"Caller-side request timeout in milliseconds."},"jitterMs":{"type":"number","minimum":0,"maximum":120000,"default":0,"description":"Maximum modeled latency variation in milliseconds."}}},"ProviderApiRetry":{"type":"object","additionalProperties":false,"description":"Generic caller/client retry settings, not AWS SDK or service defaults.\nIf supplied together with an AWS SDK retry profile, these settings\ntake precedence and use the generic retry mechanics.\nWhen omitted, the scheduler uses one attempt with no backoff and no\nretries: maxAttempts 1, baseBackoffMs 0,\nmaxBackoffMs 0, backoffMultiplier 2, jitterRatio 0.\n","properties":{"maxAttempts":{"type":"integer","minimum":1,"maximum":10,"default":1},"baseBackoffMs":{"type":"number","minimum":0,"maximum":60000,"default":0},"maxBackoffMs":{"type":"number","minimum":0,"maximum":600000,"default":0},"backoffMultiplier":{"type":"number","minimum":1,"maximum":10,"default":2},"jitterRatio":{"type":"number","minimum":0,"maximum":1,"default":0},"retryable":{"type":"array","maxItems":3,"uniqueItems":true,"items":{"type":"string","enum":["throttled","timeout","server_error"]}}}},"AwsSdkRetryProfile":{"type":"object","additionalProperties":false,"required":["profile"],"description":"Explicit JavaScript v3 Standard/Adaptive shared retry-layer profile.\nAdaptive's per-client request-rate limiter and cross-operation coupling\nare not modeled. AWS has not confirmed an exact default rollout day;\nprofile selection is explicit and never retroactively changes saved\nsimulations. Explicit caller `retry` settings take precedence.\nSources:\nhttps://docs.aws.amazon.com/sdkref/latest/guide/feature-retry-behavior.html\nhttps://github.com/aws/aws-sdk-js-v3/issues/8037\n","properties":{"profile":{"type":"string","enum":["aws-js-v3-standard-pre-2026","aws-js-v3-standard-2026"]},"mode":{"type":"string","enum":["standard","adaptive"],"default":"standard"}}},"ProviderApiRetryBehavior":{"type":"object","additionalProperties":false,"required":["source","selectedProfile","mode","serviceDefault","mechanics","adaptiveRateLimiterModeled","historicalRunsRewritten","maxAttempts","retryable","generic","transientBaseDelayMs","throttlingBaseDelayMs","retryTokenCapacity","transientRetryTokenCost","throttlingRetryTokenCost","maxBackoffCapMs","retryAfterMaxExtraMs","rolloutNote","clientBudgets"],"description":"Resolved retry contract. It is distinct from provider quota-policy resolution.","properties":{"source":{"type":"string","enum":["aws_sdk_profile","caller_override","caller_generic","legacy_generic"]},"selectedProfile":{"type":"string","nullable":true,"enum":["aws-js-v3-standard-pre-2026","aws-js-v3-standard-2026"]},"mode":{"type":"string","nullable":true,"enum":["standard","adaptive"]},"serviceDefault":{"type":"string","nullable":true,"enum":["dynamodb"]},"mechanics":{"type":"string","enum":["aws_shared_retry_layer","caller_generic"]},"adaptiveRateLimiterModeled":{"type":"boolean","enum":[false]},"historicalRunsRewritten":{"type":"boolean","enum":[false]},"maxAttempts":{"type":"integer","minimum":1},"retryable":{"type":"array","items":{"type":"string","enum":["throttled","timeout","server_error"]}},"generic":{"type":"object","nullable":true,"additionalProperties":false,"required":["baseBackoffMs","maxBackoffMs","backoffMultiplier","jitterRatio"],"properties":{"baseBackoffMs":{"type":"number","minimum":0},"maxBackoffMs":{"type":"number","minimum":0},"backoffMultiplier":{"type":"number","minimum":1},"jitterRatio":{"type":"number","minimum":0,"maximum":1}}},"transientBaseDelayMs":{"type":"number","nullable":true,"minimum":0},"throttlingBaseDelayMs":{"type":"number","nullable":true,"minimum":0},"retryTokenCapacity":{"type":"integer","nullable":true,"minimum":1},"transientRetryTokenCost":{"type":"integer","nullable":true,"minimum":1},"throttlingRetryTokenCost":{"type":"integer","nullable":true,"minimum":1},"maxBackoffCapMs":{"type":"number","nullable":true,"minimum":0},"retryAfterMaxExtraMs":{"type":"number","nullable":true,"minimum":0},"rolloutNote":{"type":"string","nullable":true},"clientBudgets":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["clientId","capacity","remaining","retriesDenied","tokensConsumed","tokensRestored","tokensSpent"],"properties":{"clientId":{"type":"string"},"capacity":{"type":"integer","minimum":1},"remaining":{"type":"integer","minimum":0},"retriesDenied":{"type":"integer","minimum":0},"tokensConsumed":{"type":"integer","minimum":0},"tokensRestored":{"type":"integer","minimum":0},"tokensSpent":{"type":"integer","minimum":0}}}}}},"ProviderApiQuotaScope":{"type":"object","additionalProperties":false,"properties":{"account":{"type":"string","minLength":1,"maxLength":128},"subscription":{"type":"string","minLength":1,"maxLength":128},"project":{"type":"string","minLength":1,"maxLength":128},"servicePrincipal":{"type":"string","minLength":1,"maxLength":128},"region":{"type":"string","minLength":1,"maxLength":128},"zone":{"type":"string","minLength":1,"maxLength":128},"resource":{"type":"string","minLength":1,"maxLength":128},"service":{"type":"string","minLength":1,"maxLength":128},"operation":{"type":"string","minLength":1,"maxLength":128}}},"ProviderApiQuotaProvenance":{"type":"object","additionalProperties":false,"required":["sourceKind","sourceReference","asOfDate","customerSpecific","observed","confidence"],"properties":{"sourceKind":{"type":"string","enum":["provider_documentation","provider_quota_documentation","provider_observation","caller_supplied"]},"sourceReference":{"type":"string","minLength":1,"maxLength":128},"asOfDate":{"type":"string","format":"date"},"customerSpecific":{"type":"boolean"},"observed":{"type":"boolean"},"confidence":{"type":"string","enum":["low","medium","high"]}}},"ProviderApiRateQuantity":{"type":"object","additionalProperties":false,"required":["limit","unit","windowSeconds"],"properties":{"limit":{"type":"number","exclusiveMinimum":0,"maximum":1000000000},"unit":{"type":"string","enum":["requests_per_second","requests_per_minute","requests_per_hour","requests_per_window","operations_per_second","operations_per_minute","operations_per_window","concurrent_requests"]},"windowSeconds":{"type":"number","exclusiveMinimum":0,"maximum":86400}}},"ProviderApiBurstCapacity":{"type":"object","additionalProperties":false,"required":["capacity","unit","windowSeconds","refill"],"properties":{"capacity":{"type":"number","exclusiveMinimum":0,"maximum":1000000000},"unit":{"type":"string","enum":["requests_per_second","requests_per_minute","requests_per_hour","requests_per_window","operations_per_second","operations_per_minute","operations_per_window","concurrent_requests"]},"windowSeconds":{"type":"number","exclusiveMinimum":0,"maximum":86400},"refill":{"type":"string","enum":["none","fixed_window","token_bucket"]},"refillRate":{"type":"number","exclusiveMinimum":0,"maximum":1000000000}}},"ProviderApiQuotaPolicy":{"type":"object","additionalProperties":false,"required":["policyId","provider","service","operationCategory","operation","scope","classification","provenance","enforcement","notes"],"properties":{"policyId":{"type":"string","minLength":1,"maxLength":128},"provider":{"type":"string","enum":["aws","gcp","azure","oci","digitalocean"]},"service":{"type":"string","minLength":1,"maxLength":128},"operationCategory":{"type":"string","enum":["compute","database","storage","network","kubernetes","identity","monitoring","quota","other"]},"operation":{"type":"string","minLength":1,"maxLength":128},"scope":{"$ref":"#/components/schemas/ProviderApiQuotaScope"},"classification":{"type":"string","enum":["documented_public_default","configurable_account_quota","observed_behavior"]},"provenance":{"$ref":"#/components/schemas/ProviderApiQuotaProvenance"},"sustainedRate":{"$ref":"#/components/schemas/ProviderApiRateQuantity"},"burstCapacity":{"$ref":"#/components/schemas/ProviderApiBurstCapacity"},"concurrencyCeiling":{"type":"integer","minimum":1,"maximum":1000},"concurrencyOverflow":{"type":"string","enum":["queue","reject"],"description":"Optional concurrency-ceiling overflow behavior. Absent retains the existing reject behavior; queue waits for an in-process request to finish."},"enforcement":{"type":"string","enum":["token_bucket","fixed_window","sliding_window","concurrency_ceiling","provider_defined"]},"notes":{"type":"string","minLength":1,"maxLength":512}}},"ProviderApiLimitResolution":{"type":"object","additionalProperties":false,"required":["contractVersion","status","accounting"],"properties":{"contractVersion":{"type":"integer","enum":[1]},"status":{"type":"string","enum":["supported","unsupported"]},"operationCategory":{"type":"string","enum":["compute","database","storage","network","kubernetes","identity","monitoring","quota","other"]},"operation":{"type":"string","minLength":1,"maxLength":128},"resource":{"type":"string","minLength":1,"maxLength":128},"policySource":{"type":"string","enum":["catalog_default","caller_override"]},"policy":{"$ref":"#/components/schemas/ProviderApiQuotaPolicy"},"reason":{"type":"string","minLength":1,"maxLength":512},"accounting":{"$ref":"#/components/schemas/ProviderApiOperationAccounting"}}},"ProviderApiOperationAccounting":{"type":"object","additionalProperties":false,"required":["plannedOperations","attemptedOperations","retryOperations","throttledOperations","completedOperations","denominator"],"properties":{"plannedOperations":{"type":"integer","minimum":0},"attemptedOperations":{"type":"integer","minimum":0},"retryOperations":{"type":"integer","minimum":0},"throttledOperations":{"type":"integer","minimum":0},"completedOperations":{"type":"integer","minimum":0},"denominator":{"type":"string","enum":["planned_operations"]}}},"ProviderApiSchedulerAccounting":{"allOf":[{"$ref":"#/components/schemas/ProviderApiOperationAccounting"},{"type":"object","required":["failedOperations","timedOutOperations","dependencyBlockedOperations","unsupportedOperations","pendingOperations","terminalOperations"],"properties":{"failedOperations":{"type":"integer","minimum":0},"timedOutOperations":{"type":"integer","minimum":0},"dependencyBlockedOperations":{"type":"integer","minimum":0},"unsupportedOperations":{"type":"integer","minimum":0},"pendingOperations":{"type":"integer","minimum":0},"terminalOperations":{"type":"integer","minimum":0}}}]},"ProviderApiUnitOutcome":{"type":"object","additionalProperties":false,"required":["operationId","operation","category","unitIndex","outcome","attempts","retryOperations","throttledAttempts","timedOutAttempts","readyAtMs","terminalAtMs","queueWaitMs","throttleWaitMs","retryBackoffMs","serviceTimeMs"],"properties":{"operationId":{"type":"string"},"operation":{"type":"string"},"category":{"type":"string"},"resource":{"type":"string"},"unitIndex":{"type":"integer","minimum":0},"outcome":{"type":"string","enum":["completed","failed","timed_out","dependency_blocked","unsupported_policy"]},"attempts":{"type":"integer","minimum":0},"retryOperations":{"type":"integer","minimum":0},"throttledAttempts":{"type":"integer","minimum":0},"timedOutAttempts":{"type":"integer","minimum":0},"readyAtMs":{"type":"number","minimum":0},"firstAttemptAtMs":{"type":"number","minimum":0},"terminalAtMs":{"type":"number","minimum":0},"queueWaitMs":{"type":"number","minimum":0},"throttleWaitMs":{"type":"number","minimum":0},"retryBackoffMs":{"type":"number","minimum":0},"serviceTimeMs":{"type":"number","minimum":0}}},"ProviderApiSchedulerResult":{"type":"object","additionalProperties":false,"required":["schedulerVersion","contractVersion","status","seed","concurrency","policyResolutions","unsupportedGroups","accounting","byCategory","operations","queue","timing","retryBehavior","optimization"],"properties":{"schedulerVersion":{"type":"integer","enum":[1]},"contractVersion":{"type":"integer","minimum":1},"status":{"type":"string","enum":["supported","unsupported_policy","rejected"]},"seed":{"type":"integer","minimum":0,"maximum":4294967295},"concurrency":{"type":"integer","minimum":1},"policyResolutions":{"type":"array","items":{"$ref":"#/components/schemas/ProviderApiLimitResolution"}},"unsupportedGroups":{"type":"array","items":{"type":"object","additionalProperties":false,"required":["operationCategory","operation","reason"],"properties":{"operationCategory":{"type":"string"},"operation":{"type":"string"},"resource":{"type":"string"},"reason":{"type":"string"}}}},"accounting":{"$ref":"#/components/schemas/ProviderApiSchedulerAccounting"},"byCategory":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/ProviderApiSchedulerAccounting"},{"type":"object","required":["category"],"properties":{"category":{"type":"string"}}}]}},"operations":{"type":"array","maxItems":100000,"description":"Per-unit outcomes. Empty when the workload has more than 1,000\nplanned operations; use the accounting and timing summaries in\nthat case.\n","items":{"$ref":"#/components/schemas/ProviderApiUnitOutcome"}},"outputDetail":{"type":"string","enum":["full","summary_only"],"description":"Optional detail marker. `summary_only` is returned when more than\n1,000 planned operations cause `operations` to be empty. `full`\ndenotes itemized operation output when explicitly present;\nomission also means full itemized output.\n"},"queue":{"type":"object","additionalProperties":false,"required":["currentDepth","peakDepth","backpressureEvents"],"properties":{"currentDepth":{"type":"integer","minimum":0},"peakDepth":{"type":"integer","minimum":0},"backpressureEvents":{"type":"integer","minimum":0}}},"timing":{"type":"object","additionalProperties":false,"required":["completionTimeMs","terminalCompletionTimeMs","successfulCompletionTimeMs","completionRatio","effectiveThroughputPerSecond","totalQueueWaitMs","totalServiceTimeMs"],"properties":{"completionTimeMs":{"type":"number","nullable":true,"minimum":0},"terminalCompletionTimeMs":{"type":"number","nullable":true,"minimum":0},"successfulCompletionTimeMs":{"type":"number","nullable":true,"minimum":0},"completionRatio":{"type":"number","minimum":0,"maximum":1},"effectiveThroughputPerSecond":{"type":"number","minimum":0},"totalQueueWaitMs":{"type":"number","minimum":0},"totalServiceTimeMs":{"type":"number","minimum":0}}},"retryBehavior":{"$ref":"#/components/schemas/ProviderApiRetryBehavior"},"optimization":{"type":"object","additionalProperties":false,"required":["objective","maxThrottleRate","acceptableConcurrency","recommendedConcurrency","optimalConcurrencyRange","comparisons"],"properties":{"objective":{"type":"string"},"maxThrottleRate":{"type":"number","minimum":0,"maximum":1},"acceptableConcurrency":{"type":"array","items":{"type":"integer","minimum":1}},"recommendedConcurrency":{"type":"integer","nullable":true,"minimum":1,"description":"Deterministic recommended candidate. Null when no candidate is\nsupported, fully completes, and stays within maxThrottleRate.\n"},"optimalConcurrencyRange":{"type":"object","nullable":true,"required":["min","max"],"properties":{"min":{"type":"integer","minimum":1},"max":{"type":"integer","minimum":1}}},"comparisons":{"type":"array","maxItems":16,"items":{"type":"object","additionalProperties":false,"required":["concurrency","status","plannedOperations","attemptedOperations","completedOperations","completionTimeMs","terminalCompletionTimeMs","successfulCompletionTimeMs","completionRatio","effectiveThroughputPerSecond","throttledOperations","retryOperations","failedOperations","timedOutOperations","dependencyBlockedOperations","unsupportedOperations","pendingOperations","terminalOperations","queuePeak","objectiveValue"],"properties":{"concurrency":{"type":"integer","minimum":1},"status":{"type":"string","enum":["supported","unsupported_policy","rejected"]},"plannedOperations":{"type":"integer","minimum":0},"attemptedOperations":{"type":"integer","minimum":0},"completedOperations":{"type":"integer","minimum":0},"completionTimeMs":{"type":"number","nullable":true,"minimum":0},"terminalCompletionTimeMs":{"type":"number","nullable":true,"minimum":0},"successfulCompletionTimeMs":{"type":"number","nullable":true,"minimum":0},"completionRatio":{"type":"number","minimum":0,"maximum":1},"effectiveThroughputPerSecond":{"type":"number","minimum":0},"throttledOperations":{"type":"integer","minimum":0},"retryOperations":{"type":"integer","minimum":0},"failedOperations":{"type":"integer","minimum":0},"timedOutOperations":{"type":"integer","minimum":0},"dependencyBlockedOperations":{"type":"integer","minimum":0},"unsupportedOperations":{"type":"integer","minimum":0},"pendingOperations":{"type":"integer","minimum":0},"terminalOperations":{"type":"integer","minimum":0},"queuePeak":{"type":"integer","minimum":0},"objectiveValue":{"type":"number","nullable":true,"minimum":0}}}}}},"reason":{"type":"string","minLength":1}}},"ProviderApiExecutionError":{"type":"object","additionalProperties":false,"required":["error","code","retryable"],"properties":{"error":{"type":"string","description":"Human-readable rejection or interruption reason."},"code":{"type":"string","enum":["PROVIDER_LIMIT_BUSY","PROVIDER_LIMIT_DUPLICATE","PROVIDER_LIMIT_WORK_BUDGET","PROVIDER_LIMIT_DEADLINE","PROVIDER_LIMIT_DISCONNECTED"]},"retryable":{"type":"boolean","enum":[false],"description":"Execution-protection failures must not be automatically retried."}}}},"responses":{"BadRequest":{"description":"Invalid request data — structured field-level error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"Unauthorized":{"description":"Authentication required or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Insufficient permissions — the API key lacks the required scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"WalletAuthUnauthorized":{"description":"No credentials provided to a wallet-session-protected endpoint. The\nresponse body includes a machine-readable `walletAuth` block with the\nendpoints and ordered steps needed to complete EIP-191 wallet\nauthentication and obtain a JWT.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletAuth401Response"}}}},"ApiKeyUnauthorized":{"description":"No credentials provided to an API-key-protected endpoint. The response\nbody includes a machine-readable `apiKeyAuth` block pointing to the key\ncreation endpoint so agents can self-serve an API key without consulting\nthe spec.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyAuth401Response"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InternalError":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"TooManyRequests":{"description":"Rate limit exceeded — slow down requests or upgrade your API key's rate limit","headers":{"Retry-After":{"description":"Seconds until the rate-limit window resets","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"X402PaymentRequired":{"description":"Payment required — the endpoint is metered and no API key was provided.\n\nThe response body follows the [x402 v2 protocol](https://x402.org) and describes\none or more USDC payment options that the caller can select before retrying the request.\nBase is always the first option and uses `X-PAYMENT`. When an `accepts` entry for Solana\nis present, it is an additional USDC-SPL option that uses `PAYMENT-SIGNATURE` and its\n`extra.feePayer` value. The server verifies either payment through the PayAI facilitator\nat `https://facilitator.payai.network`.\n\nThe `PAYMENT-REQUIRED` response header carries this entire JSON body as a\nbase64-encoded string for protocol-aware clients.\n\nCall `GET /api/billing/x402/config` to retrieve the current price table and wallet\naddress and `paymentOptions` before constructing a payment.\n","content":{"application/json":{"schema":{"type":"object","required":["x402Version","error","resource","accepts"],"properties":{"x402Version":{"type":"integer","example":2},"error":{"type":"string","example":"Payment required. Send an X-PAYMENT header containing a signed USDC payment on Base (x402 protocol)."},"resource":{"type":"object","required":["url","description","mimeType"],"properties":{"url":{"type":"string","description":"Full URL of the endpoint being paid for."},"description":{"type":"string","description":"Human-readable description of the metered call."},"mimeType":{"type":"string","example":"application/json"}}},"accepts":{"type":"array","items":{"type":"object","required":["scheme","network","amount","payTo","maxTimeoutSeconds","asset"],"properties":{"scheme":{"type":"string","example":"exact"},"network":{"type":"string","description":"CAIP-2 network identifier.","example":"eip155:8453"},"amount":{"type":"string","description":"Payment amount in USDC atomic units (6-decimal integer string).","example":"5000"},"payTo":{"type":"string","description":"Recipient wallet address on the selected network."},"maxTimeoutSeconds":{"type":"integer","example":300},"asset":{"type":"string","description":"USDC contract or mint address on the selected network.","example":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"},"extra":{"type":"object","description":"Network-specific x402 details. Solana options include\n`feePayer`, the PayAI wallet that sponsors the transaction fee.\n"}}}},"extensions":{"type":"object","description":"Optional protocol extensions (empty object in standard use)."}}}}}}},"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Key or Wallet Session JWT","description":"API key or wallet-session JWT authentication using Bearer tokens in the Authorization header.\n\n**How to authenticate:**\n1. Create an API key using POST /api/keys\n2. Include the key in the Authorization header: `Bearer cwm_live_<your_key>`\n3. All RL environment endpoints require authentication\n\n**Rate Limits:**\n- Default: 1000 requests/hour per key\n- Configurable per key\n- Returns 429 when exceeded\n"},"UiSessionCookie":{"type":"apiKey","in":"cookie","name":"cwm_ui_session","description":"HttpOnly browser-workspace session cookie issued by\n`POST /api/ui/simulations`. It authorizes UI mutations for simulations\nowned by that browser workspace.\n"},"x402":{"type":"apiKey","in":"header","name":"X-PAYMENT","description":"x402 v2 Base micropayment option. Select the Base entry from\n`GET /api/billing/x402/config` `paymentOptions`, then send its signed USDC\nmicro-payment in the `X-PAYMENT` header to get full access to the metered\nendpoint in a single round-trip — no account, no API key, no sign-up required.\n\n**402 → pay → retry loop:**\n1. Call the endpoint without an `X-PAYMENT` header.\n2. Receive a `402 Payment Required` response. The body is a\n standard JSON `PaymentRequired` object. The same payload is also\n echoed as a base64-encoded string in the `PAYMENT-REQUIRED`\n response header for clients that cannot access the body.\n3. Decode the `PaymentRequired` body (or the `PAYMENT-REQUIRED`\n header) to read the exact `payTo` wallet,\n USDC amount, network, and facilitator URL.\n4. Construct and sign the payment with your x402 client library.\n5. Retry the Base request with the `X-PAYMENT` header — the server\n verifies via `https://facilitator.payai.network` and responds `200`.\n\n**Optional Solana:** When `paymentOptions` advertises a Solana entry, callers\nmay instead select it and use `PAYMENT-SIGNATURE`. Solana is an additional\noption; use `X-PAYMENT` for the Base option shown by this security scheme.\n\n**Discovery:** `GET /api/billing/x402/config` returns the live `payTo`\naddress, canonical `paymentOptions`, per-call-type USDC amounts, facilitator\nURL, and the credits-per-USDC conversion rate. Always fetch this before\nhardcoding any amounts or a payment header.\n\n**Price range:** $0.0030 per 3-credit call — $0.0050 per 5-credit\ncall (see `x-x402-price-usdc` on each\noperation for the exact amount).\n\n**Facilitator:** `https://facilitator.payai.network` (PayAI-operated,\nverifies the advertised Base and optional Solana options).\n"},"x402Solana":{"type":"apiKey","in":"header","name":"PAYMENT-SIGNATURE","description":"Optional x402 v2 Solana payment alternative. Use this security option only\nwhen `GET /api/billing/x402/config` `paymentOptions` advertises a Solana\nnetwork, then send its signed Solana USDC payment payload in the\n`PAYMENT-SIGNATURE` header. Include the option's `feePayer` detail when\nconstructing the transaction.\n\nFor the primary Base payment option, select the `x402` security option\ninstead and send its signed payment payload in the `X-PAYMENT` header.\n"}}}}