Go SDK
Not published yet
go get will not resolve it today. Until it ships, call the REST API from Go with net/http (example below). The SDK code further down shows the planned surface.Today: REST from Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
body, _ := json.Marshal(map[string]any{"daily_budget": 75.0, "platform": "GOOGLE"})
req, _ := http.NewRequest(http.MethodPatch,
"https://api.synterai.com/v1/campaigns/1234567890/budget", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("SYNTER_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var out map[string]any
json.NewDecoder(resp.Body).Decode(&out)
// 200 = applied. 202 with error "APPROVAL_REQUIRED" = held or refused by
// the safety gate (see safety_gate_action); nothing ran.
fmt.Println(resp.StatusCode, out["success"], out["error"])
}Pause works the same way with POST /v1/campaigns/{id}/pause. See Pause Campaign and Update Budget.
| Surface | Accepted | Use |
|---|---|---|
| REST tools endpoint | Authorization: Bearer syn_…, X-Synter-Key: syn_…, X-API-Key: syn_… | Authorization: Bearer syn_… |
| REST resource endpoints | Authorization: Bearer syn_…, X-Synter-Key: syn_… | Authorization: Bearer syn_… |
| SDKs (Python, TypeScript, Rust, Java, Go) and the synter CLI | The SDK sets the header for you | Pass the key to the client. Every SDK sends Authorization: Bearer syn_… |
| Local stdio MCP (npm @synterai/mcp-server) | Set SYNTER_API_KEY in the server's env block | The package sends Authorization: Bearer syn_… |
| Hosted MCP | Browser OAuth (the client sends Authorization: Bearer <OAuth access token>), or X-Synter-Key: syn_… for headless clients | OAuth. Headless: X-Synter-Key. Do not put an API key in Authorization on this host. |
Keys start with syn_ (sandbox keys with syn_test_). Create one at synterai.com/developer, keep it in SYNTER_API_KEY or a secret store, and send one header per request. On the hosted MCP endpoint, the Authorization header is reserved for the OAuth token the client gets from browser sign-in, so a failed key there shows up as a sign-in prompt rather than a key error. Use X-Synter-Key when you connect a hosted MCP client with a key. Details: Authentication.
Planned SDK surface
Once released, the client sends Authorization: Bearer syn_… to /api/v1/tools/run. Pause and budget calls go to Google Ads regardless of the platform argument.
client := synter.NewClient(os.Getenv("SYNTER_API_KEY"))
ctx := context.Background()
// Pause (Google Ads)
paused, err := client.Campaigns.Pause(ctx, "1234567890", "google")
// Change the daily budget. Execute uses map keys as flags verbatim.
result, err := client.Execute(ctx, "update_campaign_budget", map[string]any{
"--campaign-id": "1234567890",
"--daily-budget": 75.0,
}, "google")
if err == nil && result["status"] == "pending_review" {
fmt.Println("Held for approval, nothing ran:", result["audit_id"])
}Writes the safety gate holds or refuses
With the default workspace policy, pausing a campaign and changing a budget run straight away and are recorded in the audit log. Enabling or resuming a campaign, deleting or removing anything, sending creative to a platform, and creating new campaigns can be held for approval. A held write returns HTTP 202 with status: "pending_review", an audit_id and the exact reviewed_args. Nothing has run at that point. The SDKs return this body as a normal result, so check status before you report success.
A write the policy refuses (blocked tool, outside operating hours, over a monetary limit) returns HTTP 403 with status: "blocked", and the SDKs raise their error type. Approval cannot override a block.
An API key cannot approve its own pending write. See Approval model and safety controls for who approves what and where.
