@signaliz/sdk 1.0.1 → 1.0.4

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 CHANGED
@@ -2,32 +2,390 @@
2
2
 
3
3
  The official Signaliz SDK for JavaScript/TypeScript — GTM intelligence for AI agents.
4
4
 
5
+ Current surfaces include the GTM Kernel, Campaign Builder, provider routing,
6
+ Nango-managed customer API connections, MCP/Ops control-plane methods, and
7
+ enrichment primitives. Available data is returned before fresh enrichment;
8
+ live email verification is 0.02 fresh enrichment credits when a new verification is needed.
9
+
10
+ ## Which Surface Should I Use?
11
+
12
+ Signaliz is the GTM brain over any stack. The SDK is for application code,
13
+ scripts, and agent runtimes that need typed access to the same GTM Kernel, Ops,
14
+ integration, and approval primitives exposed through MCP and CLI.
15
+
16
+ - Use MCP or Codex when an agent should reason, choose tools, and build or audit
17
+ a campaign from a plain-English goal.
18
+ - Use the CLI when a human operator or local agent needs a repeatable command,
19
+ JSON receipt, readiness check, or debug trail. Start with `signaliz start`.
20
+ - Use the SDK when product code or automation needs typed methods.
21
+ - Use the UI as the human cockpit to inspect state, connect tools, approve gated
22
+ actions, and monitor results.
23
+
5
24
  ## Installation
6
25
 
7
26
  ```bash
8
27
  npm install @signaliz/sdk
9
28
  ```
10
29
 
11
- ## Quick Start
30
+ ## Quick Start — Campaign Builder
31
+
32
+ Build a targeted lead list, wait for completion, then retrieve rows and CSV:
12
33
 
13
34
  ```typescript
14
35
  import { Signaliz } from '@signaliz/sdk';
15
36
 
16
- const signaliz = new Signaliz({
17
- apiKey: process.env.SIGNALIZ_API_KEY,
37
+ const signaliz = new Signaliz({ apiKey: process.env.SIGNALIZ_API_KEY });
38
+
39
+ // 1. Start a campaign build
40
+ const { campaignBuildId } = await signaliz.campaigns.build({
41
+ name: 'Q3 SaaS Outbound',
42
+ prompt: 'Series A-C SaaS companies in fintech hiring SDRs',
43
+ targetCount: 500,
44
+ confirmSpend: true,
45
+ delivery: { destinationType: 'csv' },
46
+ });
47
+
48
+ // 2. Wait for completion with progress updates
49
+ const final = await signaliz.campaigns.wait(campaignBuildId, {
50
+ onProgress: (s) => console.log(`${s.recordsProcessed}/${s.recordsTotal} rows`),
51
+ });
52
+
53
+ // 3. Retrieve structured rows
54
+ const { rows } = await signaliz.campaigns.rows(campaignBuildId, { limit: 100 });
55
+ rows.forEach((r) => console.log(r.data.email, r.data.company_name));
56
+
57
+ // 4. Download CSV artifact
58
+ const artifacts = await signaliz.campaigns.artifacts(campaignBuildId);
59
+ const csv = artifacts.find((a) => a.artifactType === 'csv');
60
+ console.log('Download:', csv?.signedUrl);
61
+ ```
62
+
63
+ > **Downstream routing:** Signaliz outputs JSON, CSV, and webhook. Your agent routes rows to CRMs, outreach tools, or warehouses using their own APIs/MCPs.
64
+
65
+ ## GTM Kernel + Brain
66
+
67
+ Use `signaliz.gtm` when an agent needs the substrate directly: workspace context, first-class campaign objects, ranked memory, Brain patterns/defaults, and provider routes.
68
+
69
+ ```typescript
70
+ const context = await signaliz.gtm.context({
71
+ includeCampaigns: true,
72
+ includeMemory: true,
73
+ includeBrain: true,
74
+ includeConnections: true,
75
+ });
76
+
77
+ const campaign = await signaliz.gtm.createCampaign({
78
+ name: 'Series B Engineering Leaders',
79
+ targetIcp: { personas: ['VP Engineering'], segment: 'Series B SaaS' },
80
+ leadListRefs: [{ source: 'signaliz_cache' }],
81
+ sendConfig: { daily_cap: 50 },
82
+ providerLinks: [{ provider: 'instantly', providerCampaignId: 'instantly_123' }],
83
+ });
84
+
85
+ const seed = await signaliz.gtm.seedBrainDefaults({
86
+ campaignId: campaign.campaign?.id,
87
+ campaignBrief: 'Heads of engineering at Series B SaaS',
88
+ layers: ['icp', 'lead_generation', 'copy_enrichment'],
89
+ includeGlobal: true,
90
+ });
91
+ console.log({
92
+ workspacePatterns: seed.seed?.evidence?.workspace_pattern_count,
93
+ networkPatterns: seed.seed?.evidence?.global_pattern_count,
94
+ activeRoutes: seed.seed?.recommended_defaults?.provider_strategy?.active_layer_routes?.length ?? 0,
95
+ privacyPolicy: seed.privacy_policy,
96
+ });
97
+
98
+ const failures = await signaliz.gtm.failurePatterns({
99
+ campaignId: campaign.campaign?.id,
100
+ dimensions: ['provider_chain', 'icp_shape', 'copy_pattern'],
101
+ days: 90,
102
+ });
103
+
104
+ const aggregate = await signaliz.gtm.aggregateBrainPatterns({
105
+ patternTypes: ['icp', 'provider_chain', 'failure'],
106
+ minWorkspaceCount: 3,
107
+ minPrivacyK: 20,
108
+ dryRun: true,
109
+ });
110
+
111
+ const calibrationAggregate = await signaliz.gtm.aggregateBrainCalibrations({
112
+ dimensionTypes: ['email_domain', 'verification_source', 'provider_chain'],
113
+ minWorkspaceCount: 3,
114
+ minPrivacyK: 20,
115
+ dryRun: true,
116
+ });
117
+
118
+ const learningCycle = await signaliz.gtm.learningCyclePlan({
119
+ campaignId: campaign.campaign?.id,
120
+ campaignBrief: 'Heads of engineering at Series B SaaS',
121
+ includeNetwork: true,
122
+ writeMode: 'dry_run',
123
+ });
124
+
125
+ const campaignLearning = await signaliz.gtm.campaignLearningStatus({
126
+ campaignId: campaign.campaign?.id,
127
+ campaignBuildId: campaign.campaign?.campaign_build_id,
128
+ writeMode: 'dry_run',
129
+ });
130
+
131
+ const readyLanes = campaignLearning.learning_lanes?.filter((lane) => lane.state === 'ready') ?? [];
132
+ const networkLane = campaignLearning.learning_lanes?.find((lane) => lane.id === 'network_patterns');
133
+
134
+ const memory = await signaliz.gtm.searchMemory({
135
+ query: 'subject lines that worked for SaaS engineering leaders',
136
+ outcomeType: 'meeting_booked',
137
+ targetIcp: { persona: 'Head of Engineering', segment: 'Series B SaaS' },
138
+ layers: ['copy_enrichment', 'sender'],
139
+ limit: 5,
140
+ });
141
+ const topMemory = memory.memories?.[0];
142
+ console.log({
143
+ title: topMemory?.title,
144
+ outcomeClass: Array.isArray(topMemory?.match_reasons) ? undefined : topMemory?.match_reasons?.outcome_class,
145
+ requestedOutcomeMatch: Array.isArray(topMemory?.match_reasons) ? false : topMemory?.match_reasons?.requested_outcome_match,
146
+ outcomeScore: Array.isArray(topMemory?.match_reasons) ? undefined : topMemory?.match_reasons?.outcome_score,
147
+ });
148
+
149
+ await signaliz.gtm.createIntegrationRecipe({
150
+ providerId: 'clay_webhook',
151
+ providerName: 'Clay Webhook',
152
+ layerCapabilities: ['find_company', 'find_people', 'lead_generation'],
153
+ invocationType: 'webhook',
154
+ authStrategy: 'webhook_secret',
155
+ endpointUrl: 'https://hooks.example.com/clay',
156
+ status: 'active',
157
+ });
158
+
159
+ const routeProof = await signaliz.gtm.previewLayerRoute({
160
+ layer: 'lead_generation',
161
+ campaignId: campaign.campaign?.id,
162
+ sampleRecords: [{ email: 'buyer@example.com' }],
163
+ context: { source: 'sdk_quickstart' },
164
+ });
165
+
166
+ console.log(routeProof.next_action);
167
+ console.log(failures.failure_patterns?.[0]?.recommendation?.next_step);
168
+ console.log({ readyLanes: readyLanes.length, networkPolicy: networkLane?.raw_data_policy });
169
+ ```
170
+
171
+ Provider routes can point to Signaliz native tools, Clay webhooks, Airbyte,
172
+ Nango-managed customer APIs, Apollo, AI Ark, Octave, Instantly, HeyReach,
173
+ Smartlead, custom APIs, custom MCP servers, or manual review gates. Nango routes
174
+ keep customer credentials in Nango while Signaliz stores only the workspace
175
+ connection reference and approval/audit context.
176
+
177
+ ### Nango-Managed API Tools
178
+
179
+ Use Nango when a workspace brings its own provider account and the action should
180
+ run through Nango-managed auth. Start by listing available tools for the saved
181
+ workspace connection, dry-run the action, then confirm the write only after the
182
+ preview is approved.
183
+
184
+ ```typescript
185
+ const tools = await signaliz.gtm.listNangoTools({
186
+ workspaceConnectionId: 'workspace_conn_nango_hubspot',
187
+ providerConfigKey: 'hubspot',
188
+ includeRaw: false,
189
+ });
190
+
191
+ const preview = await signaliz.gtm.callNangoTool({
192
+ workspaceConnectionId: 'workspace_conn_nango_hubspot',
193
+ actionName: 'upsert-contact',
194
+ input: { email: 'buyer@example.com', company: 'Acme' },
195
+ dryRun: true,
196
+ confirm: false,
197
+ });
198
+
199
+ if (preview.ready_for_confirmation) {
200
+ const confirmed = await signaliz.gtm.callNangoTool({
201
+ workspaceConnectionId: 'workspace_conn_nango_hubspot',
202
+ actionName: 'upsert-contact',
203
+ input: { email: 'buyer@example.com', company: 'Acme' },
204
+ dryRun: false,
205
+ confirm: true,
206
+ async: true,
207
+ });
208
+
209
+ if (confirmed.action_id || confirmed.status_url) {
210
+ await signaliz.gtm.getNangoActionResult({
211
+ actionId: confirmed.action_id,
212
+ statusUrl: confirmed.status_url,
213
+ });
214
+ }
215
+ }
216
+ ```
217
+
218
+ ### GTM Kernel Smoke Test
219
+
220
+ After CMM imports or Campaign Builder backfills, run the read-only/dry-run
221
+ smoke harness before launching spendful provider work:
222
+
223
+ ```bash
224
+ SIGNALIZ_API_KEY=sk_... node scripts/cmm-kernel-smoke.mjs \
225
+ --campaign-build-id <campaign_build_id>
226
+ ```
227
+
228
+ The receipt includes campaign/build/workspace IDs, execution stage, feedback and
229
+ memory counts, workspace bootstrap gate readiness, Brain phase readiness,
230
+ blockers/warnings, the next safe MCP action, and explicit
231
+ no-spend/no-write/no-sender/no-delivery confirmation.
232
+
233
+ ## MCP/Ops Control Plane
234
+
235
+ Fresh-enrichment throughput is now higher across every plan: Free 60/min,
236
+ Builder 300/min, Team 600/min, Agency 1,000/min, and Pay-As-You-Go 1,000/min,
237
+ with a 5,000/hour per-workspace safety cap. Cache hits, API/MCP reads, and
238
+ included standard data do not count against those fresh-enrichment ceilings.
239
+
240
+ ```typescript
241
+ // Workspace and platform health
242
+ const workspace = await signaliz.getWorkspace();
243
+ const health = await signaliz.getPlatformHealth();
244
+
245
+ // Tool/capability discovery
246
+ const tools = await signaliz.listTools();
247
+ const opsMatches = await signaliz.discover('campaign build with webhook delivery', 'ops');
248
+
249
+ // Prompt-first Ops loop
250
+ const plan = await signaliz.ops.plan(
251
+ 'Monitor stripe.com and plaid.com every day and Slack me new hiring signals',
252
+ );
253
+ const op = await signaliz.ops.create({
254
+ prompt: plan.goal,
255
+ blueprint: plan.blueprint,
256
+ target_count: plan.target_count,
257
+ cadence: plan.cadence,
258
+ confirm_spend: true,
259
+ });
260
+ await signaliz.ops.run(op.op_id);
261
+ const status = await signaliz.ops.status(op.op_id);
262
+ const results = await signaliz.ops.results({ op_id: op.op_id, limit: 100 });
263
+
264
+ // Spendful lead generation requires explicit approval
265
+ try {
266
+ await signaliz.leads.generate({
267
+ prompt: 'VP Sales at B2B SaaS companies, 50-500 employees',
268
+ maxLeads: 100,
269
+ });
270
+ } catch (error) {
271
+ // APPROVAL_REQUIRED includes estimated_credits and retry_arguments in details.
272
+ }
273
+
274
+ const leadJob = await signaliz.leads.generate({
275
+ prompt: 'VP Sales at B2B SaaS companies, 50-500 employees',
276
+ maxLeads: 100,
277
+ confirmSpend: true,
18
278
  });
279
+ const leadStatus = await signaliz.leads.checkStatus(leadJob.jobId);
280
+ ```
281
+
282
+ ## Custom AI Enrichment - Multi Model
283
+
284
+ Use OpenRouter model IDs directly and attach images, PDFs, audio, or video when the selected model supports them. OpenRouter Fusion handles web search/fetch inside the deliberation step, so there is no separate search toggle.
285
+
286
+ ```typescript
287
+ const result = await signaliz.ai.multiModel({
288
+ model: 'google/gemini-2.5-flash',
289
+ prompt: 'Research {{company_name}} and score whether they match our ICP.',
290
+ records: [{ company_name: 'Stripe', company_domain: 'stripe.com' }],
291
+ outputFields: [
292
+ { name: 'fit_score', type: 'number', description: 'ICP fit from 1-10' },
293
+ { name: 'reasoning', type: 'text', description: 'Short evidence-backed rationale' },
294
+ ],
295
+ fusion: {
296
+ analysis_models: ['~google/gemini-flash-latest', '~openai/gpt-latest'],
297
+ judge_model: '~anthropic/claude-opus-latest',
298
+ },
299
+ });
300
+
301
+ console.log(result.results);
302
+ ```
303
+
304
+ ## Campaigns API
305
+
306
+ ### `campaigns.build(request, options?)`
307
+
308
+ Start an async campaign build. Returns immediately with a `campaignBuildId`.
309
+
310
+ ```typescript
311
+ const result = await signaliz.campaigns.build(
312
+ {
313
+ name: 'Test',
314
+ prompt: 'Find CFOs at mid-market healthcare companies',
315
+ dryRun: true,
316
+ allowDownscale: true,
317
+ confirmSpend: true,
318
+ },
319
+ { idempotencyKey: 'my-unique-key-123' } // prevent duplicate builds
320
+ );
321
+ ```
322
+
323
+ When `dryRun: true` is passed, the result is a plan (`status: 'dry_run'`) with
324
+ `campaignBuildId: null`, planned target count, estimated credits, and duration.
19
325
 
326
+ ### `campaigns.status(id)`
327
+
328
+ Get a concise status snapshot — ideal for polling.
329
+
330
+ ### `campaigns.get(id)`
331
+
332
+ Get the full campaign build record including delivery details.
333
+
334
+ ### `campaigns.rows(id, options?)`
335
+
336
+ Retrieve paginated rows from a completed build.
337
+
338
+ ```typescript
339
+ const page = await signaliz.campaigns.rows(buildId, {
340
+ limit: 50,
341
+ segment: 'signal',
342
+ qualified: true,
343
+ });
344
+ // page.rows, page.nextCursor, page.hasMore
345
+ ```
346
+
347
+ ### `campaigns.artifacts(id)`
348
+
349
+ List all artifacts (CSV exports, signed URLs).
350
+
351
+ ### `campaigns.cancel(id, reason?)`
352
+
353
+ Cancel a queued or running build.
354
+
355
+ ### `campaigns.wait(id, options?)`
356
+
357
+ Poll until completed, failed, or canceled.
358
+
359
+ ```typescript
360
+ const final = await signaliz.campaigns.wait(buildId, {
361
+ intervalMs: 3000,
362
+ timeoutMs: 300_000,
363
+ onProgress: (s) => console.log(s.status, s.recordsProcessed),
364
+ });
365
+ ```
366
+
367
+ ## Other Resources
368
+
369
+ ```typescript
20
370
  // Find and verify an email
21
371
  const email = await signaliz.emails.find({
22
372
  fullName: 'Jane Smith',
23
373
  companyDomain: 'stripe.com',
24
374
  });
25
- console.log(email);
26
- // → { email: 'jane.smith@stripe.com', verificationStatus: 'valid', confidence: 0.95 }
27
375
 
28
- // Verify an email
29
- const result = await signaliz.emails.verify('jane@stripe.com');
30
- // → { isValid: true, isDeliverable: true, isCatchAll: false, confidenceScore: 0.98 }
376
+ // Submit batch email jobs at scale, then poll status
377
+ const job = await signaliz.emails.verifyBatch([
378
+ 'jane@example.com',
379
+ { email: 'sam@example.com' },
380
+ ]);
381
+ const status = await signaliz.emails.checkStatus(job.jobId);
382
+
383
+ // Local-business lead generation
384
+ const localJob = await signaliz.leads.generateLocal({
385
+ prompt: 'dentists in Phoenix, AZ',
386
+ targetVerified: 50,
387
+ confirmSpend: true,
388
+ });
31
389
 
32
390
  // Company signals
33
391
  const signals = await signaliz.signals.enrich({
@@ -40,28 +398,28 @@ const system = await signaliz.systems.create({
40
398
  name: 'Email Pipeline',
41
399
  capabilities: ['mcp_input', 'find_emails_with_verification', 'mcp_output'],
42
400
  });
43
- const run = await signaliz.systems.run(system.id, { data: [...] });
44
-
45
- // Stream progress
46
- for await (const event of signaliz.runs.stream(run.runId)) {
47
- console.log(`${event.completedRows}/${event.totalRows} — ${event.status}`);
48
- }
49
401
 
50
402
  // HTTP proxy
51
403
  const response = await signaliz.http.request({
52
404
  url: 'https://api.example.com/data',
53
405
  method: 'GET',
54
406
  });
407
+
408
+ // Ops routines and output sinks
409
+ const sinks = await signaliz.ops.listOutputSinks();
410
+ const routines = await signaliz.ops.listRoutines({ status: 'active' });
55
411
  ```
56
412
 
57
413
  ## MCP Setup
58
414
 
59
- Connect Signaliz to your AI agent in one command:
60
-
61
415
  ```bash
62
- npx @signaliz/mcp
416
+ npx -p @signaliz/sdk signaliz-mcp
63
417
  ```
64
418
 
419
+ The setup helper writes a local stdio MCP config that runs `@signaliz/mcp-server`
420
+ with `SIGNALIZ_API_KEY` in the environment. It does not put API keys in a
421
+ query-string URL.
422
+
65
423
  ## Authentication
66
424
 
67
425
  ### API Key (simplest)
@@ -83,12 +441,12 @@ const signaliz = new Signaliz({
83
441
  import { Signaliz, SignalizError } from '@signaliz/sdk';
84
442
 
85
443
  try {
86
- await signaliz.emails.find({ fullName: 'Test', companyDomain: 'example.com' });
444
+ await signaliz.campaigns.build({ name: 'Test', prompt: '...' });
87
445
  } catch (e) {
88
446
  if (e instanceof SignalizError) {
89
- console.log(e.errorType); // 'rate_limited' | 'validation' | 'timeout' | ...
90
- console.log(e.retryAfter); // seconds to wait (for 429s)
91
- console.log(e.isRetryable); // boolean
447
+ console.log(e.errorType); // 'rate_limited' | 'validation' | 'timeout' | ...
448
+ console.log(e.retryAfter); // seconds to wait (for 429s)
449
+ console.log(e.isRetryable); // boolean — SDK retries 429 and 5xx automatically
92
450
  }
93
451
  }
94
452
  ```