@signaliz/sdk
v1.0.18
Published
Signaliz SDK — GTM Kernel, Nango routes, MCP, and enrichment for AI agents
Readme
@signaliz/sdk
The official Signaliz SDK for JavaScript/TypeScript — GTM intelligence for AI agents.
Current surfaces include the GTM Kernel, Campaign Builder, provider routing, Nango-managed customer API connections, MCP/Ops control-plane methods, and enrichment primitives. Available data is returned before fresh enrichment; live email verification is 0.02 fresh enrichment credits when a new verification is needed. Builder includes unlimited cache search, Campaign Builder, API, MCP, CLI, Ops, and integrations. Team is the $499/month public plan and adds unlimited live Find People and Find Companies in Signaliz; Agency stays $999/month with higher credits and throughput.
Which Surface Should I Use?
Signaliz is the GTM brain over any stack. The SDK is for application code, scripts, and agent runtimes that need typed access to the same GTM Kernel, Ops, integration, and approval primitives exposed through MCP and CLI.
- Use MCP or Codex when an agent should reason, choose tools, and build or audit a campaign from a plain-English goal.
- Use the CLI when a human operator or local agent needs a repeatable command,
JSON receipt, readiness check, or debug trail. Start with
signaliz start. - Use the SDK when product code or automation needs typed methods.
- Use the UI as the human cockpit to inspect state, connect tools, approve gated actions, and monitor results.
Installation
npm install @signaliz/sdkQuick Start - Ops
Use Ops when a script or agent needs to turn a plain-English GTM outcome into durable work, wait for completion, and retrieve rows in one flow.
import { Signaliz } from '@signaliz/sdk';
const signaliz = new Signaliz({ apiKey: process.env.SIGNALIZ_API_KEY });
const result = await signaliz.ops.executeAndWait({
prompt: 'Build 50 approved ICP leads and return reviewed rows',
targetCount: 50,
confirmSpend: true,
limit: 50,
});
console.log(result.status.status);
console.log(result.results?.results);If the Op already exists, queue it and wait for results with runAndWait():
const run = await signaliz.ops.runAndWait({
opId: 'op_123',
limit: 100,
});For manual polling or partial-output recovery, use waitForResults() directly:
const waited = await signaliz.ops.waitForResults({
opId: 'op_123',
maxPolls: 60,
intervalMs: 5000,
includeFailedRuns: true,
});Quick Start — Campaign Builder
Build a targeted lead list, wait for completion, then retrieve rows and CSV:
import { Signaliz } from '@signaliz/sdk';
const signaliz = new Signaliz({ apiKey: process.env.SIGNALIZ_API_KEY });
// 1. Start a campaign build
const { campaignBuildId } = await signaliz.campaigns.build({
name: 'Q3 SaaS Outbound',
prompt: 'Series A-C SaaS companies in fintech hiring SDRs',
targetCount: 500,
confirmSpend: true,
delivery: { destinationType: 'csv' },
});
// 2. Wait for completion with progress updates
const final = await signaliz.campaigns.wait(campaignBuildId, {
onProgress: (s) => console.log(`${s.recordsProcessed}/${s.recordsTotal} rows`),
});
// 3. Retrieve structured rows
const { rows } = await signaliz.campaigns.rows(campaignBuildId, { limit: 100 });
rows.forEach((r) => console.log(r.data.email, r.data.company_name));
// 4. Download CSV artifact
const artifacts = await signaliz.campaigns.artifacts(campaignBuildId);
const csv = artifacts.find((a) => a.artifactType === 'csv');
console.log('Download:', csv?.signedUrl);Downstream routing: Signaliz outputs JSON, CSV, and webhook. Your agent routes rows to CRMs, outreach tools, or warehouses using their own APIs/MCPs.
Campaign Builder Agent
Use campaignBuilderAgent when application code should compose the same
strategy-template MCP flow exposed in the CLI: strategy-memory readiness,
Signaliz-native sourcing, email finding, verification, signals, Brain risk, and
BYO tool routes.
const plan = await signaliz.campaignBuilderAgent.createPlan({
goal: 'Build a strategy-template campaign for mature local operators',
strategyTemplate: 'non-medical-home-care',
targetCount: 500,
builtIns: ['lead_generation', 'local_leads', 'email_finding', 'email_verification', 'signals'],
integrations: [{
provider: 'workspace_mcp',
layer: 'enrichment',
gtmLayers: ['company_enrichment'],
toolName: 'account_enrich',
mcpServerName: 'workspace-mcp',
mode: 'required',
approvalRequired: true,
}],
agencyContext: {
strategyModel: 'strategy_template',
includeStrategyPatterns: true,
includeWorkflowPatterns: true,
includeNangoCatalog: true,
},
});
const dryRun = await signaliz.campaignBuilderAgent.dryRunPlan(plan);
const readiness = await signaliz.campaignBuilderAgent.readiness({
goal: 'Build a strategy-template campaign for mature local operators',
strategyTemplate: 'non-medical-home-care',
targetCount: 500,
builtIns: ['lead_generation', 'local_leads', 'email_finding', 'email_verification', 'signals'],
});
const proof = await signaliz.campaignBuilderAgent.proof({
goal: 'Build a strategy-template campaign for mature local operators',
strategyTemplate: 'non-medical-home-care',
targetCount: 25,
builtIns: ['lead_generation', 'local_leads', 'email_finding', 'email_verification', 'signals'],
});
const kit = signaliz.campaignBuilderAgent.buildKit({
goal: 'Build a strategy-template campaign for mature local operators',
strategyTemplate: 'non-medical-home-care',
targetCount: 250,
includeLocalLeads: true,
includeCustomToolPlaceholder: true,
});
const memoryKit = signaliz.campaignBuilderAgent.memoryKit({
goal: 'Build a strategy-template campaign for mature local operators',
strategyTemplate: 'non-medical-home-care',
targetCount: 250,
includeLocalLeads: true,
source: 'agency',
});
console.log(plan.strategyMemoryStatus?.ready);
console.log(readiness.summary.ready);
console.log(proof.success);
console.log(kit.mcp_calls.find((call) => call.tool === 'gtm_campaign_agent_proof'));
console.log(memoryKit.mcp_calls.find((call) => call.tool === 'gtm_campaign_strategy_memory_status'));
console.log(plan.mcpFlow.filter((step) =>
['gtm_provider_recipe_prepare', 'gtm_provider_route_activate'].includes(step.tool)
));
// After an approved launch, keep review and delivery in the agent surface.
const review = await signaliz.campaignBuilderAgent.reviewBuild('campaign_build_id', {
limit: 100,
});
const finalStatus = await signaliz.campaignBuilderAgent.getBuildStatus('campaign_build_id');
const reviewRows = await signaliz.campaignBuilderAgent.getBuildRows('campaign_build_id', {
limit: 100,
qualified: true,
});
const artifacts = await signaliz.campaignBuilderAgent.listBuildArtifacts('campaign_build_id');
console.log(review.summary.readyForDeliveryApproval);The CLI can run the same request shape from a reusable JSON file, with flags overriding file fields:
npx @signaliz/sdk campaign-agent init \
--strategy-template non-medical-home-care \
--target-count 250 \
--use-local-leads \
--with-byo-placeholder \
--output-file campaign-request.json
npx @signaliz/sdk campaign-agent kit \
--strategy-template non-medical-home-care \
--target-count 250 \
--use-local-leads \
--with-byo-placeholder \
--json
npx @signaliz/sdk campaign-agent memory-kit \
--strategy-template non-medical-home-care \
--target-count 250 \
--use-local-leads \
--source agency \
--json
npx @signaliz/sdk campaign-agent plan \
--request-file campaign-request.json \
--target-count 250 \
--json
npx @signaliz/sdk campaign-agent doctor \
--request-file campaign-request.json \
--json
npx @signaliz/cli build "Build a strategy-template campaign for local operators" \
--strategy-template non-medical-home-care \
--target-count 25 \
--use-local-leads
npx @signaliz/sdk campaign-agent status campaign_build_id --json
npx @signaliz/sdk campaign-agent review campaign_build_id --limit 100 --json
npx @signaliz/sdk campaign-agent rows campaign_build_id --limit 100 --jsonUse the hosted MCP campaign-agent planner when you want Signaliz to return the same strategy-template runbook exposed through MCP:
const memoryStatus = await signaliz.gtm.campaignStrategyMemoryStatus({
strategyTemplate: 'non-medical-home-care',
includeSamples: true,
});
const hostedTemplate = await signaliz.gtm.campaignAgentRequestTemplate({
goal: 'Build a reusable strategy-template campaign request',
strategyTemplate: 'non-medical-home-care',
targetCount: 250,
includeLocalLeads: true,
includeByoPlaceholder: true,
privacyMode: 'workspace_only',
});
const hostedPlan = await signaliz.gtm.campaignAgentPlan({
goal: 'Build a strategy-template campaign for local operators',
strategyTemplate: 'non-medical-home-care',
targetCount: 500,
builtIns: ['lead_generation', 'local_leads', 'email_finding', 'email_verification', 'signals'],
customTools: [{
provider: 'workspace_mcp',
layer: 'enrichment',
tool: 'account_enrich',
mcp: 'workspace-mcp',
}],
});GTM Kernel + Brain
Use signaliz.gtm when an agent needs the substrate directly: workspace context, first-class campaign objects, ranked memory, Brain patterns/defaults, and provider routes.
const context = await signaliz.gtm.context({
includeCampaigns: true,
includeMemory: true,
includeBrain: true,
includeConnections: true,
});
const campaign = await signaliz.gtm.createCampaign({
name: 'Series B Engineering Leaders',
targetIcp: { personas: ['VP Engineering'], segment: 'Series B SaaS' },
leadListRefs: [{ source: 'signaliz_cache' }],
sendConfig: { daily_cap: 50 },
providerLinks: [{ provider: 'instantly', providerCampaignId: 'instantly_123' }],
});
const seed = await signaliz.gtm.seedBrainDefaults({
campaignId: campaign.campaign?.id,
campaignBrief: 'Heads of engineering at Series B SaaS',
layers: ['icp', 'lead_generation', 'copy_enrichment'],
includeGlobal: true,
});
console.log({
workspacePatterns: seed.seed?.evidence?.workspace_pattern_count,
networkPatterns: seed.seed?.evidence?.global_pattern_count,
activeRoutes: seed.seed?.recommended_defaults?.provider_strategy?.active_layer_routes?.length ?? 0,
privacyPolicy: seed.privacy_policy,
});
const failures = await signaliz.gtm.failurePatterns({
campaignId: campaign.campaign?.id,
dimensions: ['provider_chain', 'icp_shape', 'copy_pattern'],
days: 90,
});
const aggregate = await signaliz.gtm.aggregateBrainPatterns({
patternTypes: ['icp', 'provider_chain', 'failure'],
minWorkspaceCount: 3,
minPrivacyK: 20,
dryRun: true,
});
const calibrationAggregate = await signaliz.gtm.aggregateBrainCalibrations({
dimensionTypes: ['email_domain', 'verification_source', 'provider_chain'],
minWorkspaceCount: 3,
minPrivacyK: 20,
dryRun: true,
});
const learningCycle = await signaliz.gtm.learningCyclePlan({
campaignId: campaign.campaign?.id,
campaignBrief: 'Heads of engineering at Series B SaaS',
includeNetwork: true,
writeMode: 'dry_run',
});
const campaignLearning = await signaliz.gtm.campaignLearningStatus({
campaignId: campaign.campaign?.id,
campaignBuildId: campaign.campaign?.campaign_build_id,
writeMode: 'dry_run',
});
const readyLanes = campaignLearning.learning_lanes?.filter((lane) => lane.state === 'ready') ?? [];
const networkLane = campaignLearning.learning_lanes?.find((lane) => lane.id === 'network_patterns');
const memory = await signaliz.gtm.searchMemory({
query: 'subject lines that worked for SaaS engineering leaders',
outcomeType: 'meeting_booked',
targetIcp: { persona: 'Head of Engineering', segment: 'Series B SaaS' },
layers: ['copy_enrichment', 'sender'],
limit: 5,
});
const topMemory = memory.memories?.[0];
console.log({
title: topMemory?.title,
outcomeClass: Array.isArray(topMemory?.match_reasons) ? undefined : topMemory?.match_reasons?.outcome_class,
requestedOutcomeMatch: Array.isArray(topMemory?.match_reasons) ? false : topMemory?.match_reasons?.requested_outcome_match,
outcomeScore: Array.isArray(topMemory?.match_reasons) ? undefined : topMemory?.match_reasons?.outcome_score,
});
await signaliz.gtm.createIntegrationRecipe({
providerId: 'clay_webhook',
providerName: 'Clay Webhook',
layerCapabilities: ['find_company', 'find_people', 'lead_generation'],
invocationType: 'webhook',
authStrategy: 'webhook_secret',
endpointUrl: 'https://hooks.example.com/clay',
status: 'active',
});
const routeProof = await signaliz.gtm.previewLayerRoute({
layer: 'lead_generation',
campaignId: campaign.campaign?.id,
sampleRecords: [{ email: '[email protected]' }],
context: { source: 'sdk_quickstart' },
});
console.log(routeProof.next_action);
console.log(failures.failure_patterns?.[0]?.recommendation?.next_step);
console.log({ readyLanes: readyLanes.length, networkPolicy: networkLane?.raw_data_policy });Provider routes can point to Signaliz native tools, Clay webhooks, Airbyte, Nango-managed customer APIs, Apollo, AI Ark, Octave, Instantly, HeyReach, Smartlead, custom APIs, custom MCP servers, or manual review gates. Nango routes keep customer credentials in Nango while Signaliz stores only the workspace connection reference and approval/audit context.
Nango-Managed API Tools
Use Nango when a workspace brings its own provider account and the action should run through Nango-managed auth. If the vendor is not connected yet, create a Connect session and send the user to the returned auth link. Then list tools for the saved workspace connection, dry-run the action, and confirm the write only after the preview is approved.
const session = await signaliz.gtm.createNangoConnectSession({
providerId: 'apollo',
integrationId: 'apollo',
});
console.log(session.connect_link);
const tools = await signaliz.gtm.listNangoTools({
workspaceConnectionId: 'workspace_conn_nango_hubspot',
providerConfigKey: 'hubspot',
includeRaw: false,
});
const preview = await signaliz.gtm.callNangoTool({
workspaceConnectionId: 'workspace_conn_nango_hubspot',
actionName: 'upsert-contact',
input: { email: '[email protected]', company: 'Acme' },
dryRun: true,
confirm: false,
});
if (preview.ready_for_confirmation) {
const confirmed = await signaliz.gtm.callNangoTool({
workspaceConnectionId: 'workspace_conn_nango_hubspot',
actionName: 'upsert-contact',
input: { email: '[email protected]', company: 'Acme' },
dryRun: false,
confirm: true,
async: true,
});
if (confirmed.action_id || confirmed.status_url) {
await signaliz.gtm.getNangoActionResult({
actionId: confirmed.action_id,
statusUrl: confirmed.status_url,
});
}
}The Ops namespace exposes the same Nango bridge, plus a one-call helper backed
by nango_mcp_tool_call_and_wait when an agent needs the result packet
immediately:
const nangoResult = await signaliz.ops.callNangoToolAndWait({
workspaceConnectionId: 'workspace_conn_nango_hubspot',
actionName: 'upsert-contact',
input: { email: '[email protected]', company: 'Acme' },
dryRun: false,
confirm: true,
maxPolls: 6,
});
console.log(nangoResult.result);For recurring Ops, use the schedule-and-wait helper to activate the Op and
retrieve the first result packet in one call. This maps to native MCP
ops_schedule_and_wait:
const recurring = await signaliz.ops.scheduleAndWait({
prompt: 'Monitor strategic accounts daily and send new funding signals to Slack',
destinations: [{ type: 'slack' }],
wakeOnEvents: ['company_funded'],
confirmSpend: true,
limit: 100,
});
console.log(recurring.results?.results);For Nango-backed recurrence, the Ops SDK sends the natural Nango fields through
native ops_nango_schedule / ops_nango_schedule_and_wait:
const hubspotOp = await signaliz.ops.scheduleNangoActionAndWait({
prompt: 'Monitor strategic accounts daily and upsert approved rows to HubSpot',
workspaceConnectionId: 'workspace_conn_nango_hubspot',
providerConfigKey: 'hubspot',
actionName: 'upsert-contact',
writeConfirmed: true,
confirmSpend: true,
maxPolls: 6,
limit: 100,
});
console.log(hubspotOp.results?.results);GTM Kernel Smoke Test
After strategy imports or Campaign Builder backfills, run the read-only/dry-run smoke harness before launching spendful provider work:
Run the internal smoke harness with SIGNALIZ_API_KEY and
--campaign-build-id <campaign_build_id> or --campaign-id <campaign_id>.
The receipt includes campaign/build/workspace IDs, execution stage, feedback and memory counts, workspace bootstrap gate readiness, Brain phase readiness, blockers/warnings, the next safe MCP action, and explicit no-spend/no-write/no-sender/no-delivery confirmation.
MCP/Ops Control Plane
Fresh-enrichment throughput is now higher across every plan: Free 60/min, Builder 300/min, Team 600/min, Agency 1,000/min, and Pay-As-You-Go 1,000/min, with a 5,000/hour per-workspace safety cap. Cache hits, API/MCP reads, and cache search do not count against those fresh-enrichment ceilings.
// Workspace and platform health
const workspace = await signaliz.getWorkspace();
const health = await signaliz.getPlatformHealth();
// Tool/capability discovery
const tools = await signaliz.listTools();
const opsMatches = await signaliz.discover('campaign build with webhook delivery', 'ops');
// North-star Ops loop: recommend, dry-run, schedule/run, poll, retrieve
const autopilot = await signaliz.ops.autopilot();
console.log(autopilot.primaryMotion?.operatingPacket?.northStar);
console.log(autopilot.primaryMotion?.operatingPacket?.nangoRoute?.dryRunArguments);
const plan = await signaliz.ops.plan(
'Monitor stripe.com and plaid.com every day and Slack me new hiring signals',
);
console.log(plan.executionContract?.sequence.map((step) => step.tool).join(' -> '));
console.log(plan.executionContract?.nangoRoute?.writeGate);
const nangoPreview = await signaliz.ops.execute({
prompt: 'Build 50 approved ICP leads and upsert them to HubSpot',
destinations: [{
type: 'nango',
connectionId: 'workspace_conn_nango_hubspot',
providerConfigKey: 'hubspot',
nangoConnectionId: 'connection_123',
actionName: 'upsert-contact',
}],
dry_run: true,
});
const op = await signaliz.ops.schedule({
prompt: plan.goal,
blueprint: plan.blueprint,
target_count: plan.target_count,
cadence: plan.cadence === 'manual' ? 'daily' : plan.cadence,
timezone: 'America/Phoenix',
wakeOnEvents: ['company_funded', 'crm.updated'],
confirm_spend: true,
});
await signaliz.ops.run(op.op_id);
const status = await signaliz.ops.status(op.op_id);
const results = await signaliz.ops.results({ op_id: op.op_id, limit: 100 });
// Spendful lead generation requires explicit approval
try {
await signaliz.leads.generate({
prompt: 'VP Sales at B2B SaaS companies, 50-500 employees',
maxLeads: 100,
});
} catch (error) {
// APPROVAL_REQUIRED includes estimated_credits and retry_arguments in details.
}
const leadJob = await signaliz.leads.generate({
prompt: 'VP Sales at B2B SaaS companies, 50-500 employees',
maxLeads: 100,
confirmSpend: true,
});
const leadStatus = await signaliz.leads.checkStatus(leadJob.jobId);Custom AI Enrichment - Multi Model
Use the Signaliz-hosted default model and attach images, PDFs, audio, or video when needed. The response includes model readback metadata for auditing.
const result = await signaliz.ai.multiModel({
prompt: 'Research {{company_name}} and score whether they match our ICP.',
records: [{ company_name: 'Stripe', company_domain: 'stripe.com' }],
outputFields: [
{ name: 'fit_score', type: 'number', description: 'ICP fit from 1-10' },
{ name: 'reasoning', type: 'text', description: 'Short evidence-backed rationale' },
],
});
console.log(result.model, result.results);Campaigns API
campaigns.build(request, options?)
Start an async campaign build. Returns immediately with a campaignBuildId.
const result = await signaliz.campaigns.build(
{
name: 'Test',
prompt: 'Find CFOs at mid-market healthcare companies',
dryRun: true,
allowDownscale: true,
confirmSpend: true,
},
{ idempotencyKey: 'my-unique-key-123' } // prevent duplicate builds
);When dryRun: true is passed, the result is a plan (status: 'dry_run') with
campaignBuildId: null, planned target count, estimated credits, and duration.
campaigns.status(id)
Get a concise status snapshot — ideal for polling.
campaigns.get(id)
Get the full campaign build record including delivery details.
campaigns.rows(id, options?)
Retrieve paginated rows from a completed build.
const page = await signaliz.campaigns.rows(buildId, {
limit: 50,
segment: 'signal',
qualified: true,
});
// page.rows, page.nextCursor, page.hasMorecampaigns.artifacts(id)
List all artifacts (CSV exports, signed URLs).
campaigns.cancel(id, reason?)
Cancel a queued or running build.
campaigns.wait(id, options?)
Poll until completed, failed, or canceled.
const final = await signaliz.campaigns.wait(buildId, {
intervalMs: 3000,
timeoutMs: 300_000,
onProgress: (s) => console.log(s.status, s.recordsProcessed),
});Other Resources
// Find and verify an email with the V3 waterfall
const email = await signaliz.emails.find({
fullName: 'Jane Smith',
companyDomain: 'stripe.com',
});
// Submit batch email jobs at scale, then poll status
const job = await signaliz.emails.verifyBatch([
'[email protected]',
{ email: '[email protected]' },
]);
const status = await signaliz.emails.checkStatus(job.jobId);
// Local-business lead generation
const localJob = await signaliz.leads.generateLocal({
prompt: 'dentists in Phoenix, AZ',
targetVerified: 50,
confirmSpend: true,
});
// Company signals
const signals = await signaliz.signals.enrich({
companyName: 'Stripe',
researchPrompt: 'Find recent hiring activities',
});
// Systems (pipelines)
const system = await signaliz.systems.create({
name: 'Email Pipeline',
capabilities: ['mcp_input', 'find_emails_with_verification', 'mcp_output'],
});
// HTTP proxy
const response = await signaliz.http.request({
url: 'https://api.example.com/data',
method: 'GET',
});
// Ops routines and output sinks
const sinks = await signaliz.ops.listOutputSinks();
const routines = await signaliz.ops.listRoutines({ status: 'active' });MCP Setup
npx -p @signaliz/sdk signaliz-mcpThe setup helper writes a local stdio MCP config that runs @signaliz/mcp-server
with SIGNALIZ_API_KEY in the environment. It does not put API keys in a
query-string URL.
Authentication
API Key (simplest)
const signaliz = new Signaliz({ apiKey: 'sk_...' });M2M OAuth
const signaliz = new Signaliz({
clientId: process.env.SIGNALIZ_CLIENT_ID,
clientSecret: process.env.SIGNALIZ_CLIENT_SECRET,
});Error Handling
import { Signaliz, SignalizError } from '@signaliz/sdk';
try {
await signaliz.campaigns.build({ name: 'Test', prompt: '...' });
} catch (e) {
if (e instanceof SignalizError) {
console.log(e.errorType); // 'rate_limited' | 'validation' | 'timeout' | ...
console.log(e.retryAfter); // seconds to wait (for 429s)
console.log(e.isRetryable); // boolean — SDK retries 429 and 5xx automatically
}
}License
MIT
