@skrybe/sdk 1.0.0 → 2.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Skrybe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,140 +1,221 @@
1
- # Skrybe TypeScript/JavaScript SDK
1
+ # @skrybe/sdk
2
2
 
3
- Official TypeScript/JavaScript SDK for the Skrybe API.
3
+ The official TypeScript/JavaScript SDK for the [Skrybe](https://skry.be) API:
4
+ email campaigns, transactional email, lists, subscribers, templates and
5
+ autoresponders.
4
6
 
5
- ## Installation
7
+ - **No dependencies.** It uses the built-in `fetch`, so installing it pulls in
8
+ nothing but this package.
9
+ - **Runs anywhere `fetch` does:** Node.js 18+, Bun, Deno, Cloudflare Workers
10
+ and other edge runtimes. Ships ESM and CommonJS, with types.
11
+ - **Typed errors.** The API often reports failures as a sentence with HTTP 200;
12
+ the SDK turns every one into a `SkrybeError` with a stable `code`.
13
+
14
+ > Your API key can send email as your brand. Use the SDK server-side only, and
15
+ > never ship the key to a browser.
16
+
17
+ ## Install
6
18
 
7
19
  ```bash
8
20
  npm install @skrybe/sdk
9
21
  ```
10
22
 
11
- ## Usage
23
+ ## Quick start
12
24
 
13
- ### Initialize the SDK
25
+ ```ts
26
+ import { Skrybe } from '@skrybe/sdk'
14
27
 
15
- ```typescript
16
- import { SkrybeSDK } from '@skrybe/sdk';
28
+ const skrybe = new Skrybe({ apiKey: process.env.SKRYBE_API_KEY! })
17
29
 
18
- const skrybe = new SkrybeSDK({
19
- apiKey: 'your-api-key'
20
- });
30
+ const lists = await skrybe.lists.list()
31
+
32
+ await skrybe.subscribers.subscribe({
33
+ listId: lists[0].id,
34
+ email: 'ada@example.com',
35
+ name: 'Ada',
36
+ fields: { Birthday: '1990-12-10' },
37
+ })
21
38
  ```
22
39
 
23
- ### Send an Email
40
+ CommonJS works too: `const { Skrybe } = require('@skrybe/sdk')`.
41
+
42
+ ### Options
24
43
 
25
- ```typescript
26
- const response = await skrybe.sendEmail({
27
- fromName: 'John Doe',
28
- fromEmail: 'john@example.com',
29
- replyTo: 'support@example.com',
30
- subject: 'Welcome!',
31
- htmlText: '<html><body>Hello [recipient.first]!</body></html>',
44
+ | Option | Default | |
45
+ |---|---|---|
46
+ | `apiKey` | required | A brand's API key, from Settings in the Skrybe dashboard. |
47
+ | `baseUrl` | `https://dashboard.skry.be` | |
48
+ | `timeoutMs` | `30000` | Per attempt. |
49
+ | `maxRetries` | `2` | Extra attempts for **reads** after a dropped connection or a 429/502/503/504. Writes are never retried: the API has no idempotency keys, so a retried send could go out twice. |
50
+ | `fetch` | `globalThis.fetch` | Your own fetch, e.g. to go through a proxy or trust a corporate CA. |
51
+
52
+ ## Usage
53
+
54
+ ### Email
55
+
56
+ ```ts
57
+ // Transactional: one email, delivered immediately. OTPs, resets, receipts.
58
+ await skrybe.emails.sendTransactional({
59
+ toEmail: 'ada@example.com',
60
+ subject: 'Your code',
61
+ htmlBody: '<p>Your code is <strong>384092</strong>.</p>',
62
+ plainBody: 'Your code is 384092.',
63
+ })
64
+
65
+ // Bulk: up to 250 addresses, or whole lists. Creates a campaign behind the scenes.
66
+ await skrybe.emails.send({
67
+ fromName: 'Ada',
68
+ fromEmail: 'ada@yourdomain.com',
69
+ replyTo: 'ada@yourdomain.com',
70
+ subject: 'Hello [recipient.first]',
71
+ htmlText: '<p>Hi [recipient.first]!</p>',
32
72
  to: ['user@example.com'],
33
- recipientVariables: {
34
- 'user@example.com': {
35
- first: 'John'
36
- }
37
- },
38
- trackOpens: 1,
39
- trackClicks: 1
40
- });
73
+ recipientVariables: { 'user@example.com': { first: 'John' } },
74
+ })
41
75
  ```
42
76
 
43
- ### Create a Campaign
44
-
45
- ```typescript
46
- const response = await skrybe.createCampaign({
47
- fromName: 'John Doe',
48
- fromEmail: 'john@example.com',
49
- replyTo: 'support@example.com',
50
- title: 'Monthly Newsletter',
51
- subject: 'Your Monthly Update',
52
- htmlText: '<html><body>Newsletter content here</body></html>',
53
- listIds: 'list-id-1,list-id-2',
54
- trackOpens: 1,
55
- trackClicks: 1,
56
- sendCampaign: 1
57
- });
77
+ ### Campaigns
78
+
79
+ ```ts
80
+ const { campaignId } = await skrybe.campaigns.create({
81
+ fromName: 'Ada',
82
+ fromEmail: 'ada@yourdomain.com',
83
+ replyTo: 'ada@yourdomain.com',
84
+ title: 'October newsletter',
85
+ subject: 'What we shipped',
86
+ htmlText: '<p>…</p>',
87
+ })
88
+
89
+ const id = Number(campaignId)
90
+ await skrybe.campaigns.testSend(id, ['me@yourdomain.com'])
91
+
92
+ // Count the recipients without sending, then send.
93
+ const { recipients } = await skrybe.campaigns.send(id, { listIds: [listId], dryRun: true })
94
+ await skrybe.campaigns.send(id, { listIds: [listId] })
95
+
96
+ // Or schedule it. Anything PHP's strtotime() reads.
97
+ await skrybe.campaigns.schedule(id, { listIds: [listId], at: '2027-06-15 18:05', timezone: 'Africa/Lagos' })
98
+
99
+ const stats = await skrybe.campaigns.stats(id)
100
+ const clickers = await skrybe.campaigns.activityAll(id, 'clicks')
58
101
  ```
59
102
 
60
- ### Get Lists
103
+ Also: `list`, `listAll`, `get`, `update`, `duplicate`, `delete`,
104
+ `unschedule`, `activity`.
61
105
 
62
- ```typescript
63
- const lists = await skrybe.getLists();
64
- // To include hidden lists
65
- const allLists = await skrybe.getLists(true);
106
+ ### Lists, subscribers and custom fields
107
+
108
+ ```ts
109
+ const list = await skrybe.lists.create({ name: 'Customers', optIn: 'single' })
110
+ await skrybe.lists.addCustomField(list.id, { name: 'Birthday', type: 'date' })
111
+
112
+ const outcome = await skrybe.subscribers.subscribe({ listId: list.id, email: 'ada@example.com' })
113
+ // 'subscribed', or 'already_subscribed' (which updates the name and fields)
114
+
115
+ const status = await skrybe.subscribers.status({ listId: list.id, email: 'ada@example.com' })
116
+ const active = await skrybe.subscribers.listAll(list.id, { status: 'active' })
66
117
  ```
67
118
 
68
- ## API Reference
119
+ List ids are encrypted strings: pass them back exactly as the API returns them.
69
120
 
70
- ### SkrybeSDK Configuration
121
+ Lists also has `get`, `update`, `delete`, `activeSubscriberCount`,
122
+ `renameCustomField` and `deleteCustomField`. Subscribers also has `unsubscribe`,
123
+ `delete` (removes the row outright) and `list` (one page).
71
124
 
72
- ```typescript
73
- interface SkrybeConfig {
74
- apiKey: string;
75
- baseURL?: string; // Defaults to https://dashboard.skry.be
76
- }
125
+ ### Templates
126
+
127
+ ```ts
128
+ const t = await skrybe.templates.create({ name: 'Welcome', htmlText: '<p>Welcome!</p>' })
129
+ await skrybe.templates.update(t.id, { fromName: 'Ada' })
77
130
  ```
78
131
 
79
- ### SendEmailOptions
80
-
81
- ```typescript
82
- interface SendEmailOptions {
83
- fromName: string;
84
- fromEmail: string;
85
- replyTo: string;
86
- subject: string;
87
- htmlText: string;
88
- plainText?: string;
89
- to?: string[];
90
- recipientVariables?: Record<string, Record<string, any>>;
91
- listIds?: string[];
92
- queryString?: string;
93
- trackOpens?: 0 | 1 | 2;
94
- trackClicks?: 0 | 1 | 2;
95
- scheduleDateTime?: string;
96
- scheduleTimezone?: string;
97
- }
132
+ Also `list`, `get` (`{ includeContent: true }` for the HTML) and `delete`.
133
+
134
+ ### Autoresponders
135
+
136
+ ```ts
137
+ const drip = await skrybe.autoresponders.create({ name: 'Onboarding', type: 'drip', listId })
138
+ await skrybe.autoresponders.createEmail(drip.id, {
139
+ subject: 'Day 3: getting started',
140
+ htmlText: '<p>…</p>',
141
+ when: '+3 days',
142
+ enabled: true, // emails are created switched off unless you say otherwise
143
+ })
144
+
145
+ // annually and on_date send on a Date custom field of the list
146
+ await skrybe.autoresponders.create({ name: 'Birthdays', type: 'annually', listId, customField: 'Birthday' })
98
147
  ```
99
148
 
100
- ### CreateCampaignOptions
101
-
102
- ```typescript
103
- interface CreateCampaignOptions {
104
- fromName: string;
105
- fromEmail: string;
106
- replyTo: string;
107
- title: string;
108
- subject: string;
109
- htmlText: string;
110
- plainText?: string;
111
- listIds?: string;
112
- segmentIds?: string;
113
- excludeListIds?: string;
114
- excludeSegmentIds?: string;
115
- queryString?: string;
116
- trackOpens?: 0 | 1 | 2;
117
- trackClicks?: 0 | 1 | 2;
118
- sendCampaign?: 0 | 1;
119
- scheduleDateTime?: string;
120
- scheduleTimezone?: string;
121
- }
149
+ Also `list`, `get` (with its emails) and `emailStats`.
150
+
151
+ ### Brands and stats
152
+
153
+ ```ts
154
+ const [brand] = await skrybe.brands.list() // the key's own brand
155
+ const sent = await skrybe.stats.totalEmailsSent() // installation-wide, public
122
156
  ```
123
157
 
124
- ## Error Handling
158
+ ## Errors
159
+
160
+ Every failure is a `SkrybeError` with a machine-readable `code`, a `type`, and
161
+ often a `hint`. Branch on the subclass or on `type`:
125
162
 
126
- The SDK throws errors for invalid API keys, missing required parameters, and other API errors. Make sure to wrap your API calls in try-catch blocks:
163
+ | Class | `type` | When |
164
+ |---|---|---|
165
+ | `AuthenticationError` | `authentication` | The API key is missing or wrong. |
166
+ | `InvalidRequestError` | `invalid_request` | The request is wrong; retrying it unchanged fails again. |
167
+ | `RateLimitError` | `rate_limit` | A quota, allowance or rate limit was hit. |
168
+ | `ConnectionError` | `connection` | The API could not be reached, or timed out (`code: 'timeout'`). |
169
+ | `ApiError` | `api` | Anything else, e.g. `list_not_found`, `campaign_not_draft`. |
170
+
171
+ ```ts
172
+ import { SkrybeError, RateLimitError } from '@skrybe/sdk'
127
173
 
128
- ```typescript
129
174
  try {
130
- const response = await skrybe.sendEmail({
131
- // ... options
132
- });
133
- } catch (error) {
134
- console.error('Error sending email:', error.message);
175
+ await skrybe.campaigns.send(id, { listIds: [listId] })
176
+ } catch (err) {
177
+ if (err instanceof RateLimitError) {
178
+ // quota_exceeded, rate_limited
179
+ } else if (err instanceof SkrybeError && err.code === 'campaign_not_draft') {
180
+ // already sent
181
+ } else {
182
+ throw err
183
+ }
135
184
  }
136
185
  ```
137
186
 
187
+ `err.raw` holds the response body and `err.status` the HTTP status, for
188
+ debugging.
189
+
190
+ ## Security
191
+
192
+ - **TLS is always verified.** There is no option to turn it off. Use the
193
+ `fetch` option to supply a custom CA or proxy instead.
194
+ - **Keep your runtime's CA store current.** Node.js bundles its own list of
195
+ trusted certificate authorities, so CA distrust events (such as eMudhra's in
196
+ October 2026) only reach you when you update Node, or when you run with
197
+ `--use-system-ca` on an updated OS.
198
+ - **No install scripts and no dependencies**, so there is no supply chain
199
+ beyond this package. Releases are published from GitHub Actions with
200
+ [npm provenance](https://docs.npmjs.com/generating-provenance-statements);
201
+ check with `npm audit signatures`.
202
+
203
+ ## Upgrading from 1.x
204
+
205
+ 2.0 is a rewrite. Methods moved onto resources and return parsed values instead
206
+ of raw response bodies:
207
+
208
+ | 1.x | 2.x |
209
+ |---|---|
210
+ | `new SkrybeSDK({ apiKey, baseURL })` | `new Skrybe({ apiKey, baseUrl })` |
211
+ | `sendEmail(opts)` | `emails.send(opts)`, returns `{ message, campaignId }` |
212
+ | `sendTransactional(opts)` | `emails.sendTransactional(opts)`, returns `{ message, messageId }` |
213
+ | `createCampaign(opts)` | `campaigns.create(opts)`. `listIds` etc. are now arrays and `sendCampaign: 1` is `send: true`. Returns `{ outcome, campaignId }`. |
214
+ | `getLists(includeHidden)` | `lists.list({ includeHidden })`, returns `[{ id, name }]` |
215
+
216
+ Failures now throw instead of resolving with an error string. The axios
217
+ dependency is gone.
218
+
138
219
  ## License
139
220
 
140
- MIT
221
+ MIT