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

  1. Sign up at /auth/signup with your work email.
  2. Verify the one-time code sent to your inbox, then set a password if prompted.
  3. Onboarding creates your organization — wallets, instances, and keys all hang off the org.
  4. Top up the org wallet (USD, minimum $20) from Billing in the console.
  5. Deploy a GPU from GPUs or Instances → Deploy. Pick a SKU, name the instance, and launch.
  6. 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_KEY

Default 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": "…" }.

MethodPathPurpose
GET/gpusList active GPU SKUs (Chassis retail $/hr)
GET/instancesList org instances
POST/instancesCreate instance (starts by default)
GET/instances/:idGet instance
PATCH/instances/:idUpdate instance
POST/instances/:id/startStart
POST/instances/:id/stopStop
POST/instances/:id/restartRestart
DELETE/instances/:idTerminate
GET/instances/:id/logsRecent instance logs
GET/clustersList clusters
POST/clustersCreate multi-node cluster
GET/clusters/:idGet cluster + nodes
POST/clusters/:id/startStart all nodes
POST/clusters/:id/stopStop all nodes
DELETE/clusters/:idTerminate cluster
GET/templatesList templates
POST/templatesCreate template
PATCH/templates/:idUpdate template
DELETE/templates/:idDelete template
GET/volumesList volumes
POST/volumesCreate volume (sizeGb 10–4000)
GET/volumes/:idGet volume
PATCH/volumes/:idUpdate volume
GET/endpointsList endpoints
POST/endpointsCreate endpoint
GET/endpoints/:idGet endpoint (includes workers when available)
PATCH/endpoints/:idUpdate endpoint workers / idle timeout
DELETE/endpoints/:idDelete endpoint
POST/endpoints/:id/runAsync serverless job
POST/endpoints/:id/runsyncSync serverless job
GET/endpoints/:id/healthEndpoint health
GET/endpoints/:id/jobs/:jobIdGet async job status
POST/endpoints/:id/jobs/:jobId/cancelCancel async job
GET/registriesList registry credentials
POST/registriesCreate 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.

Open API Keys