@fundedfast/sdk 1.3.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +352 -0
  3. package/dist/cjs/client.d.ts +130 -0
  4. package/dist/cjs/client.d.ts.map +1 -0
  5. package/dist/cjs/client.js +362 -0
  6. package/dist/cjs/client.js.map +1 -0
  7. package/dist/cjs/index.d.ts +27 -0
  8. package/dist/cjs/index.d.ts.map +1 -0
  9. package/dist/cjs/index.js +33 -0
  10. package/dist/cjs/index.js.map +1 -0
  11. package/dist/cjs/package.json +1 -0
  12. package/dist/cjs/resources/admin.d.ts +597 -0
  13. package/dist/cjs/resources/admin.d.ts.map +1 -0
  14. package/dist/cjs/resources/admin.js +842 -0
  15. package/dist/cjs/resources/admin.js.map +1 -0
  16. package/dist/cjs/resources/public.d.ts +519 -0
  17. package/dist/cjs/resources/public.d.ts.map +1 -0
  18. package/dist/cjs/resources/public.js +588 -0
  19. package/dist/cjs/resources/public.js.map +1 -0
  20. package/dist/cjs/types.d.ts +651 -0
  21. package/dist/cjs/types.d.ts.map +1 -0
  22. package/dist/cjs/types.js +11 -0
  23. package/dist/cjs/types.js.map +1 -0
  24. package/dist/cjs/webhooks.d.ts +104 -0
  25. package/dist/cjs/webhooks.d.ts.map +1 -0
  26. package/dist/cjs/webhooks.js +199 -0
  27. package/dist/cjs/webhooks.js.map +1 -0
  28. package/dist/esm/client.d.ts +130 -0
  29. package/dist/esm/client.d.ts.map +1 -0
  30. package/dist/esm/client.js +355 -0
  31. package/dist/esm/client.js.map +1 -0
  32. package/dist/esm/index.d.ts +27 -0
  33. package/dist/esm/index.d.ts.map +1 -0
  34. package/dist/esm/index.js +26 -0
  35. package/dist/esm/index.js.map +1 -0
  36. package/dist/esm/resources/admin.d.ts +597 -0
  37. package/dist/esm/resources/admin.d.ts.map +1 -0
  38. package/dist/esm/resources/admin.js +816 -0
  39. package/dist/esm/resources/admin.js.map +1 -0
  40. package/dist/esm/resources/public.d.ts +519 -0
  41. package/dist/esm/resources/public.d.ts.map +1 -0
  42. package/dist/esm/resources/public.js +578 -0
  43. package/dist/esm/resources/public.js.map +1 -0
  44. package/dist/esm/types.d.ts +651 -0
  45. package/dist/esm/types.d.ts.map +1 -0
  46. package/dist/esm/types.js +10 -0
  47. package/dist/esm/types.js.map +1 -0
  48. package/dist/esm/webhooks.d.ts +104 -0
  49. package/dist/esm/webhooks.d.ts.map +1 -0
  50. package/dist/esm/webhooks.js +159 -0
  51. package/dist/esm/webhooks.js.map +1 -0
  52. package/package.json +72 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,104 @@
1
+ # Changelog
2
+
3
+ ## 1.3.0 (2026-10-06)
4
+
5
+ First release published to npm.
6
+
7
+ ### Security
8
+ - The client refuses to start in a browser with a server key (`key-...`), which
9
+ would publish that key to every visitor. Pass `dangerouslyAllowBrowser: true`
10
+ to override. The public widget key (`pk_...`) is unaffected.
11
+ - The `onRequest` hook receives the API key redacted (`key-***abcd`), so a
12
+ request logger no longer writes the server key to disk.
13
+ - `verifyWebhookSignature` accepts only a 64-character hex string and rejects
14
+ anything else before comparing. The doc examples now `await` it: without
15
+ `await` the result is a Promise, which is always truthy, so every request
16
+ passed.
17
+
18
+ ### Fixed
19
+ - `downloadExport(date)`, `rewards.list(userId)` and `rewards.history(userId)`
20
+ send the query parameter the API requires; all three answered 400 before.
21
+ - `admin.audit.export()` documents the 90-day window per call.
22
+ - `User-Agent` reports the real SDK version (it said 1.2.0).
23
+
24
+ ### Added
25
+ - `ChallengePassedProgress`: `users.getProgress()` can return
26
+ `type: 'challenge_passed'`, with the balance and the continue/refund choices.
27
+ - `WebhookEventType` matches the events the API emits, including
28
+ `bonus.progress` and `mission.progress`.
29
+
30
+ ## 1.2.0 (2026-07-18)
31
+
32
+ Alignment release: every SDK method now maps 1:1 to a live API endpoint, and
33
+ all remaining operator-facing endpoints are covered.
34
+
35
+ ### Fixed (previously broken methods)
36
+
37
+ These methods called endpoints that do not exist in the API and always failed
38
+ with 404; no working integration can be affected by their change or removal:
39
+
40
+ - `admin.loyalty.setConfig()` now sends `PUT /admin/loyalty/config` (was `POST`, which the API rejects)
41
+ - Removed `admin.challengeTiers.get(id)` - the API has no single-tier GET; use `list()`
42
+ - Removed `admin.rulesets.get(id)` - the API has no single-ruleset GET; use `list()`
43
+ - Removed `admin.reconciliation.forUser(userId)` - use `fast.reconciliation.getForUser(userId)` (the public endpoint)
44
+ - Removed `admin.reconciliation.resolve(userId)` - the API has no flag-resolution endpoint
45
+ - `admin.reconciliation.flags(status?)` now accepts the `open`/`resolved` status filter
46
+
47
+ ### Added - operator (public) namespace
48
+
49
+ - `users.continueChallenge(userId)` - continue a passed challenge into the Reward (funded) Account
50
+ - `users.refundChallengeStake(userId)` - exit a passed challenge and refund only the own stake (voids the bonus; sends `confirm: true`)
51
+ - `users.refundFundedStake(userId)` - refund the own stake during the funded phase, if the tier allows it (sends `confirm: true`)
52
+ - `users.getTournaments(userId)` - active/upcoming/completed tournament participation
53
+ - `users.getLoyalty(userId)` - loyalty points, tier, and cashback status
54
+ - `fast.widget.getConfig()` - widget module toggles and branding configuration
55
+
56
+ ### Added - admin namespace
57
+
58
+ - `admin.users.get(userId)` - full user lookup (challenges, funded accounts, bets, achievements, rewards)
59
+ - `admin.users.revokeFunded(userId, reason?)` - manually revoke an active funded account
60
+ - `admin.webhooks.retry(id)` - retry a failed webhook delivery
61
+ - `admin.tournaments.update(id, data)` and `admin.tournaments.cancel(id)`
62
+ - `admin.settings.setWidgetLayout({ fundedOnly?, history? })`
63
+ - `admin.analytics.challenges/revenue/users/benchmarks/engagement/funnel/rewards/leaderboard`
64
+ - `admin.audit.export(options?)` (GDPR Art. 20 bulk export) and `admin.audit.retention()`
65
+ - `admin.notificationChannels` - CRUD + test for Slack/Teams/Discord/custom channels
66
+ - `admin.templates` - browse and clone challenge templates from the FAST marketplace
67
+ - `admin.support` - open, read, message, and close support requests
68
+
69
+ ## 1.0.0 (2026-06-12)
70
+
71
+ Initial release.
72
+
73
+ ### Features
74
+
75
+ - Full typed client for all operator-facing FAST API endpoints
76
+ - Automatic retries with exponential backoff and jitter
77
+ - Rate limit awareness (respects Retry-After header)
78
+ - Request/response hooks for observability (logging, metrics, alerting)
79
+ - Idempotency key support for safe retries on mutations
80
+ - Webhook signature verification (async + sync variants)
81
+ - Webhook event parsing with type helpers
82
+ - SSE token issuance and stream URL builder
83
+ - Custom fetch support (for testing/mocking)
84
+ - Debug mode with console logging
85
+ - Dual ESM/CJS builds
86
+ - Zero dependencies
87
+ - Works in Node.js 18+, Deno, Bun, Cloudflare Workers
88
+
89
+ ### API Coverage
90
+
91
+ - `fast.challenges.listTiers()` - List enabled challenge tiers
92
+ - `fast.challenges.getTierTerms(id)` - Get tier terms and disclosures
93
+ - `fast.challenges.purchase(data)` - Purchase a challenge
94
+ - `fast.bets.validate(bet)` - Validate a bet
95
+ - `fast.bets.ingest(bet)` - Ingest a bet event
96
+ - `fast.bets.ingestBatch(bets)` - Batch ingest (up to 100)
97
+ - `fast.users.getProgress(userId)` - Get user progress
98
+ - `fast.users.getRuleset(userId)` - Get active ruleset
99
+ - `fast.users.getHistory(userId)` - Get challenge/funded account history
100
+ - `fast.users.getWithdrawable(userId)` - Get withdrawable amount
101
+ - `fast.users.withdraw(userId, data)` - Process withdrawal
102
+ - `fast.reconciliation.getForUser(userId, options)` - Reconciliation data
103
+ - `fast.events.issueToken(userId)` - Issue SSE token
104
+ - `fast.events.getStreamUrl(token, userId)` - Build SSE URL
package/README.md ADDED
@@ -0,0 +1,352 @@
1
+ # @fundedfast/sdk
2
+
3
+ Official TypeScript SDK for the [FAST](https://fundedfast.eu) platform API.
4
+
5
+ Zero dependencies. Automatic retries. Rate limit aware. Works on Node.js 18+, Deno, Bun and Cloudflare Workers. Dual ESM/CJS build, fully typed.
6
+
7
+ This README is the SDK's API reference. For how the integration fits together (bet flow, webhooks, approvals, the widget, going live), read the integration guide at **[docs.fundedfast.eu](https://docs.fundedfast.eu/integration/quickstart)**.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install @fundedfast/sdk
13
+ ```
14
+
15
+ ## Constructor
16
+
17
+ ```typescript
18
+ import { FastClient } from '@fundedfast/sdk';
19
+
20
+ const fast = new FastClient({
21
+ // Required: your SERVER key (key-...). Never ship it to a browser.
22
+ apiKey: process.env.FAST_API_KEY!,
23
+
24
+ // Optional
25
+ baseUrl: 'https://api.fundedfast.eu/v1', // default
26
+ timeout: 10_000, // per request, ms (default 10s)
27
+
28
+ // Retry (set to false to disable)
29
+ retry: {
30
+ maxRetries: 3, // default 3
31
+ initialDelay: 300, // ms before first retry (default 300)
32
+ maxDelay: 10_000, // cap between retries (default 10s)
33
+ retryableStatuses: [429, 500, 502, 503, 504],
34
+ },
35
+
36
+ headers: { 'X-Request-Source': 'my-service' }, // added to every request
37
+
38
+ // Observability hooks
39
+ onRequest: ({ method, url }) => console.log(`FAST ${method} ${url}`),
40
+ onResponse: ({ status, durationMs, correlationId }) => {
41
+ if (status >= 500) console.error(`FAST ${status} (${durationMs}ms)`, correlationId);
42
+ },
43
+
44
+ debug: process.env.NODE_ENV === 'development', // logs requests to console
45
+ // dangerouslyAllowBrowser: true, // the client refuses to run in a browser with a server key
46
+ // fetch: myMockFetch, // custom fetch, e.g. for tests
47
+ });
48
+ ```
49
+
50
+ Retries back off exponentially with jitter and honour `Retry-After` on 429.
51
+
52
+ **Idempotency.** Every `POST` the SDK sends carries an `X-Idempotency-Key`, generated per call and reused across that call's retries. The money endpoints that require one (`users.withdraw`, `users.refundChallengeStake`, `users.refundFundedStake`) therefore work without you passing anything, and an SDK retry can never pay twice. The key does not survive across separate calls you make: if your process crashes mid-payout, check the account state before calling again. See [Withdrawals](https://docs.fundedfast.eu/integration/withdrawals).
53
+
54
+ ## Quick start
55
+
56
+ ```typescript
57
+ const tiers = await fast.challenges.listTiers();
58
+
59
+ const challenge = await fast.challenges.purchase({
60
+ userId: 'player-42',
61
+ challengeTierId: tiers[0].id,
62
+ });
63
+
64
+ const bet = {
65
+ userId: 'player-42',
66
+ externalBetId: 'BET-001',
67
+ stakeAmount: 5.0,
68
+ odds: 2.1,
69
+ status: 'placed' as const,
70
+ };
71
+
72
+ const { status, reasonCodes } = await fast.bets.validate(bet);
73
+ if (status === 'accepted') {
74
+ await fast.bets.ingest(bet);
75
+ }
76
+ ```
77
+
78
+ When to validate, what to send on settlement and how sync/async modes differ: [Bet Events](https://docs.fundedfast.eu/integration/bet-events).
79
+
80
+ ## Operator namespaces
81
+
82
+ | Namespace | Methods |
83
+ |---|---|
84
+ | `challenges` | `listTiers()`, `getTierTerms(tierId)`, `purchase({ userId, challengeTierId })`, `leaderboard(period?, limit?)` |
85
+ | `bets` | `validate(bet)`, `ingest(bet)`, `ingestBatch(bets)` (max 100) |
86
+ | `users` | `getProgress`, `getRuleset`, `getHistory`, `getWithdrawable`, `withdraw`, `getTimeline`, `getAchievements`, `getRewards`, `claimReward`, `notifyWithdrawal`, `continueChallenge`, `refundChallengeStake`, `refundFundedStake`, `cancelChallenge`, `cancelFundedAccount`, `getTournaments`, `getLoyalty` |
87
+ | `reconciliation` | `getForUser(userId, { from?, to? })` |
88
+ | `events` | `issueToken(userId)`, `getStreamUrl(token, userId)` |
89
+ | `widget` | `getConfig()` |
90
+ | `sandbox` | `reset()`, `resetUser(userId)` (staging tenants only) |
91
+ | `admin.*` | see [Admin API](#admin-api) |
92
+
93
+ ### Bets
94
+
95
+ ```typescript
96
+ // Settlement
97
+ await fast.bets.ingest({
98
+ userId: 'player-42',
99
+ externalBetId: 'BET-001',
100
+ stakeAmount: 5.0,
101
+ odds: 2.1,
102
+ status: 'settled-won',
103
+ settlementAmount: 10.5,
104
+ });
105
+
106
+ // Batch (partial success: check result.results for per-bet status)
107
+ const result = await fast.bets.ingestBatch([bet]);
108
+ console.log(`${result.processed}/${result.total} processed, ${result.failed} failed`);
109
+ ```
110
+
111
+ ### User progress
112
+
113
+ `getProgress` returns a discriminated union. Narrow on `type`:
114
+
115
+ ```typescript
116
+ const progress = await fast.users.getProgress('player-42');
117
+
118
+ switch (progress.type) {
119
+ case 'challenge':
120
+ console.log(`${progress.currentBalance}/${progress.targetBalance}, ${progress.daysRemaining}d left`);
121
+ break;
122
+ case 'challenge_passed':
123
+ // Target reached; the user must choose. progress.choices is ['continue', 'refund']
124
+ console.log(`Passed. Refundable own stake: ${progress.refundableOwnStake}`);
125
+ break;
126
+ case 'funded_account':
127
+ console.log(`Withdrawable: ${progress.withdrawableProfit}`);
128
+ break;
129
+ case 'none':
130
+ console.log('No active position');
131
+ break;
132
+ }
133
+ ```
134
+
135
+ Field meanings: [User Progress](https://docs.fundedfast.eu/integration/user-progress).
136
+
137
+ ### Continue or exit a passed challenge
138
+
139
+ ```typescript
140
+ // Continue into the funded account
141
+ const { fundedAccountId } = await fast.users.continueChallenge('player-42');
142
+
143
+ // Or exit with the own stake (irreversible; the SDK sends confirm: true)
144
+ const { ownStake, bonusAmount } = await fast.users.refundChallengeStake('player-42');
145
+
146
+ // During the funded phase, only on tiers with fundedStakeRefundable
147
+ const refund = await fast.users.refundFundedStake('player-42');
148
+ ```
149
+
150
+ FAST records the outcome and emits the webhook; your sportsbook moves the money.
151
+
152
+ ### Withdrawals
153
+
154
+ ```typescript
155
+ const info = await fast.users.getWithdrawable('player-42');
156
+ if (info.activeDaysMet && info.withdrawable > 0) {
157
+ const { userPayout, operatorShare, newBalance } = await fast.users.withdraw('player-42', {
158
+ amount: info.withdrawable,
159
+ });
160
+ }
161
+ ```
162
+
163
+ How the profit split is deducted and the errors to handle: [Withdrawals](https://docs.fundedfast.eu/integration/withdrawals).
164
+
165
+ ### Real-time events (SSE)
166
+
167
+ ```typescript
168
+ // Backend, with the server key: mint a per-user token (1 hour)
169
+ const { token, expiresAt } = await fast.events.issueToken('player-42');
170
+
171
+ // Hand the token to the browser; it connects with
172
+ const url = fast.events.getStreamUrl(token, 'player-42');
173
+ // new EventSource(url)
174
+ ```
175
+
176
+ The same token is what the widget takes as `data-user-token`. Event names and payloads: [Real-Time Events](https://docs.fundedfast.eu/integration/real-time-events).
177
+
178
+ ### Reconciliation
179
+
180
+ ```typescript
181
+ const data = await fast.reconciliation.getForUser('player-42', {
182
+ from: '2026-06-01T00:00:00Z',
183
+ to: '2026-06-30T23:59:59Z',
184
+ });
185
+ // data.betEvents, data.challenge, data.fundedAccount
186
+ ```
187
+
188
+ ## Webhook verification
189
+
190
+ FAST signs each delivery with HMAC-SHA256 over the **raw request body**, using your webhook secret, and sends the hex digest in the `X-Webhook-Signature` header. Verify against the raw bytes: re-serialising parsed JSON changes them and the check fails.
191
+
192
+ ```typescript
193
+ import express from 'express';
194
+ import { verifyWebhookSignature, parseWebhookEvent } from '@fundedfast/sdk/webhooks';
195
+
196
+ const app = express();
197
+
198
+ app.post('/webhooks/fast', express.raw({ type: 'application/json' }), async (req, res) => {
199
+ const rawBody = req.body.toString('utf-8');
200
+ const signature = req.header('x-webhook-signature') ?? '';
201
+
202
+ // Async: uses node:crypto, or Web Crypto on edge runtimes. Timing-safe.
203
+ if (!(await verifyWebhookSignature(rawBody, signature, process.env.FAST_WEBHOOK_SECRET!))) {
204
+ return res.status(401).send('Invalid signature');
205
+ }
206
+
207
+ const event = parseWebhookEvent(rawBody); // { eventId, eventType, userId, timestamp, data }
208
+ switch (event.eventType) {
209
+ case 'challenge.passed':
210
+ case 'funded_account.payout':
211
+ case 'cooldown.triggered':
212
+ // ...
213
+ break;
214
+ }
215
+
216
+ res.status(200).json({ received: true });
217
+ });
218
+ ```
219
+
220
+ `@fundedfast/sdk/webhooks` also exports `isChallengeEvent`, `isFundedAccountEvent` and `isUserEvent`. `WebhookEventType` lists every event FAST emits. What each event means, retries and deduplication by `eventId`: [Webhook Handling](https://docs.fundedfast.eu/integration/webhook-handling) and [Webhook Events](https://docs.fundedfast.eu/api/webhook-events).
221
+
222
+ ## Errors
223
+
224
+ ```typescript
225
+ import { FastApiError, FastTimeoutError, FastNetworkError } from '@fundedfast/sdk';
226
+
227
+ try {
228
+ await fast.challenges.purchase({ userId: 'x', challengeTierId: 'y' });
229
+ } catch (err) {
230
+ if (err instanceof FastApiError) {
231
+ err.status; // 400, 404, 409, 429, 500...
232
+ err.code; // 'validation_error', 'conflict', 'rate_limited', ...
233
+ err.message; // human-readable
234
+ err.correlationId; // quote this to support
235
+ err.isRetryable; // true for 429 and 5xx
236
+ err.rateLimitReset; // ms from Retry-After, when present
237
+ } else if (err instanceof FastTimeoutError) {
238
+ err.timeoutMs;
239
+ } else if (err instanceof FastNetworkError) {
240
+ err.cause; // the underlying error (DNS, connection refused, ...)
241
+ }
242
+ }
243
+ ```
244
+
245
+ The SDK throws only after its retries are exhausted. Code list: [Error Codes](https://docs.fundedfast.eu/api/error-codes).
246
+
247
+ ## Types
248
+
249
+ Every request and response type is exported:
250
+
251
+ ```typescript
252
+ import type {
253
+ Challenge,
254
+ ChallengeTier,
255
+ UserProgress,
256
+ ChallengeProgress,
257
+ ChallengePassedProgress,
258
+ FundedAccountProgress,
259
+ ValidationResult,
260
+ BetEvent,
261
+ WebhookPayload,
262
+ WebhookEventType,
263
+ } from '@fundedfast/sdk';
264
+ ```
265
+
266
+ ```typescript
267
+ // ESM
268
+ import { FastClient } from '@fundedfast/sdk';
269
+ // CommonJS
270
+ const { FastClient } = require('@fundedfast/sdk');
271
+ ```
272
+
273
+ ## Admin API
274
+
275
+ The `admin` namespace covers the operator backoffice, with the same server key.
276
+
277
+ ```typescript
278
+ // Challenge tiers (no single-tier GET: use list())
279
+ const tiers = await fast.admin.challengeTiers.list();
280
+ await fast.admin.challengeTiers.update(tierId, { enabled: false });
281
+
282
+ // Templates (marketplace bundles you can clone into your tenant)
283
+ const { templates } = await fast.admin.templates.list();
284
+ await fast.admin.templates.clone(templateId, { tierName: 'Starter 100', ownStake: 100 });
285
+
286
+ // Bets
287
+ const bets = await fast.admin.bets.list({ status: 'placed', userId: 'player-42' });
288
+ await fast.admin.bets.settle(betId, 'won');
289
+ await fast.admin.bets.bulkSettle([{ betEventId: betId, outcome: 'won' }]);
290
+
291
+ // Webhooks
292
+ await fast.admin.webhooks.retry(deliveryId);
293
+ const dead = await fast.admin.webhooks.deadLetters();
294
+ await fast.admin.webhooks.replay(deadLetterId);
295
+ await fast.admin.webhooks.rotateSecret();
296
+ const { deadLetters } = await fast.admin.webhooks.hookDeadLetters(); // post-bet hook failures
297
+ await fast.admin.webhooks.replayHook(hookRunId);
298
+
299
+ // Settings
300
+ await fast.admin.settings.setIntegrationMode('async');
301
+ await fast.admin.settings.setWebhookUrl('https://my.backend/webhooks');
302
+ await fast.admin.settings.setCurrency('USD', '$');
303
+
304
+ // Engagement
305
+ await fast.admin.bonuses.activate('player-42', offerId);
306
+ await fast.admin.tournaments.settle(tournamentId);
307
+ await fast.admin.loyalty.adjustPoints('player-42', 500, 'VIP bonus');
308
+ await fast.admin.rewards.grant({ userId: 'player-42', rewardType: 'balance_bonus', rewardValue: 10 });
309
+ await fast.admin.achievements.bulkGrant({ achievementId, userIds: ['player-42'] });
310
+
311
+ // Compliance and support
312
+ await fast.admin.selfExclusion.exclude({ userId: 'player-42', reason: 'request' });
313
+ await fast.admin.users.revokeFunded('player-42', 'manual_operator_revoke');
314
+ const { flags } = await fast.admin.reconciliation.flags('open');
315
+ const auditLog = await fast.admin.audit.list({ type: 'webhooks', limit: 50 });
316
+ await fast.admin.achievements.gdprErase('player-42', 'player-42'); // second arg confirms the id
317
+ const { request } = await fast.admin.support.create({
318
+ subject: 'Webhook retries stuck', body: 'Since 14:00 UTC...', category: 'technical',
319
+ });
320
+ ```
321
+
322
+ | Namespace | Methods |
323
+ |---|---|
324
+ | `admin.challengeTiers` | list, create, update, delete |
325
+ | `admin.rulesets` | list, create, update, delete |
326
+ | `admin.bets` | list, settle, bulkSettle, bulkExpire, export |
327
+ | `admin.webhooks` | list, retry, deadLetters, replay, rotateSecret, health, echo, clearEcho, hookDeadLetters, replayHook |
328
+ | `admin.audit` | list, adminLog, export, retention |
329
+ | `admin.settings` | get, getIntegrationMode, setIntegrationMode, setWebhookUrl, setApprovalUrl, setChallengeLimit, setCurrency, setExposureAlert, setWidgetLayout, configExport, downloadExport, testExport, getTheme, setTheme, clearSnapshots |
330
+ | `admin.bonuses` | listOffers, createOffer, updateOffer, deleteOffer, listActive, cancel, grantToUser, stats, getUserBonuses, activate |
331
+ | `admin.missions` | list, create, update, delete, stats, statsForMission, getUserMissions, join |
332
+ | `admin.tournaments` | list, create, update, cancel, settle, entries, invite, invites, stats, listPublic, leaderboard, join |
333
+ | `admin.loyalty` | getConfig, setConfig, getTiers, createTier, updateTier, deleteTier, listUsers, getUser, adjustPoints, getPublicTiers, stats, getUserLoyalty |
334
+ | `admin.analytics` | overview, requestLog, challenges, revenue, users, benchmarks, engagement, funnel, rewards, leaderboard |
335
+ | `admin.achievements` | list, create, update, delete, forUser, grant, bulkGrant, gdprExport, gdprErase |
336
+ | `admin.notifications` | getPreferences, setPreferences |
337
+ | `admin.notificationChannels` | list, create, update, delete, test |
338
+ | `admin.rewards` | list, history, grant, bulkGrant, delete |
339
+ | `admin.rewardRules` | list, create, update, delete |
340
+ | `admin.selfExclusion` | list, get, exclude, lift |
341
+ | `admin.adapters` | list, create, update, delete |
342
+ | `admin.reconciliation` | flags |
343
+ | `admin.users` | get, revokeFunded |
344
+ | `admin.templates` | list, get, clone |
345
+ | `admin.support` | list, get, create, addMessage, close |
346
+
347
+ ## Links
348
+
349
+ - Integration guide: https://docs.fundedfast.eu/integration/quickstart
350
+ - Getting access (keys, staging, production): https://docs.fundedfast.eu/integration/getting-access
351
+ - API reference (OpenAPI, try-it-out): https://api.fundedfast.eu/v1/docs
352
+ - Support: support@fundedfast.eu
@@ -0,0 +1,130 @@
1
+ /**
2
+ * @fundedfast/sdk - Client
3
+ *
4
+ * Production-grade HTTP client for the FAST platform API.
5
+ * Features: automatic retries with exponential backoff, rate limit awareness,
6
+ * request/response hooks, idempotency support, debug logging, and proper error handling.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { FastClient } from '@fundedfast/sdk';
11
+ *
12
+ * const fast = new FastClient({
13
+ * apiKey: process.env.FAST_API_KEY!,
14
+ * retry: { maxRetries: 3 },
15
+ * onResponse: ({ status, durationMs, correlationId }) => {
16
+ * metrics.histogram('fast.request.duration', durationMs, { status });
17
+ * },
18
+ * });
19
+ *
20
+ * const result = await fast.bets.validate({
21
+ * userId: 'player-42',
22
+ * externalBetId: 'BET-001',
23
+ * stakeAmount: 5.00,
24
+ * odds: 2.10,
25
+ * status: 'placed',
26
+ * });
27
+ * ```
28
+ *
29
+ * @module client
30
+ */
31
+ import type { FastClientConfig, FastError } from './types.js';
32
+ import { ChallengesApi, BetsApi, UsersApi, ReconciliationApi, EventsApi, SandboxApi, WidgetApi } from './resources/public.js';
33
+ import { AdminApi } from './resources/admin.js';
34
+ export declare const SDK_VERSION = "1.3.0";
35
+ /**
36
+ * Error thrown when the FAST API returns a non-2xx response.
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * try {
41
+ * await fast.challenges.purchase({ userId: 'x', challengeTierId: 'invalid' });
42
+ * } catch (err) {
43
+ * if (err instanceof FastApiError) {
44
+ * console.log(err.status); // 400
45
+ * console.log(err.code); // 'validation_error'
46
+ * console.log(err.correlationId); // 'abc-123' (for support)
47
+ * console.log(err.isRetryable); // false
48
+ * }
49
+ * }
50
+ * ```
51
+ */
52
+ export declare class FastApiError extends Error {
53
+ readonly status: number;
54
+ readonly code: string;
55
+ readonly correlationId: string;
56
+ readonly isRetryable: boolean;
57
+ readonly rateLimitReset?: number;
58
+ constructor(status: number, body: FastError, rateLimitReset?: number);
59
+ }
60
+ /**
61
+ * Error thrown when a request times out.
62
+ */
63
+ export declare class FastTimeoutError extends Error {
64
+ readonly timeoutMs: number;
65
+ constructor(timeoutMs: number);
66
+ }
67
+ /**
68
+ * Error thrown when a network error occurs (DNS, connection refused, etc.)
69
+ */
70
+ export declare class FastNetworkError extends Error {
71
+ readonly cause: Error;
72
+ constructor(cause: Error);
73
+ }
74
+ /**
75
+ * FAST Platform API client.
76
+ *
77
+ * Provides typed access to all operator-facing endpoints with automatic retry,
78
+ * rate limit handling, and observability hooks.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * const fast = new FastClient({ apiKey: 'key-...' });
83
+ *
84
+ * // Namespaced APIs
85
+ * const tiers = await fast.challenges.listTiers();
86
+ * const result = await fast.bets.validate(betEvent);
87
+ * const progress = await fast.users.getProgress('player-42');
88
+ * ```
89
+ */
90
+ export declare class FastClient {
91
+ private readonly baseUrl;
92
+ private readonly apiKey;
93
+ private readonly timeout;
94
+ private readonly retryConfig;
95
+ private readonly onRequest?;
96
+ private readonly onResponse?;
97
+ private readonly _fetch;
98
+ private readonly customHeaders;
99
+ private readonly debug;
100
+ /** Challenge purchase and tier management */
101
+ readonly challenges: ChallengesApi;
102
+ /** Bet validation and ingestion */
103
+ readonly bets: BetsApi;
104
+ /** User progress, history, and withdrawals */
105
+ readonly users: UsersApi;
106
+ /** Bet event reconciliation */
107
+ readonly reconciliation: ReconciliationApi;
108
+ /** Real-time event streaming (SSE) */
109
+ readonly events: EventsApi;
110
+ /** Sandbox operations (staging only) */
111
+ readonly sandbox: SandboxApi;
112
+ /** Widget module and branding configuration */
113
+ readonly widget: WidgetApi;
114
+ /** Admin / backoffice operations (tenant management) */
115
+ readonly admin: AdminApi;
116
+ constructor(config: FastClientConfig);
117
+ /**
118
+ * Execute an authenticated request against the FAST API.
119
+ * Handles retries, timeouts, rate limits, and hooks automatically.
120
+ *
121
+ * @internal Use the namespaced APIs (challenges, bets, users, etc.) instead.
122
+ */
123
+ request<T>(method: string, path: string, options?: {
124
+ body?: unknown;
125
+ query?: Record<string, string | undefined>;
126
+ idempotencyKey?: string;
127
+ }): Promise<T>;
128
+ private calculateDelay;
129
+ }
130
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,SAAS,EAIV,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,aAAa,EACb,OAAO,EACP,QAAQ,EACR,iBAAiB,EACjB,SAAS,EACT,UAAU,EACV,SAAS,EACV,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAQhD,eAAO,MAAM,WAAW,UAAU,CAAC;AAgCnC;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,aAAa,EAAE,MAAM,CAAC;IACtC,SAAgB,WAAW,EAAE,OAAO,CAAC;IACrC,SAAgB,cAAc,CAAC,EAAE,MAAM,CAAC;gBAE5B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,cAAc,CAAC,EAAE,MAAM;CASrE;AAED;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,SAAgB,SAAS,EAAE,MAAM,CAAC;gBAEtB,SAAS,EAAE,MAAM;CAK9B;AAED;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,SAAgB,KAAK,EAAE,KAAK,CAAC;gBAEjB,KAAK,EAAE,KAAK;CAKzB;AAMD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAe;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyB;IACvD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;IAEhC,6CAA6C;IAC7C,SAAgB,UAAU,EAAE,aAAa,CAAC;IAC1C,mCAAmC;IACnC,SAAgB,IAAI,EAAE,OAAO,CAAC;IAC9B,8CAA8C;IAC9C,SAAgB,KAAK,EAAE,QAAQ,CAAC;IAChC,+BAA+B;IAC/B,SAAgB,cAAc,EAAE,iBAAiB,CAAC;IAClD,sCAAsC;IACtC,SAAgB,MAAM,EAAE,SAAS,CAAC;IAClC,wCAAwC;IACxC,SAAgB,OAAO,EAAE,UAAU,CAAC;IACpC,+CAA+C;IAC/C,SAAgB,MAAM,EAAE,SAAS,CAAC;IAClC,wDAAwD;IACxD,SAAgB,KAAK,EAAE,QAAQ,CAAC;gBAEpB,MAAM,EAAE,gBAAgB;IAwCpC;;;;;OAKG;IACG,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;QAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,GACA,OAAO,CAAC,CAAC,CAAC;IAgKb,OAAO,CAAC,cAAc;CASvB"}