Requirements

Go 1.21+ — only the standard library is used (net/http, encoding/json, net/url). If you'd rather use a typed HTTP client wrapper, the openly published openapi.json drops cleanly into oapi-codegen.

Sanity-check the API first

Before wiring up Go, confirm the endpoint responds. This is the exact call the Go program below makes:

curl "https://api.provable.io/api/ints?clientSeed=my-app-user-42&count=5&min=1&max=100"

Set up a module

mkdir provable-quickstart && cd provable-quickstart
go mod init example.com/provable-quickstart
touch main.go

Generate an outcome

The shape returned by /api/ints matches this struct verbatim — only declare the fields you care about; the JSON decoder will skip the rest.

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "net/url"
    "os"
    "time"
)

const base = "https://api.provable.io"

type IntOutcome struct {
    Outcome    []int  `json:"outcome"`
    ClientSeed string `json:"clientSeed"`
    ServerHash string `json:"serverHash"`
    Cursor     int    `json:"cursor"`
    Nonce      int    `json:"nonce"`
    ShortID    string `json:"shortId"`
    Permalink  string `json:"permalink"`
}

var client = &http.Client{Timeout: 10 * time.Second}

func getInts(clientSeed string, count, min, max int) (*IntOutcome, error) {
    q := url.Values{}
    q.Set("clientSeed", clientSeed)
    q.Set("count", fmt.Sprint(count))
    q.Set("min", fmt.Sprint(min))
    q.Set("max", fmt.Sprint(max))

    req, err := http.NewRequest(http.MethodGet, base+"/api/ints?"+q.Encode(), nil)
    if err != nil {
        return nil, err
    }
    if key := os.Getenv("PROVABLE_API_KEY"); key != "" {
        req.Header.Set("x-api-key", key) // optional, attributes usage to your account
    }

    res, err := client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("ints: %w", err)
    }
    defer res.Body.Close()
    if res.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("ints: unexpected status %s", res.Status)
    }

    var out IntOutcome
    if err := json.NewDecoder(res.Body).Decode(&out); err != nil {
        return nil, fmt.Errorf("ints: decode: %w", err)
    }
    return &out, nil
}

func main() {
    o, err := getInts("my-app-user-42", 5, 1, 100)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("outcome:    ", o.Outcome)
    fmt.Println("serverHash: ", o.ServerHash)
    fmt.Println("permalink:  ", o.Permalink)
}

Generate random floats

Same shape, with Outcome typed as []float64. Hit /api/floats and drop min/max from the query string.

type FloatOutcome struct {
    Outcome    []float64 `json:"outcome"`
    ClientSeed string    `json:"clientSeed"`
    ServerHash string    `json:"serverHash"`
    Cursor     int       `json:"cursor"`
    Nonce      int       `json:"nonce"`
}

Verify it later

/api/verifyServerHash can return either the literal JSON true (fast-path match against the live seed state) or a richer VerifyResult object. Decode into json.RawMessage and branch:

type VerifyResult struct {
    Verified bool `json:"verified"`
}

func verify(clientSeed, serverHash string) (bool, error) {
    q := url.Values{}
    q.Set("clientSeed", clientSeed)
    q.Set("serverHash", serverHash)

    res, err := client.Get(base + "/api/verifyServerHash?" + q.Encode())
    if err != nil {
        return false, err
    }
    defer res.Body.Close()

    var raw json.RawMessage
    if err := json.NewDecoder(res.Body).Decode(&raw); err != nil {
        return false, err
    }
    // Fast path: literal "true".
    var b bool
    if err := json.Unmarshal(raw, &b); err == nil {
        return b, nil
    }
    // Otherwise it's the richer object.
    var v VerifyResult
    if err := json.Unmarshal(raw, &v); err != nil {
        return false, err
    }
    return v.Verified, nil
}

Idiomatic error handling

Next steps