Documentation

esc
    SDKs / Go

    Go SDK

    Install pantech-go, create a client, make a machine and wait for it, and handle errors, pages and retries.

    Go
    c, _ := pantech.NewClient()
    me, _ := c.Me.Get(ctx)
    fmt.Println(me.OrganizationID, me.APIKey.Scopes)

    pantech-go is the official Go library. It needs Go 1.23 or later and has no dependencies outside the standard library.

    Install

    Terminal
    $ go get github.com/Pantech-Dynamics/pantech-go
    Go
    import pantech "github.com/Pantech-Dynamics/pantech-go"

    The package is called pantech.

    Authenticate

    Make an API key and put it in PANTECH_API_KEY. NewClient reads it.

    Terminal
    $ export PANTECH_API_KEY=PAN_…

    Or pass options:

    Go
    c, err := pantech.NewClient(
    	pantech.WithAPIKey(os.Getenv("MY_PANTECH_KEY")),
    	pantech.WithBaseURL("https://api.pantechdynamics.com/public/v1"), // or PANTECH_API_URL
    	pantech.WithMaxRetries(5),                                        // default 3; 0 turns retries off
    )

    The other options are WithHTTPClient(h) (its Timeout bounds each attempt; the default is 90 seconds), WithUserAgent(ua) and WithHeader(key, value).

    c.Me.Get(ctx) describes the key, and c.Me.Revoke(ctx) revokes it (DELETE /me).

    Create a machine and wait for it

    main.go
    package main
    
    import (
    	"context"
    	"fmt"
    	"log"
    
    	pantech "github.com/Pantech-Dynamics/pantech-go"
    )
    
    func main() {
    	ctx := context.Background()
    	c, err := pantech.NewClient()
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	order, err := c.Instances.Create(ctx, pantech.CreateInstanceRequest{
    		Name:      "web-01",
    		PlanSlug:  "individual",
    		ImageSlug: "ubuntu-24-04",
    		SSHKeyID:  "sshk_…",
    	})
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	// Creating a machine places an order: payment, then provisioning.
    	// WaitForOrder returns when the order is provisioned, or an
    	// *pantech.OrderError if it ends payment_failed or failed.
    	provisioned, err := c.Instances.WaitForOrder(ctx, order.OrderID, nil)
    	if err != nil {
    		log.Fatal(err)
    	}
    	if provisioned.OperationID != nil {
    		if _, err := c.Operations.Wait(ctx, *provisioned.OperationID, nil); err != nil {
    			log.Fatal(err)
    		}
    	}
    
    	instance, err := c.Instances.Get(ctx, order.InstanceID)
    	if err != nil {
    		log.Fatal(err)
    	}
    	fmt.Println(instance.Name, instance.ObservedState)
    	if instance.PublicIPv4 != nil {
    		fmt.Println(*instance.PublicIPv4)
    	}
    }

    Optional request fields are pointers, and a nil field is not sent. pantech.Ptr sets one inline, for example Tags: pantech.Ptr(map[string]string{"env": "prod"}). c.Databases.WaitForOrder does the same for a new database.

    Read real slugs from the catalogue first: c.Catalog lists regions, plans, images, disk offerings, database engines and Kubernetes versions, and estimates prices.

    Services

    Each part of the API is a field on the client:

    c.Instances, c.Networks, c.Subnets, c.FirewallRules, c.PublicIPs, c.PortForwardingRules, c.SecurityGroups, c.LoadBalancers, c.Volumes, c.Snapshots, c.Databases, c.Kubernetes, c.SSHKeys, c.Webhooks, c.Operations, c.Catalog, c.Billing and c.Me.

    Errors

    Every API error is a *pantech.Error, with Status, Code, Title, Detail, RequestID, Errors (the invalid fields of a 422) and RetryAfter. Check the code with pantech.IsCode, and get the error with pantech.AsError (or errors.As):

    Go
    _, err := c.Instances.Get(ctx, "vm_…")
    if pantech.IsCode(err, "RESOURCE_NOT_FOUND") {
    	fmt.Println("no such machine")
    }
    
    if apiErr, ok := pantech.AsError(err); ok {
    	log.Printf("%s: %s (request %s)", apiErr.Code, apiErr.Detail, apiErr.RequestID)
    }

    Act on the code, never the wording. Every code.

    Pages

    A paginated list has two methods. List returns one *pantech.Page[T] (Data, NextCursor, HasMore()). All iterates over every item, following next_cursor for you, as a Go iter.Seq2:

    Go
    page, err := c.Instances.List(ctx, &pantech.ListParams{Limit: 50, Q: "web"})
    if err != nil {
    	log.Fatal(err)
    }
    fmt.Println(len(page.Data), page.HasMore())
    
    for instance, err := range c.Instances.All(ctx, nil) {
    	if err != nil {
    		log.Fatal(err)
    	}
    	fmt.Println(instance.Name)
    }

    Lists the API returns in one page, such as regions, plans, images and SSH keys, return a plain slice.

    Operations

    Most changes return an operation. Wait for it before you rely on the result:

    Go
    accepted, err := c.Instances.Stop(ctx, "vm_…")
    if err != nil {
    	log.Fatal(err)
    }
    op, err := c.Operations.Wait(ctx, accepted.OperationID, &pantech.WaitOptions{Timeout: 10 * time.Minute})
    var failed *pantech.OperationError
    if errors.As(err, &failed) {
    	fmt.Println(failed.Operation.Failure.Code, failed.Operation.Failure.Reason)
    } else if err == nil {
    	fmt.Println(op.Status)
    }

    Wait polls GET /operations/{id} (every 2 seconds by default; pass nil for the defaults) until the status is succeeded or failed, and returns a *pantech.OperationError when it failed. c.Operations.Stream(ctx, id) follows it over Server-Sent Events instead.

    Idempotency and retries

    Every POST, PUT, PATCH and DELETE gets an Idempotency-Key automatically (a random UUID). The same key is sent on every retry, so the change happens once. To make a change safe to repeat across runs, choose the key yourself:

    Go
    order, err := c.Instances.Create(ctx, req, pantech.WithIdempotencyKey("deploy-42-web-1"))

    The client retries network errors, 429, 500, 502, 503 and 504 up to 3 times, with a growing, random wait (0.5 s, 1 s, 2 s… up to 8 s), and honours Retry-After (up to 60 seconds). Calls the API does not replay are retried only on 429, which the API refuses before acting: creating or deleting an SSH key, opening an instance console, and testing, deleting, rotating the secret of or redelivering a webhook. The list. Cancelling the context stops a call and its retries at once.

    Calling any endpoint

    Do calls a path directly, with the same auth, retries and errors:

    Go
    var out map[string]any
    err := c.Do(ctx, "GET", "/instances", url.Values{"q": {"web"}}, nil, &out)

    Every typed method, and Do, takes per-request options: pantech.WithIdempotencyKey(key) to choose the key yourself, pantech.WithRequestHeader(key, value), and pantech.WithNoRetry() to send a call once.

    Webhooks

    pantech.VerifyWebhook(secret, header, body, tolerance) checks a delivery's signature and returns an error if it fails. pantech.ParseWebhook checks it and decodes the event in one call. Verify webhooks with the SDKs.

    Was this page helpful?

    Updated