Docs
Setup guide
Get from signup to a running GPU — then automate with the public API and official SDKs. Every identifier you see is a Chassis id.
Quickstart
- Sign up at /auth/signup with your work email.
- Verify the one-time code sent to your inbox, then set a password if prompted.
- Onboarding creates your organization — wallets, instances, and keys all hang off the org.
- Top up the org wallet (USD, minimum $20) from Billing in the console.
- Deploy a GPU from GPUs or Instances → Deploy. Pick a SKU, name the instance, and launch.
- Automate from API Keys — mint a
chs_…Bearer token and call/api/v1.
List available GPUs
Before you launch anything, call GET /gpus (or listGpus / list_gpus in the SDKs). That returns active Chassis SKUs with retail $/hr, memory, and stock. Use each SKU's id as gpuSkuId when creating instances, clusters, or endpoints. You can also browse the same catalog in the console under GPUs.
curl -s https://chassis.okeymeta.com.ng/api/v1/gpus \
-H "Authorization: Bearer chs_YOUR_KEY"
# Example shape (fields may vary by SKU):
# {
# "data": [
# {
# "id": "SKU_UUID",
# "slug": "rtx-4090",
# "displayName": "RTX 4090",
# "manufacturer": "NVIDIA",
# "memoryGb": 24,
# "pricePerHourUsd": 0.74,
# "spotPricePerHourUsd": 0.52,
# "stockStatus": "High",
# "secureAvailable": true,
# "communityAvailable": true,
# "isActive": true
# }
# ]
# }Tip: filter client-side by displayName, memoryGb, or pricePerHourUsd, then pass the chosen id into create calls.
Examples
Chassis GPUs are general-purpose machines. Host APIs, run jobs, scale serverless workers, or stand up multi-node clusters — same keys and Chassis resource IDs for every path.
Host any GPU workload (general)
Pick a SKU, choose your container image, open the ports your service needs, then connect with the instance publicIp / connection fields. Good for model APIs, media tools, notebooks, batch workers, or any CUDA app — not only training.
# 1) List SKUs
curl -s https://chassis.okeymeta.com.ng/api/v1/gpus \
-H "Authorization: Bearer chs_YOUR_KEY"
# 2) Launch your image (example: Open WebUI / custom API on 8080 + SSH)
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/instances \
-H "Authorization: Bearer chs_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"gpuSkuId": "SKU_UUID",
"name": "gpu-host-01",
"gpuCount": 1,
"imageName": "ghcr.io/YOUR_ORG/your-gpu-app:latest",
"containerDiskGb": 50,
"ports": "8080/http,22/tcp",
"env": { "MODEL_ID": "your-model" }
}'
# 3) Read connection details when status is running
curl -s https://chassis.okeymeta.com.ng/api/v1/instances/INSTANCE_ID \
-H "Authorization: Bearer chs_YOUR_KEY"
# → data.publicIp, data.connection, data.ports
# 4) Hit your service (example)
# curl -s http://PUBLIC_IP:8080/health
# 5) Stop when idle so the wallet stops drawing down
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/instances/INSTANCE_ID/stop \
-H "Authorization: Bearer chs_YOUR_KEY"Private images: create a registry credential first, then pass registryCredentialId. Persistent data: attach a network volume with networkVolumeId.
Multi-node cluster
Clusters launch 2–8 nodes that share the same image and SKU. Each node gets CHASSIS_CLUSTER_ID, CHASSIS_NODE_RANK, and CHASSIS_NODE_COUNT so distributed jobs can find peers. Manage them in the console under Clusters.
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/clusters \
-H "Authorization: Bearer chs_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "dist-train",
"gpuSkuId": "SKU_UUID",
"nodeCount": 4,
"gpusPerNode": 1,
"imageName": "pytorch/pytorch:2.1.0-cuda11.8-cudnn8-devel",
"ports": "22/tcp"
}'
curl -s https://chassis.okeymeta.com.ng/api/v1/clusters/CLUSTER_ID \
-H "Authorization: Bearer chs_YOUR_KEY"
# → data.nodes[] with per-node instance ids + connection info
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/clusters/CLUSTER_ID/stop \
-H "Authorization: Bearer chs_YOUR_KEY"
curl -s -X DELETE https://chassis.okeymeta.com.ng/api/v1/clusters/CLUSTER_ID \
-H "Authorization: Bearer chs_YOUR_KEY"Training job (dedicated GPU)
Launch a PyTorch image, run your training script on the instance, then stop or terminate when finished so the wallet stops drawing down.
1. Spin up with curl
# List SKUs, pick an id, then create
curl -s https://chassis.okeymeta.com.ng/api/v1/gpus \
-H "Authorization: Bearer chs_YOUR_KEY"
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/instances \
-H "Authorization: Bearer chs_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"gpuSkuId": "SKU_UUID",
"name": "finetune-bert",
"gpuCount": 1,
"imageName": "pytorch/pytorch:2.1.0-cuda11.8-cudnn8-devel",
"containerDiskGb": 80,
"ports": "8888/http,22/tcp"
}'2. On the GPU — example train.py
# train.py — run inside the Chassis instance (SSH or Jupyter)
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("device:", device, torch.cuda.get_device_name(0) if device.type == "cuda" else "")
# toy dataset — swap for your DataLoader / Hugging Face dataset
x = torch.randn(2048, 128)
y = torch.randint(0, 10, (2048,))
loader = DataLoader(TensorDataset(x, y), batch_size=64, shuffle=True)
model = nn.Sequential(
nn.Linear(128, 256),
nn.ReLU(),
nn.Linear(256, 10),
).to(device)
opt = torch.optim.AdamW(model.parameters(), lr=1e-3)
loss_fn = nn.CrossEntropyLoss()
for epoch in range(5):
total = 0.0
for xb, yb in loader:
xb, yb = xb.to(device), yb.to(device)
opt.zero_grad()
loss = loss_fn(model(xb), yb)
loss.backward()
opt.step()
total += loss.item()
print(f"epoch {epoch + 1} loss={total / len(loader):.4f}")
torch.save(model.state_dict(), "checkpoint.pt")
print("saved checkpoint.pt")3. Stop billing when the job finishes
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/instances/INSTANCE_ID/stop \
-H "Authorization: Bearer chs_YOUR_KEY"
# or terminate to delete the machine:
# curl -s -X DELETE https://chassis.okeymeta.com.ng/api/v1/instances/INSTANCE_ID \
# -H "Authorization: Bearer chs_YOUR_KEY"Serverless endpoints
Scale GPU workers on demand for inference, embeddings, or any request/response worker. Create an endpoint once, then call /runsync for a synchronous result (or /run + poll for async jobs). Idle workers can scale to zero when workersMin is 0.
# Create endpoint (workersMin 0 = scale to zero when idle)
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/endpoints \
-H "Authorization: Bearer chs_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "text-infer",
"gpuSkuId": "SKU_UUID",
"workersMin": 0,
"workersMax": 3
}'
# Synchronous inference
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/endpoints/ENDPOINT_ID/runsync \
-H "Authorization: Bearer chs_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"prompt": "Summarize Chassis in one sentence.",
"max_tokens": 128
}
}'
# Async job (poll with GET .../jobs/JOB_ID)
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/endpoints/ENDPOINT_ID/run \
-H "Authorization: Bearer chs_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "input": { "prompt": "hello" } }'Your worker image defines how input is handled — the API forwards the JSON body to the endpoint. Use a templateId when creating the endpoint if you have a reusable image config.
Console tour
- Overview — balance snapshot and shortcuts into active work.
- GPUs — searchable catalog of Chassis SKUs with retail $/hr.
- Instances — start, stop, restart, and terminate dedicated GPU machines.
- Clusters — multi-node GPU groups (2–8 nodes) with rank env vars for distributed jobs.
- Templates — reusable image + disk configs.
- Storage — network volumes for persistent data (10–4000 GB).
- Endpoints — serverless GPU workers that scale with demand.
- Registries — credentials for private container images.
- Billing — USD wallet top-ups (min $20) via hosted checkout, plus history.
- API Keys — mint Bearer tokens for automation.
- Docs — this guide, linked from the console sidebar.
- Settings — org and account preferences.
Billing
Every organization has a prepaid USD wallet. GPU runtime, network storage, and serverless endpoints draw it down at Chassis retail rates. Top up from Billing — minimum $20. You are redirected to a secure hosted checkout; Chassis credits the wallet when payment confirms.
Creating an instance needs enough balance for about one hour at that SKU's retail rate (HTTP 402 if underfunded). Network volumes need roughly a day of storage balance for their size. Endpoints with warm workers need about one hour of the floor rate. Catalog prices are Chassis retail rates shown in the console and API.
When the wallet cannot cover usage, Chassis stops running instances and scales warm endpoint workers to zero. Delete volumes you no longer need — storage meters while the volume exists.
API keys
Create a key in the console under API Keys. Chassis shows the plaintext once; it looks like chs_…. Only a hash is stored. Send it as a Bearer token on every /api/v1 request:
Authorization: Bearer chs_YOUR_KEYDefault scopes are instances:read and instances:write. Those cover the GPU catalog, instances, templates, volumes, endpoints, and registries. Revoking a key takes effect immediately.
Public API
Base path: https://chassis.okeymeta.com.ng/api/v1. All routes require Authorization: Bearer chs_…. Successful payloads use { "data": … }; errors use { "error": "…" }.
| Method | Path | Purpose |
|---|---|---|
| GET | /gpus | List active GPU SKUs (Chassis retail $/hr) |
| GET | /instances | List org instances |
| POST | /instances | Create instance (starts by default) |
| GET | /instances/:id | Get instance |
| PATCH | /instances/:id | Update instance |
| POST | /instances/:id/start | Start |
| POST | /instances/:id/stop | Stop |
| POST | /instances/:id/restart | Restart |
| DELETE | /instances/:id | Terminate |
| GET | /instances/:id/logs | Recent instance logs |
| GET | /clusters | List clusters |
| POST | /clusters | Create multi-node cluster |
| GET | /clusters/:id | Get cluster + nodes |
| POST | /clusters/:id/start | Start all nodes |
| POST | /clusters/:id/stop | Stop all nodes |
| DELETE | /clusters/:id | Terminate cluster |
| GET | /templates | List templates |
| POST | /templates | Create template |
| PATCH | /templates/:id | Update template |
| DELETE | /templates/:id | Delete template |
| GET | /volumes | List volumes |
| POST | /volumes | Create volume (sizeGb 10–4000) |
| GET | /volumes/:id | Get volume |
| PATCH | /volumes/:id | Update volume |
| GET | /endpoints | List endpoints |
| POST | /endpoints | Create endpoint |
| GET | /endpoints/:id | Get endpoint (includes workers when available) |
| PATCH | /endpoints/:id | Update endpoint workers / idle timeout |
| DELETE | /endpoints/:id | Delete endpoint |
| POST | /endpoints/:id/run | Async serverless job |
| POST | /endpoints/:id/runsync | Sync serverless job |
| GET | /endpoints/:id/health | Endpoint health |
| GET | /endpoints/:id/jobs/:jobId | Get async job status |
| POST | /endpoints/:id/jobs/:jobId/cancel | Cancel async job |
| GET | /registries | List registry credentials |
| POST | /registries | Create registry credential |
List available GPUs — use data[].id as gpuSkuId
curl -s https://chassis.okeymeta.com.ng/api/v1/gpus \
-H "Authorization: Bearer chs_YOUR_KEY"Create an instance — gpuSkuId and name are required. Optional: gpuCount (1–8), imageName, containerDiskGb (default 50), volumeGb, networkVolumeId, registryCredentialId, cloudType, ports, startAfterCreate (default true).
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/instances \
-H "Authorization: Bearer chs_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"gpuSkuId": "SKU_UUID",
"name": "train-01",
"gpuCount": 2,
"imageName": "pytorch/pytorch:2.1.0-cuda11.8-cudnn8-devel",
"containerDiskGb": 50
}'Restart / stop / start / terminate
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/instances/INSTANCE_ID/restart \
-H "Authorization: Bearer chs_YOUR_KEY"
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/instances/INSTANCE_ID/stop \
-H "Authorization: Bearer chs_YOUR_KEY"
curl -s -X DELETE https://chassis.okeymeta.com.ng/api/v1/instances/INSTANCE_ID \
-H "Authorization: Bearer chs_YOUR_KEY"Create a cluster — name, gpuSkuId, and nodeCount (2–8). Optional: gpusPerNode, imageName, networkVolumeId, ports.
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/clusters \
-H "Authorization: Bearer chs_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "dist-train",
"gpuSkuId": "SKU_UUID",
"nodeCount": 4,
"gpusPerNode": 1,
"imageName": "pytorch/pytorch:2.1.0-cuda11.8-cudnn8-devel"
}'Create a template — name and imageName required
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/templates \
-H "Authorization: Bearer chs_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "training-base",
"imageName": "pytorch/pytorch:2.1.0-cuda11.8-cudnn8-devel",
"containerDiskGb": 50
}'Create a volume — sizeGb must be 10–4000
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/volumes \
-H "Authorization: Bearer chs_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "datasets", "sizeGb": 100 }'Create an endpoint — name and gpuSkuId required. Optional: workersMin (default 0), workersMax (default 3), templateId.
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/endpoints \
-H "Authorization: Bearer chs_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "infer-api",
"gpuSkuId": "SKU_UUID",
"workersMin": 0,
"workersMax": 3
}'Create a registry credential — password is used once to register and is never returned
curl -s -X POST https://chassis.okeymeta.com.ng/api/v1/registries \
-H "Authorization: Bearer chs_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "ghcr-prod",
"registryHost": "ghcr.io",
"username": "YOUR_USER",
"password": "YOUR_TOKEN"
}'SDKs
Official clients wrap the same /api/v1 surface. Default base URL is https://chassis.okeymeta.com.ng/api/v1.
Install from npm (@chassis-cloud/sdk) and PyPI (chassis-cloud). Import as @chassis-cloud/sdk and chassis.
JavaScript / TypeScript — list available GPUs
npm install @chassis-cloud/sdk
import { Chassis } from '@chassis-cloud/sdk'
const chassis = new Chassis({
apiKey: process.env.CHASSIS_API_KEY!,
})
const gpus = await chassis.listGpus()
for (const gpu of gpus) {
console.log(
gpu.id,
gpu.displayName,
`$${gpu.pricePerHourUsd}/hr`,
gpu.memoryGb,
gpu.stockStatus,
)
}
// Pick a SKU for later create calls:
const gpu = gpus.find((g) => g.displayName.includes('4090')) ?? gpus[0]
console.log('using', gpu.id)JavaScript / TypeScript — host a GPU service
npm install @chassis-cloud/sdk
import { Chassis } from '@chassis-cloud/sdk'
const chassis = new Chassis({
apiKey: process.env.CHASSIS_API_KEY!,
})
const gpus = await chassis.listGpus()
const instance = await chassis.spinUp({
gpuSkuId: gpus[0].id,
name: 'gpu-host-01',
imageName: 'ghcr.io/YOUR_ORG/your-gpu-app:latest',
containerDiskGb: 50,
ports: '8080/http,22/tcp',
env: { MODEL_ID: 'your-model' },
})
const detail = await chassis.getInstance(instance.id)
console.log(detail.publicIp, detail.connection, detail.status)
// Point clients at your service on publicIp / published ports
await chassis.stop(instance.id)JavaScript / TypeScript — training
const gpu =
gpus.find((g) => g.displayName.includes('A100')) ?? gpus[0]
const train = await chassis.spinUp({
gpuSkuId: gpu.id,
name: 'finetune-bert',
imageName: 'pytorch/pytorch:2.1.0-cuda11.8-cudnn8-devel',
containerDiskGb: 80,
ports: '8888/http,22/tcp',
})
// SSH / Jupyter → run train.py (see Examples)
await chassis.stop(train.id)JavaScript / TypeScript — cluster
const cluster = (await chassis.createCluster({
name: 'dist-train',
gpuSkuId: gpus[0].id,
nodeCount: 4,
gpusPerNode: 1,
imageName: 'pytorch/pytorch:2.1.0-cuda11.8-cudnn8-devel',
})) as { id: string }
const detail = await chassis.getCluster(cluster.id)
console.log(detail)
await chassis.stopCluster(cluster.id)
// await chassis.terminateCluster(cluster.id)JavaScript / TypeScript — serverless
const endpoint = await chassis.createEndpoint({
name: 'text-infer',
gpuSkuId: gpus[0].id,
workersMin: 0,
workersMax: 3,
})
const result = await chassis.runSync(endpoint.id, {
input: { prompt: 'Summarize Chassis in one sentence.', max_tokens: 128 },
})
console.log(result)
const job = (await chassis.run(endpoint.id, {
input: { prompt: 'hello' },
})) as { id: string }
console.log(await chassis.getJob(endpoint.id, job.id))Python — list available GPUs
pip install chassis-cloud
from chassis import Chassis
with Chassis(api_key="chs_...") as client:
gpus = client.list_gpus()
for gpu in gpus:
print(
gpu["id"],
gpu.get("displayName"),
f"${gpu.get('pricePerHourUsd')}/hr",
gpu.get("memoryGb"),
gpu.get("stockStatus"),
)
gpu = next(
(g for g in gpus if "4090" in str(g.get("displayName", ""))),
gpus[0],
)
print("using", gpu["id"])Python — host a GPU service
pip install chassis-cloud
from chassis import Chassis
with Chassis(api_key="chs_...") as client:
gpus = client.list_gpus()
instance = client.spin_up(
gpuSkuId=gpus[0]["id"],
name="gpu-host-01",
imageName="ghcr.io/YOUR_ORG/your-gpu-app:latest",
containerDiskGb=50,
ports="8080/http,22/tcp",
env={"MODEL_ID": "your-model"},
)
detail = client.get_instance(instance["id"])
print(detail.get("publicIp"), detail.get("connection"), detail.get("status"))
client.stop(instance["id"])Python — training
with Chassis(api_key="chs_...") as client:
gpus = client.list_gpus()
gpu = next(
(g for g in gpus if "A100" in str(g.get("displayName", ""))),
gpus[0],
)
train = client.spin_up(
gpuSkuId=gpu["id"],
name="finetune-bert",
imageName="pytorch/pytorch:2.1.0-cuda11.8-cudnn8-devel",
containerDiskGb=80,
ports="8888/http,22/tcp",
)
# SSH / Jupyter → run train.py (see Examples)
client.stop(train["id"])Python — cluster
with Chassis(api_key="chs_...") as client:
gpus = client.list_gpus()
cluster = client.create_cluster(
name="dist-train",
gpuSkuId=gpus[0]["id"],
nodeCount=4,
gpusPerNode=1,
imageName="pytorch/pytorch:2.1.0-cuda11.8-cudnn8-devel",
)
print(client.get_cluster(cluster["id"]))
client.stop_cluster(cluster["id"])
# client.terminate_cluster(cluster["id"])Python — serverless
with Chassis(api_key="chs_...") as client:
gpus = client.list_gpus()
endpoint = client.create_endpoint(
name="text-infer",
gpuSkuId=gpus[0]["id"],
workersMin=0,
workersMax=3,
)
result = client.run_sync(
endpoint["id"],
body={"input": {"prompt": "Summarize Chassis.", "max_tokens": 128}},
)
print(result)
job = client.run(endpoint["id"], body={"input": {"prompt": "hello"}})
print(client.get_job(endpoint["id"], job["id"]))Next steps
Create an account, fund the org wallet, mint a key, then call the API. Pricing details live on the pricing page.