fdbck-node 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 +315 -0
- package/dist/index.d.mts +235 -0
- package/dist/index.d.ts +235 -0
- package/dist/index.js +393 -0
- package/dist/index.mjs +362 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# @fdbck/node
|
|
2
|
+
|
|
3
|
+
Official Node.js SDK for [fdbck](https://fdbck.sh) (read feedback) - a simple API to programmatically collect and structure feedback from your users.
|
|
4
|
+
|
|
5
|
+
Full TypeScript definitions included.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { Fdbck } from '@fdbck/node';
|
|
9
|
+
|
|
10
|
+
const fdbck = new Fdbck('sk_fdbck_...');
|
|
11
|
+
|
|
12
|
+
const question = await fdbck.questions.create({
|
|
13
|
+
question: 'How was your first purchase?',
|
|
14
|
+
type: 'rating',
|
|
15
|
+
ratingConfig: { min: 1, max: 5 },
|
|
16
|
+
expiresIn: 172800,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const token = await fdbck.tokens.create(question.id, {
|
|
20
|
+
respondent: 'user_8f2a',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Trigger the question in your app using the sdk
|
|
24
|
+
// ...
|
|
25
|
+
|
|
26
|
+
// Or send this URL to your user — they answer on fdbck's hosted page
|
|
27
|
+
console.log(token.respondUrl);
|
|
28
|
+
|
|
29
|
+
// Get a webhook for each response
|
|
30
|
+
// ...
|
|
31
|
+
|
|
32
|
+
// Later, read the results
|
|
33
|
+
const results = await fdbck.questions.results(question.id);
|
|
34
|
+
console.log(results.data); // [{ respondent: 'user_8f2a', value: 5, ... }]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
npm install @fdbck/node
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Requires Node.js 18 or later. Zero runtime dependencies.
|
|
44
|
+
|
|
45
|
+
## Get your API key
|
|
46
|
+
|
|
47
|
+
Sign up at [dashboard.fdbck.sh](https://dashboard.fdbck.sh) and grab your API key from the **API Keys** page. Keys start with `sk_fdbck_`.
|
|
48
|
+
|
|
49
|
+
## Quick start
|
|
50
|
+
|
|
51
|
+
The typical workflow is: **create a question**, **generate a token** for each respondent, **collect their answer**, and **read the results**.
|
|
52
|
+
|
|
53
|
+
Respondents can answer via fdbck's hosted response page, or directly from your app using a UI SDK ([React](https://github.com/fdbck-sh), [Flutter](https://github.com/fdbck-sh)).
|
|
54
|
+
|
|
55
|
+
### 1. Create a question
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const question = await fdbck.questions.create({
|
|
59
|
+
question: 'How would you rate our onboarding?',
|
|
60
|
+
type: 'rating',
|
|
61
|
+
ratingConfig: {
|
|
62
|
+
min: 1,
|
|
63
|
+
max: 5,
|
|
64
|
+
minLabel: 'Terrible',
|
|
65
|
+
maxLabel: 'Loved it',
|
|
66
|
+
},
|
|
67
|
+
expiresIn: 86400, // 24 hours
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Four question types are available:
|
|
72
|
+
|
|
73
|
+
| Type | Description | Required fields | Response `value` |
|
|
74
|
+
|------|-------------|-----------------|------------------|
|
|
75
|
+
| `yes_no` | Yes or no (options are generated automatically) | — | `true` or `false` |
|
|
76
|
+
| `single_choice` | Pick one option | `options` | `"Option A"` |
|
|
77
|
+
| `multiple_choice` | Pick one or more | `options` | `["Option A", "Option B"]` |
|
|
78
|
+
| `rating` | Numeric scale | `ratingConfig` | `4` |
|
|
79
|
+
|
|
80
|
+
### 2. Generate tokens
|
|
81
|
+
|
|
82
|
+
Each respondent gets a unique, single-use token that authorizes them to answer once.
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
const token = await fdbck.tokens.create(question.id, {
|
|
86
|
+
respondent: 'user_42', // your internal user ID (optional)
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// token.respondUrl → https://fdbck.sh/f/V1StGXR8_Z5jdHi6
|
|
90
|
+
// token.expiresAt → ISO timestamp (1 hour from creation)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Send `token.respondUrl` to your user however you like — email, in-app notification, SMS, etc. They open the link, answer on fdbck's hosted response page, and see a confirmation message. The page does not redirect — if you need to bring users back to your app, include a link in the question text or follow up after you receive the response.
|
|
94
|
+
|
|
95
|
+
You can also embed the question directly in your app using `@fdbck/react` or `@fdbck/flutter` — pass the `token.token` value to the UI component and it handles submission for you.
|
|
96
|
+
|
|
97
|
+
### 3. Read results
|
|
98
|
+
|
|
99
|
+
Poll for responses at any time — they're available as soon as respondents answer.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const results = await fdbck.questions.results(question.id);
|
|
103
|
+
|
|
104
|
+
for (const response of results.data) {
|
|
105
|
+
console.log(response.respondent, response.value);
|
|
106
|
+
// → 'user_42', 5
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Results are paginated — follow the cursor for more
|
|
110
|
+
if (results.pagination.hasMore) {
|
|
111
|
+
const nextPage = await fdbck.questions.results(question.id, {
|
|
112
|
+
cursor: results.pagination.nextCursor,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 4. Or use webhooks
|
|
118
|
+
|
|
119
|
+
Get notified in real time instead of polling.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
const question = await fdbck.questions.create({
|
|
123
|
+
question: 'How was your experience?',
|
|
124
|
+
type: 'yes_no',
|
|
125
|
+
expiresIn: 86400,
|
|
126
|
+
webhookUrl: 'https://myapp.com/hooks/fdbck',
|
|
127
|
+
webhookTrigger: 'each_response', // or 'expiry', 'both'
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Verify incoming webhooks with the signing secret:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
const isValid = fdbck.verifyWebhook(rawBody, signature, webhookSecret);
|
|
135
|
+
// signature = X-FDBCK-Signature header
|
|
136
|
+
// webhookSecret = question.webhookSecret from creation response
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`verifyWebhook` is also available as a standalone import if you don't need a client instance:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { verifyWebhook } from '@fdbck/node';
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## API reference
|
|
146
|
+
|
|
147
|
+
### `new Fdbck(apiKey, options?)`
|
|
148
|
+
|
|
149
|
+
| Option | Type | Default |
|
|
150
|
+
|--------|------|---------|
|
|
151
|
+
| `baseUrl` | `string` | `https://api.fdbck.sh` |
|
|
152
|
+
| `timeout` | `number` | `30000` (ms) |
|
|
153
|
+
|
|
154
|
+
### Questions
|
|
155
|
+
|
|
156
|
+
#### `fdbck.questions.create(options)` → `Question`
|
|
157
|
+
|
|
158
|
+
Creates a new question.
|
|
159
|
+
|
|
160
|
+
| Field | Type | Required | Description |
|
|
161
|
+
|-------|------|----------|-------------|
|
|
162
|
+
| `question` | `string` | Yes | The question text (max 500 chars) |
|
|
163
|
+
| `type` | `QuestionType` | Yes | `yes_no`, `single_choice`, `multiple_choice`, or `rating` |
|
|
164
|
+
| `options` | `string[]` | For choice types | 2–20 answer options |
|
|
165
|
+
| `ratingConfig` | `RatingConfig` | For `rating` | `{ min, max, minLabel?, maxLabel? }` |
|
|
166
|
+
| `expiresIn` or `expiresAt` | `number` or `string` | Yes (exactly one) | Seconds from now, or ISO 8601 timestamp |
|
|
167
|
+
| `maxResponses` | `number` | No | Auto-complete after N responses |
|
|
168
|
+
| `webhookUrl` | `string` | No | HTTPS URL to receive events |
|
|
169
|
+
| `webhookTrigger` | `WebhookTrigger` | No | `each_response`, `expiry`, or `both` |
|
|
170
|
+
| `metadata` | `Record<string, string>` | No | Arbitrary key-value pairs |
|
|
171
|
+
| `themeColor` | `string` | No | Hex color for response page |
|
|
172
|
+
| `themeMode` | `'light' \| 'dark'` | No | Response page theme |
|
|
173
|
+
| `hideBranding` | `boolean` | No | Hide "Powered by fdbck" (paid plans) |
|
|
174
|
+
|
|
175
|
+
#### `fdbck.questions.get(id)` → `Question`
|
|
176
|
+
|
|
177
|
+
Returns a single question by ID.
|
|
178
|
+
|
|
179
|
+
#### `fdbck.questions.list(options?)` → `PaginatedList<Question>`
|
|
180
|
+
|
|
181
|
+
Returns a paginated list of questions.
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
const page = await fdbck.questions.list({ status: 'active', limit: 20 });
|
|
185
|
+
|
|
186
|
+
console.log(page.data); // Question[]
|
|
187
|
+
console.log(page.pagination.hasMore); // boolean
|
|
188
|
+
console.log(page.pagination.nextCursor); // string | null
|
|
189
|
+
|
|
190
|
+
// Fetch the next page
|
|
191
|
+
if (page.pagination.nextCursor) {
|
|
192
|
+
const next = await fdbck.questions.list({
|
|
193
|
+
status: 'active',
|
|
194
|
+
cursor: page.pagination.nextCursor,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
| Option | Type | Description |
|
|
200
|
+
|--------|------|-------------|
|
|
201
|
+
| `status` | `QuestionStatus` | Filter by `active`, `completed`, `expired`, or `cancelled` |
|
|
202
|
+
| `limit` | `number` | Items per page |
|
|
203
|
+
| `cursor` | `string` | Cursor from a previous page's `pagination.nextCursor` |
|
|
204
|
+
|
|
205
|
+
#### `fdbck.questions.listAll(options?)` → `AsyncGenerator<Question>`
|
|
206
|
+
|
|
207
|
+
Auto-paginates through all questions. Same options as `list` except `cursor`.
|
|
208
|
+
|
|
209
|
+
Each page is fetched sequentially as you consume the iterator — there is no prefetching. If you have a large number of questions, be mindful of the number of API calls this produces.
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
for await (const question of fdbck.questions.listAll({ status: 'active' })) {
|
|
213
|
+
console.log(question.id, question.question);
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### `fdbck.questions.results(id, options?)` → `PaginatedList<ResponseItem>`
|
|
218
|
+
|
|
219
|
+
Returns individual responses to a question, paginated.
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
const page = await fdbck.questions.results(question.id, { limit: 50 });
|
|
223
|
+
|
|
224
|
+
for (const response of page.data) {
|
|
225
|
+
console.log(response.respondent); // 'user_42' or null
|
|
226
|
+
console.log(response.value); // answer value (type depends on question type)
|
|
227
|
+
console.log(response.createdAt); // ISO timestamp
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Next page
|
|
231
|
+
if (page.pagination.hasMore) {
|
|
232
|
+
const next = await fdbck.questions.results(question.id, {
|
|
233
|
+
cursor: page.pagination.nextCursor,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Each `ResponseItem` contains:
|
|
239
|
+
|
|
240
|
+
| Field | Type | Description |
|
|
241
|
+
|-------|------|-------------|
|
|
242
|
+
| `id` | `string` | Response ID |
|
|
243
|
+
| `questionId` | `string` | Parent question ID |
|
|
244
|
+
| `value` | `unknown` | The answer — `true`/`false` for yes_no, `"Option A"` for single_choice, `["A", "B"]` for multiple_choice, `4` for rating |
|
|
245
|
+
| `respondent` | `string \| null` | The respondent identifier you passed when creating the token |
|
|
246
|
+
| `createdAt` | `string` | ISO timestamp |
|
|
247
|
+
|
|
248
|
+
#### `fdbck.questions.cancel(id)` → `void`
|
|
249
|
+
|
|
250
|
+
Cancels a question. It stops accepting responses immediately.
|
|
251
|
+
|
|
252
|
+
#### `fdbck.questions.webhooks(id, options?)` → `PaginatedList<WebhookDelivery>`
|
|
253
|
+
|
|
254
|
+
Returns webhook delivery logs for a question. Same pagination options as `results`.
|
|
255
|
+
|
|
256
|
+
### Tokens
|
|
257
|
+
|
|
258
|
+
#### `fdbck.tokens.create(questionId, options?)` → `TokenResult`
|
|
259
|
+
|
|
260
|
+
Generates a single-use respondent token.
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
const token = await fdbck.tokens.create(question.id, {
|
|
264
|
+
respondent: 'user_42', // optional — your internal ID for this respondent
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
token.token; // the raw token string
|
|
268
|
+
token.respondUrl; // full URL to the hosted response page
|
|
269
|
+
token.expiresAt; // ISO timestamp (1 hour from creation)
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Account
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
const info = await fdbck.me();
|
|
276
|
+
// info.organization → { id, name, plan }
|
|
277
|
+
// info.usage → { responsesUsed, responsesLimit }
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Error handling
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
import { FdbckApiError, FdbckNetworkError } from '@fdbck/node';
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
await fdbck.questions.create({ ... });
|
|
287
|
+
} catch (err) {
|
|
288
|
+
if (err instanceof FdbckApiError) {
|
|
289
|
+
console.error(err.status); // 401, 422, etc.
|
|
290
|
+
console.error(err.code); // 'unauthorized', 'validation_error', etc.
|
|
291
|
+
console.error(err.message); // Human-readable message
|
|
292
|
+
console.error(err.details); // { fields: [...] } for validation errors
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (err instanceof FdbckNetworkError) {
|
|
296
|
+
console.error(err.message); // 'fetch failed', timeout, etc.
|
|
297
|
+
console.error(err.cause); // Original error
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Requirements
|
|
303
|
+
|
|
304
|
+
- Node.js 18+
|
|
305
|
+
- An API key from [dashboard.fdbck.sh](https://dashboard.fdbck.sh)
|
|
306
|
+
|
|
307
|
+
## Links
|
|
308
|
+
|
|
309
|
+
- [Documentation](https://docs.fdbck.sh)
|
|
310
|
+
- [Dashboard](https://dashboard.fdbck.sh)
|
|
311
|
+
- [GitHub](https://github.com/fdbck-sh)
|
|
312
|
+
|
|
313
|
+
## License
|
|
314
|
+
|
|
315
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/** Question type */
|
|
2
|
+
type QuestionType = 'yes_no' | 'single_choice' | 'multiple_choice' | 'rating';
|
|
3
|
+
/** Question status */
|
|
4
|
+
type QuestionStatus = 'collecting' | 'completed' | 'expired' | 'cancelled';
|
|
5
|
+
/** Webhook trigger */
|
|
6
|
+
type WebhookTrigger = 'expiry' | 'each_response' | 'both';
|
|
7
|
+
/** Rating scale configuration */
|
|
8
|
+
interface RatingConfig {
|
|
9
|
+
min: number;
|
|
10
|
+
max: number;
|
|
11
|
+
minLabel?: string;
|
|
12
|
+
maxLabel?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Options for creating a question */
|
|
15
|
+
interface CreateQuestionOptions {
|
|
16
|
+
question: string;
|
|
17
|
+
type: QuestionType;
|
|
18
|
+
options?: string[];
|
|
19
|
+
ratingConfig?: RatingConfig;
|
|
20
|
+
/** Seconds until expiry — mutually exclusive with `expiresAt` */
|
|
21
|
+
expiresIn?: number;
|
|
22
|
+
/** ISO date string — mutually exclusive with `expiresIn` */
|
|
23
|
+
expiresAt?: string;
|
|
24
|
+
maxResponses?: number;
|
|
25
|
+
webhookUrl?: string;
|
|
26
|
+
webhookTrigger?: WebhookTrigger;
|
|
27
|
+
metadata?: Record<string, string>;
|
|
28
|
+
themeColor?: string;
|
|
29
|
+
themeMode?: 'light' | 'dark';
|
|
30
|
+
hideBranding?: boolean;
|
|
31
|
+
welcomeMessage?: string;
|
|
32
|
+
thankYouMessage?: string;
|
|
33
|
+
}
|
|
34
|
+
/** A question resource */
|
|
35
|
+
interface Question {
|
|
36
|
+
id: string;
|
|
37
|
+
question: string;
|
|
38
|
+
type: QuestionType;
|
|
39
|
+
options: string[] | null;
|
|
40
|
+
ratingConfig?: RatingConfig;
|
|
41
|
+
status: QuestionStatus;
|
|
42
|
+
expiresAt: string;
|
|
43
|
+
maxResponses: number | null;
|
|
44
|
+
webhookUrl?: string;
|
|
45
|
+
webhookTrigger?: WebhookTrigger;
|
|
46
|
+
webhookSecret?: string;
|
|
47
|
+
metadata: Record<string, string> | null;
|
|
48
|
+
themeColor: string;
|
|
49
|
+
themeMode: string;
|
|
50
|
+
hideBranding: boolean;
|
|
51
|
+
welcomeMessage: string | null;
|
|
52
|
+
thankYouMessage: string | null;
|
|
53
|
+
totalResponses?: number;
|
|
54
|
+
createdAt: string;
|
|
55
|
+
updatedAt: string;
|
|
56
|
+
}
|
|
57
|
+
/** Options for listing questions */
|
|
58
|
+
interface ListQuestionsOptions {
|
|
59
|
+
cursor?: string;
|
|
60
|
+
limit?: number;
|
|
61
|
+
status?: QuestionStatus;
|
|
62
|
+
sort?: 'created_at' | 'updated_at';
|
|
63
|
+
order?: 'asc' | 'desc';
|
|
64
|
+
createdAfter?: string;
|
|
65
|
+
createdBefore?: string;
|
|
66
|
+
}
|
|
67
|
+
/** Paginated list envelope */
|
|
68
|
+
interface PaginatedList<T> {
|
|
69
|
+
data: T[];
|
|
70
|
+
pagination: {
|
|
71
|
+
nextCursor: string | null;
|
|
72
|
+
hasMore: boolean;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** A single response item */
|
|
76
|
+
interface ResponseItem {
|
|
77
|
+
id: string;
|
|
78
|
+
questionId: string;
|
|
79
|
+
value: unknown;
|
|
80
|
+
respondent: string | null;
|
|
81
|
+
createdAt: string;
|
|
82
|
+
}
|
|
83
|
+
/** Options for listing responses */
|
|
84
|
+
interface ListResponsesOptions {
|
|
85
|
+
cursor?: string;
|
|
86
|
+
limit?: number;
|
|
87
|
+
}
|
|
88
|
+
/** A webhook delivery log entry */
|
|
89
|
+
interface WebhookDelivery {
|
|
90
|
+
id: string;
|
|
91
|
+
event: string;
|
|
92
|
+
success: boolean;
|
|
93
|
+
statusCode: number | null;
|
|
94
|
+
attempts: number;
|
|
95
|
+
error: string | null;
|
|
96
|
+
createdAt: string;
|
|
97
|
+
nextRetryAt: string | null;
|
|
98
|
+
}
|
|
99
|
+
/** Options for listing webhook deliveries */
|
|
100
|
+
interface ListWebhooksOptions {
|
|
101
|
+
cursor?: string;
|
|
102
|
+
limit?: number;
|
|
103
|
+
}
|
|
104
|
+
/** Options for creating a token */
|
|
105
|
+
interface CreateTokenOptions {
|
|
106
|
+
respondent?: string;
|
|
107
|
+
metadata?: Record<string, string>;
|
|
108
|
+
}
|
|
109
|
+
/** Token creation result */
|
|
110
|
+
interface TokenResult {
|
|
111
|
+
token: string;
|
|
112
|
+
respondUrl: string;
|
|
113
|
+
expiresAt: string;
|
|
114
|
+
}
|
|
115
|
+
/** Account info from GET /v1/me */
|
|
116
|
+
interface AccountInfo {
|
|
117
|
+
user: {
|
|
118
|
+
id: string;
|
|
119
|
+
email: string;
|
|
120
|
+
name: string | null;
|
|
121
|
+
avatarUrl: string | null;
|
|
122
|
+
} | null;
|
|
123
|
+
organization: {
|
|
124
|
+
id: string;
|
|
125
|
+
name: string;
|
|
126
|
+
slug: string;
|
|
127
|
+
plan: string;
|
|
128
|
+
role: string | null;
|
|
129
|
+
responsesUsed: number;
|
|
130
|
+
responsesLimit: number | null;
|
|
131
|
+
periodStartsAt: string;
|
|
132
|
+
periodEndsAt: string | null;
|
|
133
|
+
consecutiveOverageMonths: number;
|
|
134
|
+
hasBilling: boolean;
|
|
135
|
+
} | null;
|
|
136
|
+
}
|
|
137
|
+
/** Aggregated results with paginated individual responses */
|
|
138
|
+
interface QuestionResultsResponse {
|
|
139
|
+
questionId: string;
|
|
140
|
+
type: string;
|
|
141
|
+
status: string;
|
|
142
|
+
totalResponses: number;
|
|
143
|
+
results: Record<string, unknown>;
|
|
144
|
+
data: ResponseItem[];
|
|
145
|
+
pagination: {
|
|
146
|
+
nextCursor: string | null;
|
|
147
|
+
hasMore: boolean;
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/** SDK client options */
|
|
151
|
+
interface FdbckOptions {
|
|
152
|
+
/** Base URL for the API (default: https://api.fdbck.sh) */
|
|
153
|
+
baseUrl?: string;
|
|
154
|
+
/** Request timeout in milliseconds (default: 30000) */
|
|
155
|
+
timeout?: number;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
declare class QuestionsResource {
|
|
159
|
+
private readonly client;
|
|
160
|
+
constructor(client: Fdbck);
|
|
161
|
+
/** Create a new question. */
|
|
162
|
+
create(opts: CreateQuestionOptions): Promise<Question>;
|
|
163
|
+
/** Get a question by ID. */
|
|
164
|
+
get(id: string): Promise<Question>;
|
|
165
|
+
/** List questions with pagination. */
|
|
166
|
+
list(opts?: ListQuestionsOptions): Promise<PaginatedList<Question>>;
|
|
167
|
+
/** Auto-paginate through all questions. */
|
|
168
|
+
listAll(opts?: Omit<ListQuestionsOptions, 'cursor'>): AsyncGenerator<Question>;
|
|
169
|
+
/** Get responses for a question with aggregated results. */
|
|
170
|
+
results(id: string, opts?: ListResponsesOptions): Promise<QuestionResultsResponse>;
|
|
171
|
+
/** Cancel (delete) a question. */
|
|
172
|
+
cancel(id: string): Promise<Question>;
|
|
173
|
+
/** Get webhook delivery logs for a question. */
|
|
174
|
+
webhooks(id: string, opts?: ListWebhooksOptions): Promise<PaginatedList<WebhookDelivery>>;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
declare class TokensResource {
|
|
178
|
+
private readonly client;
|
|
179
|
+
constructor(client: Fdbck);
|
|
180
|
+
/** Create a respondent token for a question. */
|
|
181
|
+
create(questionId: string, opts?: CreateTokenOptions): Promise<TokenResult>;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
interface RequestOptions {
|
|
185
|
+
body?: Record<string, unknown>;
|
|
186
|
+
query?: Record<string, string>;
|
|
187
|
+
}
|
|
188
|
+
declare class Fdbck {
|
|
189
|
+
private readonly apiKey;
|
|
190
|
+
private readonly baseUrl;
|
|
191
|
+
private readonly timeout;
|
|
192
|
+
readonly questions: QuestionsResource;
|
|
193
|
+
readonly tokens: TokensResource;
|
|
194
|
+
constructor(apiKey: string, options?: FdbckOptions);
|
|
195
|
+
/** Make an authenticated request to the fdbck API. */
|
|
196
|
+
request<T = unknown>(method: string, path: string, options?: RequestOptions): Promise<T>;
|
|
197
|
+
/** Get current account info. */
|
|
198
|
+
me(): Promise<AccountInfo>;
|
|
199
|
+
/** Verify a webhook signature. */
|
|
200
|
+
verifyWebhook(rawBody: string, signature: string, secret: string): boolean;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Base error for all fdbck SDK errors */
|
|
204
|
+
declare class FdbckError extends Error {
|
|
205
|
+
constructor(message: string);
|
|
206
|
+
}
|
|
207
|
+
/** Error returned by the fdbck API */
|
|
208
|
+
declare class FdbckApiError extends FdbckError {
|
|
209
|
+
readonly status: number;
|
|
210
|
+
readonly code: string;
|
|
211
|
+
readonly details?: {
|
|
212
|
+
fields: string[];
|
|
213
|
+
};
|
|
214
|
+
constructor(status: number, code: string, message: string, details?: {
|
|
215
|
+
fields: string[];
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
/** Network-level error (fetch failure, timeout, etc.) */
|
|
219
|
+
declare class FdbckNetworkError extends FdbckError {
|
|
220
|
+
constructor(message: string, options?: {
|
|
221
|
+
cause?: unknown;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Verify an incoming webhook signature from fdbck.
|
|
227
|
+
*
|
|
228
|
+
* @param rawBody - The raw JSON string body of the webhook request
|
|
229
|
+
* @param signature - The value of the `X-FDBCK-Signature` header
|
|
230
|
+
* @param secret - The webhook secret from question creation
|
|
231
|
+
* @returns `true` if the signature is valid
|
|
232
|
+
*/
|
|
233
|
+
declare function verifyWebhook(rawBody: string, signature: string, secret: string): boolean;
|
|
234
|
+
|
|
235
|
+
export { type AccountInfo, type CreateQuestionOptions, type CreateTokenOptions, Fdbck, FdbckApiError, FdbckError, FdbckNetworkError, type FdbckOptions, type ListQuestionsOptions, type ListResponsesOptions, type ListWebhooksOptions, type PaginatedList, type Question, type QuestionResultsResponse, type QuestionStatus, type QuestionType, type RatingConfig, type ResponseItem, type TokenResult, type WebhookDelivery, type WebhookTrigger, Fdbck as default, verifyWebhook };
|