Skip to main content

Go reference

Drop-in Go module on net/http. No dependencies. Tested against Go 1.22.

package main

import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)

const (
base = "https://api.flowbeacon.ai/api/public/v1"
apiPathPrefix = "/api/public/v1"
)

type envelope struct {
OK bool `json:"ok"`
Data json.RawMessage `json:"data"`
Error string `json:"error,omitempty"`
Detail string `json:"detail,omitempty"`
}

func signRequest(apiKey, method, path string, rawBody []byte) string {
ts := strconv.FormatInt(time.Now().Unix(), 10)
msg := ts + "." + method + "." + path + "." + string(rawBody)
mac := hmac.New(sha256.New, []byte(apiKey))
mac.Write([]byte(msg))
return "t=" + ts + ",v1=" + hex.EncodeToString(mac.Sum(nil))
}

func call(method, path string, body any) (json.RawMessage, error) {
var raw []byte
var buf io.Reader
if body != nil {
raw, _ = json.Marshal(body)
buf = bytes.NewReader(raw)
}
req, _ := http.NewRequest(method, base+path, buf)
apiKey := os.Getenv("FLOWBEACON_API_KEY")
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
req.Header.Set("X-FB-Signature", signRequest(apiKey, method, apiPathPrefix+path, raw))

res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()

respBody, _ := io.ReadAll(res.Body)
var env envelope
_ = json.Unmarshal(respBody, &env)
if res.StatusCode >= 400 || !env.OK {
msg := env.Error
if msg == "" {
msg = env.Detail
}
return nil, fmt.Errorf("flowbeacon: %s (status %d)", msg, res.StatusCode)
}
return env.Data, nil
}

func main() {
orgID := os.Getenv("FLOWBEACON_ORG_ID")

submit, err := call("POST", "/evaluate", map[string]any{
"scenario_ids": []string{"4729318"},
"org_id": orgID,
})
if err != nil {
panic(err)
}

var s struct {
EvaluationID string `json:"evaluation_id"`
}
_ = json.Unmarshal(submit, &s)

const maxAttempts = 120
for attempt := 0; attempt < maxAttempts; attempt++ {
got, err := call("GET", "/evaluations/"+s.EvaluationID, nil)
if err != nil {
panic(err)
}
var out struct {
Status string `json:"status"`
}
_ = json.Unmarshal(got, &out)
if out.Status == "complete" || out.Status == "error" {
fmt.Println(string(got))
return
}
time.Sleep(2 * time.Second)
}

panic(fmt.Errorf("timed out waiting for evaluation %s", s.EvaluationID))
}

Notes

  • json.Marshal emits compact JSON — exactly what's signed and what's transmitted. Reuse raw for both.
  • http.NewRequestWithContext is preferred in production; this minimal example omits it for brevity.
  • Substitute hmac.Equal for any signature verification (e.g. webhooks).