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
MavenGradle (Kotlin)
```
ai.syntermedia
synter-sdk
0.1.1
```
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/](https://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](https://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();
```
| 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 ), 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](https://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](https://docs.synterai.com/api/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 campaigns = synter.campaigns().list(
ListCampaignsRequest.builder()
.platform("google")
.status("ENABLED")
.build());
Map 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 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 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](https://docs.synterai.com/mcp/approval-model) for who approves what and where.
## Errors
| HTTP | Meaning | What to do |
| --- | --- | --- |
| 400 | Invalid arguments, for example UNRECOGNIZED_ARGS | Fix the arguments. Rejected before submission; nothing is charged. |
| 401 | Missing or invalid key | Check SYNTER_API_KEY |
| 402 | Not enough credit, or a payment method is needed for a launch | Read the response body; check billing. Do not retry blindly. |
| 403 | Missing scope, workspace access, or a safety-policy block | Check the key's scopes and the status field |
| 429 | Rate limited | The SDK retries up to three times and honors Retry-After |
Was this page helpful?
YesNo
[Previous Rust SDK](https://docs.synterai.com/sdk/rust) [Next Go SDK](https://docs.synterai.com/sdk/go)
---
Source: https://docs.synterai.com/sdk/java
Full docs as one file: https://docs.synterai.com/llms-full.txt