TypeScript SDK

@outlevo/sdk 0.3.0: a thin, typed client for https://api.outlevo.com/v1. For server code only: Node 18+, Bun, Deno and edge runtimes. MIT licensed.

Install

npm install https://developers.outlevo.com/outlevo-sdk-0.3.0.tgz

Version 0.3.0 of @outlevo/sdk and @outlevo/components is coming to npm; until then it installs from the developer hub, and the import is the same. npm still has 0.2.0, without the assistant. No Node project? Every call is plain HTTPS: see the API with fetch.

Create a key at app.outlevo.com → Developers and keep it in a server environment variable (OUTLEVO_KEY). Start with a test key: it works on the sandbox. A live key (olv_live_…) refuses to run in a browser.

import { Outlevo, OutlevoError, verifyWebhook } from '@outlevo/sdk';
const ol = new Outlevo({ apiKey: process.env.OUTLEVO_KEY! });

Companies, with your own id and data

const company = await ol.companies.upsert({ domain: 'acme.com', external_id: 'site-17', attributes: { pages_exposed: 412 } });
await ol.companies.update(company.id as string, { attributes: { pages_exposed: 650 } });
const same = await ol.companies.findByExternalId('site-17');
await ol.companies.bulk([{ domain: 'globex.com', external_id: 'site-18' }]);        // up to 1,000; returns a job

upsert creates the company, or updates it when it already exists (409 COMPANY_EXISTS carries the existing company_id). Attributes are a flat map; numbers and booleans are stored as text.

Metered calls quote first

const quote = await ol.companies.qualify(company.id as string);                    // { credit_estimate, confirm_required }
const started = await ol.companies.qualify(company.id as string, { confirm: true }) as { job_id: string };
const job = await ol.jobs.wait(started.job_id);                                     // polls GET /v1/jobs/{id}

The same pattern runs companies.research (standard or deep), contacts.discover and contacts.verify. Without confirm: true nothing is spent. In a web request, start the job and poll it from a background worker, or wait for the job.completed webhook.

Every page of a list

for await (const c of ol.paginate((cursor) => ol.companies.list({ limit: 200, cursor }))) console.log(c.domain);

Campaigns, handoffs and Revenue Actions

const stats = await ol.campaigns.stats(campaignId);       // leads, sent, replied, bounced, rates
const leads = await ol.campaigns.leads(campaignId, { external_id: 'site-17' });
const cards = await ol.handoffs.list({ status: 'delivered' });   // Readiness Cards waiting for a rep
await ol.handoffs.accept(cards.data[0]!.id as string);
const queued = await ol.revenueActions.list({ status: 'queued' });

Campaign writes and accepting or rejecting a handoff need a key created with "Acts as me". revenueActions.propose records a proposal only: a person or an approved policy decides. No call approves, launches or sends.

The assistant (chat-first)

const a = await ol.assistant.send('Add acme.com and qualify it', { campaignId });
console.log(a.reply);
for (const [i, card] of a.cards.entries()) {
  if (card.type === 'confirm' && await yourUserConfirms(card)) await ol.assistant.confirm(a.message_id!, i, card);
}
const thread = await ol.assistant.thread({ campaignId });
// a.events: what changed; ol.assistant.undo(event.id) reverses an undoable one

The key needs assistant:write plus the scopes of the work. Credits are spent only when your user presses Confirm on a confirm card.

Webhooks

const sub = await ol.webhookSubscriptions.create({ url: 'https://your.app/api/outlevo-webhook', events: ['lead.replied'] });
// sub.secret is shown once: keep it as OUTLEVO_WEBHOOK_SECRET
await ol.webhookSubscriptions.test(sub.id);
const event = await verifyWebhook(rawBody, req.headers['outlevo-signature'], process.env.OUTLEVO_WEBHOOK_SECRET!);

More in the webhooks guide.

Behaviour

Without the SDK

Any language works over plain HTTPS. In TypeScript with fetch:

const OL = 'https://api.outlevo.com';
const headers = (write = false) => ({ authorization: `Bearer ${process.env.OUTLEVO_KEY}`, 'content-type': 'application/json', ...(write ? { 'idempotency-key': crypto.randomUUID() } : {}) });
const { data } = await (await fetch(`${OL}/v1/companies?limit=50`, { headers: headers() })).json();
const started = await (await fetch(`${OL}/v1/companies/${data[0].id}/qualify?confirm=true`, { method: 'POST', headers: headers(true) })).json(); // { job_id }

PHP and Laravel: the Laravel quickstart. Every endpoint: the API reference.