> ## Documentation Index
> Fetch the complete documentation index at: https://docs.synq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Monitors predictions

### monitors\_predictions

Find full example [here](https://github.com/getsynq/api/tree/main/examples/golang/monitors_predictions)

#### main.go

```go theme={null}
package main

import (
	"context"
	"crypto/tls"
	"flag"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	predictionsv1grpc "buf.build/gen/go/getsynq/api/grpc/go/synq/monitors/predictions/v1/predictionsv1grpc"
	predictionsv1 "buf.build/gen/go/getsynq/api/protocolbuffers/go/synq/monitors/predictions/v1"
	"golang.org/x/oauth2/clientcredentials"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/credentials/oauth"
	"google.golang.org/protobuf/types/known/timestamppb"
)

// Fetches monitor predictions via GetMonitorPredictions.
//
// Flags win over env vars of the same name.
//
// Required:
//
//	-client-id / SYNQ_CLIENT_ID, -client-secret / SYNQ_CLIENT_SECRET  — SCOPE_MONITORS_READ
//	-synq-path / SYNQ_PATH  — monitor synq path, e.g. monitor::ch-prod::default::runs::volume
//
// Optional:
//
//	-endpoint / API_ENDPOINT     — developer.synq.io (EU, default), api.us.synq.io (US), api.au.synq.io (AU)
//	-metrics-version / METRICS_VERSION  — omit to use the monitor's current version
//	-from / FROM, -to / TO       — RFC3339; omit for server defaults (to=now, from=to-30d; max lookback 30d)
//	-segments / SEGMENTS         — comma-separated; empty string is the unsegmented series.
//	                               omit to return all segments
func main() {
	clientID := flag.String("client-id", os.Getenv("SYNQ_CLIENT_ID"), "OAuth client id (SYNQ_CLIENT_ID)")
	clientSecret := flag.String("client-secret", os.Getenv("SYNQ_CLIENT_SECRET"), "OAuth client secret (SYNQ_CLIENT_SECRET)")
	synqPath := flag.String("synq-path", envOr("SYNQ_PATH", os.Getenv("MONITOR_PATH")), "monitor synq path (SYNQ_PATH / MONITOR_PATH)")
	host := flag.String("endpoint", envOr("API_ENDPOINT", "developer.synq.io"), "API host without port (API_ENDPOINT)")
	metricsVersionRaw := flag.String("metrics-version", os.Getenv("METRICS_VERSION"), "metrics version (METRICS_VERSION); omit for current")
	fromRaw := flag.String("from", os.Getenv("FROM"), "RFC3339 start (FROM); omit for server default")
	toRaw := flag.String("to", os.Getenv("TO"), "RFC3339 end (TO); omit for now")
	segmentsRaw := flag.String("segments", os.Getenv("SEGMENTS"), "comma-separated segments (SEGMENTS); omit for all")
	flag.Parse()

	segmentsSet := envSet("SEGMENTS")
	flag.Visit(func(f *flag.Flag) {
		if f.Name == "segments" {
			segmentsSet = true
		}
	})

	if *clientID == "" || *clientSecret == "" {
		panic("-client-id / SYNQ_CLIENT_ID and -client-secret / SYNQ_CLIENT_SECRET must be set")
	}
	if *synqPath == "" {
		panic("-synq-path / SYNQ_PATH must be set (e.g. monitor::<asset_path>::volume)")
	}

	req := &predictionsv1.GetMonitorPredictionsRequest{
		SynqPath: *synqPath,
	}

	if *metricsVersionRaw != "" {
		n, err := strconv.ParseInt(*metricsVersionRaw, 10, 32)
		if err != nil {
			panic(fmt.Sprintf("-metrics-version: %v", err))
		}
		v := int32(n)
		req.MetricsVersion = &v
	}

	if *fromRaw != "" {
		t, err := time.Parse(time.RFC3339, *fromRaw)
		if err != nil {
			panic(fmt.Sprintf("-from: %v", err))
		}
		req.From = timestamppb.New(t)
	}
	if *toRaw != "" {
		t, err := time.Parse(time.RFC3339, *toRaw)
		if err != nil {
			panic(fmt.Sprintf("-to: %v", err))
		}
		req.To = timestamppb.New(t)
	}

	if segmentsSet {
		req.Segments = strings.Split(*segmentsRaw, ",")
	}

	ctx := context.Background()
	apiURL := fmt.Sprintf("%s:443", *host)
	tokenURL := fmt.Sprintf("https://%s/oauth2/token", *host)

	oauthTokenSource := oauth.TokenSource{TokenSource: (&clientcredentials.Config{
		ClientID:     *clientID,
		ClientSecret: *clientSecret,
		TokenURL:     tokenURL,
	}).TokenSource(ctx)}

	conn, err := grpc.NewClient(apiURL,
		grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
		grpc.WithPerRPCCredentials(oauthTokenSource),
		grpc.WithAuthority(*host),
	)
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	fmt.Printf("Connected to %s\n", *host)

	resp, err := predictionsv1grpc.NewMonitorPredictionsServiceClient(conn).GetMonitorPredictions(ctx, req)
	if err != nil {
		panic(err)
	}

	versionLabel := "current"
	if req.MetricsVersion != nil {
		versionLabel = strconv.Itoa(int(*req.MetricsVersion))
	}
	fromLabel, toLabel := "default", "default"
	if req.From != nil {
		fromLabel = req.From.AsTime().UTC().Format(time.RFC3339)
	}
	if req.To != nil {
		toLabel = req.To.AsTime().UTC().Format(time.RFC3339)
	}

	fmt.Printf("synq_path=%s version=%s from=%s to=%s predictions=%d\n",
		*synqPath, versionLabel, fromLabel, toLabel, len(resp.Predictions))

	for i, p := range resp.Predictions {
		seg := "<nil>"
		if p.Segment != nil {
			seg = fmt.Sprintf("%q", *p.Segment)
		}
		vu, vl := "nil", "nil"
		if p.UpperBound != nil {
			vu = fmt.Sprintf("%.6g", p.UpperBound.Value)
		}
		if p.LowerBound != nil {
			vl = fmt.Sprintf("%.6g", p.LowerBound.Value)
		}
		st := "nil"
		if p.Stddev != nil {
			st = fmt.Sprintf("%.6g", *p.Stddev)
		}
		pattern := ""
		if p.Pattern != nil {
			pattern = *p.Pattern
		}
		fmt.Printf("%d  t=%s  m=%s  s=%s  v=%.6g  e=%.6g  st=%s  vl=%s  vu=%s  p=%s  f=%s  corrected=%v\n",
			i, p.ScheduledAt.AsTime().UTC().Format(time.RFC3339), p.MetricId, seg, p.Value, p.Expected, st, vl, vu, pattern, p.Field, p.IsCorrected)
	}
}

func envOr(key, fallback string) string {
	if v := os.Getenv(key); v != "" {
		return v
	}
	return fallback
}

func envSet(key string) bool {
	_, ok := os.LookupEnv(key)
	return ok
}
```
