@synterai/sdk-js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +133 -0
- package/dist/index.d.mts +1260 -0
- package/dist/index.d.ts +1260 -0
- package/dist/index.js +1461 -0
- package/dist/index.mjs +1408 -0
- package/package.json +67 -0
package/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# @synterai/sdk-js
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for the [Synter](https://syntermedia.ai) advertising API — manage campaigns across Google Ads, Meta, LinkedIn, Microsoft Ads, Reddit, TikTok, and X from your own code, with full types and IDE autocomplete.
|
|
4
|
+
|
|
5
|
+
> **Not yet published to npm.** This package isn't on the npm registry — install it from source (see below) until Joel ships a public release.
|
|
6
|
+
|
|
7
|
+
## What this actually talks to
|
|
8
|
+
|
|
9
|
+
Every method in this SDK calls the same live, production endpoint that powers Synter's MCP server (`@synterai/mcp-server`) and the `synter` CLI: `POST https://syntermedia.ai/api/v1/tools/run`. There is no separate "SDK backend" — if a method works here, it works because the exact same call already works for every MCP client (Claude, Cursor, Codex, ChatGPT) today.
|
|
10
|
+
|
|
11
|
+
See [`../sdk-shared/SPEC.md`](../sdk-shared/SPEC.md) and [`../sdk-shared/catalog.json`](../sdk-shared/catalog.json) for the full transport contract this SDK — and every sibling-language SDK — implements.
|
|
12
|
+
|
|
13
|
+
## Install (from source)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
git clone https://github.com/jshorwitz/synter-media.git
|
|
17
|
+
cd synter-media/packages/sdk-js
|
|
18
|
+
npm install
|
|
19
|
+
npm run build
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Then depend on it locally (e.g. `npm install ../path/to/synter-media/packages/sdk-js` or a workspace reference) until it's published.
|
|
23
|
+
|
|
24
|
+
## Authentication
|
|
25
|
+
|
|
26
|
+
Get an API key at [syntermedia.ai/developer](https://syntermedia.ai/developer). Keys look like `syn_` followed by 32 base64url characters.
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { Synter } from '@synterai/sdk-js';
|
|
30
|
+
|
|
31
|
+
const synter = new Synter(process.env.SYNTER_API_KEY!);
|
|
32
|
+
// or, with options:
|
|
33
|
+
const synter = new Synter({
|
|
34
|
+
apiKey: process.env.SYNTER_API_KEY!,
|
|
35
|
+
timeout: 30_000, // ms, default 30s
|
|
36
|
+
maxRetries: 3, // default 3
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quickstart
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { Synter } from '@synterai/sdk-js';
|
|
44
|
+
|
|
45
|
+
const synter = new Synter(process.env.SYNTER_API_KEY!);
|
|
46
|
+
|
|
47
|
+
// List campaigns (defaults to Google if no platform is given)
|
|
48
|
+
const campaigns = await synter.campaigns.list({ status: 'ENABLED', limit: 10 });
|
|
49
|
+
|
|
50
|
+
// Create a Google Search campaign
|
|
51
|
+
await synter.campaigns.createSearch({
|
|
52
|
+
campaign_name: 'Q4 Launch',
|
|
53
|
+
daily_budget: 50,
|
|
54
|
+
keywords: ['running shoes'],
|
|
55
|
+
headlines: ['Fast. Light. Yours.', 'New Season, New PR', 'Free Shipping Today'],
|
|
56
|
+
descriptions: ['Premium running shoes built for speed.', 'Order today, ships free.'],
|
|
57
|
+
final_url: 'https://example.com/shoes',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Pull performance metrics
|
|
61
|
+
const perf = await synter.analytics.getPerformance({ date_range: 'LAST_30_DAYS' });
|
|
62
|
+
|
|
63
|
+
// Generate an AI creative
|
|
64
|
+
const image = await synter.creative.generateImage({ prompt: 'a running shoe on a cloud, product photography' });
|
|
65
|
+
|
|
66
|
+
// Escape hatch: call any of the 140+ backend scripts by name
|
|
67
|
+
await synter.execute('google_ads_list_audiences', { status: 'ENABLED' }, 'google');
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## API surface
|
|
71
|
+
|
|
72
|
+
Methods are grouped by category, matching the tool catalog:
|
|
73
|
+
|
|
74
|
+
| Namespace | Methods |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `synter.campaigns` | `list`, `createSearch`, `createDisplay`, `createPmax`, `pause`, `updateBudget` |
|
|
77
|
+
| `synter.analytics` | `getPerformance`, `getDailySpend` |
|
|
78
|
+
| `synter.keywords` | `add`, `addNegative` |
|
|
79
|
+
| `synter.conversions` | `create`, `list`, `diagnoseTracking` |
|
|
80
|
+
| `synter.creative` | `generateImage`, `generateVideo` |
|
|
81
|
+
| `synter.meta` | `createCampaign` |
|
|
82
|
+
| `synter.linkedin` | `createCampaign` |
|
|
83
|
+
| `synter.reddit` | `createCampaign` |
|
|
84
|
+
| `synter.audiences` | `stageArtifact`, `sync`, `manage` |
|
|
85
|
+
| `synter` (top level) | `listAdAccounts`, `uploadImage`, `listLandingPages`, `execute` |
|
|
86
|
+
|
|
87
|
+
Every method takes a fully-typed input object (required fields are non-optional in TypeScript) and returns `Promise<Record<string, unknown>>` — the backend scripts return script-specific JSON that isn't worth over-narrowing today.
|
|
88
|
+
|
|
89
|
+
`synter.execute(scriptName, args, platform?)` is the universal escape hatch: it can call any of the 140+ backend scripts beyond the typed methods above, converting an idiomatic `{flagName: value}` map into the CLI-flag wire format internally.
|
|
90
|
+
|
|
91
|
+
## Errors
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { SynterError, SynterValidationError } from '@synterai/sdk-js';
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
await synter.campaigns.createSearch({ /* missing required fields */ } as any);
|
|
98
|
+
} catch (err) {
|
|
99
|
+
if (err instanceof SynterValidationError) {
|
|
100
|
+
// Caught client-side, before any network call — e.g. a missing required field.
|
|
101
|
+
} else if (err instanceof SynterError) {
|
|
102
|
+
// The API rejected the call: err.status, err.message, err.code, err.details
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The client automatically retries up to 3 times (exponential backoff, 1s base / 10s cap) on `429` responses (honoring `Retry-After`) and on network/timeout errors. Other 4xx/5xx responses are not retried.
|
|
108
|
+
|
|
109
|
+
## Known issues (intentionally preserved, not "fixed" here)
|
|
110
|
+
|
|
111
|
+
These mirror real, current production behavior of the backend scripts. The SDK calls the backend exactly as it works today rather than silently diverging — see `../sdk-shared/SPEC.md`'s "Known issues" section and `catalog.json`'s `knownIssues`:
|
|
112
|
+
|
|
113
|
+
1. `campaigns.pause()` and `campaigns.updateBudget()` accept a `platform` field (their schemas advertise all 7 platforms), but the live dispatch always uses `platform=google` regardless of what's passed.
|
|
114
|
+
2. `uploadImage()` accepts an optional `platform` (google/meta/linkedin), but live dispatch always hardcodes `platform=google`.
|
|
115
|
+
3. `campaigns.createDisplay()` and `campaigns.createPmax()` use different flag names for the same concept — `--landscape-image`/`--square-image` vs. `--landscape-image-url`/`--square-image-url`.
|
|
116
|
+
|
|
117
|
+
These are flagged to Joel as product bugs to fix upstream (in `catalog.json` first, then every SDK), not something to patch ad hoc in one language.
|
|
118
|
+
|
|
119
|
+
## Amp Toolbox
|
|
120
|
+
|
|
121
|
+
`synter.amp` is a separate, AI-powered toolbox for Amp integration (campaign generation, creative variants, budget optimization, etc.) — see [`AMP_TOOLBOX_README.md`](./AMP_TOOLBOX_README.md). It predates and is independent of the catalog-driven transport rewrite above.
|
|
122
|
+
|
|
123
|
+
## Development
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npm run build # tsup, dual ESM/CJS + .d.ts
|
|
127
|
+
npm run typecheck # tsc --noEmit
|
|
128
|
+
npx vitest run # test suite (mocked fetch, no real network/API key required)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
MIT
|