npm.io
0.1.1 • Published yesterday

@synterai/sdk-js

Licence
MIT
Version
0.1.1
Deps
0
Size
187 kB
Vulns
0
Weekly
0

@synterai/sdk-js

TypeScript SDK for the Synter 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.

0.1.0 — early release. Live on npm and installable, but pre-1.0: the surface may change before a stable 1.0. Pin a version in production.

What this actually talks to

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.

See ../sdk-shared/SPEC.md and ../sdk-shared/catalog.json for the full transport contract this SDK — and every sibling-language SDK — implements.

Install

npm install @synterai/sdk-js

Authentication

Get an API key at syntermedia.ai/developer. Keys look like syn_ followed by 32 base64url characters.

Server-side only. Your SYNTER_API_KEY is a secret that can spend money and modify your ad accounts. Use this SDK from a backend — a Node service, a Next.js API route or Server Action, an edge/serverless function. Never instantiate Synter in client-side code (React components, browser bundles, mobile apps); the key would ship to every visitor. For a React/SPA frontend, call your own backend, and have the backend call Synter.

import { Synter } from '@synterai/sdk-js';

const synter = new Synter(process.env.SYNTER_API_KEY!);
// or, with options:
const synter = new Synter({
  apiKey: process.env.SYNTER_API_KEY!,
  timeout: 30_000, // ms, default 30s
  maxRetries: 3,   // default 3
});

Quickstart

import { Synter } from '@synterai/sdk-js';

const synter = new Synter(process.env.SYNTER_API_KEY!);

// List campaigns (defaults to Google if no platform is given)
const campaigns = await synter.campaigns.list({ status: 'ENABLED', limit: 10 });

// Create a Google Search campaign
await synter.campaigns.createSearch({
  campaign_name: 'Q4 Launch',
  daily_budget: 50,
  keywords: ['running shoes'],
  headlines: ['Fast. Light. Yours.', 'New Season, New PR', 'Free Shipping Today'],
  descriptions: ['Premium running shoes built for speed.', 'Order today, ships free.'],
  final_url: 'https://example.com/shoes',
});

// Pull performance metrics
const perf = await synter.analytics.getPerformance({ date_range: 'LAST_30_DAYS' });

// Generate an AI creative
const image = await synter.creative.generateImage({ prompt: 'a running shoe on a cloud, product photography' });

// Escape hatch: call any of the 140+ backend scripts by name
await synter.execute('google_ads_list_audiences', { status: 'ENABLED' }, 'google');

API surface

Methods are grouped by category, matching the tool catalog:

Namespace Methods
synter.campaigns list, createSearch, createDisplay, createPmax, pause, updateBudget
synter.analytics getPerformance, getDailySpend
synter.keywords add, addNegative
synter.conversions create, list, diagnoseTracking
synter.creative generateImage, generateVideo
synter.meta createCampaign
synter.linkedin createCampaign
synter.reddit createCampaign
synter.audiences stageArtifact, sync, manage
synter (top level) listAdAccounts, uploadImage, listLandingPages, execute

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.

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.

Errors

import { SynterError, SynterValidationError } from '@synterai/sdk-js';

try {
  await synter.campaigns.createSearch({ /* missing required fields */ } as any);
} catch (err) {
  if (err instanceof SynterValidationError) {
    // Caught client-side, before any network call — e.g. a missing required field.
  } else if (err instanceof SynterError) {
    // The API rejected the call: err.status, err.message, err.code, err.details
  }
}

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.

Known issues (intentionally preserved, not "fixed" here)

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:

  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.
  2. uploadImage() accepts an optional platform (google/meta/linkedin), but live dispatch always hardcodes platform=google.
  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.

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.

Amp Toolbox

synter.amp is a separate, AI-powered toolbox for Amp integration (campaign generation, creative variants, budget optimization, etc.) — see AMP_TOOLBOX_README.md. It predates and is independent of the catalog-driven transport rewrite above.

Development

npm run build      # tsup, dual ESM/CJS + .d.ts
npm run typecheck  # tsc --noEmit
npx vitest run     # test suite (mocked fetch, no real network/API key required)

License

MIT

Keywords