View as Markdown
SDK

Java SDK

ai.syntermedia:synter-sdk on Maven Central. A typed Java 17+ client for POST /api/v1/tools/run, built on java.net.http. Use version 0.1.1 or later.

Install

<dependency>
  <groupId>ai.syntermedia</groupId>
  <artifactId>synter-sdk</artifactId>
  <version>0.1.1</version>
</dependency>

If a search says the artifact does not exist

search.maven.org and central.sonatype.com can lag behind new releases. The artifact is on Maven Central itself: repo1.maven.org/maven2/ai/syntermedia/synter-sdk/0.1.1/. Maven and Gradle resolve from there, so the search index does not matter. Do not hand-roll an HTTP client because of a search result. From 0.1.1 the POM's project and SCM links point at github.com/Synter-Media-AI/synter-docs; 0.1.0 pointed at a repository that no longer exists.

Authenticate

Pass the key to the builder. The SDK sends it as Authorization: Bearer syn_… on every request. Read it from the environment; never hardcode it.

java
import ai.syntermedia.sdk.Synter;

Synter synter = Synter.builder()
    .apiKey(System.getenv("SYNTER_API_KEY"))
    .build();
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.

Read: last 7 days of Google Ads performance

java
import ai.syntermedia.sdk.Synter;
import ai.syntermedia.sdk.SynterException;
import ai.syntermedia.sdk.requests.GetPerformanceRequest;
import ai.syntermedia.sdk.requests.ListCampaignsRequest;
import java.util.Map;

public class PerformanceReport {
    public static void main(String[] args) {
        Synter synter = Synter.builder()
            .apiKey(System.getenv("SYNTER_API_KEY"))
            .build();

        try {
            Map<String, Object> campaigns = synter.campaigns().list(
                ListCampaignsRequest.builder()
                    .platform("google")
                    .status("ENABLED")
                    .build());

            Map<String, Object> performance = synter.analytics().getPerformance(
                GetPerformanceRequest.builder()
                    .platform("google")
                    .dateRange("LAST_7_DAYS")
                    .build());
            System.out.println(performance.keySet());
        } catch (SynterException error) {
            System.err.println("Request failed (status " + error.getStatus()
                + ", code " + error.getCode() + ")");
        }
    }
}

getPerformance sends the REST script pull_google_ads with --days 7. pull_google_ads_performance is the hosted MCP tool name, not a REST script name.

Write: pause a campaign and change its daily budget

Both calls change a live Google Ads campaign. In 0.1.1 the platform argument is required but the request always goes to Google Ads. Use the REST endpoints below for other platforms.

java
import java.util.Map;

String campaignId = "1234567890";

// Pause (Google Ads)
Map<String, Object> paused = synter.campaigns().pause(campaignId, "google");

// Change the daily budget to 75.00 in the account currency.
// 0.1.1's campaigns().updateBudget(...) sends --budget, which the API rejects
// with 400 UNRECOGNIZED_ARGS before anything runs. Use execute() until the
// next release:
Map<String, Object> budget = synter.execute(
    "update_campaign_budget",
    Map.of("campaign_id", campaignId, "daily_budget", 75.0),
    "google");

if ("pending_review".equals(budget.get("status"))) {
    // Held by the safety gate: nothing ran. See "Writes the safety gate holds".
    System.out.println("Waiting for approval: " + budget.get("audit_id"));
}

execute(scriptName, args, platform) turns each map key into a flag (daily_budget becomes --daily-budget). 2xx responses, including 202, come back as a Map; anything else throws SynterException.

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.

Errors

HTTPMeaningWhat to do
400Invalid arguments, for example UNRECOGNIZED_ARGSFix the arguments. Rejected before submission; nothing is charged.
401Missing or invalid keyCheck SYNTER_API_KEY
402Not enough credit, or a payment method is needed for a launchRead the response body; check billing. Do not retry blindly.
403Missing scope, workspace access, or a safety-policy blockCheck the key's scopes and the status field
429Rate limitedThe SDK retries up to three times and honors Retry-After
Was this page helpful?