@tollara/service-sdk 0.0.1

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,190 @@
1
+ # Tollara SDK (JavaScript/TypeScript)
2
+
3
+ **Package:** `@tollara/service-sdk` (version **0.0.1** in this repo)
4
+
5
+ Verify inbound HMAC, validate **service keys**, run usage pre-flight (service-key **and** JWT paths), **gateway invoke** (sync/async), report usage, progress, completion, and poll async job status.
6
+
7
+ This README covers the public SDK contract and usage examples.
8
+
9
+ ## API origin
10
+
11
+ By default, the SDK uses the production Tollara API origin. Override when needed (non-production or private deployments):
12
+
13
+ - **`TollaraClient`:** pass `apiUrl`, or set **`TOLLARA_API_URL`**
14
+
15
+ ## Tollara client (recommended)
16
+
17
+ `TollaraClient` uses optional **`TOLLARA_SERVICE_ID`** (service UUID), required **`TOLLARA_SERVICE_SECRET`** (unless passed as `serviceSecret`), and optional **`TOLLARA_API_URL`**.
18
+
19
+ ```ts
20
+ import { TollaraClient } from '@tollara/service-sdk';
21
+
22
+ const client = new TollaraClient({
23
+ serviceId: 'service-uuid',
24
+ serviceSecret: 'secret',
25
+ });
26
+ await client.getRequestStatus(requestId, serviceKey);
27
+ await client.reportUsage(userId, serviceId, 1);
28
+ await client.validateServiceKey(serviceKey);
29
+ const estimate = await client.estimateUsage(serviceKey, 1);
30
+ if (estimate) {
31
+ const allowed = estimate.wouldAllow;
32
+ const status = estimate.httpStatus;
33
+ }
34
+
35
+ // JWT usage estimate (unsigned Core response): internal Core user id + service id + units
36
+ // await client.estimateUsageWithJwt(bearerJwt, coreUserId, serviceId, 1);
37
+
38
+ // Gateway invoke (Bearer = service key): method, serviceId, endpointId, serviceKey, optional body + async flag
39
+ // await client.invokeService('POST', serviceId, endpointId, serviceKey, { body: '{}', async: false });
40
+ ```
41
+
42
+ ### Verify signature and user context together
43
+
44
+ Verification defaults to signing version **v2** (newer user-context suffix, no quota segment in the signed material). `verifySignatureFromHeaders` also reads `X-Tollara-Signing-Version` when present.
45
+
46
+ ```ts
47
+ import { verifySignatureFromHeadersAndGetUserContext } from '@tollara/service-sdk';
48
+
49
+ const ctx = verifySignatureFromHeadersAndGetUserContext(serviceSecret, headers, rawBody);
50
+ if (ctx) { /* trusted */ }
51
+ ```
52
+
53
+ ## Install
54
+
55
+ ```bash
56
+ npm install @tollara/service-sdk
57
+ ```
58
+
59
+ ## API highlights
60
+
61
+ - `TollaraHeaders` — canonical `X-Tollara-*` names (including signing-version for gateway HMAC v2)
62
+ - `buildGatewayUserContextString` / `buildGatewayUserContextStringV2` — inbound suffix helpers
63
+ - `verifyInboundHmac` / `verifySignatureFromHeaders` — inbound gateway HMAC
64
+ - `getUserContext` — parses headers (case-insensitive keys)
65
+ - `TollaraClient` — validate key, estimates, invoke, usage reporting, gateway polling
66
+ - `validateServiceKey` / `estimateUsage` — Core **service-key** paths; response HMAC verified when headers present
67
+ - `estimateUsageWithJwt` — Core `POST …/billing/usage/estimate` with Bearer JWT (unsigned response)
68
+ - `invokeService` — gateway `…/service/{serviceId}/endpoint/…/invoke` and `…/invoke/async`
69
+ - `reportUsage`, `reportProgress`, `reportCompletion` — usage service (**report** body uses ISO `timestamp`; `X-Tollara-Timestamp` = epoch **seconds** for HMAC)
70
+ - `getRequestStatus`, `getRequestResult` — async job polling
71
+
72
+ ## Examples
73
+
74
+ ### Verify HMAC (backend)
75
+
76
+ ```ts
77
+ import { verifySignatureFromHeaders, getUserContext } from '@tollara/service-sdk';
78
+
79
+ const serviceSecret = 'your-service-shared-secret';
80
+ const valid = verifySignatureFromHeaders(serviceSecret, req.headers, rawBodyString);
81
+ if (valid) {
82
+ const ctx = getUserContext(req.headers);
83
+ }
84
+ ```
85
+
86
+ ### Validate service key (caller)
87
+
88
+ ```ts
89
+ import { validateServiceKey } from '@tollara/service-sdk';
90
+
91
+ const result = await validateServiceKey({
92
+ serviceKey: 'bearer-token',
93
+ serviceId: 'service-id',
94
+ serviceSecret: 'service-secret',
95
+ });
96
+ ```
97
+
98
+ Optional `baseUrl` when not using the default production origin. Successful validate results include **`serviceKeyId`** when Core returns it (§2.1).
99
+
100
+ ### Usage estimate (caller)
101
+
102
+ Same trust model as validate: JSON body with the service key (no separate bearer on Core). Response HMAC is verified for success and typical denial statuses when signature headers are present.
103
+
104
+ ```ts
105
+ import { estimateUsage } from '@tollara/service-sdk';
106
+
107
+ const est = await estimateUsage({
108
+ serviceKey: 'bearer-token',
109
+ serviceId: 'service-id',
110
+ serviceSecret: 'service-secret',
111
+ estimatedUnits: 1,
112
+ });
113
+ ```
114
+
115
+ ### Report usage
116
+
117
+ ```ts
118
+ import { reportUsage } from '@tollara/service-sdk';
119
+
120
+ await reportUsage({
121
+ userId: 'u1',
122
+ serviceId: 'a1',
123
+ unitsUsed: 1,
124
+ serviceSecret: 'secret',
125
+ });
126
+ ```
127
+
128
+ ### Progress and completion (async)
129
+
130
+ URLs come from the platform (`progress_url`, `callback_url`).
131
+
132
+ ```ts
133
+ import { CompletionStatus, reportProgress, reportCompletionWithResult } from '@tollara/service-sdk';
134
+
135
+ await reportProgress({
136
+ progressUrl,
137
+ requestId,
138
+ stage: 'processing',
139
+ percentageComplete: 50,
140
+ serviceSecret,
141
+ });
142
+ await reportCompletionWithResult({
143
+ callbackUrl,
144
+ requestId,
145
+ status: CompletionStatus.Completed,
146
+ result: 'done',
147
+ serviceSecret,
148
+ units: 1,
149
+ });
150
+ ```
151
+
152
+ ### Job status / result (caller)
153
+
154
+ ```ts
155
+ import { getRequestStatus, getRequestResult } from '@tollara/service-sdk';
156
+
157
+ const st = await getRequestStatus({ requestId, serviceKey });
158
+ const res = await getRequestResult({ requestId, serviceKey });
159
+ ```
160
+
161
+ Optional `baseUrl` on each call when not using the default origin.
162
+
163
+ ## Build & test
164
+
165
+ ```bash
166
+ npm ci
167
+ npm run build
168
+ npm test
169
+ ```
170
+
171
+ ## Release (npm)
172
+
173
+ Package name: **`@tollara/service-sdk`** ([npm scoped packages](https://docs.npmjs.com/about-scopes-and-packages)).
174
+
175
+ 1. **Version** — Bump `"version"` in [`package.json`](package.json) (SemVer). npm will not let you publish the same version twice.
176
+ 2. **Verify** — `npm ci`, `npm test`, and `npm run build` (or rely on `prepublishOnly`, which runs `build` on `npm publish`).
177
+ 3. **Login** — `npm login` on the machine that will publish, or use an **automation token** / `NPM_TOKEN` in CI (see [access tokens](https://docs.npmjs.com/about-access-tokens) and [CI workflows](https://docs.npmjs.com/using-private-packages-in-a-ci-cd-workflow)).
178
+ 4. **Publish** — From `sdk-js`:
179
+
180
+ ```bash
181
+ npm publish --access public
182
+ ```
183
+
184
+ The first publish of a **scoped** package to the public registry must use `--access public` (subsequent publishes can omit it if the package is already public).
185
+
186
+ 5. **Tag** — Tag the Git commit that matches the published version.
187
+
188
+ Optional: `npm publish --dry-run` to inspect the tarball without uploading. `repository`, `files` (`dist`, `README.md`), and `prepublishOnly` are already set in `package.json`.
189
+
190
+ Contract reference: this README and the package API surface.
@@ -0,0 +1,372 @@
1
+ /** Canonical Tollara HTTP header names (gateway-signed requests and signed responses). */
2
+ declare const TollaraHeaders: {
3
+ readonly SIGNATURE: "X-Tollara-Signature";
4
+ readonly TIMESTAMP: "X-Tollara-Timestamp";
5
+ readonly USER_ID: "X-Tollara-User-ID";
6
+ readonly PLAN: "X-Tollara-Plan";
7
+ readonly ROLES: "X-Tollara-Roles";
8
+ readonly QUOTA_REMAINING: "X-Tollara-Quota-Remaining";
9
+ readonly SUBSCRIPTION_ACTIVE: "X-Tollara-Subscription-Active";
10
+ readonly BILLING_MODEL: "X-Tollara-Billing-Model";
11
+ readonly MEASUREMENT_TYPE: "X-Tollara-Measurement-Type";
12
+ readonly UNIT_LABEL: "X-Tollara-Unit-Label";
13
+ /** Gateway HMAC user-context schema: `2` = v2 suffix (leading `2`, no quota segment). */
14
+ readonly SIGNING_VERSION: "X-Tollara-Signing-Version";
15
+ };
16
+ type TollaraHeaderName = (typeof TollaraHeaders)[keyof typeof TollaraHeaders];
17
+
18
+ /**
19
+ * HMAC-SHA256 with UTF-8 key/message and Base64 output (per docs/hmac-spec.md).
20
+ */
21
+ declare function calculateHmac(data: string, key: string): string;
22
+ /**
23
+ * Outbound signing: canonical string = bodyString + timestamp.
24
+ */
25
+ declare function calculateHmacWithTimestamp(bodyString: string, timestamp: string | number, key: string): string;
26
+ /**
27
+ * Constant-time string comparison to avoid timing attacks.
28
+ */
29
+ declare function constantTimeEquals(a: string, b: string): boolean;
30
+ /**
31
+ * Validate that signature matches HMAC(payloadString, key).
32
+ */
33
+ declare function validateHmacSignature(signature: string, payloadString: string, key: string): boolean;
34
+
35
+ interface UserContext {
36
+ userId: string | null;
37
+ plan: string | null;
38
+ roles: string[];
39
+ quotaRemaining: number | null;
40
+ subscriptionActive: boolean;
41
+ billingModelType: string | null;
42
+ measurementType: string | null;
43
+ unitLabel: string | null;
44
+ }
45
+ interface VerifySignatureInput {
46
+ signature: string;
47
+ timestamp: string;
48
+ payload: string | object | null;
49
+ userId: string | null;
50
+ plan: string | null;
51
+ roles: string[];
52
+ quotaRemaining: number | string | null;
53
+ /** Must match X-Tollara-Subscription-Active for verification. */
54
+ subscriptionActive: boolean;
55
+ billingModelType?: string | null;
56
+ measurementType?: string | null;
57
+ unitLabel?: string | null;
58
+ /** When `2`, uses HMAC user-context v2 (see TollaraHeaders.SIGNING_VERSION). */
59
+ signingVersion?: string | null;
60
+ }
61
+ /** User fields that participate in inbound HMAC userContextString (docs/hmac-spec.md). */
62
+ interface SignedUserContext {
63
+ userId: string | null;
64
+ plan: string | null;
65
+ roles: string[];
66
+ quotaRemaining: number | string | null;
67
+ subscriptionActive: boolean;
68
+ billingModelType?: string | null;
69
+ measurementType?: string | null;
70
+ unitLabel?: string | null;
71
+ }
72
+ interface InboundHmacRequest {
73
+ signature: string;
74
+ timestamp: string;
75
+ payload: string | object | null;
76
+ signedUserContext: SignedUserContext;
77
+ signingVersion?: string | null;
78
+ }
79
+ /** Loose header map (e.g. Node/Express lowercases keys). */
80
+ type HeaderBag = Record<string, string | string[] | undefined | null>;
81
+ /**
82
+ * Gateway inbound HMAC suffix: userId + plan + rolesCsv + quota + subscriptionActive + billing + measurement + unit.
83
+ */
84
+ declare function buildGatewayUserContextString(userId: string | null, plan: string | null, roles: string[], quotaRemaining: number | string | null, subscriptionActive: boolean, billingModelType: string | null, measurementType: string | null, unitLabel: string | null): string;
85
+ /** Gateway HMAC user-context v2: leading `2`, then userId, plan, roles, subscription, billing fields (no quota). */
86
+ declare function buildGatewayUserContextStringV2(userId: string | null, plan: string | null, roles: string[], subscriptionActive: boolean, billingModelType: string | null, measurementType: string | null, unitLabel: string | null): string;
87
+ /**
88
+ * Verifies HMAC on an inbound gateway request.
89
+ * Canonical string: payload + timestamp + userContextString (see docs/hmac-spec.md).
90
+ */
91
+ declare function verifySignature(serviceSecret: string, input: VerifySignatureInput): boolean;
92
+ /**
93
+ * Verifies inbound HMAC using {@link InboundHmacRequest} (preferred typed entry point).
94
+ */
95
+ declare function verifyInboundHmac(serviceSecret: string, request: InboundHmacRequest): boolean;
96
+ /**
97
+ * Verifies inbound HMAC from a case-insensitive header bag and payload.
98
+ */
99
+ declare function verifySignatureFromHeaders(serviceSecret: string, headers: HeaderBag, payload: string | object | null): boolean;
100
+ /**
101
+ * Verifies inbound HMAC; if valid returns user context, else `null` (do not trust headers).
102
+ * Parses `X-Tollara-*` headers into {@link UserContext} (case-insensitive header names).
103
+ */
104
+ declare function verifySignatureFromHeadersAndGetUserContext(serviceSecret: string, headers: HeaderBag, payload: string | object | null): UserContext | null;
105
+ declare function getUserContext(headers: HeaderBag): UserContext;
106
+
107
+ interface ServiceKeyValidationResult {
108
+ userId: string | null;
109
+ serviceId: string | null;
110
+ /** Core service-key row id when present in the validate JSON body. */
111
+ serviceKeyId: string | null;
112
+ plan: string | null;
113
+ roles: string[];
114
+ quotaRemaining: number | null;
115
+ subscriptionActive: boolean;
116
+ billingModelType: string | null;
117
+ measurementType: string | null;
118
+ unitLabel: string | null;
119
+ }
120
+ interface UsageEstimateResult {
121
+ sufficientCredits: boolean;
122
+ wouldExceedCap: boolean;
123
+ wouldAllow: boolean;
124
+ estimatedCost: number | null;
125
+ remainingCredits: number | null;
126
+ remainingSpendingCap: number | null;
127
+ billingModelType: string | null;
128
+ measurementType: string | null;
129
+ unitLabel: string | null;
130
+ breakdown: Record<string, unknown> | null;
131
+ estimateSchemaVersion: number;
132
+ timestamp: number;
133
+ httpStatus: number;
134
+ }
135
+ /**
136
+ * Validates a service key via the Tollara API and verifies response HMAC.
137
+ * Uses `baseUrl` (default production API origin) + `/api/v1/service-keys/validate`.
138
+ * Optional in-memory cache with 60s TTL via {@link createValidationCache}.
139
+ */
140
+ declare function validateServiceKey(params: {
141
+ /** API origin; defaults to `https://api.tollara.ai`. */
142
+ baseUrl?: string | null;
143
+ serviceKey: string;
144
+ serviceId: string | null;
145
+ serviceSecret: string;
146
+ fetch?: typeof globalThis.fetch;
147
+ }): Promise<ServiceKeyValidationResult | null>;
148
+ /**
149
+ * Usage pre-flight for a service key (Core). Same trust model as {@link validateServiceKey}: JSON body, response HMAC.
150
+ * Verifies signatures on 200 / 403 / 429 when `X-Tollara-Signature` and `X-Tollara-Timestamp` are present.
151
+ */
152
+ declare function estimateUsage(params: {
153
+ baseUrl?: string | null;
154
+ serviceKey: string;
155
+ serviceId: string | null;
156
+ serviceSecret: string;
157
+ estimatedUnits: number;
158
+ fetch?: typeof globalThis.fetch;
159
+ }): Promise<UsageEstimateResult | null>;
160
+ /**
161
+ * Core JWT usage estimate (`POST …/billing/usage/estimate`). Not HMAC-signed (spec §2.2).
162
+ */
163
+ declare function estimateUsageWithJwt(params: {
164
+ baseUrl?: string | null;
165
+ corePathPrefix?: string | null;
166
+ bearerToken: string;
167
+ userId: string;
168
+ serviceId: string;
169
+ estimatedUnits: number;
170
+ fetch?: typeof globalThis.fetch;
171
+ }): Promise<UsageEstimateResult | null>;
172
+ /**
173
+ * Simple cache for validateServiceKey (optional).
174
+ */
175
+ declare function createValidationCache(): {
176
+ get(serviceKey: string): ServiceKeyValidationResult | null;
177
+ set(serviceKey: string, result: ServiceKeyValidationResult): void;
178
+ clear(): void;
179
+ };
180
+
181
+ /**
182
+ * Completion status for usage service async completion (docs/sdk-api-spec.md §3.3).
183
+ */
184
+ declare enum CompletionStatus {
185
+ Completed = "COMPLETED",
186
+ Failed = "FAILED"
187
+ }
188
+
189
+ /** Default Tollara API origin (production). */
190
+ declare const DEFAULT_API_URL = "https://api.tollara.ai";
191
+ /** Path segments joined to `baseUrl` for each service (not part of the public URL API). */
192
+ declare const DEFAULT_CORE_PATH_PREFIX = "/api/v1";
193
+ declare const DEFAULT_GATEWAY_PATH_PREFIX = "/api";
194
+ declare const DEFAULT_USAGE_PATH_PREFIX = "/api/usage";
195
+
196
+ /** Builds `{baseUrl}/api/usage/report` using the default usage path. */
197
+ declare function buildUsageReportUrl(baseUrl: string): string;
198
+ type ReportProgressParams = {
199
+ progressUrl: string;
200
+ requestId: string;
201
+ stage: string;
202
+ percentageComplete: number;
203
+ /** Omit or leave unset when there is no error (avoids passing null). */
204
+ errorMessage?: string | null;
205
+ serviceSecret: string;
206
+ fetch?: typeof globalThis.fetch;
207
+ };
208
+ /**
209
+ * POST to progressUrl with signed body (optional errorMessage).
210
+ */
211
+ declare function reportProgress(params: ReportProgressParams): Promise<boolean>;
212
+ type ReportCompletionParams = {
213
+ callbackUrl: string;
214
+ requestId: string;
215
+ status: CompletionStatus;
216
+ result?: string | null;
217
+ resultUrl?: string | null;
218
+ contentType?: string | null;
219
+ units?: number | null;
220
+ serviceSecret: string;
221
+ fetch?: typeof globalThis.fetch;
222
+ };
223
+ /**
224
+ * POST completion with status and optional units (defaults to 0).
225
+ */
226
+ declare function reportCompletion(params: Pick<ReportCompletionParams, 'callbackUrl' | 'requestId' | 'status' | 'serviceSecret' | 'fetch'> & {
227
+ units?: number | null;
228
+ }): Promise<boolean>;
229
+ /**
230
+ * POST completion with inline result text.
231
+ */
232
+ declare function reportCompletionWithResult(params: Pick<ReportCompletionParams, 'callbackUrl' | 'requestId' | 'status' | 'result' | 'units' | 'serviceSecret' | 'fetch'>): Promise<boolean>;
233
+ /**
234
+ * POST to callbackUrl with signed body (all optional fields).
235
+ */
236
+ declare function reportCompletionFull(params: ReportCompletionParams): Promise<boolean>;
237
+ interface UsageReportResponse {
238
+ status?: string;
239
+ warning?: string;
240
+ isOverLimit?: boolean;
241
+ remainingRequestsPerPeriod?: number;
242
+ remainingTimeUnitsPerPeriod?: number;
243
+ remainingSpendingCap?: number;
244
+ overageRate?: number;
245
+ }
246
+ /**
247
+ * POST usage report with signed body (`{baseUrl}/api/usage/report`).
248
+ */
249
+ declare function reportUsage(params: {
250
+ /** API origin; defaults to `https://api.tollara.ai`. */
251
+ baseUrl?: string | null;
252
+ userId: string;
253
+ serviceId: string;
254
+ unitsUsed: number;
255
+ timestamp?: number | Date | null;
256
+ serviceSecret: string;
257
+ fetch?: typeof globalThis.fetch;
258
+ }): Promise<UsageReportResponse>;
259
+
260
+ /**
261
+ * Caller-side gateway polling for async jobs.
262
+ */
263
+ interface GatewayPollResult {
264
+ ok: boolean;
265
+ status: number;
266
+ body: string;
267
+ }
268
+ /**
269
+ * GET .../requests/{requestId}/status with Bearer service key.
270
+ */
271
+ declare function getRequestStatus(params: {
272
+ /** API origin; defaults to `https://api.tollara.ai`. */
273
+ baseUrl?: string | null;
274
+ requestId: string;
275
+ serviceKey: string;
276
+ fetch?: typeof globalThis.fetch;
277
+ }): Promise<GatewayPollResult>;
278
+ /**
279
+ * GET .../requests/{requestId}/result with Bearer service key.
280
+ */
281
+ declare function getRequestResult(params: {
282
+ /** API origin; defaults to `https://api.tollara.ai`. */
283
+ baseUrl?: string | null;
284
+ requestId: string;
285
+ serviceKey: string;
286
+ fetch?: typeof globalThis.fetch;
287
+ }): Promise<GatewayPollResult>;
288
+
289
+ type GatewayHttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
290
+ type GatewayInvokeAsyncEnvelope = {
291
+ requestId: string;
292
+ callbackUrl: string;
293
+ progressUrl: string;
294
+ };
295
+ type GatewayInvokeResult = {
296
+ statusCode: number;
297
+ body: string;
298
+ asyncEnvelope?: GatewayInvokeAsyncEnvelope;
299
+ };
300
+ /**
301
+ * Gateway service invoke (sync or async). See `docs-sdk/MAIN-SDK-API-SPEC.md` §1.1–1.2.
302
+ */
303
+ declare function invokeService(params: {
304
+ baseUrl?: string | null;
305
+ gatewayPathPrefix?: string | null;
306
+ method: GatewayHttpMethod;
307
+ serviceId: string;
308
+ endpointId: string;
309
+ serviceKey: string;
310
+ body?: string | null;
311
+ async?: boolean;
312
+ fetch?: typeof globalThis.fetch;
313
+ }): Promise<GatewayInvokeResult | null>;
314
+
315
+ declare const ENV_API_URL = "TOLLARA_API_URL";
316
+ declare const ENV_SERVICE_ID = "TOLLARA_SERVICE_ID";
317
+ declare const ENV_SERVICE_SECRET = "TOLLARA_SERVICE_SECRET";
318
+
319
+ type TollaraClientOptions = {
320
+ /**
321
+ * API origin (scheme + host). Defaults to `https://api.tollara.ai` or `TOLLARA_API_URL`.
322
+ * All service calls use this origin with fixed paths (validate, usage, gateway polling).
323
+ */
324
+ apiUrl?: string | null;
325
+ /** Service UUID; falls back to `TOLLARA_SERVICE_ID`. */
326
+ serviceId?: string | null;
327
+ /** Shared secret; falls back to `TOLLARA_SERVICE_SECRET`. */
328
+ serviceSecret?: string | null;
329
+ fetch?: typeof globalThis.fetch;
330
+ };
331
+ /**
332
+ * Unified client: validate service key, report usage/progress/complete, poll gateway for async jobs.
333
+ * Omitted options fall back to `TOLLARA_*` environment variables (when `process.env` exists).
334
+ */
335
+ declare class TollaraClient {
336
+ static readonly ENV_API_URL = "TOLLARA_API_URL";
337
+ static readonly ENV_SERVICE_ID = "TOLLARA_SERVICE_ID";
338
+ static readonly ENV_SERVICE_SECRET = "TOLLARA_SERVICE_SECRET";
339
+ static readonly DEFAULT_API_URL = "https://api.tollara.ai";
340
+ static readonly DEFAULT_CORE_PATH_PREFIX = "/api/v1";
341
+ static readonly DEFAULT_GATEWAY_PATH_PREFIX = "/api";
342
+ static readonly DEFAULT_USAGE_PATH_PREFIX = "/api/usage";
343
+ private readonly apiOrigin;
344
+ private readonly serviceId;
345
+ private readonly serviceSecret;
346
+ private readonly fetchFn;
347
+ constructor(options?: TollaraClientOptions);
348
+ validateServiceKey(serviceKey: string): Promise<ServiceKeyValidationResult | null>;
349
+ estimateUsage(serviceKey: string, estimatedUnits: number): Promise<UsageEstimateResult | null>;
350
+ /**
351
+ * Core JWT usage estimate (`POST …/billing/usage/estimate`). Response is not HMAC-signed.
352
+ */
353
+ estimateUsageWithJwt(bearerToken: string, userId: string, serviceId: string, estimatedUnits: number): Promise<UsageEstimateResult | null>;
354
+ /**
355
+ * Gateway service invoke (sync or async). See platform spec §1.1–1.2.
356
+ */
357
+ invokeService(method: GatewayHttpMethod, serviceId: string, endpointId: string, serviceKey: string, options?: {
358
+ body?: string | null;
359
+ async?: boolean;
360
+ }): Promise<GatewayInvokeResult | null>;
361
+ reportUsage(userId: string, serviceId: string, unitsUsed: number): Promise<UsageReportResponse>;
362
+ sendProgressUpdate(progressUrl: string, requestId: string, stage: string, percentageComplete: number, errorMessage?: string | null): Promise<boolean>;
363
+ sendCompletion(callbackUrl: string, requestId: string, status: CompletionStatus, units: number, options?: {
364
+ result?: string | null;
365
+ resultUrl?: string | null;
366
+ contentType?: string | null;
367
+ }): Promise<boolean>;
368
+ getRequestStatus(requestId: string, serviceKey: string): Promise<GatewayPollResult>;
369
+ getRequestResult(requestId: string, serviceKey: string): Promise<GatewayPollResult>;
370
+ }
371
+
372
+ export { CompletionStatus, DEFAULT_API_URL, DEFAULT_CORE_PATH_PREFIX, DEFAULT_GATEWAY_PATH_PREFIX, DEFAULT_USAGE_PATH_PREFIX, ENV_API_URL, ENV_SERVICE_ID, ENV_SERVICE_SECRET, type GatewayHttpMethod, type GatewayInvokeAsyncEnvelope, type GatewayInvokeResult, type GatewayPollResult, type HeaderBag, type InboundHmacRequest, type ReportCompletionParams, type ReportProgressParams, type ServiceKeyValidationResult, type SignedUserContext, TollaraClient, type TollaraClientOptions, type TollaraHeaderName, TollaraHeaders, type UsageEstimateResult, type UsageReportResponse, type UserContext, type VerifySignatureInput, buildGatewayUserContextString, buildGatewayUserContextStringV2, buildUsageReportUrl, calculateHmac, calculateHmacWithTimestamp, constantTimeEquals, createValidationCache, estimateUsage, estimateUsageWithJwt, getRequestResult, getRequestStatus, getUserContext, invokeService, reportCompletion, reportCompletionFull, reportCompletionWithResult, reportProgress, reportUsage, validateHmacSignature, validateServiceKey, verifyInboundHmac, verifySignature, verifySignatureFromHeaders, verifySignatureFromHeadersAndGetUserContext };