View as Markdown
SDK

Rust SDK

The synter crate on crates.io (0.1.1). An async client (tokio + reqwest) for POST /api/v1/tools/run. Every call returns Result<serde_json::Value, SynterError>.

Install and authenticate

bash
cargo add synter serde_json
cargo add tokio --features macros,rt-multi-thread
rust
use synter::{Client, Platform, SynterError};

#[tokio::main]
async fn main() -> Result<(), SynterError> {
    let client = Client::builder()
        .api_key(std::env::var("SYNTER_API_KEY").expect("SYNTER_API_KEY not set"))
        .build()?;

    let campaigns = client.campaigns().list(Default::default()).await?;
    println!("{campaigns}");
    Ok(())
}

The client sends Authorization: Bearer syn_….

SurfaceAcceptedUse
REST tools endpointAuthorization: Bearer syn_…, X-Synter-Key: syn_…, X-API-Key: syn_…Authorization: Bearer syn_…
REST resource endpointsAuthorization: Bearer syn_…, X-Synter-Key: syn_…Authorization: Bearer syn_…
SDKs (Python, TypeScript, Rust, Java, Go) and the synter CLIThe SDK sets the header for youPass 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 blockThe package sends Authorization: Bearer syn_…
Hosted MCPBrowser OAuth (the client sends Authorization: Bearer <OAuth access token>), or X-Synter-Key: syn_… for headless clientsOAuth. 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.

Write: pause a campaign and change its daily budget

These calls change a live Google Ads campaign. The Platform argument is required, but the request always goes to Google Ads; use the REST endpoints for other platforms.

rust
use std::collections::HashMap;
use serde_json::json;

let campaign_id = "1234567890";

// Pause (Google Ads)
let paused = client.campaigns().pause(campaign_id, Platform::Google).await?;

// Change the daily budget to 75.00 in the account currency.
// campaigns().update_budget(...) in 0.1.1 sends --budget, which the API rejects
// with 400 UNRECOGNIZED_ARGS before anything runs. Use execute() until the next
// release. Keys are used as flag names verbatim ("daily-budget" -> --daily-budget).
let mut args = HashMap::new();
args.insert("campaign-id".to_string(), json!(campaign_id));
args.insert("daily-budget".to_string(), json!(75.0));
let result = client
    .execute("update_campaign_budget", args, Some("google"))
    .await?;

if result["status"] == "pending_review" {
    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.

Was this page helpful?