API reference · Using the API / Examples
Examples
Complete scripts for the common jobs, including a standard server, a web server in a VPC, nightly snapshots and a clean teardown.
Each script runs as it is with bash, curl and jq, and a key in PANTECH_API_KEY (a write key, for anything that creates). Start every script with these helpers:
#!/usr/bin/env bash
set -euo pipefail
API="https://api.pantechdynamics.com/public/v1"
# pt METHOD PATH [JSON]: call the API. Writes get a fresh Idempotency-Key.
pt() {
local method=$1 path=$2 body=${3:-}
local args=(-sS --fail-with-body -X "$method" "$API$path" -H "Authorization: Bearer $PANTECH_API_KEY")
if [ "$method" != GET ]; then args+=(-H "Idempotency-Key: $(uuidgen)"); fi
if [ -n "$body" ]; then args+=(-H "Content-Type: application/json" -d "$body"); fi
curl "${args[@]}"
}
# wait_op OPERATION_ID: wait for an operation to finish; fail if it failed.
wait_op() {
local status
while :; do
status=$(pt GET "/operations/$1" | jq -r .status)
case "$status" in
succeeded) return 0 ;;
failed) pt GET "/operations/$1" | jq .failure >&2; return 1 ;;
esac
sleep 3
done
}A standard server
A VPS with its own public IPv4 address, reachable over SSH with your key.
source pantech.sh
# Your public key, registered once.
KEY_ID=$(pt POST /ssh-keys "$(jq -n --arg k "$(cat ~/.ssh/id_ed25519.pub)" '{name: "laptop", public_key: $k}')" | jq -r .id)
# Order the instance. It is paid for, then built.
ORDER=$(pt POST /instances "$(jq -n --arg key "$KEY_ID" \
'{name: "app-1", plan_slug: "starter", image_slug: "ubuntu-24-04", ssh_key_id: $key}')")
ORDER_ID=$(echo "$ORDER" | jq -r .order_id)
VM=$(echo "$ORDER" | jq -r .instance_id)
# Wait for the order to finish.
while :; do
STATUS=$(pt GET "/instance-orders/$ORDER_ID" | jq -r .status)
case "$STATUS" in
provisioned) break ;;
failed|payment_failed) echo "order $STATUS" >&2; exit 1 ;;
esac
sleep 5
done
IP=$(pt GET "/instances/$VM" | jq -r .public_ipv4)
echo "ssh ubuntu@$IP" # the image's user: ubuntu on Ubuntu, debian on DebianThe default security group drops all inbound traffic. Open SSH to your own address with a security group rule, or ask for temporary SSH access.
A web server in a VPC
A private network with one subnet, a firewall that lets HTTPS in, an instance in the subnet, and a public IP mapped to it.
source pantech.sh
# 1. The VPC and a subnet. Wait for each before using it.
OP=$(pt POST /networks '{"name": "prod", "cidr": "10.0.0.0/16", "region": "af-abj"}')
NET=$(echo "$OP" | jq -r .resource_id); wait_op "$(echo "$OP" | jq -r .operation_id)"
OP=$(pt POST "/networks/$NET/subnets" '{"name": "web", "cidr": "10.0.1.0/24"}')
SUBNET=$(echo "$OP" | jq -r .resource_id); wait_op "$(echo "$OP" | jq -r .operation_id)"
# 2. Let HTTPS in. Lower numbers are checked first.
OP=$(pt POST "/subnets/$SUBNET/firewall-rules" \
'{"number": 100, "protocol": "tcp", "port_start": 443, "cidr": "0.0.0.0/0"}')
wait_op "$(echo "$OP" | jq -r .operation_id)"
# 3. An instance in the subnet.
ORDER_ID=$(pt POST /instances "$(jq -n --arg s "$SUBNET" --arg k "$KEY_ID" \
'{name: "web-1", plan_slug: "developer", image_slug: "ubuntu-24-04", subnet_id: $s, ssh_key_id: $k}')" | jq -r .order_id)
until [ "$(pt GET "/instance-orders/$ORDER_ID" | jq -r .status)" = provisioned ]; do sleep 5; done
VM=$(pt GET "/instance-orders/$ORDER_ID" | jq -r .instance_id)
# 4. A public IP mapped to it.
OP=$(pt POST /public-ips "$(jq -n --arg n "$NET" --arg vm "$VM" '{network_id: $n, instance_id: $vm, purpose: "static_nat"}')")
PIP=$(echo "$OP" | jq -r .resource_id); wait_op "$(echo "$OP" | jq -r .operation_id)"
echo "https://$(pt GET "/public-ips/$PIP" | jq -r .address)"Nightly snapshots
Let the platform do it: a schedule takes a snapshot every day and keeps the newest seven.
source pantech.sh
pt PUT "/instances/$VM/snapshot-schedule" '{"frequency": "daily", "retention_count": 7}'Or take one yourself before a risky change, and roll back if it goes wrong:
source pantech.sh
OP=$(pt POST "/instances/$VM/snapshots" '{"name": "pre-upgrade"}')
SNAP=$(echo "$OP" | jq -r .resource_id); wait_op "$(echo "$OP" | jq -r .operation_id)"
# … the upgrade went wrong. Everything written since the snapshot is lost:
NAME=$(pt GET "/instances/$VM" | jq -r .name)
pt POST "/instances/$VM/restore" "$(jq -n --arg s "$SNAP" --arg n "$NAME" '{snapshot_id: $s, confirm_name: $n}')"Find what costs the most
A read key is enough.
source pantech.sh
pt GET "/usage/by-resource?period=month" \
| jq -r '.data | sort_by(-.cost_minor) | .[:5][] | "\(.cost_minor / 100)\t\(.resource_kind)\t\(.resource_name)"'Tear everything down
Delete in the reverse of creation: what uses a thing goes before the thing.
source pantech.sh
wait_op "$(pt DELETE "/public-ips/$PIP" | jq -r .operation_id)"
wait_op "$(pt DELETE "/instances/$VM" | jq -r .operation_id)"
wait_op "$(pt DELETE "/subnets/$SUBNET" | jq -r .operation_id)"
wait_op "$(pt DELETE "/networks/$NET" | jq -r .operation_id)"Deleting an instance keeps a safety snapshot of its root disk for 24 hours. Volumes attached to it are detached, not deleted, so delete them separately.
Walk every page of a list
source pantech.sh
CURSOR=""
while :; do
PAGE=$(pt GET "/instances?limit=100${CURSOR:+&cursor=$CURSOR}")
echo "$PAGE" | jq -r '.data[] | "\(.id)\t\(.observed_state)\t\(.name)"'
CURSOR=$(echo "$PAGE" | jq -r '.next_cursor // empty')
[ -z "$CURSOR" ] && break
done