{"openapi":"3.0.3","info":{"title":"Lakelyon Cloud API","version":"1.0.0","description":"Customer-facing developer API for Lakelyon Cloud. Authenticate with your session cookie (dashboard) or a developer API key sent as `Authorization: Bearer lky_live_...`. The first API release is read-only for keys; customer mutations stay session-bound.","contact":{"name":"Lakelyon Cloud Support","url":"https://cloud.lakelyon.com/dashboard/support"}},"servers":[{"url":"https://cloud.lakelyon.com","description":"Production"},{"url":"http://localhost:3006","description":"Local development"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Orders","description":"The caller's own orders."},{"name":"Resources","description":"Provisioned compute resources owned by the caller."},{"name":"Billing","description":"Subscription, invoices and portal link."},{"name":"Profile","description":"The caller's Lakelyon account."},{"name":"Webhooks","description":"Outbound webhook endpoints and delivery log (session only)."},{"name":"Health","description":"Liveness and readiness probes."}],"paths":{"/api/orders":{"get":{"tags":["Orders"],"summary":"List the caller's orders","description":"Returns up to the 100 most recent orders with items, recent history and fulfillment. Accepts a session cookie or an API key with the `read:orders` scope.","operationId":"listOrders","responses":{"200":{"description":"Orders, newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"orders":{"type":"array","items":{"$ref":"#/components/schemas/Order"}}},"required":["orders"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/api/orders/{id}":{"get":{"tags":["Orders"],"summary":"Get one owned order","description":"Returns a single order with items, 50 history entries and recent operations (provider errors sanitized). Returns 404 for orders that do not exist or are not owned by the caller.","operationId":"getOrder","parameters":[{"$ref":"#/components/parameters/OrderId"}],"responses":{"200":{"description":"The order.","content":{"application/json":{"schema":{"type":"object","properties":{"order":{"$ref":"#/components/schemas/Order"}},"required":["order"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}},"post":{"tags":["Orders"],"summary":"Cancel a pending order (session only)","description":"Customer mutations are session-bound: API keys are read-only in the first API release. Only `PENDING_PAYMENT` orders can be cancelled by the customer.","operationId":"cancelOrder","parameters":[{"$ref":"#/components/parameters/OrderId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["cancel"]}},"required":["action"]}}}},"responses":{"200":{"description":"Updated order."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/resources":{"get":{"tags":["Resources"],"summary":"List the caller's resources","description":"Returns up to 200 owned resources, newest first. `providerStatus` is never returned. Accepts a session cookie or an API key with the `read:resources` scope.","operationId":"listResources","parameters":[{"name":"q","in":"query","required":false,"description":"Optional free-text search over name, hostname and IPs (ownership scope unchanged).","schema":{"type":"string"}}],"responses":{"200":{"description":"Resources.","content":{"application/json":{"schema":{"type":"object","properties":{"q":{"type":["string","null"]},"resources":{"type":"array","items":{"$ref":"#/components/schemas/Resource"}}},"required":["resources"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/api/billing":{"get":{"tags":["Billing"],"summary":"Get billing summary","description":"Returns the portal URL, active subscription (if any), the 20 most recent invoices and the current balance due. Accepts a session cookie or an API key with the `read:billing` scope.","operationId":"getBilling","responses":{"200":{"description":"Billing summary.","content":{"application/json":{"schema":{"type":"object","properties":{"portalUrl":{"type":"string"},"subscription":{"type":["object","null"],"properties":{"status":{"type":"string"},"priceId":{"type":"string"},"productId":{"type":"string"},"interval":{"type":"string"},"currentPeriodEnd":{"type":"string","format":"date-time"},"cancelAtPeriodEnd":{"type":"boolean"},"productName":{"type":"string"}}},"invoices":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"}},"balanceDue":{"type":"integer"}},"required":["portalUrl","invoices","balanceDue"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"503":{"description":"Customer portal is not available right now."}}}},"/api/profile":{"get":{"tags":["Profile"],"summary":"Get the caller's profile","description":"Returns the signed-in user's own profile (name, email, role, MFA status). Accepts a session cookie or an API key with the `read:profile` scope.","operationId":"getProfile","responses":{"200":{"description":"Profile.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Profile"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}},"patch":{"tags":["Profile"],"summary":"Update the caller's profile (session only)","description":"Profile mutations are session-bound — API keys are read-only.","operationId":"updateProfile","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100}},"required":["name"]}}}},"responses":{"200":{"description":"Updated profile.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Profile"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/profile/webhooks":{"get":{"tags":["Webhooks"],"summary":"List webhook endpoints (session only)","description":"Lists the customer's outbound webhook endpoints. Signing secrets are never returned on list.","operationId":"listWebhookEndpoints","responses":{"200":{"description":"Endpoints.","content":{"application/json":{"schema":{"type":"object","properties":{"endpoints":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}}},"required":["endpoints"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"tags":["Webhooks"],"summary":"Create a webhook endpoint (session only)","description":"Creates an endpoint. The signing `secret` is returned in plaintext exactly once — store it now.","operationId":"createWebhookEndpoint","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","maxLength":2048},"eventTypes":{"type":"array","items":{"type":"string"}},"secret":{"type":"string","description":"Optional customer-chosen signing secret; a random one is generated when omitted."}},"required":["url"]}}}},"responses":{"201":{"description":"Created. `{ endpoint, secret }` — the secret is shown only once."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/profile/webhooks/{id}":{"patch":{"tags":["Webhooks"],"summary":"Update a webhook endpoint (session only)","description":"Update url, eventTypes, enabled state or rotate the secret (`secret` in the body). A rotated secret is returned in plaintext exactly once.","operationId":"updateWebhookEndpoint","parameters":[{"$ref":"#/components/parameters/EndpointId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"eventTypes":{"type":"array","items":{"type":"string"}},"secret":{"type":"string"},"enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated endpoint (rotated secret included when rotated)."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Webhooks"],"summary":"Delete a webhook endpoint (session only)","operationId":"deleteWebhookEndpoint","parameters":[{"$ref":"#/components/parameters/EndpointId"}],"responses":{"200":{"description":"Deleted."},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/profile/webhooks/{id}/deliveries":{"get":{"tags":["Webhooks"],"summary":"Get the delivery log for an endpoint (session only)","operationId":"listWebhookDeliveries","parameters":[{"$ref":"#/components/parameters/EndpointId"}],"responses":{"200":{"description":"Deliveries for the endpoint."},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/health":{"get":{"tags":["Health"],"summary":"Liveness probe","description":"Always returns 200 when the process is alive. No external probes.","operationId":"liveness","security":[],"responses":{"200":{"description":"Process is alive.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"uptimeSeconds":{"type":"number"}}}}}}}}},"/api/health/ready":{"get":{"tags":["Health"],"summary":"Readiness probe","description":"200 when the instance may serve traffic (database up; payment/provider issues reported as degraded, not fatal). 503 when it should not receive traffic.","operationId":"readiness","security":[],"responses":{"200":{"description":"Ready to serve traffic."},"503":{"description":"Not ready."}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"developer API key (lky_live_...)","description":"Send `Authorization: Bearer <key>` with a developer API key, or use the dashboard session cookie. Keys are scoped (read-only in the first API release)."}},"parameters":{"OrderId":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Order ID."},"EndpointId":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Webhook endpoint ID."}},"responses":{"Unauthorized":{"description":"Missing or invalid credentials (401).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Authenticated but the credential's scope does not allow this operation (403), or the API-key feature is not enabled for the account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"BadRequest":{"description":"Invalid request body or parameters (400).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource does not exist or is not owned by the caller (404).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message."}},"required":["error"]},"Order":{"type":"object","properties":{"id":{"type":"string"},"orderNumber":{"type":"string"},"status":{"type":"string","enum":["PENDING_PAYMENT","PAID","ACTIVE","CANCELED","FULFILLED","FAILED"]},"stripeTestMode":{"type":"boolean"},"currency":{"type":"string"},"subtotalMinor":{"type":"integer","description":"Amount in minor currency units (e.g. cents)."},"taxMinor":{"type":"integer"},"totalMinor":{"type":"integer"},"configurationJson":{"type":"string"},"stripeSubscriptionId":{"type":["string","null"]},"paidAt":{"type":["string","null"],"format":"date-time"},"fulfilledAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"checkoutAttempt":{"type":["string","null"]},"lastCheckoutError":{"type":["string","null"]},"items":{"type":"array","items":{"type":"object"}},"history":{"type":"array","items":{"type":"object"}},"fulfillment":{"type":["object","null"]}},"required":["id","orderNumber","status","currency","totalMinor","createdAt"]},"Resource":{"type":"object","properties":{"id":{"type":"string"},"displayName":{"type":"string"},"hostname":{"type":["string","null"]},"publicIps":{"type":"array","items":{"type":"string"}},"region":{"type":"string"},"image":{"type":"string"},"specSnapshotJson":{"type":"string"},"status":{"type":"string"},"lastSyncedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"orderItem":{"type":["object","null"],"properties":{"orderId":{"type":"string"},"productName":{"type":"string"},"variantName":{"type":"string"},"unitAmountMinor":{"type":"integer"},"currency":{"type":"string"}}}},"required":["id","displayName","status","createdAt"]},"Invoice":{"type":"object","properties":{"id":{"type":"string"},"amountDue":{"type":"integer"},"amountPaid":{"type":"integer"},"currency":{"type":"string"},"status":{"type":"string"},"hostedInvoiceUrl":{"type":["string","null"]},"invoicePdf":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","amountDue","currency","status","createdAt"]},"Profile":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"role":{"type":"string"},"mfaEnabled":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","email","role"]},"WebhookEndpoint":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"eventTypes":{"type":"array","items":{"type":"string"}},"enabled":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","url","eventTypes","enabled"]}}}}