API reference · Using the API / Requests and responses
Requests and responses
Asynchronous changes, idempotency, pagination, filtering, rate limits and the shape of every response.
Requests
-
Base URL: every path in this reference starts here.
HTTP https://api.pantechdynamics.com/public/v1 -
Bodies are JSON. Send
Content-Type: application/json. Unknown fields are rejected with422and the field's name, so a typo is caught instead of ignored. -
Ids look like
vm_06gk3x9tq7m2v8r4y1c5d0zp8w. The prefix says what it is:vm_instance,ord_order,op_operation,net_VPC,snet_subnet,aclr_firewall rule,pip_public IP,pfr_port forwarding rule,sg_security group,vol_volume,snap_snapshot,sshk_SSH key,sshg_SSH access grant. -
Times are RFC 3339 in UTC:
2026-10-03T09:30:00Z. -
Money is an integer in minor units with its currency:
{ "amount_minor": 1059200, "currency": "NGN" }is ₦10,592.00. USD amounts are cents.
Responses
-
One resource comes back as the object itself.
-
A list comes back in an envelope:
JSON { "data": [ … ], "next_cursor": "eyJhdCI6…" } -
No value is
null, never an empty string or a zero that could be mistaken for a real one. A field is always present. -
Every response carries
X-Request-Id. Quote it if you contact support.
Asynchronous changes
Creating, changing and deleting infrastructure takes time, so writes do not wait for it. A write returns 202 Accepted with the operation doing the work:
{
"operation_id": "op_06gk3x9tq7m2v8r4y1c5d0zp8w",
"resource_id": "vol_06gk3x9tq7m2v8r4y1c5d0zp8w",
"status": "submitting"
}The Location header points at it. Poll GET /operations/{operation_id} every few seconds until status is succeeded or failed, then read the resource. Or open GET /operations/{operation_id}/events: a Server-Sent Events stream that sends the operation each time it changes and closes when it finishes.
# Wait for an operation to finish. Needs jq.
while :; do
status=$(curl -s "https://api.pantechdynamics.com/public/v1/operations/$OP" \
-H "Authorization: Bearer $PANTECH_API_KEY" | jq -r .status)
case "$status" in succeeded|failed) break ;; esac
sleep 3
done
echo "$status"A new instance is different. It is paid for before it is built, so POST /instances returns an order. Poll the order until its status is provisioned; the instance is then ready.
A resource also reports its own progress: desired_state is what you asked for, observed_state what the platform last saw. When they match, the change has landed.
Idempotency
Every POST, PUT and DELETE needs an Idempotency-Key header: a unique value per change, such as a UUID.
Idempotency-Key: 5f1c2a34-8d4e-4b8e-9a51-0e2f6f7b9c10If a request fails on the network, or you are not sure it arrived, send it again with the same key. You get the original result, and the change is never made twice, so you will not create two instances or pay twice. Using the same key for a different request returns 409 IDEMPOTENCY_CONFLICT.
Make a new key for each new change, and keep it until that change is done.
Pagination
Lists return up to limit items (default 50, at most 100) and a next_cursor. Pass it back as cursor for the next page; it is null on the last page.
$ curl "https://api.pantechdynamics.com/public/v1/instances?limit=100&cursor=$NEXT" \ -H "Authorization: Bearer $PANTECH_API_KEY"A cursor belongs to the filters it was made with. Changing the filters and keeping the cursor returns 400 INVALID_CURSOR; start again without one.
Filtering
The lists of instances, VPCs, subnets, security groups, volumes and snapshots take three filters:
q: names containing this text.desired_state: e.g.runningorstoppedfor instances,presentfor everything else.observed_state: e.g.running,stopped,failedfor instances,activeorfailedfor everything else.
$ curl "https://api.pantechdynamics.com/public/v1/instances?observed_state=failed" \ -H "Authorization: Bearer $PANTECH_API_KEY"Rate limits
Each key has a budget of requests per minute. Every response to a request with a valid key says where you stand:
| Header | Meaning |
|---|---|
RateLimit-Limit | Requests allowed in each window |
RateLimit-Remaining | Requests left in this window |
RateLimit-Reset | Seconds until the window resets |
Past the limit you get 429 RATE_LIMITED with Retry-After in seconds. Wait that long, then retry with the same Idempotency-Key. Slow down before you get there: when RateLimit-Remaining is low, wait for RateLimit-Reset.
Retries
| Response | Retry? |
|---|---|
| Network error, timeout | Yes, with the same Idempotency-Key |
429 | Yes, after Retry-After seconds |
503, 500 | Yes, with backoff (1 s, 2 s, 4 s…), same Idempotency-Key |
409 CONCURRENT_UPDATE | Yes, same request and key |
Any other 4xx | No, fix the request first |
Versioning
The version is in the path: /public/v1. Within v1, changes only add things: new endpoints, new optional request fields and new response fields. Write clients that ignore fields they do not know. Anything that would break a client comes as /public/v2, announced in the changelog with time to move.