lighthouse-private-markets-sdk
v3.6.0
Published
Official Lighthouse SDK — thin REST client for the Lighthouse API (CRM, Discovery, Reports, Documents).
Maintainers
Readme
Lighthouse JS SDK
Official JavaScript/TypeScript client for the Lighthouse REST API. Wraps the CRM, Discovery, Reports, and Documents endpoints behind a single typed Lighthouse class. Zero runtime dependencies — uses the global fetch.
Installation
npm i lighthouse-private-markets-sdk
# or
yarn add lighthouse-private-markets-sdkWorks in Node 18+, modern browsers, Deno, Bun, and edge runtimes.
Quick Start
Generate an API key at Settings → API Keys in your Lighthouse workspace.
import { Lighthouse } from 'lighthouse-private-markets-sdk'
const client = new Lighthouse('lgt_live_...')
// Who am I?
const me = await client.users.me()
console.log(me.data)
// Search your CRM
const res = await client.records.search('company', {
filters: {
operator: 'AND',
conditions: [
{ field: 'domain', operator: 'contains_any', value: ['acme.com'] },
],
},
limit: 10,
})
console.log(res.data, res.meta)Every method resolves to an APIResponse:
interface APIResponse<T = unknown> {
data: T | null
error: unknown | null
meta?: Record<string, unknown>
status: number
}Authentication
The client sends Authorization: Bearer <api_key> on every request. Keep API keys secret — never ship them in client-side code.
Resources
| Namespace | Endpoints |
|-----------------------|-----------|
| client.records | search / get / create / update / delete records (company, person, deal, custom objects) |
| client.lists | list / get / create / update / delete, getRecords, addRecord, removeRecord, shareWithTeams, revokeTeams, updateSharing |
| client.notes | list / get / create / delete, linkRecord, unlinkRecord |
| client.tasks | list / get / create / update / delete |
| client.attributes | list / get / create / update / delete, colors |
| client.options | list / create / delete select-field options |
| client.views | list / get / update / delete, shareWithTeams, revokeTeams, updateSharing |
| client.users | me, list, get |
| client.teams | list, get |
| client.dashboards | list / get / create / update / delete, listWidgets, createWidget, updateWidget, deleteWidget, widgetData, shareWithTeams, revokeTeams |
| client.discovery | getAttributes, searchCompanies, searchPeople, semanticSearchCompanies, semanticSearchPeople, lookupCompaniesByDomain, lookupCompaniesByLinkedin, lookupPeopleByLinkedin, getSavedSearches, getSearchResults, getLocations, getIndustries, getTags, searchInvestors, searchOrganizations, searchSchools |
| client.documents | folders + files (v2): listFolders, createFolder, updateFolder, deleteFolder, link/unlinkFolder, uploadUrl, getFile, updateFile, deleteFile, link/unlinkFile, listRecordDocuments, upload() one-shot helper |
| client.inbox | emailAccountMe, emailAccounts, emailConversations, emailConversation, createEmailDraft, downloadEmailAttachment, linkedinAccountMe, linkedinAccounts, linkedinChats, linkedinChatMessages, downloadLinkedinAttachment |
Examples
Inbox
// Accounts you can access (own + shared), with permission flags
const { data: accounts } = await client.inbox.emailAccounts()
// Email conversations with a CRM company (its domains), newest first
const { data: conversations, meta } = await client.inbox.emailConversations({
record_id: companyId,
record_type: 'company',
limit: 50,
})
// meta.accounts reports per-account failures (rate_limited, account_disconnected)
// Read one conversation (account_id is required: the row's account.id)
const { data: convo } = await client.inbox.emailConversation(conversations[0].id, {
account_id: conversations[0].account.id,
})
// Download an attachment from a message
const msg = convo.messages[0]
if (msg.attachments?.length) {
const file = await client.inbox.downloadEmailAttachment(msg.id, msg.attachments[0].id, {
account_id: msg.account.id,
})
// file.data is an ArrayBuffer, file.contentType the media type.
// Attachments over 25 MB are not served (413 attachment_too_large).
}
// Draft a reply. The draft is saved in the mailbox, never sent. Recipients
// are always explicit, and reply_to_message_id is a MESSAGE id.
const { data: draft } = await client.inbox.createEmailDraft({
account_id: msg.account.id,
to: [msg.from.email],
subject: `Re: ${msg.subject}`,
body: '<p>Thanks for the note, following up shortly.</p>',
conversation_id: convo.conversation.id,
reply_to_message_id: msg.id,
})
// LinkedIn: find chats with a person, then read one
const { data: chats } = await client.inbox.linkedinChats({ person_linkedin: 'https://linkedin.com/in/username' })
const { data: chat } = await client.inbox.linkedinChatMessages(chats[0].id)Records
// Create
const { data: created } = await client.records.create('company', {
name: 'Acme',
domain: ['acme.com'],
})
const recordId = (created as any).id
// Update — relation fields are replaced entirely; pass the full desired array
await client.records.update('company', recordId, {
domain: ['acme.com', 'acme.io'],
})
// Get
await client.records.get('company', recordId)
// Delete
await client.records.delete('company', recordId)Filtering with attributes
const attrs = await client.attributes.list({ record_type: 'company' })
// pick a field permalink from attrs.data, then:
await client.records.search('company', {
filters: {
operator: 'AND',
conditions: [{ field: 'headcount', operator: 'gte', value: 50 }],
},
sort: [{ field: 'name', direction: 'asc' }],
limit: 25,
})Lists
const { data: newList } = await client.lists.create({
name: 'Hot leads',
record_type: 'company',
})
const listId = (newList as any).id
await client.lists.addRecord(listId, '…record_uuid…')
await client.lists.getRecords(listId, { limit: 50 })Notes
await client.notes.create({
title: 'Intro call',
content: '<p>Met the founder, looking strong.</p>',
records: { company: ['…uuid…'], person: ['…uuid…'] },
tags: ['follow_up'],
})
// Manage the workspace tag vocabulary and attach tags to a note.
const { data: tag } = await client.noteTags.create({ label: 'Follow up' })
await client.notes.addTags('…note_uuid…', [tag.permalink])Tasks
await client.tasks.create({
title: 'Send follow-up',
due_date: '2026-06-01',
assigned_to: ['…user_uuid…'],
records: { company: ['…uuid…'] },
})tasks.list() filters and sorts on these fields: title, status,
priority, due_date, created_at, updated_at, assigned_to,
created_by, records, plus any custom task field permalinks. Status values
are workspace-specific (fetch the valid values with
client.options.list('task', 'status')).
await client.tasks.list({
filters: {
operator: 'AND',
conditions: [{ field: 'status', operator: 'eq', value: 'todo' }],
},
sort: [{ field: 'due_date', direction: 'asc' }],
limit: 50,
})Discovery
// Always inspect attributes first to discover valid field keys
await client.discovery.getAttributes({ type: 'company' })
// Search Lighthouse's global database
const companies = await client.discovery.searchCompanies({
filters: {
operator: 'AND',
conditions: [
{ field: 'company_hq_country', operator: 'contains_any', value: ['US'] },
{
field: 'company_headcount',
operator: 'between',
value: { min: 10, max: 200 },
},
],
},
limit: 25,
})
// Enrich by domain / LinkedIn
await client.discovery.lookupCompaniesByDomain(['stripe.com', 'openai.com'])
// Semantic search — rank by a plain-language query (optionally narrowed by
// the same filters). min_similarity (0–0.99) drops weak matches.
await client.discovery.semanticSearchCompanies({
query: 'AI-native developer tooling startups in Europe',
filters: {
operator: 'AND',
conditions: [{ field: 'company_hq_country', operator: 'contains_any', value: ['GB', 'DE', 'FR'] }],
},
min_similarity: 0.4,
limit: 25,
})Documents
// One-shot helper handles reserve + S3 pre-signed POST upload (size is set
// server-side from S3; confirmed automatically on first read)
const file = await client.documents.upload({
name: 'pitch.pdf',
content: pitchBlob, // Blob | ArrayBuffer | Uint8Array
content_type: 'application/pdf',
sharing_status: 'WORKSPACE',
})
const fileId = (file.data as any).id
await client.documents.linkFileToRecord(fileId, 'company', '…uuid…')Error handling
import { Lighthouse, LighthouseError } from 'lighthouse-private-markets-sdk'
const client = new Lighthouse('lgt_live_…')
const res = await client.records.search('company')
if (res.error) {
console.error('API error:', res.status, res.error)
} else {
console.log(res.data)
}Network failures and malformed JSON responses throw LighthouseError.
Low-level escape hatch
For endpoints not yet wrapped by a namespace:
await client.request('GET', '/v1/some/new/endpoint', {
params: { foo: 'bar' },
})
await client.request('POST', '/v1/some/other', {
body: { hello: 'world' },
})License
MIT
