@astermd-hq/sdk 0.0.2
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 +21 -0
- package/README.md +253 -0
- package/dist/index.cjs +3338 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2825 -0
- package/dist/index.d.ts +2825 -0
- package/dist/index.js +3298 -0
- package/dist/index.js.map +1 -0
- package/package.json +80 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,2825 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Immutable value object holding a JWT bearer token and its server-stated
|
|
3
|
+
* expiry.
|
|
4
|
+
*
|
|
5
|
+
* {@link TokenManager} builds one after a successful credential exchange and
|
|
6
|
+
* hands it to a {@link TokenStore}. Before each request the manager calls
|
|
7
|
+
* {@link Token.isExpired} to decide whether the cached token can be reused or a
|
|
8
|
+
* fresh one must be acquired.
|
|
9
|
+
*
|
|
10
|
+
* The organisation the token authorises is encoded inside the JWT itself, which
|
|
11
|
+
* is why no SDK method takes an organisation argument.
|
|
12
|
+
*/
|
|
13
|
+
declare class Token {
|
|
14
|
+
#private;
|
|
15
|
+
/**
|
|
16
|
+
* @param value The raw JWT string, to be sent as `Authorization: Bearer <value>`.
|
|
17
|
+
* @param expiresAt Expiry as declared by the token response's
|
|
18
|
+
* `access_token_expiry` field. Copied, so a later mutation of
|
|
19
|
+
* the caller's `Date` cannot change the token.
|
|
20
|
+
*/
|
|
21
|
+
constructor(value: string, expiresAt: Date);
|
|
22
|
+
/**
|
|
23
|
+
* Returns the raw JWT string.
|
|
24
|
+
*
|
|
25
|
+
* @returns The JWT, ready for use as a bearer credential.
|
|
26
|
+
*/
|
|
27
|
+
value(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Returns the server-stated expiry.
|
|
30
|
+
*
|
|
31
|
+
* A fresh `Date` each call, so callers cannot mutate the token's state. Note
|
|
32
|
+
* that the SDK treats the token as expired before this moment - see
|
|
33
|
+
* {@link Token.isExpired}.
|
|
34
|
+
*
|
|
35
|
+
* @returns The expiry as declared in the token response.
|
|
36
|
+
*/
|
|
37
|
+
expiresAt(): Date;
|
|
38
|
+
/**
|
|
39
|
+
* Reports whether this token should be considered expired and replaced.
|
|
40
|
+
*
|
|
41
|
+
* The pre-buffer avoids a race where the token is valid when checked but
|
|
42
|
+
* expires in transit before the server processes the request. The default 30
|
|
43
|
+
* seconds means the SDK only trusts a token with at least half a minute of
|
|
44
|
+
* validity left. Pass a custom value only when you know something specific
|
|
45
|
+
* about clock skew in your environment.
|
|
46
|
+
*
|
|
47
|
+
* @param preBufferSeconds Seconds to subtract from the stated expiry when
|
|
48
|
+
* computing the effective cutoff.
|
|
49
|
+
* @returns `true` when the token is at or past its effective expiry.
|
|
50
|
+
*/
|
|
51
|
+
isExpired(preBufferSeconds?: number): boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Pluggable persistence for the bearer token.
|
|
56
|
+
*
|
|
57
|
+
* The default is {@link InMemoryTokenStore}, which caches the token for the life
|
|
58
|
+
* of the process. That is the right choice for a long-lived server. It is the
|
|
59
|
+
* wrong choice when every request runs in a fresh process or isolate - a
|
|
60
|
+
* serverless function, for instance - because each one would then perform its own
|
|
61
|
+
* credential exchange. {@link FileTokenStore} covers the single-machine case, and
|
|
62
|
+
* this interface covers everything else.
|
|
63
|
+
*
|
|
64
|
+
* Every method is asynchronous so a store can talk to a remote system:
|
|
65
|
+
*
|
|
66
|
+
* ```ts
|
|
67
|
+
* class RedisTokenStore implements TokenStore {
|
|
68
|
+
* constructor(private readonly redis: MyRedisClient) {}
|
|
69
|
+
*
|
|
70
|
+
* async get(): Promise<Token | null> {
|
|
71
|
+
* const raw = await this.redis.get('astermd:token');
|
|
72
|
+
* if (raw === null) return null;
|
|
73
|
+
* const { value, expiresAt } = JSON.parse(raw);
|
|
74
|
+
* return new Token(value, new Date(expiresAt));
|
|
75
|
+
* }
|
|
76
|
+
*
|
|
77
|
+
* async put(token: Token): Promise<void> {
|
|
78
|
+
* await this.redis.set(
|
|
79
|
+
* 'astermd:token',
|
|
80
|
+
* JSON.stringify({ value: token.value(), expiresAt: token.expiresAt() }),
|
|
81
|
+
* );
|
|
82
|
+
* }
|
|
83
|
+
*
|
|
84
|
+
* async clear(): Promise<void> {
|
|
85
|
+
* await this.redis.del('astermd:token');
|
|
86
|
+
* }
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* A stored token is a live credential. Whatever backs the store must be as
|
|
91
|
+
* protected as the client secret itself.
|
|
92
|
+
*/
|
|
93
|
+
interface TokenStore {
|
|
94
|
+
/**
|
|
95
|
+
* Returns the cached token, or `null` when there is none.
|
|
96
|
+
*
|
|
97
|
+
* Expiry is not this method's concern - {@link TokenManager} checks it. A store
|
|
98
|
+
* should return whatever it holds and treat unreadable or corrupt state as
|
|
99
|
+
* absence rather than raising.
|
|
100
|
+
*
|
|
101
|
+
* @returns The stored token, or `null`.
|
|
102
|
+
*/
|
|
103
|
+
get(): Promise<Token | null>;
|
|
104
|
+
/**
|
|
105
|
+
* Stores a freshly acquired token, replacing any previous one.
|
|
106
|
+
*
|
|
107
|
+
* @param token The token to persist.
|
|
108
|
+
*/
|
|
109
|
+
put(token: Token): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Discards the stored token so the next {@link TokenStore.get} returns `null`.
|
|
112
|
+
*
|
|
113
|
+
* Called when the server rejects the current token, before a fresh one is
|
|
114
|
+
* acquired.
|
|
115
|
+
*/
|
|
116
|
+
clear(): Promise<void>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Options accepted by {@link Config}. */
|
|
120
|
+
interface ConfigOptions {
|
|
121
|
+
/** OAuth2 client ID issued by AsterMD. Must be non-empty. */
|
|
122
|
+
clientId: string;
|
|
123
|
+
/** OAuth2 client secret corresponding to `clientId`. Must be non-empty. */
|
|
124
|
+
clientSecret: string;
|
|
125
|
+
/** Bare API hostname without scheme or path. Defaults to `api.astermd.com`. */
|
|
126
|
+
baseHost?: string;
|
|
127
|
+
/** Per-request timeout in seconds. Must be >= 1. Defaults to 10. */
|
|
128
|
+
timeoutSeconds?: number;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Immutable value object holding SDK configuration.
|
|
132
|
+
*
|
|
133
|
+
* Stores the OAuth2 credentials and connection settings every outbound request
|
|
134
|
+
* needs. It is constructed once inside {@link AsterMDClient} and never mutated.
|
|
135
|
+
* Validation happens eagerly in the constructor, so a misconfigured client fails
|
|
136
|
+
* at boot rather than at its first network call.
|
|
137
|
+
*
|
|
138
|
+
* `baseHost` is a bare hostname. The SDK always prepends `https://` and appends
|
|
139
|
+
* `/v1/{service}/{path}` itself - never pass a full URL.
|
|
140
|
+
*/
|
|
141
|
+
declare class Config {
|
|
142
|
+
#private;
|
|
143
|
+
/**
|
|
144
|
+
* @param options Credentials and connection settings.
|
|
145
|
+
* @throws {TypeError} If `clientId` or `clientSecret` is empty, if `baseHost`
|
|
146
|
+
* contains a scheme or a path, or if `timeoutSeconds` is below 1.
|
|
147
|
+
*/
|
|
148
|
+
constructor(options: ConfigOptions);
|
|
149
|
+
/**
|
|
150
|
+
* Returns the OAuth2 client ID used to acquire JWT bearer tokens.
|
|
151
|
+
*
|
|
152
|
+
* @returns The client ID, guaranteed non-empty.
|
|
153
|
+
*/
|
|
154
|
+
clientId(): string;
|
|
155
|
+
/**
|
|
156
|
+
* Returns the OAuth2 client secret used to acquire JWT bearer tokens.
|
|
157
|
+
*
|
|
158
|
+
* @returns The client secret, guaranteed non-empty.
|
|
159
|
+
*/
|
|
160
|
+
clientSecret(): string;
|
|
161
|
+
/**
|
|
162
|
+
* Returns the bare API hostname used to build request URLs.
|
|
163
|
+
*
|
|
164
|
+
* Never contains a scheme or a path. {@link UrlBuilder} prepends `https://`
|
|
165
|
+
* and appends `/v1/{service}/{path}` at request time.
|
|
166
|
+
*
|
|
167
|
+
* @returns The hostname, e.g. `api.astermd.com`.
|
|
168
|
+
*/
|
|
169
|
+
baseHost(): string;
|
|
170
|
+
/**
|
|
171
|
+
* Returns the per-request HTTP timeout in seconds.
|
|
172
|
+
*
|
|
173
|
+
* Applied by {@link FetchHttpClient} as an `AbortSignal` deadline covering the
|
|
174
|
+
* whole request. A custom {@link HttpClient} may use it at its own discretion.
|
|
175
|
+
*
|
|
176
|
+
* @returns The timeout in seconds, always >= 1.
|
|
177
|
+
*/
|
|
178
|
+
timeoutSeconds(): number;
|
|
179
|
+
/**
|
|
180
|
+
* Builds the fully-qualified CDN URL for a media asset path returned by the API.
|
|
181
|
+
*
|
|
182
|
+
* API responses carry relative asset paths such as
|
|
183
|
+
* `{org}/{channel}/products/{file}.png`. Pass one here to resolve it.
|
|
184
|
+
*
|
|
185
|
+
* Treat the result as a *fetch* URL, not a *serve* URL: download the asset
|
|
186
|
+
* once, store it on your own filesystem or CDN, and serve your copy to end
|
|
187
|
+
* users. Hot-linking the AsterMD CDN from a storefront is not supported and is
|
|
188
|
+
* subject to rate limiting.
|
|
189
|
+
*
|
|
190
|
+
* @param path Relative asset path as returned by the API; a leading slash is
|
|
191
|
+
* optional and is trimmed.
|
|
192
|
+
* @returns The fully-qualified URL.
|
|
193
|
+
*/
|
|
194
|
+
assetUrl(path: string): string;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The SDK's HTTP injection seam: one method, taking a platform `Request` and
|
|
199
|
+
* returning a platform `Response`.
|
|
200
|
+
*
|
|
201
|
+
* The default implementation is {@link FetchHttpClient}, which wraps the global
|
|
202
|
+
* `fetch`. Supplying your own is how you route SDK traffic through a proxy
|
|
203
|
+
* agent, add your own retry policy, pin TLS settings, or stub the network out in
|
|
204
|
+
* tests:
|
|
205
|
+
*
|
|
206
|
+
* ```ts
|
|
207
|
+
* class ProxiedHttpClient implements HttpClient {
|
|
208
|
+
* async sendRequest(request: Request): Promise<Response> {
|
|
209
|
+
* return await fetch(request, { dispatcher: myProxyAgent });
|
|
210
|
+
* }
|
|
211
|
+
* }
|
|
212
|
+
*
|
|
213
|
+
* const client = new AsterMDClient({
|
|
214
|
+
* clientId: process.env.ASTERMD_CLIENT_ID!,
|
|
215
|
+
* clientSecret: process.env.ASTERMD_CLIENT_SECRET!,
|
|
216
|
+
* httpClient: new ProxiedHttpClient(),
|
|
217
|
+
* });
|
|
218
|
+
* ```
|
|
219
|
+
*
|
|
220
|
+
* An implementation should let a network-level failure surface as
|
|
221
|
+
* {@link TransportError}. {@link Transport} wraps anything else this throws
|
|
222
|
+
* into a {@link TransportError} as well, so every error reaching a caller of
|
|
223
|
+
* the SDK still extends {@link AsterMDError} - but that is a safety net, not
|
|
224
|
+
* licence to throw arbitrary values: prefer throwing {@link TransportError}
|
|
225
|
+
* directly, or an {@link AsterMDError} subclass, so the original error type is
|
|
226
|
+
* preserved rather than rewrapped.
|
|
227
|
+
*
|
|
228
|
+
* The debug logger is itself an implementation of this interface that decorates
|
|
229
|
+
* another one, which is how a single seam covers both API traffic and the
|
|
230
|
+
* credential exchange.
|
|
231
|
+
*/
|
|
232
|
+
interface HttpClient {
|
|
233
|
+
/**
|
|
234
|
+
* Sends one request and resolves with the response.
|
|
235
|
+
*
|
|
236
|
+
* @param request The fully-built request, including URL, method, headers, and
|
|
237
|
+
* body. Implementations must not mutate it.
|
|
238
|
+
* @returns The response as received. A non-2xx status is a normal return, not
|
|
239
|
+
* an error - {@link Transport} maps statuses to errors.
|
|
240
|
+
* @throws {TransportError} On a network-level failure, where no response
|
|
241
|
+
* arrived.
|
|
242
|
+
*/
|
|
243
|
+
sendRequest(request: Request): Promise<Response>;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Destination for debug log entries.
|
|
248
|
+
*
|
|
249
|
+
* Called once per request with a fully formatted entry - timestamp, curl
|
|
250
|
+
* command, and response. Return a promise to have the SDK await the write. Any
|
|
251
|
+
* error it throws is swallowed: a failing log must never fail an API call.
|
|
252
|
+
*/
|
|
253
|
+
type DebugSink = (entry: string) => void | Promise<void>;
|
|
254
|
+
|
|
255
|
+
/** Options accepted by {@link Response}. */
|
|
256
|
+
interface ResponseOptions<T> {
|
|
257
|
+
/** HTTP status code of the response (200–299). */
|
|
258
|
+
statusCode: number;
|
|
259
|
+
/** Contents of the envelope's `data` key. */
|
|
260
|
+
data: T;
|
|
261
|
+
/** Contents of the envelope's `meta` key; may be empty. */
|
|
262
|
+
meta: Record<string, unknown>;
|
|
263
|
+
/** Human-readable `message` string from the envelope. */
|
|
264
|
+
message: string;
|
|
265
|
+
/** Raw response body, before decoding. */
|
|
266
|
+
raw: string;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Value object representing a successful API response with its envelope
|
|
270
|
+
* already decoded.
|
|
271
|
+
*
|
|
272
|
+
* Every AsterMD response that returns HTTP 2xx is wrapped in a standard JSON
|
|
273
|
+
* envelope:
|
|
274
|
+
*
|
|
275
|
+
* ```json
|
|
276
|
+
* { "success": true, "message": "Record created.", "data": { }, "meta": { } }
|
|
277
|
+
* ```
|
|
278
|
+
*
|
|
279
|
+
* {@link Transport} decodes that envelope and builds a `Response` from it.
|
|
280
|
+
* `data()` holds the primary resource payload - `{ session: "<uuid>" }` for
|
|
281
|
+
* session creation, for instance - while `meta()` carries pagination cursors and
|
|
282
|
+
* other out-of-band information where the endpoint emits them.
|
|
283
|
+
*
|
|
284
|
+
* Non-2xx responses are never wrapped in this object; they raise
|
|
285
|
+
* {@link ApiError} or one of its subclasses instead.
|
|
286
|
+
*
|
|
287
|
+
* The shape of `data()` varies by endpoint, so it defaults to an untyped record.
|
|
288
|
+
* A caller who knows the shape may narrow it:
|
|
289
|
+
*
|
|
290
|
+
* ```ts
|
|
291
|
+
* const patient = await client.patients().view<Patient>('p-1');
|
|
292
|
+
* ```
|
|
293
|
+
*
|
|
294
|
+
* The instance and its top-level `data`/`meta` objects are frozen, which guards
|
|
295
|
+
* against accidental reassignment or a stray `delete` on this response. That
|
|
296
|
+
* freeze is shallow, though: an object or array nested inside `data()` is the
|
|
297
|
+
* server's payload passed through unchanged, and remains as mutable as any
|
|
298
|
+
* other object you receive from `JSON.parse`. Treat nested values as you would
|
|
299
|
+
* any other deserialised response - copy before mutating if that matters to
|
|
300
|
+
* you - rather than relying on this class to protect them.
|
|
301
|
+
*/
|
|
302
|
+
declare class Response$1<T = Record<string, unknown>> {
|
|
303
|
+
#private;
|
|
304
|
+
/**
|
|
305
|
+
* @param options The decoded envelope fields.
|
|
306
|
+
*/
|
|
307
|
+
constructor(options: ResponseOptions<T>);
|
|
308
|
+
/**
|
|
309
|
+
* Returns the HTTP status code, always 200–299 for a `Response` instance.
|
|
310
|
+
*
|
|
311
|
+
* @returns The status code, e.g. `200` or `201`.
|
|
312
|
+
*/
|
|
313
|
+
statusCode(): number;
|
|
314
|
+
/**
|
|
315
|
+
* Returns the primary resource payload from the envelope's `data` key.
|
|
316
|
+
*
|
|
317
|
+
* The shape varies by endpoint - see the TSDoc on the resource method you
|
|
318
|
+
* called. Empty when the server returned no data.
|
|
319
|
+
*
|
|
320
|
+
* @returns The decoded `data` object.
|
|
321
|
+
*/
|
|
322
|
+
data(): T;
|
|
323
|
+
/**
|
|
324
|
+
* Returns the envelope's `meta` key, typically pagination information.
|
|
325
|
+
*
|
|
326
|
+
* Present on paginated list endpoints such as `products/list` and
|
|
327
|
+
* `treatments/list`, where it may carry `total`, `per_page`, `current_page`,
|
|
328
|
+
* and `last_page`. Empty for endpoints that emit no `meta` block.
|
|
329
|
+
*
|
|
330
|
+
* @returns The decoded `meta` object.
|
|
331
|
+
*/
|
|
332
|
+
meta(): Readonly<Record<string, unknown>>;
|
|
333
|
+
/**
|
|
334
|
+
* Returns the human-readable `message` string from the envelope.
|
|
335
|
+
*
|
|
336
|
+
* Usually a short confirmation such as `"Session created."`. Empty when the
|
|
337
|
+
* server omitted the field.
|
|
338
|
+
*
|
|
339
|
+
* @returns The envelope message, possibly empty.
|
|
340
|
+
*/
|
|
341
|
+
message(): string;
|
|
342
|
+
/**
|
|
343
|
+
* Returns the raw response body exactly as received, before decoding.
|
|
344
|
+
*
|
|
345
|
+
* Useful for low-level debugging. Do not log this value for endpoints carrying
|
|
346
|
+
* PHI, such as `patients/health-information`.
|
|
347
|
+
*
|
|
348
|
+
* @returns The raw JSON body.
|
|
349
|
+
*/
|
|
350
|
+
raw(): string;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** Overrides accepted by {@link FileUpload.fromPath}. */
|
|
354
|
+
interface FileUploadFromPathOptions {
|
|
355
|
+
/** Name to send in the part header. Defaults to the path's base name. */
|
|
356
|
+
fileName?: string;
|
|
357
|
+
/** MIME type to send. Defaults to one derived from the extension. */
|
|
358
|
+
mimeType?: string;
|
|
359
|
+
}
|
|
360
|
+
/** Overrides accepted by {@link FileUpload.fromContents}. */
|
|
361
|
+
interface FileUploadFromContentsOptions {
|
|
362
|
+
/** MIME type to send. Defaults to one derived from the file name. */
|
|
363
|
+
mimeType?: string;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* An immutable file, in memory, ready to be sent as a `multipart/form-data`
|
|
367
|
+
* part.
|
|
368
|
+
*
|
|
369
|
+
* A handful of AsterMD endpoints take bytes rather than JSON - the
|
|
370
|
+
* intake-submission file endpoints on the `sales` service are the current
|
|
371
|
+
* example. Those bytes come from very different places: a file your users just
|
|
372
|
+
* uploaded and that now sits on local disk, or a slice of a large file being fed
|
|
373
|
+
* through the multipart flow one chunk at a time. This class is the single shape
|
|
374
|
+
* resource methods accept for both, so {@link Transport} has exactly one thing
|
|
375
|
+
* to encode.
|
|
376
|
+
*
|
|
377
|
+
* Build it through one of the two factories - the constructor is private, so an
|
|
378
|
+
* instance always carries a file name and a MIME type:
|
|
379
|
+
*
|
|
380
|
+
* ```ts
|
|
381
|
+
* // From a file on disk; name and MIME type come from the path.
|
|
382
|
+
* await client.intakeSubmissions().uploadFile(
|
|
383
|
+
* sessionId,
|
|
384
|
+
* await FileUpload.fromPath('/var/uploads/id-front.jpg'),
|
|
385
|
+
* );
|
|
386
|
+
*
|
|
387
|
+
* // From bytes already in memory: one 10 MB slice of a large video.
|
|
388
|
+
* await client.intakeSubmissions().uploadMultipartPart({
|
|
389
|
+
* uploadId,
|
|
390
|
+
* partNumber: 1,
|
|
391
|
+
* part: FileUpload.fromContents(chunk, 'consult-video.mp4'),
|
|
392
|
+
* });
|
|
393
|
+
* ```
|
|
394
|
+
*
|
|
395
|
+
* The whole file is held in memory, so it is deliberately not the right tool for
|
|
396
|
+
* a 200 MB video - use {@link IntakeSubmissions.uploadLargeFile}, which streams
|
|
397
|
+
* from disk and only ever materialises one part at a time.
|
|
398
|
+
*
|
|
399
|
+
* File names are normalised on construction: directory components are dropped
|
|
400
|
+
* and quote, backslash, and newline characters removed, so a caller-supplied
|
|
401
|
+
* name can never break out of the header it is written into.
|
|
402
|
+
*/
|
|
403
|
+
declare class FileUpload {
|
|
404
|
+
#private;
|
|
405
|
+
private constructor();
|
|
406
|
+
/**
|
|
407
|
+
* Reads a file from local disk into an upload.
|
|
408
|
+
*
|
|
409
|
+
* The file is read in full and held in memory, so keep this for the sizes the
|
|
410
|
+
* single-shot endpoints allow. Without `options.fileName` the path's base name
|
|
411
|
+
* is used, and without `options.mimeType` the type is derived from the
|
|
412
|
+
* extension, falling back to `application/octet-stream`.
|
|
413
|
+
*
|
|
414
|
+
* @param path Path to an existing, readable file.
|
|
415
|
+
* @param options Optional name and MIME type overrides.
|
|
416
|
+
* @returns An upload carrying the file's bytes, name, and MIME type.
|
|
417
|
+
* @throws {TypeError} If `path` is not a readable file, or if the resulting
|
|
418
|
+
* file name normalises to nothing usable.
|
|
419
|
+
*/
|
|
420
|
+
static fromPath(path: string, options?: FileUploadFromPathOptions): Promise<FileUpload>;
|
|
421
|
+
/**
|
|
422
|
+
* Wraps bytes already held in memory into an upload.
|
|
423
|
+
*
|
|
424
|
+
* Use this when the bytes never came from a file on disk: a slice of a larger
|
|
425
|
+
* file being sent through the multipart flow, a payload received over the
|
|
426
|
+
* wire, or content generated at runtime. `fileName` is required because there
|
|
427
|
+
* is no path to derive one from.
|
|
428
|
+
*
|
|
429
|
+
* @param contents The raw bytes to send as the part body.
|
|
430
|
+
* @param fileName Name to send in the part header.
|
|
431
|
+
* @param options Optional MIME type override.
|
|
432
|
+
* @returns An upload carrying the given bytes, name, and MIME type.
|
|
433
|
+
* @throws {TypeError} If `fileName` normalises to nothing usable.
|
|
434
|
+
*/
|
|
435
|
+
static fromContents(contents: Uint8Array, fileName: string, options?: FileUploadFromContentsOptions): FileUpload;
|
|
436
|
+
/**
|
|
437
|
+
* Returns the raw bytes that will form the body of the multipart part.
|
|
438
|
+
*
|
|
439
|
+
* @returns The file's contents.
|
|
440
|
+
*/
|
|
441
|
+
contents(): Uint8Array;
|
|
442
|
+
/**
|
|
443
|
+
* Returns the normalised file name sent in the part header.
|
|
444
|
+
*
|
|
445
|
+
* The server uses this to derive the stored object name, inserting a unique
|
|
446
|
+
* identifier before the extension so repeat uploads of the same name never
|
|
447
|
+
* collide.
|
|
448
|
+
*
|
|
449
|
+
* @returns The file name, stripped of directory components and
|
|
450
|
+
* header-breaking characters.
|
|
451
|
+
*/
|
|
452
|
+
fileName(): string;
|
|
453
|
+
/**
|
|
454
|
+
* Returns the MIME type sent for the part.
|
|
455
|
+
*
|
|
456
|
+
* @returns The MIME type, e.g. `image/jpeg`.
|
|
457
|
+
*/
|
|
458
|
+
mimeType(): string;
|
|
459
|
+
/**
|
|
460
|
+
* Returns the size of the upload in bytes.
|
|
461
|
+
*
|
|
462
|
+
* Useful for checking a file against an endpoint's documented limit before
|
|
463
|
+
* spending a round trip on it, and for the `fileSize` value the multipart
|
|
464
|
+
* initiate call requires.
|
|
465
|
+
*
|
|
466
|
+
* @returns The number of bytes in {@link FileUpload.contents}.
|
|
467
|
+
*/
|
|
468
|
+
size(): number;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/** Query string parameters accepted by {@link UrlBuilder.build}. */
|
|
472
|
+
type QueryParams = Record<string, string | number | boolean>;
|
|
473
|
+
/**
|
|
474
|
+
* Builds fully-qualified HTTPS request URLs following the `/v1/{service}/{path}`
|
|
475
|
+
* convention.
|
|
476
|
+
*
|
|
477
|
+
* Every AsterMD endpoint lives under `https://{host}/v1/{service}/{path}`, where
|
|
478
|
+
* `{service}` is `auth`, `sales`, or `platform`. This class is the one place that
|
|
479
|
+
* convention is encoded, so resources never construct URLs and the SDK can never
|
|
480
|
+
* be handed a full URL by a consumer.
|
|
481
|
+
*
|
|
482
|
+
* Path placeholders such as `{id}` are substituted and percent-encoded; query
|
|
483
|
+
* parameters are appended with PHP-compatible encoding so a URL built here is
|
|
484
|
+
* byte-identical to the one the PHP SDK would build for the same call.
|
|
485
|
+
*/
|
|
486
|
+
declare class UrlBuilder {
|
|
487
|
+
#private;
|
|
488
|
+
/**
|
|
489
|
+
* @param baseHost Bare hostname to prepend, e.g. `api.astermd.com`.
|
|
490
|
+
*/
|
|
491
|
+
constructor(baseHost: string);
|
|
492
|
+
/**
|
|
493
|
+
* Constructs the fully-qualified URL for a service and path.
|
|
494
|
+
*
|
|
495
|
+
* Placeholder tokens in `path` - `{id}`, `{session}`, `{form_json_identifier}`
|
|
496
|
+
* - are replaced with the matching values from `pathParams`, percent-encoded.
|
|
497
|
+
* A placeholder with no matching entry is left untouched rather than guessed
|
|
498
|
+
* at, which surfaces the programming error in the request URL. Non-empty
|
|
499
|
+
* `query` entries are serialised and appended after `?`.
|
|
500
|
+
*
|
|
501
|
+
* @param service Service segment, e.g. `sales`, `auth`, `platform`.
|
|
502
|
+
* @param path Path relative to `/v1/{service}`, with optional `{placeholder}`
|
|
503
|
+
* tokens.
|
|
504
|
+
* @param pathParams Placeholder name → replacement value.
|
|
505
|
+
* @param query Query string parameters.
|
|
506
|
+
* @returns The fully-qualified URL.
|
|
507
|
+
*/
|
|
508
|
+
build(service: string, path: string, pathParams?: Record<string, string | number>, query?: QueryParams): string;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/** One outbound API call, described declaratively. */
|
|
512
|
+
interface SendOptions {
|
|
513
|
+
/** Service segment of the URL: `sales`, `auth`, or `platform`. */
|
|
514
|
+
service: string;
|
|
515
|
+
/** HTTP method in uppercase. */
|
|
516
|
+
method: string;
|
|
517
|
+
/** Path relative to `/v1/{service}`, with `{placeholder}` tokens. */
|
|
518
|
+
path: string;
|
|
519
|
+
/** Placeholder name → value, substituted into `path`. */
|
|
520
|
+
pathParams?: Record<string, string | number>;
|
|
521
|
+
/** Query string parameters. */
|
|
522
|
+
query?: QueryParams;
|
|
523
|
+
/**
|
|
524
|
+
* Request body, serialised as JSON. An empty object is sent as `{}`, which
|
|
525
|
+
* every AsterMD endpoint requires of an object-shaped body. Omit for no body.
|
|
526
|
+
* Mutually exclusive with `file`.
|
|
527
|
+
*/
|
|
528
|
+
body?: Record<string, unknown> | null;
|
|
529
|
+
/** Additional headers, e.g. `x-phi-verification-token`. */
|
|
530
|
+
headers?: Record<string, string>;
|
|
531
|
+
/**
|
|
532
|
+
* File to send as a `multipart/form-data` body under the field name `file`.
|
|
533
|
+
* Mutually exclusive with `body`.
|
|
534
|
+
*/
|
|
535
|
+
file?: FileUpload | null;
|
|
536
|
+
}
|
|
537
|
+
/** Collaborators {@link Transport} needs. */
|
|
538
|
+
interface TransportOptions {
|
|
539
|
+
/** The client all outbound calls go through. */
|
|
540
|
+
httpClient: HttpClient;
|
|
541
|
+
/** Builds request URLs. */
|
|
542
|
+
urlBuilder: UrlBuilder;
|
|
543
|
+
/**
|
|
544
|
+
* Returns the current bearer token, or `null` for an unauthenticated request.
|
|
545
|
+
* {@link TokenManager} passes a provider that always returns `null` for its own
|
|
546
|
+
* credential exchange, which is what stops that call recursing.
|
|
547
|
+
*/
|
|
548
|
+
tokenProvider: () => Promise<string | null>;
|
|
549
|
+
/**
|
|
550
|
+
* Invoked at most once per `send()` when the server answers 401. Should
|
|
551
|
+
* refresh the token and resolve `true` to request a replay, or `false` to let
|
|
552
|
+
* the 401 surface.
|
|
553
|
+
*/
|
|
554
|
+
onUnauthorized: () => Promise<boolean>;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* The single HTTP chokepoint for every outbound API call.
|
|
558
|
+
*
|
|
559
|
+
* Each resource method delegates to {@link Transport.send}, which owns the whole
|
|
560
|
+
* request lifecycle: URL construction, bearer-token attachment, request
|
|
561
|
+
* building, body serialisation, dispatch through the {@link HttpClient} seam,
|
|
562
|
+
* envelope decoding, and the mapping from HTTP status to error class. Resources
|
|
563
|
+
* never see a `Request`, a `Response`, or a line of JSON - that separation is
|
|
564
|
+
* what keeps seventeen resource classes trivial and testable.
|
|
565
|
+
*
|
|
566
|
+
* The 401-retry protocol is built in. On a 401 the transport calls
|
|
567
|
+
* `onUnauthorized` once, which in the production wiring refreshes the token, and
|
|
568
|
+
* replays the request if that resolves `true`. A second 401 raises
|
|
569
|
+
* {@link AuthenticationError} with no further retry.
|
|
570
|
+
*
|
|
571
|
+
* Status mapping:
|
|
572
|
+
*
|
|
573
|
+
* - 200–299 → {@link Response} with the envelope decoded
|
|
574
|
+
* - 401 → {@link AuthenticationError}
|
|
575
|
+
* - 404 → {@link NotFoundError}
|
|
576
|
+
* - 422 → {@link ValidationError}, with field errors extracted from `errors`
|
|
577
|
+
* - 429 → {@link RateLimitError}, with `Retry-After` parsed when present
|
|
578
|
+
* - anything else → {@link ApiError}
|
|
579
|
+
* - no response at all → {@link TransportError}, from the {@link HttpClient}
|
|
580
|
+
*/
|
|
581
|
+
declare class Transport {
|
|
582
|
+
#private;
|
|
583
|
+
/**
|
|
584
|
+
* @param options The collaborators to use for every call.
|
|
585
|
+
*/
|
|
586
|
+
constructor(options: TransportOptions);
|
|
587
|
+
/**
|
|
588
|
+
* Dispatches one API call and returns the decoded success envelope.
|
|
589
|
+
*
|
|
590
|
+
* On a 401 the token is refreshed and the call is replayed once before
|
|
591
|
+
* {@link AuthenticationError} is raised.
|
|
592
|
+
*
|
|
593
|
+
* @param options The call to make.
|
|
594
|
+
* @returns The decoded envelope; `data()` holds the resource payload.
|
|
595
|
+
* @throws {TypeError} If both `body` and `file` are supplied.
|
|
596
|
+
* @throws {AuthenticationError} On a 401 that a token refresh did not recover.
|
|
597
|
+
* @throws {NotFoundError} On a 404.
|
|
598
|
+
* @throws {ValidationError} On a 422; inspect `fieldErrors()`.
|
|
599
|
+
* @throws {RateLimitError} On a 429; inspect `retryAfter()`.
|
|
600
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
601
|
+
* @throws {TransportError} If the request never completed.
|
|
602
|
+
*/
|
|
603
|
+
send<T = Record<string, unknown>>(options: SendOptions): Promise<Response$1<T>>;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Base class for every SDK resource; holds the shared {@link Transport}.
|
|
608
|
+
*
|
|
609
|
+
* Resource classes extend this and call `this.transport.send()` to dispatch every
|
|
610
|
+
* outbound request. A resource never builds a request, touches a header, or
|
|
611
|
+
* decodes JSON - all of that belongs to `Transport` alone. That is what keeps
|
|
612
|
+
* seventeen resource classes small enough to read at a glance and testable
|
|
613
|
+
* without a network.
|
|
614
|
+
*/
|
|
615
|
+
declare abstract class AbstractResource {
|
|
616
|
+
/** The shared transport used for all outbound API calls. */
|
|
617
|
+
protected readonly transport: Transport;
|
|
618
|
+
/**
|
|
619
|
+
* @param transport The transport every call from this resource goes through.
|
|
620
|
+
*/
|
|
621
|
+
protected constructor(transport: Transport);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/** Options for {@link Carts.create} and {@link Carts.update}. */
|
|
625
|
+
interface CartWriteOptions {
|
|
626
|
+
/** The session the cart belongs to. */
|
|
627
|
+
session: string;
|
|
628
|
+
/**
|
|
629
|
+
* The cart's full contents. Each entry identifies a product and quantity, and
|
|
630
|
+
* a variant where the product has them. This is the complete list, not a
|
|
631
|
+
* delta - see the API reference in your AsterMD dashboard for the fields.
|
|
632
|
+
*/
|
|
633
|
+
items: Record<string, unknown>[];
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Carts - what the visitor intends to buy, recorded against their session.
|
|
637
|
+
*
|
|
638
|
+
* The cart is a server-side record of intent, kept so abandonment can be measured
|
|
639
|
+
* and so the eventual order can be reconciled against what was selected. It is
|
|
640
|
+
* not a pricing engine and not a checkout: money is handled by your own payment
|
|
641
|
+
* flow, and the resulting order arrives through
|
|
642
|
+
* {@link Treatments.create} or {@link Treatments.sync}.
|
|
643
|
+
*
|
|
644
|
+
* Both methods take the cart's *whole* contents rather than a delta. Mirror your
|
|
645
|
+
* storefront's cart state into `update()` whenever it changes, and the
|
|
646
|
+
* server-side record stays in step with what the visitor is actually looking at.
|
|
647
|
+
*/
|
|
648
|
+
declare class Carts extends AbstractResource {
|
|
649
|
+
/**
|
|
650
|
+
* @param transport The shared transport.
|
|
651
|
+
*/
|
|
652
|
+
constructor(transport: Transport);
|
|
653
|
+
/**
|
|
654
|
+
* Creates the cart for a session.
|
|
655
|
+
*
|
|
656
|
+
* Call this the first time the visitor puts something in their cart. The
|
|
657
|
+
* session must already exist. If a cart is already recorded for the session,
|
|
658
|
+
* use {@link Carts.update} instead.
|
|
659
|
+
*
|
|
660
|
+
* @param options The session and the cart's contents.
|
|
661
|
+
* @returns The created cart.
|
|
662
|
+
* @throws {ValidationError} If the session or an item is rejected.
|
|
663
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
664
|
+
* @throws {TransportError} If the request never completed.
|
|
665
|
+
*/
|
|
666
|
+
create<T = Record<string, unknown>>(options: CartWriteOptions): Promise<Response$1<T>>;
|
|
667
|
+
/**
|
|
668
|
+
* Replaces the contents of a session's cart.
|
|
669
|
+
*
|
|
670
|
+
* The item list is authoritative: whatever you send becomes the cart. Send the
|
|
671
|
+
* full list on every change rather than only what moved, and pass an empty list
|
|
672
|
+
* to record that the visitor emptied their cart.
|
|
673
|
+
*
|
|
674
|
+
* @param options The session and the cart's new contents.
|
|
675
|
+
* @returns The updated cart.
|
|
676
|
+
* @throws {NotFoundError} If no cart exists for the session.
|
|
677
|
+
* @throws {ValidationError} If an item is rejected.
|
|
678
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
679
|
+
* @throws {TransportError} If the request never completed.
|
|
680
|
+
*/
|
|
681
|
+
update<T = Record<string, unknown>>(options: CartWriteOptions): Promise<Response$1<T>>;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Categories - how the catalogue is grouped for browsing.
|
|
686
|
+
*
|
|
687
|
+
* A category is a merchandising grouping a storefront renders as navigation: a
|
|
688
|
+
* condition treated, a product family, a landing-page collection. Products
|
|
689
|
+
* reference the categories they belong to, so fetching the category list is
|
|
690
|
+
* usually the first call a catalogue page makes.
|
|
691
|
+
*
|
|
692
|
+
* Read-only, and configured in your AsterMD dashboard.
|
|
693
|
+
*/
|
|
694
|
+
declare class Categories extends AbstractResource {
|
|
695
|
+
/**
|
|
696
|
+
* @param transport The shared transport.
|
|
697
|
+
*/
|
|
698
|
+
constructor(transport: Transport);
|
|
699
|
+
/**
|
|
700
|
+
* Lists categories, filtered and paginated.
|
|
701
|
+
*
|
|
702
|
+
* Pagination details come back in `meta()`. The accepted filters are in the
|
|
703
|
+
* API reference in your AsterMD dashboard.
|
|
704
|
+
*
|
|
705
|
+
* @param query Filters and pagination parameters.
|
|
706
|
+
* @returns The matching categories, with pagination in `meta()`.
|
|
707
|
+
* @throws {ValidationError} If a filter is rejected.
|
|
708
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
709
|
+
* @throws {TransportError} If the request never completed.
|
|
710
|
+
*/
|
|
711
|
+
list<T = Record<string, unknown>>(query?: QueryParams): Promise<Response$1<T>>;
|
|
712
|
+
/**
|
|
713
|
+
* Fetches one category.
|
|
714
|
+
*
|
|
715
|
+
* @param id The category's id.
|
|
716
|
+
* @returns The category record.
|
|
717
|
+
* @throws {NotFoundError} If the category does not exist or is out of scope.
|
|
718
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
719
|
+
* @throws {TransportError} If the request never completed.
|
|
720
|
+
*/
|
|
721
|
+
view<T = Record<string, unknown>>(id: string): Promise<Response$1<T>>;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Channels - the storefront's own configuration, as the server holds it.
|
|
726
|
+
*
|
|
727
|
+
* A channel is one selling surface: a brand, a site, a campaign landing page.
|
|
728
|
+
* Which products it may sell, which payment processor it uses, and which
|
|
729
|
+
* questionnaires those products require are all channel-level configuration set
|
|
730
|
+
* in your AsterMD dashboard. Your storefront reads that configuration rather than
|
|
731
|
+
* duplicating it, so changing what a brand sells does not need a deployment.
|
|
732
|
+
*
|
|
733
|
+
* Three views, in increasing depth. {@link Channels.view} returns the channel
|
|
734
|
+
* itself. {@link Channels.assignedProducts} returns what it may sell.
|
|
735
|
+
* {@link Channels.details} returns the whole tree - channel, payment processor,
|
|
736
|
+
* products, variants, prices, and per-integration mappings - in a single call,
|
|
737
|
+
* which is what a storefront usually wants at boot.
|
|
738
|
+
*
|
|
739
|
+
* Channel configuration changes rarely. Fetch it rather than embedding a copy,
|
|
740
|
+
* but caching the result for minutes is reasonable.
|
|
741
|
+
*/
|
|
742
|
+
declare class Channels extends AbstractResource {
|
|
743
|
+
/**
|
|
744
|
+
* @param transport The shared transport.
|
|
745
|
+
*/
|
|
746
|
+
constructor(transport: Transport);
|
|
747
|
+
/**
|
|
748
|
+
* Fetches a channel's own configuration.
|
|
749
|
+
*
|
|
750
|
+
* The channel record itself - name, type, status - without its product
|
|
751
|
+
* catalogue.
|
|
752
|
+
*
|
|
753
|
+
* @param id The channel's id.
|
|
754
|
+
* @returns The channel record.
|
|
755
|
+
* @throws {NotFoundError} If the channel does not exist or is out of scope.
|
|
756
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
757
|
+
* @throws {TransportError} If the request never completed.
|
|
758
|
+
*/
|
|
759
|
+
view<T = Record<string, unknown>>(id: string): Promise<Response$1<T>>;
|
|
760
|
+
/**
|
|
761
|
+
* Fetches the products assigned to a channel.
|
|
762
|
+
*
|
|
763
|
+
* What this selling surface is permitted to sell. Use it to build a catalogue
|
|
764
|
+
* page without pulling the full detail tree.
|
|
765
|
+
*
|
|
766
|
+
* @param id The channel's id.
|
|
767
|
+
* @returns The assigned products.
|
|
768
|
+
* @throws {NotFoundError} If the channel does not exist.
|
|
769
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
770
|
+
* @throws {TransportError} If the request never completed.
|
|
771
|
+
*/
|
|
772
|
+
assignedProducts<T = Record<string, unknown>>(id: string): Promise<Response$1<T>>;
|
|
773
|
+
/**
|
|
774
|
+
* Fetches a channel's complete configuration tree.
|
|
775
|
+
*
|
|
776
|
+
* Channel, payment processor, products, variants, prices, and per-integration
|
|
777
|
+
* mappings in one response - everything a storefront needs to render its
|
|
778
|
+
* catalogue without further calls. The envelope is deeply nested and carries
|
|
779
|
+
* server-side field names; see the API reference in your AsterMD dashboard for
|
|
780
|
+
* its shape. Pass `data()` through {@link ChannelDetail.from} to flatten it.
|
|
781
|
+
*
|
|
782
|
+
* @param id The channel's id.
|
|
783
|
+
* @returns The full configuration tree.
|
|
784
|
+
* @throws {NotFoundError} If the channel does not exist.
|
|
785
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
786
|
+
* @throws {TransportError} If the request never completed.
|
|
787
|
+
*/
|
|
788
|
+
details<T = Record<string, unknown>>(id: string): Promise<Response$1<T>>;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Checkout-funnel events recorded against a session.
|
|
793
|
+
*
|
|
794
|
+
* {@link CheckoutEvents.create} always records `CheckoutVisited` - it is the
|
|
795
|
+
* call that opens the funnel record. Every later state is reported through
|
|
796
|
+
* {@link CheckoutEvents.update} with one of the remaining values, which lets the
|
|
797
|
+
* server reconstruct where a prospect abandoned checkout and which upsells they
|
|
798
|
+
* were shown.
|
|
799
|
+
*/
|
|
800
|
+
declare const CheckoutEvent: Readonly<{
|
|
801
|
+
/** The prospect reached the checkout page. Recorded implicitly by `create()`. */
|
|
802
|
+
readonly CheckoutVisited: "checkout_visited";
|
|
803
|
+
/** An upsell was presented to the prospect. */
|
|
804
|
+
readonly UpsellOffered: "upsell_offered";
|
|
805
|
+
/** The prospect accepted a presented upsell. */
|
|
806
|
+
readonly UpsellAccepted: "upsell_accepted";
|
|
807
|
+
/** The prospect declined a presented upsell. */
|
|
808
|
+
readonly UpsellDeclined: "upsell_declined";
|
|
809
|
+
/** The order was placed successfully. */
|
|
810
|
+
readonly OrderPlaced: "order_placed";
|
|
811
|
+
/** The order was declined, typically by the payment processor. */
|
|
812
|
+
readonly OrderDeclined: "order_declined";
|
|
813
|
+
}>;
|
|
814
|
+
/** Any checkout-funnel event wire value. A bare string literal satisfies this type. */
|
|
815
|
+
type CheckoutEvent = (typeof CheckoutEvent)[keyof typeof CheckoutEvent];
|
|
816
|
+
|
|
817
|
+
/** Options for {@link CheckoutEvents.update}. */
|
|
818
|
+
interface CheckoutEventUpdateOptions {
|
|
819
|
+
/** The session whose checkout record is being advanced. */
|
|
820
|
+
session: string;
|
|
821
|
+
/** The funnel state now reached. */
|
|
822
|
+
event: CheckoutEvent;
|
|
823
|
+
/**
|
|
824
|
+
* Additional context for this event - which upsell was shown, the order total,
|
|
825
|
+
* a decline reason. Forwarded unmodified except that it can never override
|
|
826
|
+
* `event`.
|
|
827
|
+
*/
|
|
828
|
+
data?: Record<string, unknown>;
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Checkout events - the visitor's progress through the checkout funnel.
|
|
832
|
+
*
|
|
833
|
+
* Where {@link Sessions} records that a visit happened and {@link Carts} records
|
|
834
|
+
* what was selected, this resource records what happened at the till: the
|
|
835
|
+
* checkout page opening, each upsell offered and accepted or declined, and
|
|
836
|
+
* finally whether the order was placed or declined. Together those events are
|
|
837
|
+
* what makes checkout abandonment measurable.
|
|
838
|
+
*
|
|
839
|
+
* {@link CheckoutEvents.create} opens the record and always registers
|
|
840
|
+
* `checkout_visited`; every later state goes through
|
|
841
|
+
* {@link CheckoutEvents.update} with the matching {@link CheckoutEvent}. Neither
|
|
842
|
+
* call can have its event overridden by the extra payload, so the funnel state
|
|
843
|
+
* the server records is always the one the SDK's caller chose explicitly.
|
|
844
|
+
*/
|
|
845
|
+
declare class CheckoutEvents extends AbstractResource {
|
|
846
|
+
/**
|
|
847
|
+
* @param transport The shared transport.
|
|
848
|
+
*/
|
|
849
|
+
constructor(transport: Transport);
|
|
850
|
+
/**
|
|
851
|
+
* Opens the checkout record for a session, registering `checkout_visited`.
|
|
852
|
+
*
|
|
853
|
+
* Call this once, when the visitor reaches the checkout page. The event is
|
|
854
|
+
* fixed - this is the call that starts the funnel record - and `currency`
|
|
855
|
+
* defaults to `USD` when you do not supply one, since the server requires a
|
|
856
|
+
* currency to interpret any amounts in the payload.
|
|
857
|
+
*
|
|
858
|
+
* @param session The session reaching checkout.
|
|
859
|
+
* @param data Additional context, e.g. `cart_total`, `items_count`,
|
|
860
|
+
* `currency`. Cannot override `session` or `event`.
|
|
861
|
+
* @returns The created checkout-event record.
|
|
862
|
+
* @throws {ValidationError} If the session or payload is rejected.
|
|
863
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
864
|
+
* @throws {TransportError} If the request never completed.
|
|
865
|
+
*/
|
|
866
|
+
create<T = Record<string, unknown>>(session: string, data?: Record<string, unknown>): Promise<Response$1<T>>;
|
|
867
|
+
/**
|
|
868
|
+
* Advances a session's checkout record to a new funnel state.
|
|
869
|
+
*
|
|
870
|
+
* Send one call per state the visitor reaches: an upsell offered, then accepted
|
|
871
|
+
* or declined, then the order placed or declined. The payload carries whatever
|
|
872
|
+
* context that state needs, but it can never override `event` - the state
|
|
873
|
+
* recorded is always the one you passed explicitly.
|
|
874
|
+
*
|
|
875
|
+
* @param options The session, the new state, and any context for it.
|
|
876
|
+
* @returns The updated checkout-event record.
|
|
877
|
+
* @throws {NotFoundError} If no checkout record exists for the session.
|
|
878
|
+
* @throws {ValidationError} If the event or payload is rejected.
|
|
879
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
880
|
+
* @throws {TransportError} If the request never completed.
|
|
881
|
+
*/
|
|
882
|
+
update<T = Record<string, unknown>>(options: CheckoutEventUpdateOptions): Promise<Response$1<T>>;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/**
|
|
886
|
+
* Doctors' networks - handing a case to the clinicians who will review it.
|
|
887
|
+
*
|
|
888
|
+
* A doctors' network is the panel of licensed clinicians configured for your
|
|
889
|
+
* organisation. Syncing pushes a case to that panel so a clinician can pick it
|
|
890
|
+
* up: it is the bridge between the commercial half of the order flow and the
|
|
891
|
+
* clinical one.
|
|
892
|
+
*
|
|
893
|
+
* The single method here is unusual in the SDK for validating before it sends. A
|
|
894
|
+
* sync with neither an opportunity to attach to nor enough detail to identify the
|
|
895
|
+
* person would be accepted by the transport and rejected by the server, having
|
|
896
|
+
* cost a round trip to learn something the SDK can see locally. So it checks
|
|
897
|
+
* first, and raises `TypeError` rather than making the call.
|
|
898
|
+
*
|
|
899
|
+
* ```ts
|
|
900
|
+
* await client.doctorsNetworks().sync({ opportunity_id: opportunityId });
|
|
901
|
+
*
|
|
902
|
+
* // or, without an opportunity yet:
|
|
903
|
+
* await client.doctorsNetworks().sync({
|
|
904
|
+
* user_info: { first_name: 'Jane', email: 'jane@example.test' },
|
|
905
|
+
* });
|
|
906
|
+
* ```
|
|
907
|
+
*/
|
|
908
|
+
declare class DoctorsNetworks extends AbstractResource {
|
|
909
|
+
/**
|
|
910
|
+
* @param transport The shared transport.
|
|
911
|
+
*/
|
|
912
|
+
constructor(transport: Transport);
|
|
913
|
+
/**
|
|
914
|
+
* Pushes a case to the configured doctors' network.
|
|
915
|
+
*
|
|
916
|
+
* The payload must identify who the case is about, one of two ways: an
|
|
917
|
+
* `opportunity_id` for a prospect already recorded, or a `user_info` object
|
|
918
|
+
* carrying at least `first_name` and `email` for one who is not. Everything
|
|
919
|
+
* else is forwarded unmodified.
|
|
920
|
+
*
|
|
921
|
+
* An `emit_opportunity` key is removed before sending - that flag is the
|
|
922
|
+
* server's to set, not a caller's. Your object is not modified; the SDK copies
|
|
923
|
+
* it first.
|
|
924
|
+
*
|
|
925
|
+
* @param payload The case to push. Requires either a non-empty
|
|
926
|
+
* `opportunity_id` string, or a `user_info` object with non-empty
|
|
927
|
+
* `first_name` and `email` strings.
|
|
928
|
+
* @returns The sync result.
|
|
929
|
+
* @throws {TypeError} If the payload identifies neither an opportunity nor a
|
|
930
|
+
* person.
|
|
931
|
+
* @throws {ValidationError} If the server rejects a field.
|
|
932
|
+
* @throws {NotFoundError} If the referenced opportunity does not exist.
|
|
933
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
934
|
+
* @throws {TransportError} If the request never completed.
|
|
935
|
+
*/
|
|
936
|
+
sync<T = Record<string, unknown>>(payload: Record<string, unknown>): Promise<Response$1<T>>;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
/**
|
|
940
|
+
* Geo - where a visitor is, and whether you are allowed to serve them.
|
|
941
|
+
*
|
|
942
|
+
* Telehealth is licensed jurisdiction by jurisdiction, so location is not a
|
|
943
|
+
* personalisation detail here - it decides whether an order flow may start at
|
|
944
|
+
* all. These two endpoints answer the two different questions that follow from
|
|
945
|
+
* that.
|
|
946
|
+
*
|
|
947
|
+
* {@link Geo.info} resolves an IP address to a location, which is what you want
|
|
948
|
+
* for pre-filling a state selector or routing a visitor to the right regional
|
|
949
|
+
* content. {@link Geo.blocklist} answers the narrower question of whether this
|
|
950
|
+
* address is barred - a jurisdiction you are not licensed in, a known proxy or
|
|
951
|
+
* anonymiser. Check the blocklist at the top of the funnel: turning a visitor
|
|
952
|
+
* away on the landing page is far cheaper than doing it after they have completed
|
|
953
|
+
* an intake questionnaire.
|
|
954
|
+
*
|
|
955
|
+
* Pass the *visitor's* address, not your server's. Behind a proxy or load
|
|
956
|
+
* balancer that means reading the forwarded-for header your infrastructure sets,
|
|
957
|
+
* with the same care you would apply anywhere else - that header is
|
|
958
|
+
* caller-supplied unless your edge overwrites it.
|
|
959
|
+
*
|
|
960
|
+
* Both take their input as a query parameter, and redaction never covers the URL,
|
|
961
|
+
* so a debug entry for these calls contains the IP address that was checked.
|
|
962
|
+
*/
|
|
963
|
+
declare class Geo extends AbstractResource {
|
|
964
|
+
/**
|
|
965
|
+
* @param transport The shared transport.
|
|
966
|
+
*/
|
|
967
|
+
constructor(transport: Transport);
|
|
968
|
+
/**
|
|
969
|
+
* Resolves an IP address to a geographic location.
|
|
970
|
+
*
|
|
971
|
+
* Returns the configured provider's answer, normalised. Treat it as a strong
|
|
972
|
+
* hint rather than proof - a VPN or a mobile carrier's routing can place a
|
|
973
|
+
* visitor a long way from where they are. For a licensing decision, prefer
|
|
974
|
+
* {@link Geo.blocklist} and the state the visitor declares.
|
|
975
|
+
*
|
|
976
|
+
* @param ip The visitor's IP address, v4 or v6.
|
|
977
|
+
* @returns The resolved location.
|
|
978
|
+
* @throws {ValidationError} If the address is malformed.
|
|
979
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
980
|
+
* @throws {TransportError} If the request never completed.
|
|
981
|
+
*/
|
|
982
|
+
info<T = Record<string, unknown>>(ip: string): Promise<Response$1<T>>;
|
|
983
|
+
/**
|
|
984
|
+
* Reports whether an IP address is barred from starting an order flow.
|
|
985
|
+
*
|
|
986
|
+
* Covers jurisdictions your organisation is not licensed for, along with known
|
|
987
|
+
* proxies and anonymisers. Call it as early as the landing page: refusing a
|
|
988
|
+
* visitor before they invest time in a questionnaire is better for them and
|
|
989
|
+
* cheaper for you.
|
|
990
|
+
*
|
|
991
|
+
* @param ip The visitor's IP address, v4 or v6.
|
|
992
|
+
* @returns The blocklist verdict.
|
|
993
|
+
* @throws {ValidationError} If the address is malformed.
|
|
994
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
995
|
+
* @throws {TransportError} If the request never completed.
|
|
996
|
+
*/
|
|
997
|
+
blocklist<T = Record<string, unknown>>(ip: string): Promise<Response$1<T>>;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* Journey events recorded alongside intake-submission records.
|
|
1002
|
+
*
|
|
1003
|
+
* Every call to {@link IntakeSubmissions.create} or
|
|
1004
|
+
* {@link IntakeSubmissions.update} must carry one of these events to advance the
|
|
1005
|
+
* server-side order-flow state machine.
|
|
1006
|
+
*
|
|
1007
|
+
* Two separate flows exist, each with three events.
|
|
1008
|
+
*
|
|
1009
|
+
* Pre-qualifying flow - emitted while the prospect answers eligibility
|
|
1010
|
+
* questions:
|
|
1011
|
+
* - `PreQualifyingInitiated` → first form step (use with `create()`)
|
|
1012
|
+
* - `PreQualifyingInProgress` → intermediate steps (use with `update()`)
|
|
1013
|
+
* - `PreQualifyingCompleted` → final step / form submitted (use with `update()`)
|
|
1014
|
+
*
|
|
1015
|
+
* Intake flow - emitted while the prospect fills in the full intake
|
|
1016
|
+
* questionnaire:
|
|
1017
|
+
* - `IntakeInitiated` → first form step (use with `create()`)
|
|
1018
|
+
* - `IntakeInProgress` → intermediate steps (use with `update()`)
|
|
1019
|
+
* - `IntakeCompleted` → final step / form submitted (use with `update()`)
|
|
1020
|
+
*
|
|
1021
|
+
* In both flows the `*Initiated` event always goes to `create()`, the call that
|
|
1022
|
+
* creates the server-side record, and the `*InProgress` / `*Completed` events go
|
|
1023
|
+
* to subsequent `update()` calls.
|
|
1024
|
+
*/
|
|
1025
|
+
declare const Event: Readonly<{
|
|
1026
|
+
/** The prospect started the pre-qualifying questionnaire. Pass to `create()`. */
|
|
1027
|
+
readonly PreQualifyingInitiated: "pre_qualifying_initiated";
|
|
1028
|
+
/** The prospect advanced through an intermediate pre-qualifying step. Pass to `update()`. */
|
|
1029
|
+
readonly PreQualifyingInProgress: "pre_qualifying_inprogress";
|
|
1030
|
+
/** The prospect submitted the pre-qualifying form. Pass to `update()`. */
|
|
1031
|
+
readonly PreQualifyingCompleted: "pre_qualifying_completed";
|
|
1032
|
+
/** The prospect started the intake questionnaire. Pass to `create()`. */
|
|
1033
|
+
readonly IntakeInitiated: "intake_initiated";
|
|
1034
|
+
/** The prospect advanced through an intermediate intake step. Pass to `update()`. */
|
|
1035
|
+
readonly IntakeInProgress: "intake_inprogress";
|
|
1036
|
+
/** The prospect submitted the intake form. Pass to `update()`. */
|
|
1037
|
+
readonly IntakeCompleted: "intake_completed";
|
|
1038
|
+
}>;
|
|
1039
|
+
/** Any journey event wire value. A bare string literal satisfies this type. */
|
|
1040
|
+
type Event = (typeof Event)[keyof typeof Event];
|
|
1041
|
+
|
|
1042
|
+
/** Options for {@link IntakeSubmissions.view}. */
|
|
1043
|
+
interface IntakeViewOptions {
|
|
1044
|
+
/** The intake submission's id. */
|
|
1045
|
+
id: string;
|
|
1046
|
+
/** The teleform the submission answers. */
|
|
1047
|
+
teleformId: string;
|
|
1048
|
+
}
|
|
1049
|
+
/** Options for {@link IntakeSubmissions.create} and {@link IntakeSubmissions.update}. */
|
|
1050
|
+
interface IntakeWriteOptions {
|
|
1051
|
+
/** The session the submission belongs to. */
|
|
1052
|
+
session: string;
|
|
1053
|
+
/** The journey event this call records. */
|
|
1054
|
+
event: Event;
|
|
1055
|
+
/** The teleform being answered. */
|
|
1056
|
+
teleformId: string;
|
|
1057
|
+
/**
|
|
1058
|
+
* The answers, one entry per field: `{ id, name, label, type, value }`, where
|
|
1059
|
+
* `value` is itself a list of `{ value }` entries so a multi-select field can
|
|
1060
|
+
* carry several. Send every answer collected so far, not only what changed.
|
|
1061
|
+
*/
|
|
1062
|
+
data: Record<string, unknown>[];
|
|
1063
|
+
/**
|
|
1064
|
+
* Optional progress information for a partially completed form, e.g.
|
|
1065
|
+
* `{ current_step, total_steps }`. Lets the server report how far a prospect
|
|
1066
|
+
* got before abandoning.
|
|
1067
|
+
*/
|
|
1068
|
+
progress?: Record<string, unknown>;
|
|
1069
|
+
}
|
|
1070
|
+
/** Options for {@link IntakeSubmissions.uploadFile}. */
|
|
1071
|
+
interface UploadFileOptions {
|
|
1072
|
+
/** The session the file belongs to. */
|
|
1073
|
+
sessionId: string;
|
|
1074
|
+
/** The file to upload. */
|
|
1075
|
+
file: FileUpload;
|
|
1076
|
+
}
|
|
1077
|
+
/** Options for {@link IntakeSubmissions.initiateMultipartUpload}. */
|
|
1078
|
+
interface InitiateMultipartOptions {
|
|
1079
|
+
/** The session the file belongs to. */
|
|
1080
|
+
sessionId: string;
|
|
1081
|
+
/** Name to store the file under. */
|
|
1082
|
+
fileName: string;
|
|
1083
|
+
/** MIME type of the whole file. */
|
|
1084
|
+
mimeType: string;
|
|
1085
|
+
/** Total size of the whole file in bytes, not of one part. */
|
|
1086
|
+
fileSize: number;
|
|
1087
|
+
}
|
|
1088
|
+
/** Options for {@link IntakeSubmissions.uploadMultipartPart}. */
|
|
1089
|
+
interface UploadPartOptions {
|
|
1090
|
+
/** The upload id returned by initiate. */
|
|
1091
|
+
uploadId: string;
|
|
1092
|
+
/** 1-based part number, between 1 and 10000. */
|
|
1093
|
+
partNumber: number;
|
|
1094
|
+
/** The bytes of this part. */
|
|
1095
|
+
part: FileUpload;
|
|
1096
|
+
}
|
|
1097
|
+
/** One uploaded part, as {@link IntakeSubmissions.finishMultipartUpload} expects it. */
|
|
1098
|
+
interface MultipartPart {
|
|
1099
|
+
/** The part number that was uploaded. */
|
|
1100
|
+
part_number: number;
|
|
1101
|
+
/** The etag the server returned for that part. */
|
|
1102
|
+
etag: string;
|
|
1103
|
+
}
|
|
1104
|
+
/** Options for {@link IntakeSubmissions.finishMultipartUpload}. */
|
|
1105
|
+
interface FinishMultipartOptions {
|
|
1106
|
+
/** The upload id returned by initiate. */
|
|
1107
|
+
uploadId: string;
|
|
1108
|
+
/** Every uploaded part with its etag, in order. Must not be empty. */
|
|
1109
|
+
parts: MultipartPart[];
|
|
1110
|
+
}
|
|
1111
|
+
/** Options for {@link IntakeSubmissions.uploadLargeFile}. */
|
|
1112
|
+
interface UploadLargeFileOptions {
|
|
1113
|
+
/** The session the file belongs to. */
|
|
1114
|
+
sessionId: string;
|
|
1115
|
+
/** Path to the file on local disk. */
|
|
1116
|
+
filePath: string;
|
|
1117
|
+
/** Name to store the file under. Defaults to the path's base name. */
|
|
1118
|
+
fileName?: string;
|
|
1119
|
+
/** MIME type to send. Defaults to one derived from the file name. */
|
|
1120
|
+
mimeType?: string;
|
|
1121
|
+
/**
|
|
1122
|
+
* Bytes per part. Must be between {@link IntakeSubmissions.MIN_PART_SIZE} and
|
|
1123
|
+
* {@link IntakeSubmissions.MAX_PART_SIZE}. Defaults to
|
|
1124
|
+
* {@link IntakeSubmissions.DEFAULT_PART_SIZE}.
|
|
1125
|
+
*/
|
|
1126
|
+
partSize?: number;
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Intake submissions - the answers a prospect gives to your questionnaires.
|
|
1130
|
+
*
|
|
1131
|
+
* This is where the clinical part of the order flow happens. Two questionnaires
|
|
1132
|
+
* run through this one resource: the short pre-qualifying form that decides
|
|
1133
|
+
* whether a prospect is eligible at all, and the full intake questionnaire a
|
|
1134
|
+
* clinician later reviews. Which one a call belongs to is carried entirely by the
|
|
1135
|
+
* {@link Event} it passes.
|
|
1136
|
+
*
|
|
1137
|
+
* Both follow the same rhythm. The `*Initiated` event goes to
|
|
1138
|
+
* {@link IntakeSubmissions.create}, which creates the server-side record. Every
|
|
1139
|
+
* later step goes to {@link IntakeSubmissions.update} with `*InProgress`, and the
|
|
1140
|
+
* final submission with `*Completed`. Sending progress updates as the prospect
|
|
1141
|
+
* moves through the form is what makes partial completion visible instead of
|
|
1142
|
+
* looking like an abandoned session:
|
|
1143
|
+
*
|
|
1144
|
+
* ```ts
|
|
1145
|
+
* await client.intakeSubmissions().create({
|
|
1146
|
+
* session,
|
|
1147
|
+
* event: Event.IntakeInitiated,
|
|
1148
|
+
* teleformId,
|
|
1149
|
+
* data: answersSoFar,
|
|
1150
|
+
* });
|
|
1151
|
+
*
|
|
1152
|
+
* await client.intakeSubmissions().update({
|
|
1153
|
+
* session,
|
|
1154
|
+
* event: Event.IntakeCompleted,
|
|
1155
|
+
* teleformId,
|
|
1156
|
+
* data: allAnswers,
|
|
1157
|
+
* });
|
|
1158
|
+
* ```
|
|
1159
|
+
*
|
|
1160
|
+
* `data` is cumulative: send every answer collected so far on each call, not just
|
|
1161
|
+
* the fields that changed. Answers that are files rather than values go through
|
|
1162
|
+
* the upload methods on this same resource.
|
|
1163
|
+
*/
|
|
1164
|
+
declare class IntakeSubmissions extends AbstractResource {
|
|
1165
|
+
/**
|
|
1166
|
+
* @param transport The shared transport.
|
|
1167
|
+
*/
|
|
1168
|
+
constructor(transport: Transport);
|
|
1169
|
+
/**
|
|
1170
|
+
* Fetches one intake submission and its recorded answers.
|
|
1171
|
+
*
|
|
1172
|
+
* The teleform id is required alongside the submission id because a submission
|
|
1173
|
+
* is only meaningful against the form it answers - the server uses it to
|
|
1174
|
+
* resolve field definitions for the stored values.
|
|
1175
|
+
*
|
|
1176
|
+
* @param options The submission id and the teleform it answers.
|
|
1177
|
+
* @returns The submission and its answers.
|
|
1178
|
+
* @throws {NotFoundError} If the submission does not exist.
|
|
1179
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1180
|
+
* @throws {TransportError} If the request never completed.
|
|
1181
|
+
*/
|
|
1182
|
+
view<T = Record<string, unknown>>(options: IntakeViewOptions): Promise<Response$1<T>>;
|
|
1183
|
+
/**
|
|
1184
|
+
* Creates the intake-submission record for a session and records the first
|
|
1185
|
+
* answers.
|
|
1186
|
+
*
|
|
1187
|
+
* This is the call that opens the record, so it takes an `*Initiated` event -
|
|
1188
|
+
* `Event.PreQualifyingInitiated` or `Event.IntakeInitiated`. Every later step
|
|
1189
|
+
* goes to {@link IntakeSubmissions.update}. The server advances its order-flow
|
|
1190
|
+
* state machine on the event, so passing the wrong one puts the flow in the
|
|
1191
|
+
* wrong state rather than failing loudly.
|
|
1192
|
+
*
|
|
1193
|
+
* @param options The session, event, teleform, answers, and optional progress.
|
|
1194
|
+
* @returns The created submission.
|
|
1195
|
+
* @throws {ValidationError} If the answers or the event are rejected.
|
|
1196
|
+
* @throws {NotFoundError} If the session or teleform does not exist.
|
|
1197
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1198
|
+
* @throws {TransportError} If the request never completed.
|
|
1199
|
+
*/
|
|
1200
|
+
create<T = Record<string, unknown>>(options: IntakeWriteOptions): Promise<Response$1<T>>;
|
|
1201
|
+
/**
|
|
1202
|
+
* Records further answers against an existing intake submission.
|
|
1203
|
+
*
|
|
1204
|
+
* Takes an `*InProgress` event for an intermediate step or a `*Completed` event
|
|
1205
|
+
* for the final submission. `data` is cumulative - send every answer collected
|
|
1206
|
+
* so far, since the server treats the payload as the current state of the form
|
|
1207
|
+
* rather than a patch.
|
|
1208
|
+
*
|
|
1209
|
+
* @param options The session, event, teleform, answers, and optional progress.
|
|
1210
|
+
* @returns The updated submission.
|
|
1211
|
+
* @throws {ValidationError} If the answers or the event are rejected.
|
|
1212
|
+
* @throws {NotFoundError} If no submission exists for the session.
|
|
1213
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1214
|
+
* @throws {TransportError} If the request never completed.
|
|
1215
|
+
*/
|
|
1216
|
+
update<T = Record<string, unknown>>(options: IntakeWriteOptions): Promise<Response$1<T>>;
|
|
1217
|
+
/** Smallest part the multipart endpoint accepts: 5 MB. */
|
|
1218
|
+
static readonly MIN_PART_SIZE: number;
|
|
1219
|
+
/** Largest part the multipart endpoint accepts: 25 MB. */
|
|
1220
|
+
static readonly MAX_PART_SIZE: number;
|
|
1221
|
+
/** Part size {@link IntakeSubmissions.uploadLargeFile} uses by default: 10 MB. */
|
|
1222
|
+
static readonly DEFAULT_PART_SIZE: number;
|
|
1223
|
+
/**
|
|
1224
|
+
* Uploads one file in a single request, for a file-type answer.
|
|
1225
|
+
*
|
|
1226
|
+
* Use this for the sizes a single request can carry - a photograph of an ID, a
|
|
1227
|
+
* PDF. The whole file is held in memory and sent as one `multipart/form-data`
|
|
1228
|
+
* body. For anything large enough to risk a timeout, use
|
|
1229
|
+
* {@link IntakeSubmissions.uploadLargeFile} instead.
|
|
1230
|
+
*
|
|
1231
|
+
* The response carries the stored file's reference, which is what you place in
|
|
1232
|
+
* the `value` of the corresponding field when you next call
|
|
1233
|
+
* {@link IntakeSubmissions.update}.
|
|
1234
|
+
*
|
|
1235
|
+
* @param options The session and the file.
|
|
1236
|
+
* @returns The stored file's reference.
|
|
1237
|
+
* @throws {ValidationError} If the file type or size is rejected.
|
|
1238
|
+
* @throws {NotFoundError} If the session does not exist.
|
|
1239
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1240
|
+
* @throws {TransportError} If the request never completed.
|
|
1241
|
+
*/
|
|
1242
|
+
uploadFile<T = Record<string, unknown>>(options: UploadFileOptions): Promise<Response$1<T>>;
|
|
1243
|
+
/**
|
|
1244
|
+
* Opens a multipart upload and returns the id the parts will be sent under.
|
|
1245
|
+
*
|
|
1246
|
+
* The first of the four low-level multipart calls. Declare the *whole* file's
|
|
1247
|
+
* name, type, and size here - the server uses the size to validate the parts it
|
|
1248
|
+
* later receives. Prefer {@link IntakeSubmissions.uploadLargeFile}, which
|
|
1249
|
+
* drives all four calls; reach for these directly only when you need to
|
|
1250
|
+
* control the loop, for instance to upload parts from several workers.
|
|
1251
|
+
*
|
|
1252
|
+
* @param options The session and the whole file's metadata.
|
|
1253
|
+
* @returns The opened upload; `data().upload_id` carries the id.
|
|
1254
|
+
* @throws {ValidationError} If the metadata is rejected.
|
|
1255
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1256
|
+
* @throws {TransportError} If the request never completed.
|
|
1257
|
+
*/
|
|
1258
|
+
initiateMultipartUpload<T = Record<string, unknown>>(options: InitiateMultipartOptions): Promise<Response$1<T>>;
|
|
1259
|
+
/**
|
|
1260
|
+
* Uploads one part of an open multipart upload.
|
|
1261
|
+
*
|
|
1262
|
+
* Part numbers are 1-based and must arrive with the part they label; the
|
|
1263
|
+
* response carries an etag you must keep, because
|
|
1264
|
+
* {@link IntakeSubmissions.finishMultipartUpload} needs every part's etag to
|
|
1265
|
+
* assemble the file. Parts may be uploaded in any order.
|
|
1266
|
+
*
|
|
1267
|
+
* @param options The upload id, the part number, and the part's bytes.
|
|
1268
|
+
* @returns The stored part; `data().etag` carries the etag.
|
|
1269
|
+
* @throws {TypeError} If `partNumber` is outside 1–10000.
|
|
1270
|
+
* @throws {NotFoundError} If the upload id is unknown or already finished.
|
|
1271
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1272
|
+
* @throws {TransportError} If the request never completed.
|
|
1273
|
+
*/
|
|
1274
|
+
uploadMultipartPart<T = Record<string, unknown>>(options: UploadPartOptions): Promise<Response$1<T>>;
|
|
1275
|
+
/**
|
|
1276
|
+
* Closes a multipart upload, assembling the parts into one stored file.
|
|
1277
|
+
*
|
|
1278
|
+
* Send every part with the etag its own upload returned. The server verifies
|
|
1279
|
+
* the set against what it received, so a missing or mismatched part fails here
|
|
1280
|
+
* rather than producing a corrupt file.
|
|
1281
|
+
*
|
|
1282
|
+
* @param options The upload id and every part with its etag.
|
|
1283
|
+
* @returns The stored file's reference.
|
|
1284
|
+
* @throws {TypeError} If `parts` is empty.
|
|
1285
|
+
* @throws {ValidationError} If the parts do not match what the server holds.
|
|
1286
|
+
* @throws {NotFoundError} If the upload id is unknown.
|
|
1287
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1288
|
+
* @throws {TransportError} If the request never completed.
|
|
1289
|
+
*/
|
|
1290
|
+
finishMultipartUpload<T = Record<string, unknown>>(options: FinishMultipartOptions): Promise<Response$1<T>>;
|
|
1291
|
+
/**
|
|
1292
|
+
* Abandons a multipart upload and discards the parts already stored.
|
|
1293
|
+
*
|
|
1294
|
+
* Call this when an upload cannot be completed, so the parts do not linger.
|
|
1295
|
+
* {@link IntakeSubmissions.uploadLargeFile} does it for you on failure.
|
|
1296
|
+
*
|
|
1297
|
+
* @param uploadId The upload id to abandon.
|
|
1298
|
+
* @returns The abort confirmation.
|
|
1299
|
+
* @throws {NotFoundError} If the upload id is unknown or already finished.
|
|
1300
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1301
|
+
* @throws {TransportError} If the request never completed.
|
|
1302
|
+
*/
|
|
1303
|
+
abortMultipartUpload<T = Record<string, unknown>>(uploadId: string): Promise<Response$1<T>>;
|
|
1304
|
+
/**
|
|
1305
|
+
* Uploads a large file from disk, driving the whole multipart flow.
|
|
1306
|
+
*
|
|
1307
|
+
* This is the method to reach for when a file is too big for one request. It
|
|
1308
|
+
* streams the file one part at a time, so a 200 MB video costs one part of
|
|
1309
|
+
* memory rather than 200 MB: initiate, then read a part, upload it, keep its
|
|
1310
|
+
* etag, repeat, then finish. If any step fails the upload is aborted so no
|
|
1311
|
+
* parts are left behind, and the failure that broke the upload is what
|
|
1312
|
+
* surfaces - not the abort's own outcome.
|
|
1313
|
+
*
|
|
1314
|
+
* The first part fixes the name and MIME type for the whole upload, so
|
|
1315
|
+
* `initiate` and every part agree.
|
|
1316
|
+
*
|
|
1317
|
+
* ```ts
|
|
1318
|
+
* const stored = await client.intakeSubmissions().uploadLargeFile({
|
|
1319
|
+
* sessionId,
|
|
1320
|
+
* filePath: '/var/uploads/consult-video.mp4',
|
|
1321
|
+
* });
|
|
1322
|
+
* ```
|
|
1323
|
+
*
|
|
1324
|
+
* @param options The session, the file's path, and optional name, MIME type,
|
|
1325
|
+
* and part size.
|
|
1326
|
+
* @returns The finished upload's response - the same one
|
|
1327
|
+
* {@link IntakeSubmissions.finishMultipartUpload} returns.
|
|
1328
|
+
* @throws {TypeError} If `partSize` is outside the allowed range, or if the
|
|
1329
|
+
* file is missing, unreadable, or empty.
|
|
1330
|
+
* @throws {ApiError} If the server response omits a usable `upload_id` or
|
|
1331
|
+
* `etag`, or on any other non-2xx status.
|
|
1332
|
+
* @throws {TransportError} If a request never completed.
|
|
1333
|
+
*/
|
|
1334
|
+
uploadLargeFile<T = Record<string, unknown>>(options: UploadLargeFileOptions): Promise<Response$1<T>>;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
/**
|
|
1338
|
+
* Lab tests - the diagnostic panels a product may require.
|
|
1339
|
+
*
|
|
1340
|
+
* Some treatments cannot be prescribed without a result: a blood panel, a
|
|
1341
|
+
* hormone level. A lab test is the server-side definition of one such
|
|
1342
|
+
* requirement, and products reference the tests they depend on. A storefront reads
|
|
1343
|
+
* this to tell a buyer, before they pay, that a test will be part of their
|
|
1344
|
+
* treatment.
|
|
1345
|
+
*
|
|
1346
|
+
* Read-only, and configured in your AsterMD dashboard.
|
|
1347
|
+
*/
|
|
1348
|
+
declare class LabTests extends AbstractResource {
|
|
1349
|
+
/**
|
|
1350
|
+
* @param transport The shared transport.
|
|
1351
|
+
*/
|
|
1352
|
+
constructor(transport: Transport);
|
|
1353
|
+
/**
|
|
1354
|
+
* Lists lab tests, filtered and paginated.
|
|
1355
|
+
*
|
|
1356
|
+
* Pagination details come back in `meta()`. The accepted filters are in the
|
|
1357
|
+
* API reference in your AsterMD dashboard.
|
|
1358
|
+
*
|
|
1359
|
+
* @param query Filters and pagination parameters.
|
|
1360
|
+
* @returns The matching lab tests, with pagination in `meta()`.
|
|
1361
|
+
* @throws {ValidationError} If a filter is rejected.
|
|
1362
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1363
|
+
* @throws {TransportError} If the request never completed.
|
|
1364
|
+
*/
|
|
1365
|
+
list<T = Record<string, unknown>>(query?: QueryParams): Promise<Response$1<T>>;
|
|
1366
|
+
/**
|
|
1367
|
+
* Fetches one lab test.
|
|
1368
|
+
*
|
|
1369
|
+
* @param id The lab test's id.
|
|
1370
|
+
* @returns The lab test record.
|
|
1371
|
+
* @throws {NotFoundError} If the lab test does not exist or is out of scope.
|
|
1372
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1373
|
+
* @throws {TransportError} If the request never completed.
|
|
1374
|
+
*/
|
|
1375
|
+
view<T = Record<string, unknown>>(id: string): Promise<Response$1<T>>;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
/**
|
|
1379
|
+
* Medications - the reference list of drugs the platform knows about.
|
|
1380
|
+
*
|
|
1381
|
+
* This is a lookup table, not a catalogue of things to sell: it is what a
|
|
1382
|
+
* storefront renders when an intake form asks "which medications are you
|
|
1383
|
+
* currently taking?" Answers picked from this list arrive at the clinician as
|
|
1384
|
+
* recognised drugs rather than free text, which is the difference between a
|
|
1385
|
+
* reviewable history and a paragraph someone has to interpret.
|
|
1386
|
+
*
|
|
1387
|
+
* List-only by design - the API exposes no single-medication endpoint, so
|
|
1388
|
+
* neither does the SDK. Filter through the query parameters to implement a
|
|
1389
|
+
* type-ahead rather than downloading the whole list.
|
|
1390
|
+
*/
|
|
1391
|
+
declare class Medications extends AbstractResource {
|
|
1392
|
+
/**
|
|
1393
|
+
* @param transport The shared transport.
|
|
1394
|
+
*/
|
|
1395
|
+
constructor(transport: Transport);
|
|
1396
|
+
/**
|
|
1397
|
+
* Lists medications, filtered and paginated.
|
|
1398
|
+
*
|
|
1399
|
+
* Intended to back a search or type-ahead on an intake form. Pagination comes
|
|
1400
|
+
* back in `meta()`; the accepted filters are in the API reference in your
|
|
1401
|
+
* AsterMD dashboard.
|
|
1402
|
+
*
|
|
1403
|
+
* @param query Filters and pagination parameters.
|
|
1404
|
+
* @returns The matching medications, with pagination in `meta()`.
|
|
1405
|
+
* @throws {ValidationError} If a filter is rejected.
|
|
1406
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1407
|
+
* @throws {TransportError} If the request never completed.
|
|
1408
|
+
*/
|
|
1409
|
+
list<T = Record<string, unknown>>(query?: QueryParams): Promise<Response$1<T>>;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
/**
|
|
1413
|
+
* Opportunities - a prospect who has identified themselves and is worth
|
|
1414
|
+
* following up.
|
|
1415
|
+
*
|
|
1416
|
+
* This is the commercial record of the order flow, and the point where an
|
|
1417
|
+
* anonymous session becomes a named person. Create it once you have a name and a
|
|
1418
|
+
* contact address, typically right after the pre-qualifying form, and attach the
|
|
1419
|
+
* sessions that led to it so the whole journey stays connected:
|
|
1420
|
+
*
|
|
1421
|
+
* ```ts
|
|
1422
|
+
* const opportunity = await client.opportunities().create({
|
|
1423
|
+
* first_name: 'Jane',
|
|
1424
|
+
* email: 'jane@example.test',
|
|
1425
|
+
* sessions: [session],
|
|
1426
|
+
* });
|
|
1427
|
+
* ```
|
|
1428
|
+
*
|
|
1429
|
+
* Attaching sessions is what makes attribution work: without them the server has
|
|
1430
|
+
* a lead with no idea where it came from. An opportunity can carry several
|
|
1431
|
+
* sessions, which is what you want when a prospect returns across devices or
|
|
1432
|
+
* days.
|
|
1433
|
+
*
|
|
1434
|
+
* Where this record is commercial, {@link Patients} is its clinical counterpart -
|
|
1435
|
+
* created later, once the prospect is actually proceeding with treatment.
|
|
1436
|
+
*/
|
|
1437
|
+
declare class Opportunities extends AbstractResource {
|
|
1438
|
+
/**
|
|
1439
|
+
* @param transport The shared transport.
|
|
1440
|
+
*/
|
|
1441
|
+
constructor(transport: Transport);
|
|
1442
|
+
/**
|
|
1443
|
+
* Creates an opportunity.
|
|
1444
|
+
*
|
|
1445
|
+
* Include a `sessions` array of the session identifiers that led here, so the
|
|
1446
|
+
* server can attribute the lead. The rest of the payload is forwarded
|
|
1447
|
+
* unmodified.
|
|
1448
|
+
*
|
|
1449
|
+
* @param data The prospect's details and the sessions to attach.
|
|
1450
|
+
* @returns The created opportunity, including its id.
|
|
1451
|
+
* @throws {ValidationError} If a field is missing or rejected.
|
|
1452
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1453
|
+
* @throws {TransportError} If the request never completed.
|
|
1454
|
+
*/
|
|
1455
|
+
create<T = Record<string, unknown>>(data: Record<string, unknown>): Promise<Response$1<T>>;
|
|
1456
|
+
/**
|
|
1457
|
+
* Fetches one opportunity.
|
|
1458
|
+
*
|
|
1459
|
+
* @param id The opportunity's id.
|
|
1460
|
+
* @returns The opportunity record.
|
|
1461
|
+
* @throws {NotFoundError} If the opportunity does not exist or is out of scope.
|
|
1462
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1463
|
+
* @throws {TransportError} If the request never completed.
|
|
1464
|
+
*/
|
|
1465
|
+
view<T = Record<string, unknown>>(id: string): Promise<Response$1<T>>;
|
|
1466
|
+
/**
|
|
1467
|
+
* Updates an opportunity.
|
|
1468
|
+
*
|
|
1469
|
+
* Send only the fields you are changing. Use this to enrich a lead as more
|
|
1470
|
+
* information arrives - a phone number, a later session to attach.
|
|
1471
|
+
*
|
|
1472
|
+
* @param id The opportunity's id.
|
|
1473
|
+
* @param data The fields to change.
|
|
1474
|
+
* @returns The updated opportunity.
|
|
1475
|
+
* @throws {NotFoundError} If the opportunity does not exist.
|
|
1476
|
+
* @throws {ValidationError} If a field is rejected.
|
|
1477
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1478
|
+
* @throws {TransportError} If the request never completed.
|
|
1479
|
+
*/
|
|
1480
|
+
update<T = Record<string, unknown>>(id: string, data: Record<string, unknown>): Promise<Response$1<T>>;
|
|
1481
|
+
/**
|
|
1482
|
+
* Changes an opportunity's status.
|
|
1483
|
+
*
|
|
1484
|
+
* Kept separate from {@link Opportunities.update} so a pipeline transition is
|
|
1485
|
+
* explicit in your code and in the server's audit trail. The accepted values
|
|
1486
|
+
* are configured for your organisation; see the API reference in your AsterMD
|
|
1487
|
+
* dashboard.
|
|
1488
|
+
*
|
|
1489
|
+
* @param id The opportunity's id.
|
|
1490
|
+
* @param status The new status value, sent as `opportunity_status`.
|
|
1491
|
+
* @returns The updated opportunity.
|
|
1492
|
+
* @throws {NotFoundError} If the opportunity does not exist.
|
|
1493
|
+
* @throws {ValidationError} If the status is not an accepted value.
|
|
1494
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1495
|
+
* @throws {TransportError} If the request never completed.
|
|
1496
|
+
*/
|
|
1497
|
+
status<T = Record<string, unknown>>(id: string, status: string): Promise<Response$1<T>>;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
/** Options for {@link Patients.submitHealthInformation}. */
|
|
1501
|
+
interface SubmitHealthInformationOptions {
|
|
1502
|
+
/**
|
|
1503
|
+
* The health information to record. Forwarded unmodified; see the API
|
|
1504
|
+
* reference in your AsterMD dashboard for the accepted fields.
|
|
1505
|
+
*/
|
|
1506
|
+
data: Record<string, unknown>;
|
|
1507
|
+
/**
|
|
1508
|
+
* Verification token proving the patient authorised this submission, sent as
|
|
1509
|
+
* the `x-phi-verification-token` header. Required by the server for
|
|
1510
|
+
* submissions that need explicit patient confirmation.
|
|
1511
|
+
*/
|
|
1512
|
+
phiVerificationToken?: string;
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Patients - the person receiving care, and the most sensitive data in the SDK.
|
|
1516
|
+
*
|
|
1517
|
+
* A patient record is created once the prospect has qualified and is proceeding
|
|
1518
|
+
* with treatment, and it is what a clinician's review, a prescription, and an
|
|
1519
|
+
* order all hang from. Where an {@link Opportunities} record is a commercial
|
|
1520
|
+
* artifact, this is a clinical one.
|
|
1521
|
+
*
|
|
1522
|
+
* Everything on this resource carries personal and protected health information.
|
|
1523
|
+
* Two consequences worth knowing about:
|
|
1524
|
+
*
|
|
1525
|
+
* The SDK never writes a request or response body from any `patients/*` endpoint
|
|
1526
|
+
* to a debug log, even with `debugRedact: false` - those bodies are dropped
|
|
1527
|
+
* wholesale rather than masked. Debug entries for these calls show the method,
|
|
1528
|
+
* URL, and status only.
|
|
1529
|
+
*
|
|
1530
|
+
* Health-information submission is a two-step flow when the server requires
|
|
1531
|
+
* explicit patient confirmation: {@link Patients.verifyHealthInformationOtp}
|
|
1532
|
+
* confirms a one-time code the patient received, and the resulting token is
|
|
1533
|
+
* passed to {@link Patients.submitHealthInformation} as
|
|
1534
|
+
* `phiVerificationToken`.
|
|
1535
|
+
*
|
|
1536
|
+
* Handling this data does not by itself make your application HIPAA compliant -
|
|
1537
|
+
* that depends on your own infrastructure, policies, and agreements. Contact
|
|
1538
|
+
* info@astermd.com regarding a Business Associate Agreement.
|
|
1539
|
+
*/
|
|
1540
|
+
declare class Patients extends AbstractResource {
|
|
1541
|
+
/**
|
|
1542
|
+
* @param transport The shared transport.
|
|
1543
|
+
*/
|
|
1544
|
+
constructor(transport: Transport);
|
|
1545
|
+
/**
|
|
1546
|
+
* Creates a patient record.
|
|
1547
|
+
*
|
|
1548
|
+
* Call this once the prospect has qualified and is proceeding. The payload is
|
|
1549
|
+
* forwarded unmodified, so it carries whatever demographic and contact fields
|
|
1550
|
+
* your organisation's configuration requires.
|
|
1551
|
+
*
|
|
1552
|
+
* @param data The patient's details. See the API reference in your AsterMD
|
|
1553
|
+
* dashboard for the accepted fields.
|
|
1554
|
+
* @returns The created patient; `data()` carries the new record including its id.
|
|
1555
|
+
* @throws {ValidationError} If a field is missing or rejected.
|
|
1556
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1557
|
+
* @throws {TransportError} If the request never completed.
|
|
1558
|
+
*/
|
|
1559
|
+
create<T = Record<string, unknown>>(data: Record<string, unknown>): Promise<Response$1<T>>;
|
|
1560
|
+
/**
|
|
1561
|
+
* Fetches one patient record.
|
|
1562
|
+
*
|
|
1563
|
+
* Returns the record as your credentials are permitted to see it. Because the
|
|
1564
|
+
* organisation scope lives inside the JWT, an id belonging to another
|
|
1565
|
+
* organisation is indistinguishable from one that never existed - both raise
|
|
1566
|
+
* {@link NotFoundError}.
|
|
1567
|
+
*
|
|
1568
|
+
* @param id The patient's id.
|
|
1569
|
+
* @returns The patient record.
|
|
1570
|
+
* @throws {NotFoundError} If the patient does not exist or is out of scope.
|
|
1571
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1572
|
+
* @throws {TransportError} If the request never completed.
|
|
1573
|
+
*/
|
|
1574
|
+
view<T = Record<string, unknown>>(id: string): Promise<Response$1<T>>;
|
|
1575
|
+
/**
|
|
1576
|
+
* Updates a patient record.
|
|
1577
|
+
*
|
|
1578
|
+
* Send only the fields you are changing; the server merges them into the
|
|
1579
|
+
* existing record rather than replacing it.
|
|
1580
|
+
*
|
|
1581
|
+
* @param id The patient's id.
|
|
1582
|
+
* @param data The fields to change.
|
|
1583
|
+
* @returns The updated patient record.
|
|
1584
|
+
* @throws {NotFoundError} If the patient does not exist.
|
|
1585
|
+
* @throws {ValidationError} If a field is rejected.
|
|
1586
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1587
|
+
* @throws {TransportError} If the request never completed.
|
|
1588
|
+
*/
|
|
1589
|
+
update<T = Record<string, unknown>>(id: string, data: Record<string, unknown>): Promise<Response$1<T>>;
|
|
1590
|
+
/**
|
|
1591
|
+
* Changes a patient's status.
|
|
1592
|
+
*
|
|
1593
|
+
* A narrow call for the one field that drives workflow state, kept separate
|
|
1594
|
+
* from {@link Patients.update} so a status transition is explicit in your code
|
|
1595
|
+
* and in the server's audit trail. The accepted values are configured for your
|
|
1596
|
+
* organisation; see the API reference in your AsterMD dashboard.
|
|
1597
|
+
*
|
|
1598
|
+
* @param id The patient's id.
|
|
1599
|
+
* @param status The new status value.
|
|
1600
|
+
* @returns The updated patient record.
|
|
1601
|
+
* @throws {NotFoundError} If the patient does not exist.
|
|
1602
|
+
* @throws {ValidationError} If the status is not an accepted value.
|
|
1603
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1604
|
+
* @throws {TransportError} If the request never completed.
|
|
1605
|
+
*/
|
|
1606
|
+
status<T = Record<string, unknown>>(id: string, status: string): Promise<Response$1<T>>;
|
|
1607
|
+
/**
|
|
1608
|
+
* Submits a patient's health information.
|
|
1609
|
+
*
|
|
1610
|
+
* This is the clinical questionnaire result a clinician reviews, and the most
|
|
1611
|
+
* sensitive payload the SDK carries. Where the server requires explicit patient
|
|
1612
|
+
* confirmation, obtain a token through
|
|
1613
|
+
* {@link Patients.verifyHealthInformationOtp} first and pass it as
|
|
1614
|
+
* `phiVerificationToken`.
|
|
1615
|
+
*
|
|
1616
|
+
* Neither the request nor the response body is ever written to a debug log.
|
|
1617
|
+
*
|
|
1618
|
+
* @param options The health information, and the verification token where one
|
|
1619
|
+
* is required.
|
|
1620
|
+
* @returns The stored submission.
|
|
1621
|
+
* @throws {AuthenticationError} If the verification token is missing or invalid.
|
|
1622
|
+
* @throws {ValidationError} If the payload is rejected.
|
|
1623
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1624
|
+
* @throws {TransportError} If the request never completed.
|
|
1625
|
+
*/
|
|
1626
|
+
submitHealthInformation<T = Record<string, unknown>>(options: SubmitHealthInformationOptions): Promise<Response$1<T>>;
|
|
1627
|
+
/**
|
|
1628
|
+
* Verifies the one-time code a patient was sent, before health information is
|
|
1629
|
+
* submitted.
|
|
1630
|
+
*
|
|
1631
|
+
* The confirming half of the two-step health-information flow. On success the
|
|
1632
|
+
* response carries the verification token to pass to
|
|
1633
|
+
* {@link Patients.submitHealthInformation}.
|
|
1634
|
+
*
|
|
1635
|
+
* The one-time code is a credential: it is never written to a debug log, and it
|
|
1636
|
+
* should not be written to yours either.
|
|
1637
|
+
*
|
|
1638
|
+
* @param data The patient identifier and the one-time code. See the API
|
|
1639
|
+
* reference in your AsterMD dashboard for the exact fields.
|
|
1640
|
+
* @returns The verification result, carrying the token on success.
|
|
1641
|
+
* @throws {ValidationError} If the code is wrong, expired, or malformed.
|
|
1642
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1643
|
+
* @throws {TransportError} If the request never completed.
|
|
1644
|
+
*/
|
|
1645
|
+
verifyHealthInformationOtp<T = Record<string, unknown>>(data: Record<string, unknown>): Promise<Response$1<T>>;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* Products - the read-only catalogue your storefront sells from.
|
|
1650
|
+
*
|
|
1651
|
+
* A product carries everything a storefront needs to render it: names,
|
|
1652
|
+
* descriptions, images, variants, prices, purchase quantity limits, and the
|
|
1653
|
+
* questionnaire a buyer must complete. All of it is configured in your AsterMD
|
|
1654
|
+
* dashboard, and the SDK only reads it - a storefront never creates or edits a
|
|
1655
|
+
* product.
|
|
1656
|
+
*
|
|
1657
|
+
* Which products a given selling surface may offer is a channel decision, so
|
|
1658
|
+
* {@link Channels.assignedProducts} and {@link Channels.details} are usually the
|
|
1659
|
+
* better starting point for a catalogue page. Reach for this resource when you
|
|
1660
|
+
* need the full product list regardless of channel, or one product by id.
|
|
1661
|
+
*
|
|
1662
|
+
* Image and asset paths come back relative. Resolve them with
|
|
1663
|
+
* `client.config().assetUrl(path)`, and treat the result as a fetch URL: download
|
|
1664
|
+
* the asset once and serve your own copy rather than hot-linking.
|
|
1665
|
+
*/
|
|
1666
|
+
declare class Products extends AbstractResource {
|
|
1667
|
+
/**
|
|
1668
|
+
* @param transport The shared transport.
|
|
1669
|
+
*/
|
|
1670
|
+
constructor(transport: Transport);
|
|
1671
|
+
/**
|
|
1672
|
+
* Lists products, filtered and paginated.
|
|
1673
|
+
*
|
|
1674
|
+
* Pagination details come back in `meta()`. The accepted filters are in the
|
|
1675
|
+
* API reference in your AsterMD dashboard.
|
|
1676
|
+
*
|
|
1677
|
+
* @param query Filters and pagination parameters.
|
|
1678
|
+
* @returns The matching products, with pagination in `meta()`.
|
|
1679
|
+
* @throws {ValidationError} If a filter is rejected.
|
|
1680
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1681
|
+
* @throws {TransportError} If the request never completed.
|
|
1682
|
+
*/
|
|
1683
|
+
list<T = Record<string, unknown>>(query?: QueryParams): Promise<Response$1<T>>;
|
|
1684
|
+
/**
|
|
1685
|
+
* Fetches one product, including its variants and prices.
|
|
1686
|
+
*
|
|
1687
|
+
* @param id The product's id.
|
|
1688
|
+
* @returns The product record.
|
|
1689
|
+
* @throws {NotFoundError} If the product does not exist or is out of scope.
|
|
1690
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1691
|
+
* @throws {TransportError} If the request never completed.
|
|
1692
|
+
*/
|
|
1693
|
+
view<T = Record<string, unknown>>(id: string): Promise<Response$1<T>>;
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
/** Options for {@link Sessions.create}. */
|
|
1697
|
+
interface SessionCreateOptions {
|
|
1698
|
+
/**
|
|
1699
|
+
* Attribution and context to record against the session - UTM parameters,
|
|
1700
|
+
* referrer, landing page. Forwarded unmodified; see the API reference in your
|
|
1701
|
+
* AsterMD dashboard for the recognised keys.
|
|
1702
|
+
*/
|
|
1703
|
+
data?: Record<string, unknown>;
|
|
1704
|
+
/** The visitor's user agent, forwarded as the `User-Agent` header. */
|
|
1705
|
+
userAgent?: string;
|
|
1706
|
+
/** The visitor's IP address, forwarded as the `X-Original-Client-Ip` header. */
|
|
1707
|
+
clientIp?: string;
|
|
1708
|
+
}
|
|
1709
|
+
/** Options for {@link Sessions.view}. */
|
|
1710
|
+
interface SessionViewOptions {
|
|
1711
|
+
/** Session identifiers to fetch. Sent as a comma-separated `session_ids` query. */
|
|
1712
|
+
sessions: string[];
|
|
1713
|
+
/** IANA time zone used to render timestamps in the response. */
|
|
1714
|
+
tz?: string;
|
|
1715
|
+
}
|
|
1716
|
+
/**
|
|
1717
|
+
* Sessions - the thread that ties one visitor's whole journey together.
|
|
1718
|
+
*
|
|
1719
|
+
* A session is the first thing an order flow creates and the value every later
|
|
1720
|
+
* call is attached to: intake submissions, the cart, checkout events, and finally
|
|
1721
|
+
* the opportunity. Creating one also records an implicit `visit_page` event
|
|
1722
|
+
* server-side, so the funnel starts measuring from that moment.
|
|
1723
|
+
*
|
|
1724
|
+
* Create the session as early as the visitor's first page view, keep the returned
|
|
1725
|
+
* identifier for the length of their visit - in a cookie or your own server-side
|
|
1726
|
+
* session - and pass it to every subsequent call:
|
|
1727
|
+
*
|
|
1728
|
+
* ```ts
|
|
1729
|
+
* const { session } = (
|
|
1730
|
+
* await client.sessions().create<{ session: string }>({
|
|
1731
|
+
* userAgent: request.headers['user-agent'],
|
|
1732
|
+
* clientIp: request.ip,
|
|
1733
|
+
* })
|
|
1734
|
+
* ).data();
|
|
1735
|
+
* ```
|
|
1736
|
+
*
|
|
1737
|
+
* Passing the visitor's user agent and IP matters: the server uses them for
|
|
1738
|
+
* attribution and geographic checks, and without them every session appears to
|
|
1739
|
+
* originate from your server rather than from the visitor.
|
|
1740
|
+
*/
|
|
1741
|
+
declare class Sessions extends AbstractResource {
|
|
1742
|
+
/**
|
|
1743
|
+
* @param transport The shared transport.
|
|
1744
|
+
*/
|
|
1745
|
+
constructor(transport: Transport);
|
|
1746
|
+
/**
|
|
1747
|
+
* Creates a new session and returns its identifier.
|
|
1748
|
+
*
|
|
1749
|
+
* This is the first call of an order flow. The server records an implicit
|
|
1750
|
+
* `visit_page` event alongside the new session, so the funnel begins here.
|
|
1751
|
+
* Inspect `data().session` on the response for the identifier to thread through
|
|
1752
|
+
* every later call.
|
|
1753
|
+
*
|
|
1754
|
+
* @param options Attribution payload and visitor headers. All optional; with
|
|
1755
|
+
* none, an empty object body is sent.
|
|
1756
|
+
* @returns The created session; `data()` holds `{ session }`.
|
|
1757
|
+
* @throws {ValidationError} If the attribution payload is rejected.
|
|
1758
|
+
* @throws {AuthenticationError} If the credentials are refused.
|
|
1759
|
+
* @throws {RateLimitError} If the organisation's request allowance is exceeded.
|
|
1760
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1761
|
+
* @throws {TransportError} If the request never completed.
|
|
1762
|
+
*/
|
|
1763
|
+
create<T = Record<string, unknown>>(options?: SessionCreateOptions): Promise<Response$1<T>>;
|
|
1764
|
+
/**
|
|
1765
|
+
* Fetches one or more sessions and the events recorded against them.
|
|
1766
|
+
*
|
|
1767
|
+
* Useful for reconstructing a visitor's journey server-side - which forms they
|
|
1768
|
+
* touched, where they stopped. Several identifiers can be fetched in one call,
|
|
1769
|
+
* which is cheaper than looping. Timestamps come back in `tz` when you supply
|
|
1770
|
+
* one, and in the organisation's default zone otherwise.
|
|
1771
|
+
*
|
|
1772
|
+
* @param options The session identifiers to fetch, and an optional time zone.
|
|
1773
|
+
* @returns The sessions and their recorded events.
|
|
1774
|
+
* @throws {NotFoundError} If none of the identifiers resolve.
|
|
1775
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1776
|
+
* @throws {TransportError} If the request never completed.
|
|
1777
|
+
*/
|
|
1778
|
+
view<T = Record<string, unknown>>(options: SessionViewOptions): Promise<Response$1<T>>;
|
|
1779
|
+
/**
|
|
1780
|
+
* Updates the attribution and context recorded against an existing session.
|
|
1781
|
+
*
|
|
1782
|
+
* Call this when information arrives after the session was created - a UTM
|
|
1783
|
+
* parameter only present on a later page, a referrer resolved asynchronously.
|
|
1784
|
+
* The payload is merged server-side rather than replacing what is already
|
|
1785
|
+
* recorded.
|
|
1786
|
+
*
|
|
1787
|
+
* @param session The session identifier to update.
|
|
1788
|
+
* @param data Fields to record. Forwarded unmodified; see the API reference in
|
|
1789
|
+
* your AsterMD dashboard for the recognised keys.
|
|
1790
|
+
* @returns The updated session.
|
|
1791
|
+
* @throws {NotFoundError} If the session does not exist.
|
|
1792
|
+
* @throws {ValidationError} If the payload is rejected.
|
|
1793
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1794
|
+
* @throws {TransportError} If the request never completed.
|
|
1795
|
+
*/
|
|
1796
|
+
update<T = Record<string, unknown>>(session: string, data?: Record<string, unknown>): Promise<Response$1<T>>;
|
|
1797
|
+
/**
|
|
1798
|
+
* Deletes a session and the events recorded against it.
|
|
1799
|
+
*
|
|
1800
|
+
* Intended for honouring a visitor's erasure request, not for ordinary
|
|
1801
|
+
* cleanup - sessions age out server-side on their own. Deleting one removes the
|
|
1802
|
+
* attribution trail for anything that referenced it.
|
|
1803
|
+
*
|
|
1804
|
+
* @param session The session identifier to delete.
|
|
1805
|
+
* @returns The deletion confirmation.
|
|
1806
|
+
* @throws {NotFoundError} If the session does not exist.
|
|
1807
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1808
|
+
* @throws {TransportError} If the request never completed.
|
|
1809
|
+
*/
|
|
1810
|
+
delete<T = Record<string, unknown>>(session: string): Promise<Response$1<T>>;
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
/**
|
|
1814
|
+
* Shipping options - the delivery methods available for an order.
|
|
1815
|
+
*
|
|
1816
|
+
* Each option carries its name, price, and expected delivery window, which is
|
|
1817
|
+
* what a checkout page needs to let a buyer choose. Which options apply can depend
|
|
1818
|
+
* on the products in the cart and on the destination, so fetch them at checkout
|
|
1819
|
+
* rather than hardcoding a list.
|
|
1820
|
+
*
|
|
1821
|
+
* Read-only, and configured in your AsterMD dashboard.
|
|
1822
|
+
*/
|
|
1823
|
+
declare class Shippings extends AbstractResource {
|
|
1824
|
+
/**
|
|
1825
|
+
* @param transport The shared transport.
|
|
1826
|
+
*/
|
|
1827
|
+
constructor(transport: Transport);
|
|
1828
|
+
/**
|
|
1829
|
+
* Lists shipping options, filtered and paginated.
|
|
1830
|
+
*
|
|
1831
|
+
* Pagination details come back in `meta()`. The accepted filters are in the
|
|
1832
|
+
* API reference in your AsterMD dashboard.
|
|
1833
|
+
*
|
|
1834
|
+
* @param query Filters and pagination parameters.
|
|
1835
|
+
* @returns The matching shipping options, with pagination in `meta()`.
|
|
1836
|
+
* @throws {ValidationError} If a filter is rejected.
|
|
1837
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1838
|
+
* @throws {TransportError} If the request never completed.
|
|
1839
|
+
*/
|
|
1840
|
+
list<T = Record<string, unknown>>(query?: QueryParams): Promise<Response$1<T>>;
|
|
1841
|
+
/**
|
|
1842
|
+
* Fetches one shipping option.
|
|
1843
|
+
*
|
|
1844
|
+
* @param id The shipping option's id.
|
|
1845
|
+
* @returns The shipping option record.
|
|
1846
|
+
* @throws {NotFoundError} If the shipping option does not exist or is out of scope.
|
|
1847
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1848
|
+
* @throws {TransportError} If the request never completed.
|
|
1849
|
+
*/
|
|
1850
|
+
view<T = Record<string, unknown>>(id: string): Promise<Response$1<T>>;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
/**
|
|
1854
|
+
* Teleforms - the questionnaire definitions your storefront renders.
|
|
1855
|
+
*
|
|
1856
|
+
* A teleform is the server-side definition of a form: its fields, their types,
|
|
1857
|
+
* their order, and the validation the clinician's workflow expects. Both the
|
|
1858
|
+
* pre-qualifying questionnaire and the full intake questionnaire are teleforms.
|
|
1859
|
+
* Fetch the definition, render it in your own UI, and submit the answers through
|
|
1860
|
+
* {@link IntakeSubmissions} together with the teleform's id.
|
|
1861
|
+
*
|
|
1862
|
+
* There are two ways in. {@link Teleforms.viewByIdentifier} takes the stable
|
|
1863
|
+
* human-readable identifier you configured in your AsterMD dashboard, which is
|
|
1864
|
+
* what a storefront normally has hardcoded or in configuration.
|
|
1865
|
+
* {@link Teleforms.view} takes the generated id, which is what appears in API
|
|
1866
|
+
* responses elsewhere.
|
|
1867
|
+
*
|
|
1868
|
+
* Definitions change when someone edits the form in the dashboard, so fetch them
|
|
1869
|
+
* rather than embedding a copy - but they change rarely, so caching the result
|
|
1870
|
+
* for minutes rather than seconds is reasonable.
|
|
1871
|
+
*/
|
|
1872
|
+
declare class Teleforms extends AbstractResource {
|
|
1873
|
+
/**
|
|
1874
|
+
* @param transport The shared transport.
|
|
1875
|
+
*/
|
|
1876
|
+
constructor(transport: Transport);
|
|
1877
|
+
/**
|
|
1878
|
+
* Fetches a teleform definition by its configured identifier.
|
|
1879
|
+
*
|
|
1880
|
+
* This is the call a storefront usually wants: the identifier is the stable
|
|
1881
|
+
* name you chose in your AsterMD dashboard, so it can live in your
|
|
1882
|
+
* configuration and survive the form being edited.
|
|
1883
|
+
*
|
|
1884
|
+
* @param identifier The teleform's configured identifier.
|
|
1885
|
+
* @returns The teleform definition.
|
|
1886
|
+
* @throws {NotFoundError} If no teleform carries that identifier.
|
|
1887
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1888
|
+
* @throws {TransportError} If the request never completed.
|
|
1889
|
+
*/
|
|
1890
|
+
viewByIdentifier<T = Record<string, unknown>>(identifier: string): Promise<Response$1<T>>;
|
|
1891
|
+
/**
|
|
1892
|
+
* Fetches a teleform definition by its generated id.
|
|
1893
|
+
*
|
|
1894
|
+
* Use this when you already hold an id from another API response - a product's
|
|
1895
|
+
* assigned teleform, for instance - rather than a configured identifier.
|
|
1896
|
+
*
|
|
1897
|
+
* @param id The teleform's generated id.
|
|
1898
|
+
* @returns The teleform definition.
|
|
1899
|
+
* @throws {NotFoundError} If the teleform does not exist.
|
|
1900
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1901
|
+
* @throws {TransportError} If the request never completed.
|
|
1902
|
+
*/
|
|
1903
|
+
view<T = Record<string, unknown>>(id: string): Promise<Response$1<T>>;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
/** Options for {@link Treatments.sync}. */
|
|
1907
|
+
interface TreatmentSyncOptions {
|
|
1908
|
+
/** The session the orders belong to. */
|
|
1909
|
+
session: string;
|
|
1910
|
+
/** Order identifiers from your own commerce system. */
|
|
1911
|
+
orderIds: string[];
|
|
1912
|
+
/** Campaign attribution to record alongside the orders. */
|
|
1913
|
+
utmSource?: string;
|
|
1914
|
+
/** The visitor's user agent, forwarded as the `User-Agent` header. */
|
|
1915
|
+
userAgent?: string;
|
|
1916
|
+
}
|
|
1917
|
+
/**
|
|
1918
|
+
* Treatments - the order, once money has actually changed hands.
|
|
1919
|
+
*
|
|
1920
|
+
* A treatment is the end of the order flow and the start of the clinical one:
|
|
1921
|
+
* the record a clinician reviews, prescribes against, and that fulfilment works
|
|
1922
|
+
* from. Create it *after* your own payment flow has settled, never before - the
|
|
1923
|
+
* SDK does not process payments, and a treatment recorded against an unsettled
|
|
1924
|
+
* payment puts an order into the clinical queue that may never be paid for.
|
|
1925
|
+
*
|
|
1926
|
+
* There are two ways to record one. {@link Treatments.create} takes the order
|
|
1927
|
+
* explicitly, which is what you want when your storefront owns checkout.
|
|
1928
|
+
* {@link Treatments.sync} takes a session and a list of order identifiers from
|
|
1929
|
+
* your own commerce system, which is what you want when checkout happened
|
|
1930
|
+
* elsewhere and you are reconciling after the fact.
|
|
1931
|
+
*
|
|
1932
|
+
* ```ts
|
|
1933
|
+
* await client.treatments().create({
|
|
1934
|
+
* external_order_id: 'EXT-123',
|
|
1935
|
+
* patient: { id: patientId },
|
|
1936
|
+
* product: { id: productId },
|
|
1937
|
+
* });
|
|
1938
|
+
* ```
|
|
1939
|
+
*/
|
|
1940
|
+
declare class Treatments extends AbstractResource {
|
|
1941
|
+
/**
|
|
1942
|
+
* @param transport The shared transport.
|
|
1943
|
+
*/
|
|
1944
|
+
constructor(transport: Transport);
|
|
1945
|
+
/**
|
|
1946
|
+
* Records a treatment - an order that has been paid for.
|
|
1947
|
+
*
|
|
1948
|
+
* Call this only after your payment flow has settled. Include your own order
|
|
1949
|
+
* identifier as `external_order_id` so the record can be reconciled against
|
|
1950
|
+
* your commerce system later.
|
|
1951
|
+
*
|
|
1952
|
+
* @param data The order: patient, product, your order identifier, and whatever
|
|
1953
|
+
* else your configuration requires.
|
|
1954
|
+
* @returns The created treatment.
|
|
1955
|
+
* @throws {ValidationError} If a field is missing or rejected.
|
|
1956
|
+
* @throws {NotFoundError} If the referenced patient or product does not exist.
|
|
1957
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1958
|
+
* @throws {TransportError} If the request never completed.
|
|
1959
|
+
*/
|
|
1960
|
+
create<T = Record<string, unknown>>(data: Record<string, unknown>): Promise<Response$1<T>>;
|
|
1961
|
+
/**
|
|
1962
|
+
* Fetches one treatment, including its clinical status.
|
|
1963
|
+
*
|
|
1964
|
+
* This is how a storefront shows an order's progress - whether a clinician has
|
|
1965
|
+
* reviewed it, whether it has shipped.
|
|
1966
|
+
*
|
|
1967
|
+
* @param id The treatment's id.
|
|
1968
|
+
* @returns The treatment record.
|
|
1969
|
+
* @throws {NotFoundError} If the treatment does not exist or is out of scope.
|
|
1970
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1971
|
+
* @throws {TransportError} If the request never completed.
|
|
1972
|
+
*/
|
|
1973
|
+
view<T = Record<string, unknown>>(id: string): Promise<Response$1<T>>;
|
|
1974
|
+
/**
|
|
1975
|
+
* Lists treatments, filtered and paginated.
|
|
1976
|
+
*
|
|
1977
|
+
* Useful for an account area showing a patient's order history. Pagination
|
|
1978
|
+
* details come back in `meta()`; the accepted filters are in the API reference
|
|
1979
|
+
* in your AsterMD dashboard.
|
|
1980
|
+
*
|
|
1981
|
+
* @param query Filters and pagination parameters.
|
|
1982
|
+
* @returns The matching treatments, with pagination in `meta()`.
|
|
1983
|
+
* @throws {ValidationError} If a filter is rejected.
|
|
1984
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
1985
|
+
* @throws {TransportError} If the request never completed.
|
|
1986
|
+
*/
|
|
1987
|
+
list<T = Record<string, unknown>>(query?: QueryParams): Promise<Response$1<T>>;
|
|
1988
|
+
/**
|
|
1989
|
+
* Reconciles orders placed in your own commerce system against a session.
|
|
1990
|
+
*
|
|
1991
|
+
* The alternative to {@link Treatments.create} for flows where checkout happens
|
|
1992
|
+
* outside your storefront: hand over the session and your order identifiers and
|
|
1993
|
+
* the server creates the treatments and attributes them to that session's
|
|
1994
|
+
* journey. Several orders can be reconciled in one call.
|
|
1995
|
+
*
|
|
1996
|
+
* @param options The session, your order identifiers, and optional attribution.
|
|
1997
|
+
* @returns The created treatments.
|
|
1998
|
+
* @throws {ValidationError} If the session or an order identifier is rejected.
|
|
1999
|
+
* @throws {NotFoundError} If the session does not exist.
|
|
2000
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
2001
|
+
* @throws {TransportError} If the request never completed.
|
|
2002
|
+
*/
|
|
2003
|
+
sync<T = Record<string, unknown>>(options: TreatmentSyncOptions): Promise<Response$1<T>>;
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
/**
|
|
2007
|
+
* The kind of identity check to run through {@link Verification.verifyIdentity}.
|
|
2008
|
+
*
|
|
2009
|
+
* Each value selects a different check at the configured provider, and each
|
|
2010
|
+
* expects a different payload alongside it - a cross-check of supplied details,
|
|
2011
|
+
* a date-of-birth confirmation, or a Social Security Number confirmation. The
|
|
2012
|
+
* required fields per check are in the API reference in your AsterMD dashboard.
|
|
2013
|
+
*
|
|
2014
|
+
* These calls carry PII, and `SsnVerify` in particular. The SDK never writes
|
|
2015
|
+
* their request bodies to a debug log.
|
|
2016
|
+
*/
|
|
2017
|
+
declare const IdentityCheck: Readonly<{
|
|
2018
|
+
/** Cross-check the supplied identity details against the provider's records. */
|
|
2019
|
+
readonly Crosscheck: "crosscheck";
|
|
2020
|
+
/** Confirm the supplied date of birth. */
|
|
2021
|
+
readonly DobVerify: "dob_verify";
|
|
2022
|
+
/** Confirm the supplied Social Security Number. */
|
|
2023
|
+
readonly SsnVerify: "ssn_verify";
|
|
2024
|
+
}>;
|
|
2025
|
+
/** Any identity-check wire value. A bare string literal satisfies this type. */
|
|
2026
|
+
type IdentityCheck = (typeof IdentityCheck)[keyof typeof IdentityCheck];
|
|
2027
|
+
|
|
2028
|
+
/**
|
|
2029
|
+
* Verification - checking that the details a prospect typed are real.
|
|
2030
|
+
*
|
|
2031
|
+
* Telehealth needs more confidence in an address, an email, and an identity than
|
|
2032
|
+
* ordinary commerce does: a prescription goes to a physical person at a physical
|
|
2033
|
+
* place. These endpoints proxy the provider configured for your organisation, and
|
|
2034
|
+
* normalise its answers, so your storefront gets one shape regardless of which
|
|
2035
|
+
* provider sits behind it.
|
|
2036
|
+
*
|
|
2037
|
+
* Address autofill and verification belong at the point of entry - autofill as
|
|
2038
|
+
* the buyer types, verification before checkout completes. Email verification
|
|
2039
|
+
* catches typos and disposable addresses before they cost you a lost patient.
|
|
2040
|
+
* Identity verification is the strongest check and is usually required before a
|
|
2041
|
+
* clinician will review a case; which variant applies is carried by
|
|
2042
|
+
* {@link IdentityCheck}.
|
|
2043
|
+
*
|
|
2044
|
+
* Note where these calls appear in debug logs. Redaction covers headers and
|
|
2045
|
+
* bodies but never the URL, and the address and email endpoints take their input
|
|
2046
|
+
* as a query parameter - so a debug entry for those calls contains the address or
|
|
2047
|
+
* email that was checked. That is worth knowing when deciding where debug logs
|
|
2048
|
+
* are written and how long they are kept. Identity verification is the exception:
|
|
2049
|
+
* it is a POST, and its body is dropped from logs entirely because it carries
|
|
2050
|
+
* PII.
|
|
2051
|
+
*/
|
|
2052
|
+
declare class Verification extends AbstractResource {
|
|
2053
|
+
/**
|
|
2054
|
+
* @param transport The shared transport.
|
|
2055
|
+
*/
|
|
2056
|
+
constructor(transport: Transport);
|
|
2057
|
+
/**
|
|
2058
|
+
* Suggests complete addresses for a partial one.
|
|
2059
|
+
*
|
|
2060
|
+
* Built for a type-ahead: call it as the buyer types and render the
|
|
2061
|
+
* suggestions. Debouncing is your side's job - every keystroke here is a round
|
|
2062
|
+
* trip and counts against your request allowance.
|
|
2063
|
+
*
|
|
2064
|
+
* @param search The partial address typed so far.
|
|
2065
|
+
* @returns Candidate addresses from the configured provider.
|
|
2066
|
+
* @throws {ValidationError} If the search term is rejected.
|
|
2067
|
+
* @throws {RateLimitError} If the request allowance is exceeded.
|
|
2068
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
2069
|
+
* @throws {TransportError} If the request never completed.
|
|
2070
|
+
*/
|
|
2071
|
+
autofillAddress<T = Record<string, unknown>>(search: string): Promise<Response$1<T>>;
|
|
2072
|
+
/**
|
|
2073
|
+
* Verifies that an address exists and is deliverable.
|
|
2074
|
+
*
|
|
2075
|
+
* Call this before checkout completes rather than after: a prescription sent to
|
|
2076
|
+
* an undeliverable address is a clinical problem, not just a logistics one. The
|
|
2077
|
+
* response carries the provider's normalised form of the address, which is what
|
|
2078
|
+
* you should store.
|
|
2079
|
+
*
|
|
2080
|
+
* @param address The full address to verify.
|
|
2081
|
+
* @returns The verification result and the normalised address.
|
|
2082
|
+
* @throws {ValidationError} If the address is rejected.
|
|
2083
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
2084
|
+
* @throws {TransportError} If the request never completed.
|
|
2085
|
+
*/
|
|
2086
|
+
verifyAddress<T = Record<string, unknown>>(address: string): Promise<Response$1<T>>;
|
|
2087
|
+
/**
|
|
2088
|
+
* Verifies that an email address is real and reachable.
|
|
2089
|
+
*
|
|
2090
|
+
* Catches typos and disposable addresses at the point of entry. Since every
|
|
2091
|
+
* later notification - intake reminders, clinician messages, shipping updates -
|
|
2092
|
+
* goes to this address, a bad one silently costs you the patient.
|
|
2093
|
+
*
|
|
2094
|
+
* @param email The email address to verify.
|
|
2095
|
+
* @returns The verification result.
|
|
2096
|
+
* @throws {ValidationError} If the address is malformed.
|
|
2097
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
2098
|
+
* @throws {TransportError} If the request never completed.
|
|
2099
|
+
*/
|
|
2100
|
+
verifyEmail<T = Record<string, unknown>>(email: string): Promise<Response$1<T>>;
|
|
2101
|
+
/**
|
|
2102
|
+
* Runs an identity check against the configured provider.
|
|
2103
|
+
*
|
|
2104
|
+
* The strongest check available, and usually a precondition for clinical
|
|
2105
|
+
* review. Which variant runs is carried by `check`, and each expects a
|
|
2106
|
+
* different payload - see the API reference in your AsterMD dashboard for the
|
|
2107
|
+
* fields each one needs.
|
|
2108
|
+
*
|
|
2109
|
+
* The payload carries PII, and a Social Security Number in the `SsnVerify`
|
|
2110
|
+
* case. It is never written to a debug log, and it should not be written to
|
|
2111
|
+
* yours. The payload cannot override the check you selected.
|
|
2112
|
+
*
|
|
2113
|
+
* @param check Which identity check to run.
|
|
2114
|
+
* @param data The fields that check requires.
|
|
2115
|
+
* @returns The verification result.
|
|
2116
|
+
* @throws {ValidationError} If the payload does not satisfy the chosen check.
|
|
2117
|
+
* @throws {ApiError} On any other non-2xx status.
|
|
2118
|
+
* @throws {TransportError} If the request never completed.
|
|
2119
|
+
*/
|
|
2120
|
+
verifyIdentity<T = Record<string, unknown>>(check: IdentityCheck, data: Record<string, unknown>): Promise<Response$1<T>>;
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
/** Options accepted by {@link AsterMDClient}. */
|
|
2124
|
+
interface AsterMDClientOptions {
|
|
2125
|
+
/** OAuth2 client ID issued from your AsterMD dashboard. */
|
|
2126
|
+
clientId: string;
|
|
2127
|
+
/**
|
|
2128
|
+
* OAuth2 client secret. Load it from an environment variable or a secrets
|
|
2129
|
+
* manager - never commit it and never ship it to a browser.
|
|
2130
|
+
*/
|
|
2131
|
+
clientSecret: string;
|
|
2132
|
+
/** Bare API hostname, no scheme and no path. Defaults to `api.astermd.com`. */
|
|
2133
|
+
baseHost?: string;
|
|
2134
|
+
/** Client used for every request. Defaults to {@link FetchHttpClient}. */
|
|
2135
|
+
httpClient?: HttpClient;
|
|
2136
|
+
/** Where the bearer token is cached. Defaults to {@link InMemoryTokenStore}. */
|
|
2137
|
+
tokenStore?: TokenStore;
|
|
2138
|
+
/** Per-request timeout in seconds. Must be >= 1. Defaults to 10. */
|
|
2139
|
+
timeoutSeconds?: number;
|
|
2140
|
+
/**
|
|
2141
|
+
* Turn on debug logging of every request and response. Requires `debugFile` or
|
|
2142
|
+
* `debugSink`. Defaults to `false`.
|
|
2143
|
+
*/
|
|
2144
|
+
debug?: boolean;
|
|
2145
|
+
/**
|
|
2146
|
+
* Base path for daily log files, e.g. `/var/log/astermd/sdk.log`. Dated
|
|
2147
|
+
* filenames are derived from it. Ignored when `debugSink` is given.
|
|
2148
|
+
*/
|
|
2149
|
+
debugFile?: string;
|
|
2150
|
+
/**
|
|
2151
|
+
* Receives each formatted entry instead of a file. Supplying this makes
|
|
2152
|
+
* retention and delivery yours.
|
|
2153
|
+
*/
|
|
2154
|
+
debugSink?: DebugSink;
|
|
2155
|
+
/** IANA time zone for log timestamps and daily filenames. Defaults to `UTC`. */
|
|
2156
|
+
debugTimezone?: string;
|
|
2157
|
+
/**
|
|
2158
|
+
* Mask credentials and PHI in debug output. Defaults to `true`. Setting it
|
|
2159
|
+
* `false` writes live bearer tokens and the client secret to your sink - never
|
|
2160
|
+
* do that in production.
|
|
2161
|
+
*/
|
|
2162
|
+
debugRedact?: boolean;
|
|
2163
|
+
/** Days of log files to keep; `0` keeps everything. Defaults to 7. */
|
|
2164
|
+
debugRetentionDays?: number;
|
|
2165
|
+
}
|
|
2166
|
+
/**
|
|
2167
|
+
* The SDK's entry point: one client, seventeen resources.
|
|
2168
|
+
*
|
|
2169
|
+
* Construct it once at application start and share it. It holds the credentials,
|
|
2170
|
+
* caches the bearer token, and owns the single transport every resource dispatches
|
|
2171
|
+
* through, so creating one per request would throw away the token cache and cost a
|
|
2172
|
+
* credential exchange each time.
|
|
2173
|
+
*
|
|
2174
|
+
* ```ts
|
|
2175
|
+
* import { AsterMDClient, Event } from '@astermd-hq/sdk';
|
|
2176
|
+
*
|
|
2177
|
+
* const client = new AsterMDClient({
|
|
2178
|
+
* clientId: process.env.ASTERMD_CLIENT_ID!,
|
|
2179
|
+
* clientSecret: process.env.ASTERMD_CLIENT_SECRET!,
|
|
2180
|
+
* });
|
|
2181
|
+
*
|
|
2182
|
+
* const { session } = (
|
|
2183
|
+
* await client.sessions().create<{ session: string }>()
|
|
2184
|
+
* ).data();
|
|
2185
|
+
*
|
|
2186
|
+
* await client.intakeSubmissions().create({
|
|
2187
|
+
* session,
|
|
2188
|
+
* event: Event.PreQualifyingInitiated,
|
|
2189
|
+
* teleformId: 'your-teleform-id',
|
|
2190
|
+
* data: answers,
|
|
2191
|
+
* });
|
|
2192
|
+
* ```
|
|
2193
|
+
*
|
|
2194
|
+
* Credentials are issued from your AsterMD dashboard, which is also where the
|
|
2195
|
+
* full API reference lives. This client holds a secret, so it belongs on a
|
|
2196
|
+
* server: never construct it in code that reaches a browser.
|
|
2197
|
+
*
|
|
2198
|
+
* Authentication needs no attention from you. The token is acquired lazily on the
|
|
2199
|
+
* first call, reused until it is close to expiring, refreshed automatically if the
|
|
2200
|
+
* server rejects it, and the organisation it authorises is encoded inside it -
|
|
2201
|
+
* which is why no method here takes an organisation argument.
|
|
2202
|
+
*
|
|
2203
|
+
* Every accessor is memoized, so `client.sessions()` returns the same instance
|
|
2204
|
+
* each time and calling it in a hot path costs nothing.
|
|
2205
|
+
*/
|
|
2206
|
+
declare class AsterMDClient {
|
|
2207
|
+
#private;
|
|
2208
|
+
/**
|
|
2209
|
+
* @param options Credentials, connection settings, and debug configuration.
|
|
2210
|
+
* @throws {TypeError} If the credentials are empty, if `baseHost` carries a
|
|
2211
|
+
* scheme or path, if `timeoutSeconds` is below 1, or if `debug` is on with
|
|
2212
|
+
* neither `debugFile` nor `debugSink`.
|
|
2213
|
+
*/
|
|
2214
|
+
constructor(options: AsterMDClientOptions);
|
|
2215
|
+
/**
|
|
2216
|
+
* Returns the immutable configuration this client was built with.
|
|
2217
|
+
*
|
|
2218
|
+
* @returns The configuration, including {@link Config.assetUrl}.
|
|
2219
|
+
*/
|
|
2220
|
+
config(): Config;
|
|
2221
|
+
/**
|
|
2222
|
+
* Resolves a relative asset path returned by the API against the AsterMD CDN.
|
|
2223
|
+
*
|
|
2224
|
+
* A convenience for `client.config().assetUrl(path)`. Treat the result as a
|
|
2225
|
+
* fetch URL: download the asset once and serve your own copy rather than
|
|
2226
|
+
* hot-linking.
|
|
2227
|
+
*
|
|
2228
|
+
* @param path Relative asset path as returned by the API.
|
|
2229
|
+
* @returns The fully-qualified CDN URL.
|
|
2230
|
+
*/
|
|
2231
|
+
assetUrl(path: string): string;
|
|
2232
|
+
/** Sessions - the thread tying one visitor's journey together. */
|
|
2233
|
+
sessions(): Sessions;
|
|
2234
|
+
/** Intake submissions - pre-qualifying and intake questionnaire answers. */
|
|
2235
|
+
intakeSubmissions(): IntakeSubmissions;
|
|
2236
|
+
/** Carts - what the visitor intends to buy. */
|
|
2237
|
+
carts(): Carts;
|
|
2238
|
+
/** Checkout events - progress through the checkout funnel. */
|
|
2239
|
+
checkoutEvents(): CheckoutEvents;
|
|
2240
|
+
/** Teleforms - the questionnaire definitions your storefront renders. */
|
|
2241
|
+
teleforms(): Teleforms;
|
|
2242
|
+
/** Patients - the person receiving care. Carries PHI. */
|
|
2243
|
+
patients(): Patients;
|
|
2244
|
+
/** Opportunities - a named prospect worth following up. */
|
|
2245
|
+
opportunities(): Opportunities;
|
|
2246
|
+
/** Treatments - the order, once payment has settled. */
|
|
2247
|
+
treatments(): Treatments;
|
|
2248
|
+
/** Doctors' networks - handing a case to the clinicians who review it. */
|
|
2249
|
+
doctorsNetworks(): DoctorsNetworks;
|
|
2250
|
+
/** Channels - the storefront's own server-side configuration. */
|
|
2251
|
+
channels(): Channels;
|
|
2252
|
+
/** Products - the read-only catalogue. */
|
|
2253
|
+
products(): Products;
|
|
2254
|
+
/** Categories - how the catalogue is grouped for browsing. */
|
|
2255
|
+
categories(): Categories;
|
|
2256
|
+
/** Lab tests - diagnostic panels a product may require. */
|
|
2257
|
+
labTests(): LabTests;
|
|
2258
|
+
/** Medications - the reference list backing "what are you currently taking?". */
|
|
2259
|
+
medications(): Medications;
|
|
2260
|
+
/** Shipping options - delivery methods available for an order. */
|
|
2261
|
+
shippings(): Shippings;
|
|
2262
|
+
/** Verification - address, email, and identity checks. */
|
|
2263
|
+
verification(): Verification;
|
|
2264
|
+
/** Geo - where a visitor is, and whether you may serve them. */
|
|
2265
|
+
geo(): Geo;
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
/**
|
|
2269
|
+
* Base class for every error the SDK raises.
|
|
2270
|
+
*
|
|
2271
|
+
* Catching `AsterMDError` catches everything this SDK can throw and nothing it
|
|
2272
|
+
* cannot, which makes it the right thing to guard an integration boundary with.
|
|
2273
|
+
* Two branches descend from it: {@link TransportError} for failures where no HTTP
|
|
2274
|
+
* response was ever received, and {@link ApiError} for everything the server
|
|
2275
|
+
* answered with a non-2xx status.
|
|
2276
|
+
*
|
|
2277
|
+
* The class is abstract - the SDK never throws it directly.
|
|
2278
|
+
*/
|
|
2279
|
+
declare abstract class AsterMDError extends Error {
|
|
2280
|
+
protected constructor(message: string, options?: ErrorOptions);
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
/**
|
|
2284
|
+
* The API answered with a non-2xx status.
|
|
2285
|
+
*
|
|
2286
|
+
* This is the base for every status-carrying error and the class the SDK throws
|
|
2287
|
+
* for any status it does not map to something more specific - a 400, a 500, a
|
|
2288
|
+
* 502 from an upstream hiccup. The decoded response envelope is kept intact on
|
|
2289
|
+
* {@link ApiError.envelope} so an integrator can inspect whatever the server
|
|
2290
|
+
* chose to send alongside the status.
|
|
2291
|
+
*
|
|
2292
|
+
* Four subclasses cover the statuses worth branching on explicitly:
|
|
2293
|
+
* {@link AuthenticationError} (401), {@link NotFoundError} (404),
|
|
2294
|
+
* {@link ValidationError} (422), and {@link RateLimitError} (429).
|
|
2295
|
+
*/
|
|
2296
|
+
declare class ApiError extends AsterMDError {
|
|
2297
|
+
#private;
|
|
2298
|
+
/**
|
|
2299
|
+
* @param message Human-readable message, taken from the envelope's `message`
|
|
2300
|
+
* field when the server sent one, otherwise `HTTP {status}`.
|
|
2301
|
+
* @param statusCode The HTTP status code the server returned.
|
|
2302
|
+
* @param envelope The decoded response body. Copied and frozen, so a later
|
|
2303
|
+
* mutation of the caller's object cannot change the error.
|
|
2304
|
+
*/
|
|
2305
|
+
constructor(message: string, statusCode: number, envelope: Record<string, unknown>);
|
|
2306
|
+
/**
|
|
2307
|
+
* Returns the HTTP status code that caused this error.
|
|
2308
|
+
*
|
|
2309
|
+
* @returns The status code, e.g. `404` or `500`.
|
|
2310
|
+
*/
|
|
2311
|
+
statusCode(): number;
|
|
2312
|
+
/**
|
|
2313
|
+
* Returns the decoded response envelope as the server sent it.
|
|
2314
|
+
*
|
|
2315
|
+
* Useful for surfacing a server-supplied message or an error code the SDK does
|
|
2316
|
+
* not model. The object is frozen; treat it as read-only.
|
|
2317
|
+
*
|
|
2318
|
+
* @returns The decoded body, or an empty object when the response had none.
|
|
2319
|
+
*/
|
|
2320
|
+
envelope(): Readonly<Record<string, unknown>>;
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
/**
|
|
2324
|
+
* The API rejected the credentials or the bearer token (HTTP 401).
|
|
2325
|
+
*
|
|
2326
|
+
* The SDK does not raise this on the first 401. {@link TokenManager} refreshes
|
|
2327
|
+
* the token and the request is replayed once; only a second 401 surfaces here.
|
|
2328
|
+
* So seeing this error means a fresh token was also refused - check the client ID
|
|
2329
|
+
* and secret, and that the credentials are still active in your AsterMD
|
|
2330
|
+
* dashboard.
|
|
2331
|
+
*/
|
|
2332
|
+
declare class AuthenticationError extends ApiError {
|
|
2333
|
+
constructor(message: string, statusCode: number, envelope: Record<string, unknown>);
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
/**
|
|
2337
|
+
* The addressed resource does not exist (HTTP 404).
|
|
2338
|
+
*
|
|
2339
|
+
* Raised when an id in the request path matches nothing the calling organisation
|
|
2340
|
+
* can see. Note that the organisation scope lives inside the JWT, so an id
|
|
2341
|
+
* belonging to another organisation is indistinguishable from one that never
|
|
2342
|
+
* existed - both arrive here.
|
|
2343
|
+
*/
|
|
2344
|
+
declare class NotFoundError extends ApiError {
|
|
2345
|
+
constructor(message: string, statusCode: number, envelope: Record<string, unknown>);
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
/**
|
|
2349
|
+
* The calling organisation exceeded its request allowance (HTTP 429).
|
|
2350
|
+
*
|
|
2351
|
+
* When the server sent a `Retry-After` header, its value in seconds is available
|
|
2352
|
+
* on {@link RateLimitError.retryAfter} - wait at least that long before trying
|
|
2353
|
+
* again. The header is optional, so treat `null` as "back off on your own
|
|
2354
|
+
* schedule" rather than "retry immediately".
|
|
2355
|
+
*/
|
|
2356
|
+
declare class RateLimitError extends ApiError {
|
|
2357
|
+
#private;
|
|
2358
|
+
/**
|
|
2359
|
+
* @param message Human-readable summary from the envelope.
|
|
2360
|
+
* @param statusCode Always 429 in practice.
|
|
2361
|
+
* @param envelope The decoded response body.
|
|
2362
|
+
* @param retryAfter Seconds to wait, from the `Retry-After` header, or `null`
|
|
2363
|
+
* when the header was absent or not a plain integer.
|
|
2364
|
+
*/
|
|
2365
|
+
constructor(message: string, statusCode: number, envelope: Record<string, unknown>, retryAfter: number | null);
|
|
2366
|
+
/**
|
|
2367
|
+
* Returns the server-advised wait in seconds before retrying.
|
|
2368
|
+
*
|
|
2369
|
+
* @returns Seconds to wait, or `null` when the server did not say.
|
|
2370
|
+
*/
|
|
2371
|
+
retryAfter(): number | null;
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
/**
|
|
2375
|
+
* A request never reached the API, or its response never came back.
|
|
2376
|
+
*
|
|
2377
|
+
* Raised for DNS failures, TLS errors, connection resets, and timeouts - every
|
|
2378
|
+
* case where the SDK has no HTTP status to reason about. The originating error is
|
|
2379
|
+
* preserved on `cause`. There is no `statusCode()` here by design: nothing was
|
|
2380
|
+
* returned to have a status.
|
|
2381
|
+
*
|
|
2382
|
+
* These are usually worth retrying with backoff; an {@link ApiError} usually is
|
|
2383
|
+
* not.
|
|
2384
|
+
*/
|
|
2385
|
+
declare class TransportError extends AsterMDError {
|
|
2386
|
+
constructor(message: string, options?: ErrorOptions);
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
/**
|
|
2390
|
+
* The request was well-formed but the server refused its contents (HTTP 422).
|
|
2391
|
+
*
|
|
2392
|
+
* The interesting part is {@link ValidationError.fieldErrors}, which maps each
|
|
2393
|
+
* rejected field to the messages the server produced for it - exactly the shape
|
|
2394
|
+
* a form needs to render inline errors next to the offending inputs.
|
|
2395
|
+
*/
|
|
2396
|
+
declare class ValidationError extends ApiError {
|
|
2397
|
+
#private;
|
|
2398
|
+
/**
|
|
2399
|
+
* @param message Human-readable summary from the envelope.
|
|
2400
|
+
* @param statusCode Always 422 in practice.
|
|
2401
|
+
* @param envelope The decoded response body.
|
|
2402
|
+
* @param fieldErrors Field name → list of messages, extracted from the
|
|
2403
|
+
* envelope's `errors` key by {@link Transport}.
|
|
2404
|
+
*/
|
|
2405
|
+
constructor(message: string, statusCode: number, envelope: Record<string, unknown>, fieldErrors: Record<string, string[]>);
|
|
2406
|
+
/**
|
|
2407
|
+
* Returns the per-field validation messages the server produced.
|
|
2408
|
+
*
|
|
2409
|
+
* @returns Field name → messages. Empty when the server sent no `errors` key.
|
|
2410
|
+
*/
|
|
2411
|
+
fieldErrors(): Readonly<Record<string, string[]>>;
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
/**
|
|
2415
|
+
* The default {@link HttpClient}: the platform `fetch` plus a timeout.
|
|
2416
|
+
*
|
|
2417
|
+
* This is what {@link AsterMDClient} uses unless you inject something else. It
|
|
2418
|
+
* adds exactly two things to `fetch`. First, a deadline: every request carries an
|
|
2419
|
+
* `AbortSignal.timeout()` built from the configured `timeoutSeconds`, covering
|
|
2420
|
+
* connection and response together. Second, uniform failures: a DNS error, a
|
|
2421
|
+
* connection reset, a TLS failure, or a timeout all surface as
|
|
2422
|
+
* {@link TransportError} with the original error preserved on `cause`, so
|
|
2423
|
+
* integrators can catch one type instead of guessing at platform error shapes.
|
|
2424
|
+
*
|
|
2425
|
+
* It holds no connection state, so one instance is safely shared across
|
|
2426
|
+
* concurrent requests.
|
|
2427
|
+
*/
|
|
2428
|
+
declare class FetchHttpClient implements HttpClient {
|
|
2429
|
+
#private;
|
|
2430
|
+
/**
|
|
2431
|
+
* @param timeoutSeconds Per-request deadline in seconds. Must be >= 1.
|
|
2432
|
+
* @param fetchImpl The `fetch` implementation to call. Defaults to the global
|
|
2433
|
+
* one; override it to supply an instrumented or stubbed
|
|
2434
|
+
* `fetch` without implementing the whole interface.
|
|
2435
|
+
* @throws {TypeError} If `timeoutSeconds` is below 1.
|
|
2436
|
+
*/
|
|
2437
|
+
constructor(timeoutSeconds?: number, fetchImpl?: typeof fetch);
|
|
2438
|
+
/**
|
|
2439
|
+
* Sends the request through `fetch` under the configured deadline.
|
|
2440
|
+
*
|
|
2441
|
+
* @param request The request to send.
|
|
2442
|
+
* @returns The response as received, whatever its status.
|
|
2443
|
+
* @throws {TransportError} If the request aborts on the deadline or fails at
|
|
2444
|
+
* the network layer.
|
|
2445
|
+
*/
|
|
2446
|
+
sendRequest(request: Request): Promise<Response>;
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
/**
|
|
2450
|
+
* The default {@link TokenStore}: keeps the token in memory for the life of the
|
|
2451
|
+
* process.
|
|
2452
|
+
*
|
|
2453
|
+
* Right for a long-running server - an Express or Fastify app, a worker - where
|
|
2454
|
+
* one credential exchange serves every request until the token expires. Nothing
|
|
2455
|
+
* is written anywhere, so nothing can leak to disk.
|
|
2456
|
+
*
|
|
2457
|
+
* Wrong where each request runs in a fresh process or isolate, since the cache
|
|
2458
|
+
* dies with it and every request pays for its own token exchange. Use
|
|
2459
|
+
* {@link FileTokenStore} or your own {@link TokenStore} there.
|
|
2460
|
+
*/
|
|
2461
|
+
declare class InMemoryTokenStore implements TokenStore {
|
|
2462
|
+
#private;
|
|
2463
|
+
/**
|
|
2464
|
+
* Returns the token held in memory, or `null` before the first exchange.
|
|
2465
|
+
*
|
|
2466
|
+
* @returns The cached token, or `null`.
|
|
2467
|
+
*/
|
|
2468
|
+
get(): Promise<Token | null>;
|
|
2469
|
+
/**
|
|
2470
|
+
* Replaces the token held in memory.
|
|
2471
|
+
*
|
|
2472
|
+
* @param token The token to cache.
|
|
2473
|
+
*/
|
|
2474
|
+
put(token: Token): Promise<void>;
|
|
2475
|
+
/**
|
|
2476
|
+
* Drops the token held in memory.
|
|
2477
|
+
*/
|
|
2478
|
+
clear(): Promise<void>;
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2481
|
+
/**
|
|
2482
|
+
* File-backed {@link TokenStore} that persists the JWT across processes.
|
|
2483
|
+
*
|
|
2484
|
+
* Unlike {@link InMemoryTokenStore}, this writes the token as JSON to disk so it
|
|
2485
|
+
* survives a process boundary. That makes it the right choice where each request
|
|
2486
|
+
* or invocation gets a fresh process and an in-memory cache would never be hit,
|
|
2487
|
+
* as long as those processes share a filesystem.
|
|
2488
|
+
*
|
|
2489
|
+
* ```ts
|
|
2490
|
+
* const client = new AsterMDClient({
|
|
2491
|
+
* clientId: process.env.ASTERMD_CLIENT_ID!,
|
|
2492
|
+
* clientSecret: process.env.ASTERMD_CLIENT_SECRET!,
|
|
2493
|
+
* tokenStore: new FileTokenStore('/var/cache/astermd/token.json'),
|
|
2494
|
+
* });
|
|
2495
|
+
* ```
|
|
2496
|
+
*
|
|
2497
|
+
* The parent directory is created if absent. The file is written `0o600` - owner
|
|
2498
|
+
* read and write only - so the token is not world-readable on a shared host. It
|
|
2499
|
+
* must be writable by the service account and should sit outside any document
|
|
2500
|
+
* root. One file per set of API credentials is enough.
|
|
2501
|
+
*
|
|
2502
|
+
* Writes are atomic: the content goes to a uniquely named temporary file in the
|
|
2503
|
+
* same directory and is then renamed over the target, so a concurrent reader
|
|
2504
|
+
* sees either the whole previous token or the whole new one. Both reads and
|
|
2505
|
+
* writes treat any failure - missing file, bad permissions, a read-only cache
|
|
2506
|
+
* directory, truncated or corrupt JSON - as "no token" rather than raising,
|
|
2507
|
+
* which makes a damaged cache cost one extra credential exchange instead of
|
|
2508
|
+
* breaking the request it was only meant to optimise.
|
|
2509
|
+
*/
|
|
2510
|
+
declare class FileTokenStore implements TokenStore {
|
|
2511
|
+
#private;
|
|
2512
|
+
/**
|
|
2513
|
+
* @param path Path to the JSON file used for token storage. The parent
|
|
2514
|
+
* directory is created on first write if it does not exist.
|
|
2515
|
+
*/
|
|
2516
|
+
constructor(path: string);
|
|
2517
|
+
/**
|
|
2518
|
+
* Reads the cached token from the file.
|
|
2519
|
+
*
|
|
2520
|
+
* @returns The stored token, or `null` when the file is absent, unreadable, or
|
|
2521
|
+
* does not hold a usable token.
|
|
2522
|
+
*/
|
|
2523
|
+
get(): Promise<Token | null>;
|
|
2524
|
+
/**
|
|
2525
|
+
* Writes the token to the backing file, replacing any existing content.
|
|
2526
|
+
*
|
|
2527
|
+
* Persistence is best-effort: a filesystem failure - a read-only or
|
|
2528
|
+
* misconfigured cache directory, a permissions problem - is swallowed rather
|
|
2529
|
+
* than thrown, matching the PHP SDK's `put()`, which returns early on a
|
|
2530
|
+
* `fopen` failure. This method runs on every token acquisition, so failing it
|
|
2531
|
+
* would fail the API call it was only meant to optimise; the cost of a
|
|
2532
|
+
* degraded cache is an extra credential exchange next time, not a broken
|
|
2533
|
+
* request.
|
|
2534
|
+
*
|
|
2535
|
+
* @param token The freshly acquired token to persist.
|
|
2536
|
+
*/
|
|
2537
|
+
put(token: Token): Promise<void>;
|
|
2538
|
+
/**
|
|
2539
|
+
* Deletes the backing file so the next {@link FileTokenStore.get} returns
|
|
2540
|
+
* `null`.
|
|
2541
|
+
*
|
|
2542
|
+
* A no-op when the file does not exist, including when another process
|
|
2543
|
+
* removed it first. Best-effort otherwise: any other filesystem failure (for
|
|
2544
|
+
* example a permissions problem) is swallowed rather than thrown, since a
|
|
2545
|
+
* cache that fails to clear only costs a stale-token read on the next call,
|
|
2546
|
+
* which {@link FileTokenStore.get} already tolerates.
|
|
2547
|
+
*/
|
|
2548
|
+
clear(): Promise<void>;
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
/** Options accepted by {@link DailyFileLogSink}. */
|
|
2552
|
+
interface DailyFileLogSinkOptions {
|
|
2553
|
+
/**
|
|
2554
|
+
* Undated path the daily filenames are derived from, e.g.
|
|
2555
|
+
* `/var/log/astermd/sdk.log`. A path with no extension gets `.log`.
|
|
2556
|
+
*/
|
|
2557
|
+
basePath: string;
|
|
2558
|
+
/**
|
|
2559
|
+
* Days of logs to keep. A file is removed once its date is more than this many
|
|
2560
|
+
* days before today. Use `0` to keep every file forever. Defaults to 7.
|
|
2561
|
+
*/
|
|
2562
|
+
retentionDays?: number;
|
|
2563
|
+
/**
|
|
2564
|
+
* IANA time zone deciding which calendar day an entry belongs to. Pass the same
|
|
2565
|
+
* zone used for log timestamps so filenames and their contents agree. Defaults
|
|
2566
|
+
* to `UTC`.
|
|
2567
|
+
*/
|
|
2568
|
+
timeZone?: string;
|
|
2569
|
+
}
|
|
2570
|
+
/**
|
|
2571
|
+
* Debug-log sink that writes one file per calendar day and prunes old files.
|
|
2572
|
+
*
|
|
2573
|
+
* The debug logger emits one entry per HTTP request. Appending those to a single
|
|
2574
|
+
* file grows without bound, which is why this sink dates each file and deletes
|
|
2575
|
+
* the ones that have aged out - the same arrangement as conventional daily log
|
|
2576
|
+
* rotation, but self-contained, so no external rotation tooling is needed.
|
|
2577
|
+
*
|
|
2578
|
+
* You give it a *base path* and it derives dated filenames from it, keeping the
|
|
2579
|
+
* directory, stem, and extension you chose:
|
|
2580
|
+
*
|
|
2581
|
+
* ```
|
|
2582
|
+
* basePath: /var/log/astermd/sdk.log
|
|
2583
|
+
*
|
|
2584
|
+
* /var/log/astermd/sdk-2026-09-08.log ← today, appended to
|
|
2585
|
+
* /var/log/astermd/sdk-2026-09-07.log
|
|
2586
|
+
* /var/log/astermd/sdk-2026-09-01.log ← deleted once it is over 7 days old
|
|
2587
|
+
* ```
|
|
2588
|
+
*
|
|
2589
|
+
* {@link AsterMDClient} builds one for you when you pass `debugFile`; construct
|
|
2590
|
+
* it directly only if you want to reuse one sink across several clients.
|
|
2591
|
+
*
|
|
2592
|
+
* Pruning reads the date out of each filename rather than the filesystem's
|
|
2593
|
+
* modification time, so a file that was merely touched is still removed on
|
|
2594
|
+
* schedule and unrelated files sharing the directory are never matched. It runs
|
|
2595
|
+
* at most once per instance, on the first write, which keeps it off the hot path.
|
|
2596
|
+
*
|
|
2597
|
+
* Writes are serialized through an internal promise chain, so concurrent requests
|
|
2598
|
+
* cannot interleave halves of two entries. Every I/O failure is swallowed: losing
|
|
2599
|
+
* a debug entry is never worth failing the request that produced it.
|
|
2600
|
+
*
|
|
2601
|
+
* Supplying your own `debugSink` bypasses this class entirely, and retention then
|
|
2602
|
+
* becomes that system's responsibility.
|
|
2603
|
+
*/
|
|
2604
|
+
declare class DailyFileLogSink {
|
|
2605
|
+
#private;
|
|
2606
|
+
/**
|
|
2607
|
+
* @param options Base path, retention window, and time zone.
|
|
2608
|
+
* @throws {TypeError} If `basePath` is empty or carries no file name, or if
|
|
2609
|
+
* `retentionDays` is negative.
|
|
2610
|
+
*/
|
|
2611
|
+
constructor(options: DailyFileLogSinkOptions);
|
|
2612
|
+
/**
|
|
2613
|
+
* Appends one entry to today's log file, pruning aged-out files on the first
|
|
2614
|
+
* call.
|
|
2615
|
+
*
|
|
2616
|
+
* Resolves once the entry is on disk. Writes are chained, so entries land whole
|
|
2617
|
+
* and in call order even under concurrency. Never rejects: an I/O failure is
|
|
2618
|
+
* swallowed rather than propagated into the API call being logged.
|
|
2619
|
+
*
|
|
2620
|
+
* @param entry The formatted log entry to append.
|
|
2621
|
+
*/
|
|
2622
|
+
write(entry: string): Promise<void>;
|
|
2623
|
+
/**
|
|
2624
|
+
* Returns the absolute path entries are currently written to.
|
|
2625
|
+
*
|
|
2626
|
+
* Useful in tests and for a "where are my logs" diagnostic. The path changes at
|
|
2627
|
+
* midnight in the configured time zone.
|
|
2628
|
+
*
|
|
2629
|
+
* @returns The dated file path, e.g. `/var/log/astermd/sdk-2026-09-08.log`.
|
|
2630
|
+
*/
|
|
2631
|
+
currentFile(): string;
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
/** One integration's mapping for a variant or a single-price product. */
|
|
2635
|
+
interface ChannelProductMapping {
|
|
2636
|
+
/** The integration's type, from the mapping's `integration.type`. */
|
|
2637
|
+
type: unknown;
|
|
2638
|
+
/** Every other key of the raw `mapping` object, verbatim. */
|
|
2639
|
+
[key: string]: unknown;
|
|
2640
|
+
}
|
|
2641
|
+
/** A purchasable variant of a product, with its price and integration mapping. */
|
|
2642
|
+
interface ChannelProductVariant {
|
|
2643
|
+
id: unknown;
|
|
2644
|
+
name: unknown;
|
|
2645
|
+
sku: unknown;
|
|
2646
|
+
price: unknown;
|
|
2647
|
+
intro_price: unknown;
|
|
2648
|
+
sale_price: unknown;
|
|
2649
|
+
sale_start: unknown;
|
|
2650
|
+
sale_end: unknown;
|
|
2651
|
+
mapping: ChannelProductMapping | null;
|
|
2652
|
+
}
|
|
2653
|
+
/** Single-purchase pricing for a product that has no variants. */
|
|
2654
|
+
interface ChannelProductSingle {
|
|
2655
|
+
price: unknown;
|
|
2656
|
+
intro_price: unknown;
|
|
2657
|
+
sale_price: unknown;
|
|
2658
|
+
sale_start: unknown;
|
|
2659
|
+
sale_end: unknown;
|
|
2660
|
+
mapping: ChannelProductMapping | null;
|
|
2661
|
+
}
|
|
2662
|
+
/** A product as the channel offers it. */
|
|
2663
|
+
interface ChannelProduct {
|
|
2664
|
+
id: unknown;
|
|
2665
|
+
name: unknown;
|
|
2666
|
+
type: unknown;
|
|
2667
|
+
sku: unknown;
|
|
2668
|
+
status: unknown;
|
|
2669
|
+
visibility: unknown;
|
|
2670
|
+
image: unknown;
|
|
2671
|
+
description_long: unknown;
|
|
2672
|
+
description_short: unknown;
|
|
2673
|
+
restrict_multiple: unknown;
|
|
2674
|
+
min_buy_qty: unknown;
|
|
2675
|
+
max_buy_qty: unknown;
|
|
2676
|
+
stock: unknown;
|
|
2677
|
+
categories: unknown[];
|
|
2678
|
+
condition_treated: unknown[];
|
|
2679
|
+
teleforms: unknown;
|
|
2680
|
+
labtest: unknown[];
|
|
2681
|
+
add_ons: ChannelProduct[];
|
|
2682
|
+
single: ChannelProductSingle | null;
|
|
2683
|
+
variants: ChannelProductVariant[];
|
|
2684
|
+
}
|
|
2685
|
+
/** The channel's payment processor configuration. */
|
|
2686
|
+
interface ChannelPaymentProcessor {
|
|
2687
|
+
id: unknown;
|
|
2688
|
+
name: unknown;
|
|
2689
|
+
provider: unknown;
|
|
2690
|
+
type: unknown;
|
|
2691
|
+
config: unknown;
|
|
2692
|
+
}
|
|
2693
|
+
/** The flattened channel-detail envelope. */
|
|
2694
|
+
interface ChannelDetailView {
|
|
2695
|
+
id: unknown;
|
|
2696
|
+
name: unknown;
|
|
2697
|
+
description: unknown;
|
|
2698
|
+
status: unknown;
|
|
2699
|
+
type: unknown;
|
|
2700
|
+
auto_inc_id: unknown;
|
|
2701
|
+
payment_processor: ChannelPaymentProcessor | null;
|
|
2702
|
+
products: ChannelProduct[];
|
|
2703
|
+
}
|
|
2704
|
+
/**
|
|
2705
|
+
* Read model over the channel-detail envelope.
|
|
2706
|
+
*
|
|
2707
|
+
* {@link Channels.details} returns everything a storefront needs in one
|
|
2708
|
+
* response, which makes it efficient and awkward in equal measure: the payload is
|
|
2709
|
+
* deeply nested, uses the server's own field names, and scatters each variant's
|
|
2710
|
+
* integration mapping into a separate parallel list keyed by variant id. Rendering
|
|
2711
|
+
* straight from it means writing that reassembly into your view layer.
|
|
2712
|
+
*
|
|
2713
|
+
* This presenter does the reassembly once. It renames `_id` to `id` throughout,
|
|
2714
|
+
* joins each variant to its mapping, hoists single-purchase pricing out of its
|
|
2715
|
+
* one-element list, flattens reference objects, and recurses through add-ons so
|
|
2716
|
+
* they look like ordinary products:
|
|
2717
|
+
*
|
|
2718
|
+
* ```ts
|
|
2719
|
+
* const detail = ChannelDetail.from((await client.channels().details(channelId)).data());
|
|
2720
|
+
*
|
|
2721
|
+
* for (const product of detail.products) {
|
|
2722
|
+
* for (const variant of product.variants) {
|
|
2723
|
+
* render(product.name, variant.name, variant.price, variant.mapping);
|
|
2724
|
+
* }
|
|
2725
|
+
* }
|
|
2726
|
+
* ```
|
|
2727
|
+
*
|
|
2728
|
+
* Field names in the output stay snake_case, matching the PHP SDK's presenter, so
|
|
2729
|
+
* a value produced by one can be compared against the other. Anything the server
|
|
2730
|
+
* omitted comes back as `null` rather than `undefined`, and an omitted list comes
|
|
2731
|
+
* back empty - so a view can read straight through without optional chaining at
|
|
2732
|
+
* every level.
|
|
2733
|
+
*
|
|
2734
|
+
* The class is not instantiable; it exposes only {@link ChannelDetail.from}.
|
|
2735
|
+
*/
|
|
2736
|
+
declare class ChannelDetail {
|
|
2737
|
+
private constructor();
|
|
2738
|
+
/**
|
|
2739
|
+
* Flattens a channel-detail payload into a shape a view can render directly.
|
|
2740
|
+
*
|
|
2741
|
+
* Pass `response.data()` from {@link Channels.details}. Malformed or missing
|
|
2742
|
+
* branches degrade to `null` and `[]` rather than raising, because a channel is
|
|
2743
|
+
* configuration a storefront needs at boot: failing the page over one absent
|
|
2744
|
+
* optional field would be the wrong trade.
|
|
2745
|
+
*
|
|
2746
|
+
* @param data The raw `data` object from the channel-detail response.
|
|
2747
|
+
* @returns The flattened view.
|
|
2748
|
+
*/
|
|
2749
|
+
static from(data: Record<string, unknown>): ChannelDetailView;
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
/**
|
|
2753
|
+
* Encrypts and decrypts URL query-parameter tokens using AES-128-CBC, with a
|
|
2754
|
+
* random 16-byte IV prepended to the ciphertext and the whole thing
|
|
2755
|
+
* base64url-encoded.
|
|
2756
|
+
*
|
|
2757
|
+
* Tracking parameters are encrypted so a browser extension cannot read them in
|
|
2758
|
+
* transit. The codec is parameter-agnostic: it carries no knowledge of what it
|
|
2759
|
+
* is transporting.
|
|
2760
|
+
*
|
|
2761
|
+
* Decryption is deliberately total. A malformed, truncated, tampered, or
|
|
2762
|
+
* wrongly-keyed token yields an empty object rather than raising, so a storefront
|
|
2763
|
+
* can fall back to plain parameters instead of failing a page load:
|
|
2764
|
+
*
|
|
2765
|
+
* ```ts
|
|
2766
|
+
* const params = QueryParamCipher.decrypt(
|
|
2767
|
+
* request.query._amd ?? '',
|
|
2768
|
+
* process.env.ASTERMD_PARAM_KEY!,
|
|
2769
|
+
* );
|
|
2770
|
+
* // => { aff_id: '123', utm_source: 'news' } (or {} on any failure)
|
|
2771
|
+
* ```
|
|
2772
|
+
*
|
|
2773
|
+
* The AES key is **never** stored in this SDK. Supply it at every call site from
|
|
2774
|
+
* your own configuration - an environment variable, a secrets manager - and keep
|
|
2775
|
+
* it in step with whichever service mints the tokens. Contact info@astermd.com to
|
|
2776
|
+
* obtain or rotate the key for your organisation.
|
|
2777
|
+
*
|
|
2778
|
+
* **On integrity:** AES-CBC provides confidentiality but not authentication, so a
|
|
2779
|
+
* token cannot be proven unmodified. Treat decrypted parameters as untrusted
|
|
2780
|
+
* input and validate them before use.
|
|
2781
|
+
*
|
|
2782
|
+
* **On the wire format and the PHP SDK:** the encrypted payload is a JSON *list*
|
|
2783
|
+
* of `{ key, value }` objects, which is what the PHP SDK writes and reads, so a
|
|
2784
|
+
* token minted by either SDK is readable by the other. This class's own surface
|
|
2785
|
+
* differs from the PHP one, though: it takes and returns a plain object, since
|
|
2786
|
+
* that is what a JS caller wants, and converts at the boundary. A duplicate key
|
|
2787
|
+
* on the wire resolves to its last occurrence.
|
|
2788
|
+
*/
|
|
2789
|
+
declare class QueryParamCipher {
|
|
2790
|
+
private constructor();
|
|
2791
|
+
/**
|
|
2792
|
+
* Decrypts a query-parameter token into tracking parameters.
|
|
2793
|
+
*
|
|
2794
|
+
* Call this at your storefront's entry point with the raw token from the URL
|
|
2795
|
+
* and the key from your own configuration. Any failure along the way - bad
|
|
2796
|
+
* encoding, wrong or malformed key, decryption error, non-list payload -
|
|
2797
|
+
* returns an empty object so the caller can fall back to plain parameters.
|
|
2798
|
+
*
|
|
2799
|
+
* @param token The base64url token, `iv(16) || ciphertext`.
|
|
2800
|
+
* @param keyHex 32 hex characters - a 16-byte AES-128 key. Supplied by you;
|
|
2801
|
+
* the SDK ships no default.
|
|
2802
|
+
* @returns The decrypted parameters, or `{}` on any failure.
|
|
2803
|
+
*/
|
|
2804
|
+
static decrypt(token: string, keyHex: string): Record<string, string>;
|
|
2805
|
+
/**
|
|
2806
|
+
* Encrypts tracking parameters into a token.
|
|
2807
|
+
*
|
|
2808
|
+
* Provided mainly for round-trip tests and local tooling. A fresh random IV is
|
|
2809
|
+
* generated on every call and prepended to the ciphertext before encoding, so
|
|
2810
|
+
* the same parameters never produce the same token twice.
|
|
2811
|
+
*
|
|
2812
|
+
* Unlike {@link QueryParamCipher.decrypt}, this is strict: a bad key raises
|
|
2813
|
+
* rather than being swallowed, because a failed encrypt is a programming error
|
|
2814
|
+
* and not untrusted input.
|
|
2815
|
+
*
|
|
2816
|
+
* @param params The parameters to encrypt. Written to the wire as the
|
|
2817
|
+
* `[{ key, value }, …]` list the PHP SDK also reads.
|
|
2818
|
+
* @param keyHex 32 hex characters - a 16-byte AES-128 key.
|
|
2819
|
+
* @returns The base64url token, `iv(16) || ciphertext`.
|
|
2820
|
+
* @throws {TypeError} If `keyHex` is not a valid 16-byte hex key.
|
|
2821
|
+
*/
|
|
2822
|
+
static encrypt(params: Record<string, string>, keyHex: string): string;
|
|
2823
|
+
}
|
|
2824
|
+
|
|
2825
|
+
export { AbstractResource, ApiError, AsterMDClient, type AsterMDClientOptions, AsterMDError, AuthenticationError, type CartWriteOptions, Carts, Categories, ChannelDetail, type ChannelDetailView, type ChannelPaymentProcessor, type ChannelProduct, type ChannelProductMapping, type ChannelProductSingle, type ChannelProductVariant, Channels, CheckoutEvent, type CheckoutEventUpdateOptions, CheckoutEvents, Config, type ConfigOptions, DailyFileLogSink, type DailyFileLogSinkOptions, type DebugSink, DoctorsNetworks, Event, FetchHttpClient, FileTokenStore, FileUpload, type FileUploadFromContentsOptions, type FileUploadFromPathOptions, type FinishMultipartOptions, Geo, type HttpClient, IdentityCheck, InMemoryTokenStore, type InitiateMultipartOptions, IntakeSubmissions, type IntakeViewOptions, type IntakeWriteOptions, LabTests, Medications, type MultipartPart, NotFoundError, Opportunities, Patients, Products, QueryParamCipher, RateLimitError, Response$1 as Response, type ResponseOptions, type SessionCreateOptions, type SessionViewOptions, Sessions, Shippings, type SubmitHealthInformationOptions, Teleforms, Token, type TokenStore, TransportError, type TreatmentSyncOptions, Treatments, type UploadFileOptions, type UploadLargeFileOptions, type UploadPartOptions, ValidationError, Verification };
|