{"openapi":"3.1.0","info":{"title":"Cycls Cloud API","description":"\n## Deploy serverless functions in seconds\n\nCycls Cloud lets you deploy containerized functions to the cloud with a single command.\n\n### Authentication\n\nAll endpoints require authentication via:\n- **API Key**: Pass `X-API-Key` header\n- **JWT**: Pass `Authorization: Bearer <token>` header\n\n### Quick Start\n\n```bash\ncurl -X POST https://api.cycls.ai/v1/deploy \\\n  -H \"X-API-Key: your_api_key\" \\\n  -F \"function_name=hello\" \\\n  -F \"source=@source.tar.gz\"\n```\n","contact":{"name":"Cycls","url":"https://cycls.com/"},"version":"1.0.0"},"paths":{"/v1/deployment/check-name":{"get":{"tags":["Deployments"],"summary":"Check Name Availability","description":"Check if a deployment name is available for the authenticated user.\nReturns whether the name can be used and a reason if not.","operationId":"check_name_v1_deployment_check_name_get","security":[{"HTTPBearer":[]},{"APIKeyHeader":[]}],"parameters":[{"name":"name","in":"query","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NameCheckResponse"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"},"503":{"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/deploy":{"post":{"tags":["Deployments"],"summary":"Deploy","description":"Deploy a service. Returns NDJSON stream with progress updates.\n\nPost the source as `source_archive` — the tar.gz, up to 100 MiB. Ship\nlarge files via volumes (`cycls volume put`), not the source archive.\n\n`timeout` is how long a single request to the deployed service may run —\nraise it for long compute, up to Cloud Run's 3600s ceiling. `cpu` is raised\nautomatically if `memory` requires more than you asked for.\n\n`concurrency` is how many requests one instance serves at once. Leave it high\nfor I/O-bound apps; set it to 1 for compute, so each call gets its own instance.\n\n`max_instances` caps how many instances the service may scale to. 0 (default)\nleaves scaling to the platform.\n\n`volumes` maps mount paths to volumes (JSON), e.g. `{\"/data\": \"shared\"}` or\n`{\"/data\": {\"name\": \"shared\", \"read_only\": true, \"sub_path\": \"v1\"}}`.\nRequired — `{}` declares no storage; the deploy is a complete declaration.\nUnknown volumes are created (announced in the stream).\n\n`schedule` fires the deployed function on a cron: `{\"cron\": \"0 3 * * *\",\n\"timezone\": \"Asia/Riyadh\"}`. Empty or omitted removes any schedule — the\nsame complete-declaration rule.\n\nEvents: `BUILDING` → `DEPLOYING` → `DONE` (or `ERROR`)","operationId":"deploy_v1_deploy_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_deploy_v1_deploy_post"}}},"required":true},"responses":{"200":{"description":"NDJSON stream of deployment progress","content":{"application/json":{"schema":{}},"application/x-ndjson":{}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"},"503":{"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]},{"APIKeyHeader":[]}]}},"/v1/deployment/list":{"get":{"tags":["Deployments"],"summary":"List Service","description":"Lists all services for the authenticated user or organization.","operationId":"list_services_v1_deployment_list_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Service"},"type":"array","title":"Response List Services V1 Deployment List Get"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"},"503":{"description":"Service Unavailable"}},"security":[{"HTTPBearer":[]},{"APIKeyHeader":[]}]}},"/v1/deployment/delete":{"post":{"tags":["Deployments"],"summary":"Delete a Service","operationId":"delete_service_v1_deployment_delete_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceDeleteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceDeleteResponse"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"},"503":{"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]},{"APIKeyHeader":[]}]}},"/v1/volume/create":{"post":{"tags":["Volumes"],"summary":"Create a Volume","description":"Explicitly create a volume (idempotent). Deploys referencing an unknown\nvolume also create it; content operations never do.","operationId":"volume_create_v1_volume_create_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VolumeCreateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VolumeCreateResponse"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"},"503":{"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]},{"APIKeyHeader":[]}]}},"/v1/volume/list":{"get":{"tags":["Volumes"],"summary":"List Volumes","description":"Volumes with the deployments currently mounting them, read live from\nthe service templates — the sole store of attachments.","operationId":"volume_list_v1_volume_list_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/VolumeInfo"},"type":"array","title":"Response Volume List V1 Volume List Get"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"},"503":{"description":"Service Unavailable"}},"security":[{"HTTPBearer":[]},{"APIKeyHeader":[]}]}},"/v1/volume/delete":{"post":{"tags":["Volumes"],"summary":"Delete a Volume","description":"Refuses while any deployment mounts it. Bucket deletion is backstopped\nby GCS soft delete.","operationId":"volume_delete_v1_volume_delete_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VolumeDeleteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VolumeDeleteResponse"}}}},"409":{"description":"Volume is attached to deployments"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"},"503":{"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]},{"APIKeyHeader":[]}]}},"/v1/volume/upload":{"post":{"tags":["Volumes"],"summary":"Get a Signed Upload URL for a Volume Object","description":"Strict: the volume must already exist — uploads never create one.","operationId":"volume_upload_v1_volume_upload_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VolumeObjectRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedUrlResponse"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"},"503":{"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]},{"APIKeyHeader":[]}]}},"/v1/volume/download":{"post":{"tags":["Volumes"],"summary":"Get a Signed Download URL for a Volume Object","operationId":"volume_download_v1_volume_download_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VolumeObjectRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignedUrlResponse"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"},"503":{"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]},{"APIKeyHeader":[]}]}},"/v1/volume/objects":{"get":{"tags":["Volumes"],"summary":"List Objects in a Volume","operationId":"volume_objects_v1_volume_objects_get","security":[{"HTTPBearer":[]},{"APIKeyHeader":[]}],"parameters":[{"name":"volume","in":"query","required":true,"schema":{"type":"string","title":"Volume"}},{"name":"prefix","in":"query","required":false,"schema":{"type":"string","default":"","title":"Prefix"}},{"name":"page_token","in":"query","required":false,"schema":{"type":"string","default":"","title":"Page Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VolumeObjectsResponse"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"},"503":{"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/volume/delete-object":{"post":{"tags":["Volumes"],"summary":"Delete an Object from a Volume","operationId":"volume_delete_object_v1_volume_delete_object_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VolumeObjectRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VolumeDeleteResponse"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"},"503":{"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]},{"APIKeyHeader":[]}]}},"/v1/deployment/logs":{"post":{"tags":["Logs"],"summary":"Get Service Logs","description":"Fetch logs from Cloud Logging for a specific service.\nUse `cursor` from response as `since` to poll for new logs.","operationId":"get_deployment_logs_v1_deployment_logs_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogsResponse"}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"},"503":{"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]},{"APIKeyHeader":[]}]}},"/v1/sql":{"post":{"tags":["Logs"],"summary":"SQL over your logs and billing","description":"Run SQL against your own logs and billing data. Two views are pre-set in your\ndefault dataset:\n\n  - `logs`    — Cloud Logging entries for your deployments\n  - `billing` — Cloud Billing rows for your deployments\n\nEach tenant gets their own BigQuery dataset and service account; cross-tenant\nreferences fail at the IAM layer.","operationId":"sql_v1_sql_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SqlRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"},"503":{"description":"Service Unavailable"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]},{"APIKeyHeader":[]}]}},"/v1/user/subscription":{"get":{"tags":["Billing"],"summary":"Get User Subscription","description":"Fetches the billing subscription details for the authenticated user from Clerk.","operationId":"get_user_subscription_v1_user_subscription_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"},"503":{"description":"Service Unavailable"}},"security":[{"HTTPBearer":[]},{"APIKeyHeader":[]}]}}},"components":{"schemas":{"Body_deploy_v1_deploy_post":{"properties":{"function_name":{"type":"string","title":"Function Name"},"port":{"type":"integer","title":"Port","default":8080},"memory":{"type":"string","title":"Memory","default":"1Gi"},"source_archive":{"anyOf":[{"type":"string","format":"binary"},{"type":"null"}],"title":"Source Archive"},"source_object":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Object"},"session_affinity":{"type":"boolean","title":"Session Affinity","default":false},"use_http2":{"type":"boolean","title":"Use Http2","default":false},"timeout":{"type":"integer","maximum":3600.0,"minimum":1.0,"title":"Timeout","default":600},"cpu":{"type":"integer","title":"Cpu","default":1},"concurrency":{"type":"integer","maximum":1000.0,"minimum":1.0,"title":"Concurrency","default":80},"max_instances":{"type":"integer","maximum":1000.0,"minimum":0.0,"title":"Max Instances","default":0},"volumes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Volumes"},"schedule":{"type":"string","title":"Schedule","default":""},"timezone":{"type":"string","title":"Timezone","default":""}},"type":"object","required":["function_name"],"title":"Body_deploy_v1_deploy_post"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"LogEntry":{"properties":{"timestamp":{"type":"string","title":"Timestamp","description":"ISO 8601 timestamp","examples":["2024-01-15T10:30:00+00:00"]},"severity":{"type":"string","title":"Severity","description":"Log level","examples":["DEFAULT"]},"message":{"type":"string","title":"Message","description":"Log message content","examples":["INFO: 127.0.0.1 - \"GET / HTTP/1.1\" 200 OK"]}},"type":"object","required":["timestamp","severity","message"],"title":"LogEntry"},"LogsRequest":{"properties":{"deployment_name":{"type":"string","title":"Deployment Name","description":"Service name to fetch logs for","examples":["my-function"]},"since":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Since","description":"Fetch logs after this timestamp (use cursor from previous response)"},"limit":{"type":"integer","maximum":1000.0,"minimum":1.0,"title":"Limit","description":"Maximum number of log entries to return","default":100},"query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query","description":"Cloud Logging filter expression to AND with the base filter (e.g. 'jsonPayload.level=\"error\"'). Parentheses must be balanced."}},"type":"object","required":["deployment_name"],"title":"LogsRequest"},"LogsResponse":{"properties":{"logs":{"items":{"$ref":"#/components/schemas/LogEntry"},"type":"array","title":"Logs","description":"List of log entries (oldest first)"},"cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor","description":"Pass as 'since' to fetch newer logs"}},"type":"object","required":["logs"],"title":"LogsResponse"},"NameCheckResponse":{"properties":{"available":{"type":"boolean","title":"Available","description":"Whether the name can be used by the caller"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Reason if unavailable"}},"type":"object","required":["available"],"title":"NameCheckResponse"},"Service":{"properties":{"id":{"type":"string","title":"Id","description":"Unique deployment ID","examples":["550e8400-e29b-41d4-a716-446655440000"]},"name":{"type":"string","title":"Name","description":"Service name","examples":["my-function"]},"url":{"type":"string","title":"Url","description":"Public URL for the service","examples":["https://my-function-abc123.run.app"]},"created_at":{"type":"string","format":"date-time","title":"Created At","description":"Deployment timestamp"},"region":{"type":"string","title":"Region","description":"Cloud region","examples":["me-central1"]},"owner_id":{"type":"string","title":"Owner Id","description":"Owner ID (user or organization)"}},"type":"object","required":["id","name","url","created_at","region","owner_id"],"title":"Service"},"ServiceDeleteRequest":{"properties":{"name":{"type":"string","title":"Name","description":"Service name to delete","examples":["my-function"]}},"type":"object","required":["name"],"title":"ServiceDeleteRequest"},"ServiceDeleteResponse":{"properties":{"detail":{"type":"string","title":"Detail","description":"Result message","examples":["Service 'my-function' deleted successfully."]}},"type":"object","required":["detail"],"title":"ServiceDeleteResponse"},"SignedUrlResponse":{"properties":{"url":{"type":"string","title":"Url","description":"Signed URL, valid for 15 minutes"}},"type":"object","required":["url"],"title":"SignedUrlResponse"},"SqlRequest":{"properties":{"query":{"type":"string","title":"Query","description":"BigQuery SQL. Your default dataset contains views `logs` and `billing` scoped to deployments you own.","examples":["SELECT timestamp, json_payload FROM logs WHERE json_payload.level='error' LIMIT 10"]}},"type":"object","required":["query"],"title":"SqlRequest"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VolumeCreateRequest":{"properties":{"name":{"type":"string","title":"Name","description":"Volume name","examples":["shared-data"]}},"type":"object","required":["name"],"title":"VolumeCreateRequest"},"VolumeCreateResponse":{"properties":{"name":{"type":"string","title":"Name"},"created":{"type":"boolean","title":"Created","description":"False if the volume already existed"}},"type":"object","required":["name","created"],"title":"VolumeCreateResponse"},"VolumeDeleteRequest":{"properties":{"name":{"type":"string","title":"Name","description":"Volume to delete","examples":["shared-data"]}},"type":"object","required":["name"],"title":"VolumeDeleteRequest"},"VolumeDeleteResponse":{"properties":{"detail":{"type":"string","title":"Detail"}},"type":"object","required":["detail"],"title":"VolumeDeleteResponse"},"VolumeInfo":{"properties":{"name":{"type":"string","title":"Name"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"attached_to":{"items":{"type":"string"},"type":"array","title":"Attached To","description":"Deployments currently mounting this volume"}},"type":"object","required":["name","created_at","attached_to"],"title":"VolumeInfo"},"VolumeObject":{"properties":{"path":{"type":"string","title":"Path"},"size":{"type":"integer","title":"Size"},"updated":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated"}},"type":"object","required":["path","size"],"title":"VolumeObject"},"VolumeObjectRequest":{"properties":{"volume":{"type":"string","title":"Volume","description":"Volume name","examples":["shared-data"]},"path":{"type":"string","title":"Path","description":"Object path within the volume","examples":["models/model.bin"]}},"type":"object","required":["volume","path"],"title":"VolumeObjectRequest"},"VolumeObjectsResponse":{"properties":{"objects":{"items":{"$ref":"#/components/schemas/VolumeObject"},"type":"array","title":"Objects"},"next_page_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Page Token"}},"type":"object","required":["objects"],"title":"VolumeObjectsResponse"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"},"APIKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key"}}},"tags":[{"name":"Deployments","description":"Deploy, list, and manage your services"},{"name":"Volumes","description":"Named storage, attached to deployments by mount path"},{"name":"Logs","description":"Fetch service logs"},{"name":"Billing","description":"Subscription and usage"}]}