geodedo 0.1.0

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 ADDED
@@ -0,0 +1,414 @@
1
+ # GeoDedo TypeScript SDK
2
+
3
+ Official TypeScript/JavaScript SDK for the [GeoDedo API](https://geodedo-api.vercel.app) — a white-label GTM outreach platform covering contact search, email/LinkedIn/Instagram/SMS messaging, AI chat, and more.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install geodedo
9
+ ```
10
+
11
+ > Requires Node.js 18+ (uses the built-in `fetch` API).
12
+
13
+ ## Quick Start
14
+
15
+ ```ts
16
+ import { GeoDedo } from 'geodedo';
17
+
18
+ const client = new GeoDedo({ apiKey: 'gd_live_xxx' });
19
+
20
+ const contacts = await client.contacts.search({ titles: ['CEO'], locations: ['San Francisco'] });
21
+ console.log(contacts.data);
22
+ ```
23
+
24
+ ## Authentication
25
+
26
+ Get your API key at [geodedo-api.vercel.app](https://geodedo-api.vercel.app) — sign up, go to Dashboard, and copy your key.
27
+
28
+ Pass the key directly:
29
+
30
+ ```ts
31
+ const client = new GeoDedo({ apiKey: 'gd_live_xxx' });
32
+ ```
33
+
34
+ Or set the `GEODEDO_API_KEY` environment variable and omit the option:
35
+
36
+ ```ts
37
+ const client = new GeoDedo();
38
+ ```
39
+
40
+ ## Multi-User Support
41
+
42
+ If you are building a platform on top of GeoDedo and need to scope API calls to individual end-users, pass a `userId`:
43
+
44
+ ```ts
45
+ const client = new GeoDedo({
46
+ apiKey: 'gd_live_xxx',
47
+ userId: 'user_abc123',
48
+ });
49
+ ```
50
+
51
+ The `userId` is sent as an `X-User-Id` header on every request. You can also override it per-request:
52
+
53
+ ```ts
54
+ const contacts = await client.contacts.list(undefined, { userId: 'user_other' });
55
+ ```
56
+
57
+ ## Configuration
58
+
59
+ | Option | Type | Default | Description |
60
+ |--------|------|---------|-------------|
61
+ | `apiKey` | `string` | `process.env.GEODEDO_API_KEY` | API key for authentication |
62
+ | `userId` | `string` | — | Default user ID for user-scoped operations |
63
+ | `baseURL` | `string` | `https://geodedo-api.vercel.app` | API base URL |
64
+ | `maxRetries` | `number` | `2` | Max retries on 429/5xx and network errors |
65
+ | `timeout` | `number` | `60000` | Request timeout in milliseconds |
66
+ | `dangerouslyAllowBrowser` | `boolean` | `false` | Allow usage in browser environments |
67
+
68
+ ## Resources
69
+
70
+ ### Contacts
71
+
72
+ Search, enrich, import, and list contacts.
73
+
74
+ | Method | Signature | Credits | Description |
75
+ |--------|-----------|---------|-------------|
76
+ | `search` | `search(params: ContactSearchParams): Promise<Page<Contact>>` | 10 | Search contacts by ICP criteria |
77
+ | `enrich` | `enrich(params: ContactEnrichParams): Promise<Contact>` | 25 | Enrich a single contact |
78
+ | `importCsv` | `importCsv(params: ContactImportParams): Promise<ImportResult>` | 1 | Import a batch of contacts |
79
+ | `list` | `list(params?): Promise<Page<StoredContact>>` | 1 | List stored contacts (paginated) |
80
+ | `csvLists` | `csvLists(): Promise<CsvList[]>` | 1 | List all CSV import lists |
81
+
82
+ ```ts
83
+ // Search for VP-level contacts in tech
84
+ const page = await client.contacts.search({
85
+ titles: ['VP Engineering', 'VP Product'],
86
+ locations: ['New York'],
87
+ employeeRanges: ['51-200'],
88
+ perPage: 10,
89
+ });
90
+
91
+ // Enrich a contact by email
92
+ const contact = await client.contacts.enrich({ email: 'jane@example.com' });
93
+ ```
94
+
95
+ ### Sequences
96
+
97
+ Create and manage automated outreach sequences.
98
+
99
+ | Method | Signature | Credits | Description |
100
+ |--------|-----------|---------|-------------|
101
+ | `create` | `create(params: SequenceCreateParams): Promise<Sequence>` | 5 | Create a new sequence |
102
+ | `list` | `list(params?): Promise<Page<SequenceWithStats>>` | 1 | List all sequences (paginated) |
103
+ | `get` | `get(sequenceId: string): Promise<SequenceWithStats>` | 1 | Get a sequence by ID |
104
+ | `pause` | `pause(sequenceId: string): Promise<Sequence>` | 1 | Pause a running sequence |
105
+ | `resume` | `resume(sequenceId: string): Promise<Sequence>` | 1 | Resume a paused sequence |
106
+ | `stop` | `stop(sequenceId: string): Promise<Sequence>` | 1 | Stop (terminate) a sequence |
107
+ | `status` | `status(sequenceId: string): Promise<SequenceWithStats>` | 1 | Get sequence status with stats |
108
+
109
+ ```ts
110
+ const seq = await client.sequences.create({
111
+ name: 'Q2 Outreach',
112
+ icp: 'SaaS founders, Series A, US-based',
113
+ channelType: 'email',
114
+ contactsPerDay: 50,
115
+ startHour: 9,
116
+ endHour: 17,
117
+ activeDays: ['mon', 'tue', 'wed', 'thu', 'fri'],
118
+ timezone: 'America/New_York',
119
+ });
120
+
121
+ const status = await client.sequences.status(seq.id);
122
+ console.log(status.stats); // { sent: 12, replied: 3 }
123
+ ```
124
+
125
+ ### Drafts
126
+
127
+ Generate AI-written outreach drafts, review, approve, and send them.
128
+
129
+ | Method | Signature | Credits | Description |
130
+ |--------|-----------|---------|-------------|
131
+ | `generate` | `generate(params: DraftGenerateParams): Promise<DraftBatchResult>` | 20 | Generate drafts for contacts |
132
+ | `list` | `list(params?): Promise<Page<Draft>>` | 1 | List drafts (paginated, filterable) |
133
+ | `update` | `update(draftId: string, params: DraftUpdateParams): Promise<Draft>` | 1 | Edit a draft's subject/body |
134
+ | `approve` | `approve(params: DraftApproveParams): Promise<DraftApproveResult>` | 1 | Approve drafts by ID |
135
+ | `send` | `send(params: DraftSendParams): Promise<DraftSendResult>` | 2 | Send approved drafts |
136
+
137
+ ```ts
138
+ const batch = await client.drafts.generate({
139
+ contacts: [
140
+ { fullName: 'Jane Smith', title: 'CTO', company: 'Acme', email: 'jane@acme.com' },
141
+ ],
142
+ channel: 'email',
143
+ context: 'Introducing our developer tools platform',
144
+ });
145
+
146
+ // Review and send
147
+ await client.drafts.approve({ draftIds: batch.drafts.map(d => d.id) });
148
+ const result = await client.drafts.send({ draftIds: batch.drafts.map(d => d.id) });
149
+ console.log(`Sent: ${result.sent}, Failed: ${result.failed}`);
150
+ ```
151
+
152
+ ### Messages
153
+
154
+ Send messages directly across channels.
155
+
156
+ | Method | Signature | Credits | Description |
157
+ |--------|-----------|---------|-------------|
158
+ | `sendEmail` | `sendEmail(params: EmailSendParams): Promise<EmailResult>` | 2 | Send an email |
159
+ | `sendLinkedIn` | `sendLinkedIn(params: LinkedInSendParams): Promise<LinkedInResult>` | 10 | Send a LinkedIn message |
160
+ | `sendInstagram` | `sendInstagram(params: InstagramSendParams): Promise<InstagramResult>` | 10 | Send an Instagram DM |
161
+ | `sendSms` | `sendSms(params: SmsSendParams): Promise<SmsResult>` | 5 | Send an SMS |
162
+ | `inbox` | `inbox(channel: InboxChannel): Promise<Record<string, unknown>>` | 2 | Get unified inbox for a channel |
163
+
164
+ ```ts
165
+ const result = await client.messages.sendEmail({
166
+ to: 'lead@example.com',
167
+ subject: 'Quick question',
168
+ body: 'Hi, I wanted to reach out about...',
169
+ provider: 'gmail',
170
+ });
171
+ console.log(result.sent); // true
172
+ ```
173
+
174
+ ### Chat
175
+
176
+ Interact with the GeoDedo AI assistant. Conversations persist across messages.
177
+
178
+ | Method | Signature | Credits | Description |
179
+ |--------|-----------|---------|-------------|
180
+ | `create` | `create(params: ChatCreateParams): Promise<ChatResponse>` | 5 | Send a message to the AI |
181
+ | `conversations.list` | `conversations.list(): Promise<Conversation[]>` | 1 | List all conversations |
182
+ | `conversations.get` | `conversations.get(id: string): Promise<ConversationMessage[]>` | 1 | Get messages in a conversation |
183
+ | `conversations.delete` | `conversations.delete(id: string): Promise<void>` | 0 | Delete a conversation |
184
+
185
+ ```ts
186
+ // Start a conversation
187
+ const chat = await client.chat.create({ message: 'Find me SaaS CTOs in NYC' });
188
+ console.log(chat.response);
189
+
190
+ // Continue the conversation
191
+ const followUp = await client.chat.create({
192
+ message: 'Now create an email sequence for them',
193
+ conversationId: chat.conversationId,
194
+ });
195
+ ```
196
+
197
+ ### Channels
198
+
199
+ Connect and manage outreach channels (Gmail, LinkedIn, Instagram, AgentMail, SMS).
200
+
201
+ | Method | Signature | Credits | Description |
202
+ |--------|-----------|---------|-------------|
203
+ | `status` | `status(): Promise<ChannelStatus>` | 0 | Get status of all channels |
204
+ | `disconnect` | `disconnect(channel: ChannelName): Promise<void>` | 0 | Disconnect a channel |
205
+ | `gmail.connect` | `gmail.connect(): Promise<GmailConnectResult>` | 0 | Start Gmail OAuth flow |
206
+ | `linkedin.connect` | `linkedin.connect(params): Promise<ConnectionResult>` | 0 | Connect LinkedIn |
207
+ | `linkedin.checkpoint` | `linkedin.checkpoint(params): Promise<ConnectionResult>` | 0 | Resolve LinkedIn 2FA |
208
+ | `instagram.connect` | `instagram.connect(params): Promise<ConnectionResult>` | 0 | Connect Instagram |
209
+ | `instagram.checkpoint` | `instagram.checkpoint(params): Promise<ConnectionResult>` | 0 | Resolve Instagram 2FA |
210
+ | `agentmail.create` | `agentmail.create(params): Promise<AgentMailCreateResult>` | 0 | Create an AgentMail address |
211
+ | `sms.verify` | `sms.verify(params): Promise<SmsVerifyResult>` | 0 | Start SMS verification |
212
+ | `sms.confirm` | `sms.confirm(params): Promise<SmsVerifyResult>` | 0 | Confirm SMS with code |
213
+
214
+ ```ts
215
+ // Check which channels are connected
216
+ const status = await client.channels.status();
217
+ console.log(status.gmail.connected); // true
218
+
219
+ // Connect Gmail (returns an OAuth URL)
220
+ const gmail = await client.channels.gmail.connect();
221
+ console.log(gmail.authUrl); // User visits this URL
222
+
223
+ // Create an AgentMail address
224
+ const mail = await client.channels.agentmail.create({ username: 'outreach' });
225
+ console.log(mail.email); // outreach@agentmail.geodedo.com
226
+ ```
227
+
228
+ ### Documents
229
+
230
+ Upload and manage documents for AI context.
231
+
232
+ | Method | Signature | Credits | Description |
233
+ |--------|-----------|---------|-------------|
234
+ | `upload` | `upload(params: DocumentUploadParams): Promise<Document>` | 1 | Upload a document |
235
+ | `list` | `list(params?): Promise<Page<Document>>` | 1 | List documents (paginated) |
236
+ | `delete` | `delete(documentId: string): Promise<void>` | 0 | Delete a document |
237
+
238
+ ```ts
239
+ const doc = await client.documents.upload({
240
+ filename: 'product-overview.txt',
241
+ content: 'Our platform helps sales teams...',
242
+ });
243
+ ```
244
+
245
+ ### Recommendations
246
+
247
+ Get AI-powered recommendations for your outreach strategy.
248
+
249
+ | Method | Signature | Credits | Description |
250
+ |--------|-----------|---------|-------------|
251
+ | `icpAnalysis` | `icpAnalysis(): Promise<ICPAnalysis>` | 5 | Analyze ideal customer profile |
252
+ | `channel` | `channel(): Promise<ChannelRecommendation>` | 5 | Get channel recommendation |
253
+ | `sequenceStrategy` | `sequenceStrategy(): Promise<SequenceStrategy>` | 5 | Get sequence strategy advice |
254
+
255
+ ```ts
256
+ const icp = await client.recommendations.icpAnalysis();
257
+ console.log(icp.recommendation);
258
+ ```
259
+
260
+ ### Billing
261
+
262
+ Check credit balance and track usage.
263
+
264
+ | Method | Signature | Credits | Description |
265
+ |--------|-----------|---------|-------------|
266
+ | `balance` | `balance(): Promise<CreditBalance>` | 0 | Get current credit balance |
267
+ | `usage` | `usage(params?: UsageListParams): Promise<Page<UsageEntry>>` | 0 | List usage entries (paginated) |
268
+ | `usageSummary` | `usageSummary(): Promise<UsageSummary>` | 0 | Get aggregated usage summary |
269
+
270
+ ```ts
271
+ const balance = await client.billing.balance();
272
+ console.log(`${balance.credits_remaining} credits remaining`);
273
+
274
+ const summary = await client.billing.usageSummary();
275
+ for (const entry of summary.summary) {
276
+ console.log(`${entry.operation}: ${entry.total_calls} calls, ${entry.total_credits} credits`);
277
+ }
278
+ ```
279
+
280
+ ### Users
281
+
282
+ Register and manage user profiles.
283
+
284
+ | Method | Signature | Credits | Description |
285
+ |--------|-----------|---------|-------------|
286
+ | `register` | `register(params: UserRegisterParams): Promise<User>` | 0 | Register a new user |
287
+ | `me` | `me(): Promise<User>` | 0 | Get current user profile |
288
+ | `update` | `update(params: UserUpdateParams): Promise<User>` | 0 | Update user profile |
289
+
290
+ ```ts
291
+ const user = await client.users.register({
292
+ externalUserId: 'usr_12345',
293
+ aboutMe: 'Sales leader at Acme Corp',
294
+ icp: 'SaaS CTOs at Series A startups',
295
+ cta: 'Book a 15-min demo call',
296
+ });
297
+ ```
298
+
299
+ ## Pagination
300
+
301
+ List endpoints return a `Page<T>` object that supports async iteration:
302
+
303
+ ### Async Iteration (recommended)
304
+
305
+ Automatically fetches all pages:
306
+
307
+ ```ts
308
+ for await (const contact of await client.contacts.search({ titles: ['CEO'] })) {
309
+ console.log(contact.fullName);
310
+ }
311
+ ```
312
+
313
+ ### Manual Page Access
314
+
315
+ ```ts
316
+ const page = await client.contacts.list({ page: 1, perPage: 25 });
317
+ console.log(page.data); // items on this page
318
+ console.log(page.total); // total items across all pages
319
+ console.log(page.page); // current page number (1-indexed)
320
+
321
+ if (page.hasNextPage()) {
322
+ const next = await page.getNextPage();
323
+ console.log(next.data);
324
+ }
325
+ ```
326
+
327
+ ## Error Handling
328
+
329
+ All errors extend `GeoDedoError`. API errors include the HTTP status code and response body:
330
+
331
+ ```ts
332
+ import {
333
+ GeoDedo,
334
+ APIError,
335
+ AuthenticationError,
336
+ InsufficientCreditsError,
337
+ RateLimitError,
338
+ ValidationError,
339
+ NotFoundError,
340
+ } from 'geodedo';
341
+
342
+ try {
343
+ await client.contacts.search({ titles: ['CEO'] });
344
+ } catch (err) {
345
+ if (err instanceof InsufficientCreditsError) {
346
+ console.log(`Need ${err.creditsRequired} credits, have ${err.creditsRemaining}`);
347
+ } else if (err instanceof AuthenticationError) {
348
+ console.log('Invalid API key');
349
+ } else if (err instanceof RateLimitError) {
350
+ console.log(`Rate limited — retry after ${err.retryAfter}s`);
351
+ } else if (err instanceof ValidationError) {
352
+ console.log('Invalid request:', err.message);
353
+ } else if (err instanceof NotFoundError) {
354
+ console.log('Resource not found');
355
+ } else if (err instanceof APIError) {
356
+ console.log(`API error ${err.status}: ${err.message}`);
357
+ }
358
+ }
359
+ ```
360
+
361
+ ### Error Classes
362
+
363
+ | Class | Status | Description |
364
+ |-------|--------|-------------|
365
+ | `GeoDedoError` | — | Base class for all SDK errors |
366
+ | `APIError` | any | Generic API error with status/body |
367
+ | `AuthenticationError` | 401 | Invalid or missing API key |
368
+ | `InsufficientCreditsError` | 402 | Not enough credits |
369
+ | `PermissionDeniedError` | 403 | Not authorized for this operation |
370
+ | `NotFoundError` | 404 | Resource not found |
371
+ | `ValidationError` | 422 | Invalid request parameters |
372
+ | `RateLimitError` | 429 | Rate limit exceeded (has `retryAfter`) |
373
+ | `InternalServerError` | 5xx | Server error |
374
+ | `APIConnectionError` | — | Network-level failure |
375
+ | `APITimeoutError` | — | Request timed out |
376
+
377
+ ## Per-Request Options
378
+
379
+ Every method accepts an optional `RequestOptions` object as the last argument:
380
+
381
+ ```ts
382
+ await client.contacts.search(
383
+ { titles: ['CEO'] },
384
+ {
385
+ apiKey: 'gd_live_override', // Override API key
386
+ userId: 'user_override', // Override user ID
387
+ timeout: 30000, // Override timeout (ms)
388
+ headers: { 'X-Custom': '1' }, // Additional headers
389
+ signal: AbortSignal.timeout(5000), // Cancel with AbortSignal
390
+ },
391
+ );
392
+ ```
393
+
394
+ ## Retries
395
+
396
+ The SDK automatically retries on:
397
+ - HTTP 429 (rate limit), 409, 500, 502, 503, 504
398
+ - Network connection errors
399
+ - Request timeouts
400
+
401
+ Retries use exponential backoff with jitter (500ms base, 5s max). Configure with `maxRetries` (default: 2).
402
+
403
+ ## Examples
404
+
405
+ See the [`examples/`](./examples) directory for complete working scripts:
406
+
407
+ - [`search-contacts.ts`](./examples/search-contacts.ts) — Search and enrich contacts
408
+ - [`create-sequence.ts`](./examples/create-sequence.ts) — Create and monitor a sequence
409
+ - [`send-email.ts`](./examples/send-email.ts) — Send an email and check balance
410
+ - [`ai-chat.ts`](./examples/ai-chat.ts) — Chat with the AI assistant
411
+
412
+ ## License
413
+
414
+ MIT