Skip to main content

Documentation Index

Fetch the complete documentation index at: https://developer.alterscope.org/llms.txt

Use this file to discover all available pages before exploring further.

There is no first-party Go SDK yet. The API is plain HTTPS + JSON, so calling it from Go takes a small net/http wrapper. This walks a Go 1.22+ developer through install, Bearer auth, one REST call with the agentic envelope, a WebSocket stream, and a retry helper. Expect about five minutes end-to-end. The full program is checked in at sdks/go/examples/quickstart/main.go — what follows is the same code, broken into steps.

1. Initialize the module

mkdir alterscope-quickstart && cd alterscope-quickstart
go mod init example.com/alterscope-quickstart
go get github.com/gorilla/websocket@v1.5.1
Go 1.22+ is required.

2. Define the response types

The agentic envelope is the same on every v2 endpoint — define it once and embed it in any specific response struct.
// AgenticFreshness mirrors response.meta._agentic.freshness.
// status is one of "realtime" | "fresh" | "stale" | "unknown".
type AgenticFreshness struct {
    ComputedAt           string  `json:"computed_at"`
    AgeSeconds           float64 `json:"age_seconds"`
    UpdateCadenceSeconds float64 `json:"update_cadence_seconds"`
    Status               string  `json:"status"`
    ShouldRetry          bool    `json:"should_retry"`
}

type AgenticQualityGate struct {
    Verdict        string   `json:"verdict"` // "pass" | "warn" | "fail"
    DegradedFields []string `json:"degraded_fields,omitempty"`
}

type AgenticMeta struct {
    SchemaVersion string              `json:"schema_version"`
    Confidence    float64             `json:"confidence"`
    Freshness     *AgenticFreshness   `json:"freshness,omitempty"`
    QualityGate   *AgenticQualityGate `json:"quality_gate,omitempty"`
}

type OracleClassificationResponse struct {
    Data json.RawMessage `json:"data"`
    Meta struct {
        Agentic *AgenticMeta `json:"_agentic,omitempty"`
    } `json:"meta"`
}

3. Build the client

The two things every call needs: a Bearer header and a retry loop.
const (
    defaultBaseURL = "https://dev.alterscope.org/api"
    maxRetries     = 3
    requestTimeout = 30 * time.Second
)

type Client struct {
    BaseURL    string
    APIKey     string
    HTTPClient *http.Client
}

func New() (*Client, error) {
    apiKey := os.Getenv("ALTERSCOPE_API_KEY")
    if apiKey == "" {
        return nil, errors.New("ALTERSCOPE_API_KEY env var is required")
    }
    baseURL := os.Getenv("ALTERSCOPE_BASE_URL")
    if baseURL == "" {
        baseURL = defaultBaseURL
    }
    return &Client{
        BaseURL:    baseURL,
        APIKey:     apiKey,
        HTTPClient: &http.Client{Timeout: requestTimeout},
    }, nil
}

// Do issues an authenticated request with retry on 429/5xx.
// Honors Retry-After on 429; otherwise exponential backoff capped at 60s.
func (c *Client) Do(ctx context.Context, method, path string) (*http.Response, error) {
    var lastErr error
    for attempt := 0; attempt < maxRetries; attempt++ {
        req, err := http.NewRequestWithContext(ctx, method, c.BaseURL+path, nil)
        if err != nil {
            return nil, err
        }
        req.Header.Set("Authorization", "Bearer "+c.APIKey)
        req.Header.Set("Accept", "application/json")

        resp, err := c.HTTPClient.Do(req)
        if err != nil {
            lastErr = err
            time.Sleep(backoff(attempt, nil))
            continue
        }
        if resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode >= 500 {
            delay := backoff(attempt, resp)
            _ = resp.Body.Close()
            if attempt == maxRetries-1 {
                return nil, fmt.Errorf("alterscope: %s after %d attempts", resp.Status, maxRetries)
            }
            time.Sleep(delay)
            continue
        }
        return resp, nil
    }
    return nil, fmt.Errorf("alterscope: request failed: %w", lastErr)
}

func backoff(attempt int, resp *http.Response) time.Duration {
    if resp != nil && resp.StatusCode == http.StatusTooManyRequests {
        if ra := resp.Header.Get("Retry-After"); ra != "" {
            if secs, err := strconv.Atoi(ra); err == nil {
                return time.Duration(secs) * time.Second
            }
        }
    }
    d := time.Duration(math.Pow(2, float64(attempt))) * time.Second
    if d > 60*time.Second {
        d = 60 * time.Second
    }
    return d
}

4. Your first call

The cheapest “is it working?” call is an oracle classification — the response always includes the agentic envelope, so you can confirm auth + parsing are correct in one round-trip.
func (c *Client) ClassifyOracle(ctx context.Context, marketID string) (*OracleClassificationResponse, error) {
    resp, err := c.Do(ctx, http.MethodGet, "/v2/oracle/"+url.PathEscape(marketID)+"/classification")
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return nil, fmt.Errorf("read body: %w", err)
    }
    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("alterscope: %s: %s", resp.Status, string(body))
    }

    var out OracleClassificationResponse
    if err := json.Unmarshal(body, &out); err != nil {
        return nil, fmt.Errorf("decode response: %w", err)
    }
    return &out, nil
}

func main() {
    client, err := New()
    if err != nil {
        log.Fatal(err)
    }

    ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
    defer cancel()

    resp, err := client.ClassifyOracle(ctx,
        "0x3a85e619b69e7a0c4dd158d61eb41e95b6f25cf3aa3c0f1e5f6e7d8c9b0a1d2f")
    if err != nil {
        log.Fatal(err)
    }

    if a := resp.Meta.Agentic; a != nil {
        fmt.Printf("schema_version: %s\n", a.SchemaVersion)
        fmt.Printf("confidence: %.2f\n", a.Confidence)
        if a.Freshness != nil {
            fmt.Printf("freshness: %s (age=%.0fs)\n", a.Freshness.Status, a.Freshness.AgeSeconds)
        }
        if a.QualityGate != nil {
            fmt.Printf("quality: %s\n", a.QualityGate.Verdict)
        }
    }
}
Expected first-call output (real shape — values vary per market and run):
schema_version: 2.0.0
confidence: 0.94
freshness: fresh (age=42s)
quality: pass
Freshness.Status is one of "realtime", "fresh", "stale", or "unknown". Treat stale as a soft-error in your agent loop — re-fetch before acting on the data.

5. Stream events

Risk events are pushed over a WebSocket. Authenticate with the same API key as a ?token= query parameter.
func streamOracleFailures(ctx context.Context, apiKey string) error {
    dialURL := "wss://dev.alterscope.org/ws?token=" + url.QueryEscape(apiKey)
    ws, _, err := websocket.DefaultDialer.DialContext(ctx, dialURL, nil)
    if err != nil {
        return fmt.Errorf("dial ws: %w", err)
    }
    defer ws.Close()

    if err := ws.WriteJSON(map[string]any{
        "type":        "subscribe",
        "channel":     "events",
        "event_types": []string{"oracle_failure", "exploit"},
    }); err != nil {
        return fmt.Errorf("write subscribe: %w", err)
    }

    for {
        var msg map[string]any
        if err := ws.ReadJSON(&msg); err != nil {
            return fmt.Errorf("read ws: %w", err)
        }
        if msg["type"] == "market_event" && msg["event_type"] == "oracle_failure" {
            fmt.Printf("oracle failure on %v: %v\n", msg["protocol"], msg["title"])
        }
    }
}
Channel / event-type reference: WebSockets API.

6. Receive a webhook

Webhooks are plain HTTPS POSTs with a JSON body. Until signature verification ships, restrict the receiver to source IPs you control.
http.HandleFunc("/alterscope-webhook", func(w http.ResponseWriter, r *http.Request) {
    body, err := io.ReadAll(r.Body)
    if err != nil {
        http.Error(w, "read body", http.StatusBadRequest)
        return
    }
    var event struct {
        Type string          `json:"type"`
        Data json.RawMessage `json:"data"`
    }
    if err := json.Unmarshal(body, &event); err != nil {
        http.Error(w, "bad json", http.StatusBadRequest)
        return
    }
    log.Printf("%s: %s", event.Type, event.Data)
    w.WriteHeader(http.StatusNoContent)
})
log.Fatal(http.ListenAndServe(":8080", nil))
Signature verification ships with the GA SDK release. Until then, restrict the webhook URL to source IPs you control and rotate the secret if it leaks. Track via the changelog.

Run it

go mod tidy
ALTERSCOPE_API_KEY=sk_live_... go run main.go
The full single-file version is at sdks/go/examples/quickstart/main.go.

Next steps

Pricing & quotas

Free, Pro, Pro+, and Enterprise tiers — what each gets you.

Changelog

What changed in the API and SDKs, week by week.

API Reference

Every operation with try-it-out.

Recipes

End-to-end flows: depeg detection, scoring, treasury automation.