@labyrinthe/connect 0.0.0-stage → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +101 -0
- package/LICENSE +21 -0
- package/README.fr.md +819 -0
- package/README.md +808 -2
- package/dist/express.cjs +87 -0
- package/dist/express.d.cts +49 -0
- package/dist/express.d.ts +49 -0
- package/dist/express.js +85 -0
- package/dist/index.cjs +1721 -0
- package/dist/index.d.cts +943 -0
- package/dist/index.d.ts +943 -0
- package/dist/index.js +1667 -0
- package/dist/next.cjs +48 -0
- package/dist/next.d.cts +31 -0
- package/dist/next.d.ts +31 -0
- package/dist/next.js +46 -0
- package/package.json +119 -4
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,943 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types of the SDK: camelCase versions of the API objects described in
|
|
3
|
+
* openapi/openapi.yaml. Dates are `Date` objects; enums are unions of string
|
|
4
|
+
* literals with a constant object of the same name.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Lifecycle of a message.
|
|
8
|
+
*
|
|
9
|
+
* `unknown` is never sent by the API: the SDK uses it when the API returns a
|
|
10
|
+
* status this version does not know yet.
|
|
11
|
+
*/
|
|
12
|
+
declare const MessageStatus: {
|
|
13
|
+
/** Queued at Labyrinthe Connect. */
|
|
14
|
+
readonly Queued: "queued";
|
|
15
|
+
/** Handed over to the SMS provider. */
|
|
16
|
+
readonly Submitted: "submitted";
|
|
17
|
+
/** Accepted by the mobile operator. */
|
|
18
|
+
readonly Sent: "sent";
|
|
19
|
+
/** Delivered to the handset (final). */
|
|
20
|
+
readonly Delivered: "delivered";
|
|
21
|
+
/** Failed (final). */
|
|
22
|
+
readonly Failed: "failed";
|
|
23
|
+
/** Rejected by the provider or the operator (final). */
|
|
24
|
+
readonly Rejected: "rejected";
|
|
25
|
+
/** Expired before delivery (final). */
|
|
26
|
+
readonly Expired: "expired";
|
|
27
|
+
/** Cannot be delivered to this number (final). */
|
|
28
|
+
readonly Undeliverable: "undeliverable";
|
|
29
|
+
/** Status not known by this SDK version. */
|
|
30
|
+
readonly Unknown: "unknown";
|
|
31
|
+
};
|
|
32
|
+
type MessageStatus = (typeof MessageStatus)[keyof typeof MessageStatus];
|
|
33
|
+
/** True when the status will not change any more (delivered or failed). */
|
|
34
|
+
declare function isFinalStatus(status: MessageStatus): boolean;
|
|
35
|
+
/** True for `failed`, `rejected`, `expired` and `undeliverable`. */
|
|
36
|
+
declare function isFailedStatus(status: MessageStatus): boolean;
|
|
37
|
+
/** True for `queued`, `submitted` and `sent`. */
|
|
38
|
+
declare function isPendingStatus(status: MessageStatus): boolean;
|
|
39
|
+
/** Character encoding of an SMS: GSM-7 (160 characters) or UCS-2 (70 characters). */
|
|
40
|
+
declare const Encoding: {
|
|
41
|
+
readonly Gsm7: "gsm7";
|
|
42
|
+
readonly Ucs2: "ucs2";
|
|
43
|
+
};
|
|
44
|
+
type Encoding = (typeof Encoding)[keyof typeof Encoding];
|
|
45
|
+
declare const SenderIdStatus: {
|
|
46
|
+
readonly Pending: "pending";
|
|
47
|
+
readonly PendingProvider: "pending_provider";
|
|
48
|
+
readonly Approved: "approved";
|
|
49
|
+
readonly Rejected: "rejected";
|
|
50
|
+
readonly Suspended: "suspended";
|
|
51
|
+
/** Status not known by this SDK version. */
|
|
52
|
+
readonly Unknown: "unknown";
|
|
53
|
+
};
|
|
54
|
+
type SenderIdStatus = (typeof SenderIdStatus)[keyof typeof SenderIdStatus];
|
|
55
|
+
/** Why a recipient was refused (`RecipientNotSendableError`). */
|
|
56
|
+
declare const RejectionReason: {
|
|
57
|
+
readonly InvalidPhone: "invalid_phone";
|
|
58
|
+
readonly Blacklisted: "blacklisted";
|
|
59
|
+
readonly OptedOut: "opted_out";
|
|
60
|
+
/** Reason not known by this SDK version. */
|
|
61
|
+
readonly Unknown: "unknown";
|
|
62
|
+
};
|
|
63
|
+
type RejectionReason = (typeof RejectionReason)[keyof typeof RejectionReason];
|
|
64
|
+
/** Why an OTP verification failed (`verified: false`). */
|
|
65
|
+
declare const OtpFailureReason: {
|
|
66
|
+
/** Wrong code: one attempt was consumed, the user may try again. */
|
|
67
|
+
readonly InvalidCode: "invalid_code";
|
|
68
|
+
/** The code expired: send a new one. */
|
|
69
|
+
readonly Expired: "expired";
|
|
70
|
+
/** The code was already verified (each code is single-use). */
|
|
71
|
+
readonly AlreadyUsed: "already_used";
|
|
72
|
+
/** Too many wrong attempts: the code is permanently blocked. */
|
|
73
|
+
readonly MaxAttemptsReached: "max_attempts_reached";
|
|
74
|
+
/** Reason not known by this SDK version. */
|
|
75
|
+
readonly Unknown: "unknown";
|
|
76
|
+
};
|
|
77
|
+
type OtpFailureReason = (typeof OtpFailureReason)[keyof typeof OtpFailureReason];
|
|
78
|
+
/** Scope of an `OTP_RATE_LIMITED` error: the phone number or the whole account. */
|
|
79
|
+
declare const OtpRateLimitScope: {
|
|
80
|
+
readonly Phone: "phone";
|
|
81
|
+
readonly Account: "account";
|
|
82
|
+
/** Scope not known by this SDK version. */
|
|
83
|
+
readonly Unknown: "unknown";
|
|
84
|
+
};
|
|
85
|
+
type OtpRateLimitScope = (typeof OtpRateLimitScope)[keyof typeof OtpRateLimitScope];
|
|
86
|
+
/** Alphabet of an OTP code (`otp.send()` → `codeType`). */
|
|
87
|
+
declare const OtpCodeType: {
|
|
88
|
+
/** Digits 0-9. */
|
|
89
|
+
readonly Numeric: "numeric";
|
|
90
|
+
/** Upper-case letters and digits without ambiguous characters; typing is case-insensitive. */
|
|
91
|
+
readonly Alphanumeric: "alphanumeric";
|
|
92
|
+
/** Alphabet not known by this SDK version. */
|
|
93
|
+
readonly Unknown: "unknown";
|
|
94
|
+
};
|
|
95
|
+
type OtpCodeType = (typeof OtpCodeType)[keyof typeof OtpCodeType];
|
|
96
|
+
/** Final status of an OTP verification, in the `otp.verified` / `otp.failed` webhooks. */
|
|
97
|
+
declare const OtpVerificationStatus: {
|
|
98
|
+
/** The right code was typed (`otp.verified`). */
|
|
99
|
+
readonly Verified: "verified";
|
|
100
|
+
/** Too many wrong attempts (`otp.failed`, reason `max_attempts_reached`). */
|
|
101
|
+
readonly Failed: "failed";
|
|
102
|
+
/** The code expired (`otp.failed`, reason `expired`). */
|
|
103
|
+
readonly Expired: "expired";
|
|
104
|
+
/** Status not known by this SDK version. */
|
|
105
|
+
readonly Unknown: "unknown";
|
|
106
|
+
};
|
|
107
|
+
type OtpVerificationStatus = (typeof OtpVerificationStatus)[keyof typeof OtpVerificationStatus];
|
|
108
|
+
/** Webhook event types emitted by the API. */
|
|
109
|
+
declare const WebhookEventType: {
|
|
110
|
+
readonly MessageStatusChanged: "message.status_changed";
|
|
111
|
+
readonly CampaignCompleted: "campaign.completed";
|
|
112
|
+
readonly BalanceLow: "balance.low";
|
|
113
|
+
readonly OtpVerified: "otp.verified";
|
|
114
|
+
readonly OtpFailed: "otp.failed";
|
|
115
|
+
readonly TestPing: "test.ping";
|
|
116
|
+
};
|
|
117
|
+
type WebhookEventType = (typeof WebhookEventType)[keyof typeof WebhookEventType];
|
|
118
|
+
/** OTP message languages of the standard templates. */
|
|
119
|
+
type OtpLocale = 'fr' | 'en';
|
|
120
|
+
/** An SMS, one per recipient. The text is deliberately not returned by the API (it may contain an OTP code). */
|
|
121
|
+
interface Message {
|
|
122
|
+
id: string;
|
|
123
|
+
/** Your reference, given at send time. */
|
|
124
|
+
reference: string | null;
|
|
125
|
+
/** International format without "+", e.g. "243812345678". */
|
|
126
|
+
recipient: string;
|
|
127
|
+
/** Sender shown on the handset. */
|
|
128
|
+
sender: string | null;
|
|
129
|
+
encoding: Encoding | null;
|
|
130
|
+
status: MessageStatus;
|
|
131
|
+
segments: number;
|
|
132
|
+
/** Credits charged (equal to `segments`; fictitious with a test key). */
|
|
133
|
+
creditsCharged: number;
|
|
134
|
+
/** Campaign of origin (messages sent from the client area). */
|
|
135
|
+
campaignId: string | null;
|
|
136
|
+
providerMessageId: string | null;
|
|
137
|
+
errorCode: string | null;
|
|
138
|
+
errorMessage: string | null;
|
|
139
|
+
createdAt: Date | null;
|
|
140
|
+
/** Handed over to the provider. */
|
|
141
|
+
submittedAt: Date | null;
|
|
142
|
+
/** Delivery receipt. */
|
|
143
|
+
deliveredAt: Date | null;
|
|
144
|
+
}
|
|
145
|
+
/** Result of `sms.sendMany()`: one message per distinct recipient. */
|
|
146
|
+
interface MessageBatch {
|
|
147
|
+
messages: Message[];
|
|
148
|
+
/** The Idempotency-Key used for this send (yours, or the one generated by the SDK). */
|
|
149
|
+
idempotencyKey: string;
|
|
150
|
+
/** True when the API replayed an earlier identical request: nothing was sent or billed again. */
|
|
151
|
+
replayed: boolean;
|
|
152
|
+
/** `X-Request-Id` of the response, for the support; null when absent. */
|
|
153
|
+
requestId: string | null;
|
|
154
|
+
}
|
|
155
|
+
/** Cost of a send, computed by the API without sending anything. */
|
|
156
|
+
interface Estimate {
|
|
157
|
+
encoding: Encoding;
|
|
158
|
+
characters: number;
|
|
159
|
+
segmentsPerMessage: number;
|
|
160
|
+
/** Valid recipients only (invalid numbers are ignored, not rejected). */
|
|
161
|
+
recipientsCount: number;
|
|
162
|
+
totalCredits: number;
|
|
163
|
+
/** Balance after the send (negative when insufficient); null if the account never had credits. */
|
|
164
|
+
balanceAfter: number | null;
|
|
165
|
+
/** True when the current balance covers the send (`balanceAfter >= 0`). */
|
|
166
|
+
isAffordable: boolean;
|
|
167
|
+
}
|
|
168
|
+
/** Credit balance, in SMS credits (1 credit = 1 segment). */
|
|
169
|
+
interface Balance {
|
|
170
|
+
/** Available credits. */
|
|
171
|
+
balance: number;
|
|
172
|
+
/** Credits held by running campaigns. */
|
|
173
|
+
held: number;
|
|
174
|
+
}
|
|
175
|
+
/** An approved sender name, usable as `sender` when sending. */
|
|
176
|
+
interface SenderId {
|
|
177
|
+
id: string;
|
|
178
|
+
/** The name shown on the handset, e.g. "MYSHOP" (11 characters max). */
|
|
179
|
+
value: string;
|
|
180
|
+
status: SenderIdStatus;
|
|
181
|
+
purpose: string | null;
|
|
182
|
+
rejectionReason: string | null;
|
|
183
|
+
isDefault: boolean;
|
|
184
|
+
createdAt: Date | null;
|
|
185
|
+
}
|
|
186
|
+
/** A refused recipient of an all-or-nothing send. */
|
|
187
|
+
interface RejectedRecipient {
|
|
188
|
+
/** Number as received, or normalised. */
|
|
189
|
+
recipient: string;
|
|
190
|
+
reason: RejectionReason;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Result of `otp.verify()`. A wrong code is not an exception.
|
|
194
|
+
*
|
|
195
|
+
* `attemptsRemaining`: attempts still possible for this code — an integer
|
|
196
|
+
* after `invalid_code`, `0` with `max_attempts_reached`, null when the code is
|
|
197
|
+
* verified, already used or expired (and with an API older than 1.2.0).
|
|
198
|
+
*/
|
|
199
|
+
type OtpVerifyResult = {
|
|
200
|
+
verified: true;
|
|
201
|
+
reason: null;
|
|
202
|
+
attemptsRemaining: null;
|
|
203
|
+
} | {
|
|
204
|
+
verified: false;
|
|
205
|
+
reason: OtpFailureReason;
|
|
206
|
+
attemptsRemaining: number | null;
|
|
207
|
+
};
|
|
208
|
+
/** What the SDK read from a successful response (`onResponse` option). */
|
|
209
|
+
interface ResponseInfo {
|
|
210
|
+
/** HTTP status, e.g. 200, 201, 202. */
|
|
211
|
+
status: number;
|
|
212
|
+
/** `X-Request-Id`: yours when you passed `requestId`, otherwise generated by the API. Null when absent. */
|
|
213
|
+
requestId: string | null;
|
|
214
|
+
/** `X-Api-Version`, the major API version (e.g. "1"); null when absent. */
|
|
215
|
+
apiVersion: string | null;
|
|
216
|
+
/** `Idempotent-Replayed: true`: replay of an earlier request with the same Idempotency-Key. */
|
|
217
|
+
replayed: boolean;
|
|
218
|
+
/** `X-RateLimit-Limit` / `X-RateLimit-Remaining`; null when absent. */
|
|
219
|
+
rateLimit: {
|
|
220
|
+
limit: number | null;
|
|
221
|
+
remaining: number | null;
|
|
222
|
+
};
|
|
223
|
+
/** Every response header (lower-case names). */
|
|
224
|
+
headers: Readonly<Record<string, string>>;
|
|
225
|
+
}
|
|
226
|
+
/** Per-call options, accepted as the last argument of every method. */
|
|
227
|
+
interface RequestOptions {
|
|
228
|
+
/** Cancels the call (including the waits between retries). */
|
|
229
|
+
signal?: AbortSignal;
|
|
230
|
+
/** Timeout of each attempt, in milliseconds (defaults to the client's `timeoutMs`). */
|
|
231
|
+
timeoutMs?: number;
|
|
232
|
+
/**
|
|
233
|
+
* Your own request identifier, sent as `X-Request-Id` (on every retry of
|
|
234
|
+
* this call) and echoed by the API in its logs and responses: 1 to 128
|
|
235
|
+
* characters among `A-Z a-z 0-9 . _ : -`. Without it, the API generates one.
|
|
236
|
+
*/
|
|
237
|
+
requestId?: string;
|
|
238
|
+
/** Called with the headers of the successful response (request id, rate limit, replay…). Errors carry them too. */
|
|
239
|
+
onResponse?: (response: ResponseInfo) => void;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
type Json = Record<string, unknown>;
|
|
243
|
+
|
|
244
|
+
/** Minimal logger: `console`, pino, winston… Never receives the key, codes, bodies or full phone numbers. */
|
|
245
|
+
interface Logger {
|
|
246
|
+
debug?(message: string, context?: Record<string, unknown>): void;
|
|
247
|
+
info?(message: string, context?: Record<string, unknown>): void;
|
|
248
|
+
warn?(message: string, context?: Record<string, unknown>): void;
|
|
249
|
+
error?(message: string, context?: Record<string, unknown>): void;
|
|
250
|
+
}
|
|
251
|
+
/** Options passed to `fetch` by the SDK. */
|
|
252
|
+
interface FetchInit {
|
|
253
|
+
method: string;
|
|
254
|
+
headers: Record<string, string>;
|
|
255
|
+
body?: string;
|
|
256
|
+
signal: AbortSignal;
|
|
257
|
+
redirect: 'manual';
|
|
258
|
+
}
|
|
259
|
+
/** The subset of the Fetch `Response` the SDK reads. */
|
|
260
|
+
interface FetchResponse {
|
|
261
|
+
status: number;
|
|
262
|
+
headers: {
|
|
263
|
+
forEach(callback: (value: string, key: string) => void): void;
|
|
264
|
+
};
|
|
265
|
+
text(): Promise<string>;
|
|
266
|
+
}
|
|
267
|
+
/** Any WHATWG-compatible `fetch` (global fetch, undici, a mock…). */
|
|
268
|
+
type FetchLike = (url: string, init: FetchInit) => Promise<FetchResponse>;
|
|
269
|
+
|
|
270
|
+
/** Credit balance (`client.balance`). */
|
|
271
|
+
declare class BalanceResource {
|
|
272
|
+
#private;
|
|
273
|
+
get(options?: RequestOptions): Promise<Balance>;
|
|
274
|
+
toJSON(): Record<string, unknown>;
|
|
275
|
+
}
|
|
276
|
+
/** Approved Sender IDs (`client.senderIds`). */
|
|
277
|
+
declare class SenderIds {
|
|
278
|
+
#private;
|
|
279
|
+
/** Sender IDs with the `approved` status, usable as `sender`. */
|
|
280
|
+
list(options?: RequestOptions): Promise<SenderId[]>;
|
|
281
|
+
toJSON(): Record<string, unknown>;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
interface SendOtpParams {
|
|
285
|
+
/** Phone number, international or local format. */
|
|
286
|
+
phone: string;
|
|
287
|
+
/** Must contain "{code}"; optional "{app_name}", "{minutes}". Defaults to the account's OTP settings. */
|
|
288
|
+
template?: string;
|
|
289
|
+
/** Language of the standard template. Defaults to the account's OTP settings. */
|
|
290
|
+
locale?: OtpLocale;
|
|
291
|
+
/**
|
|
292
|
+
* Web OTP API: host name of the site that reads the code (e.g. "app.example.com":
|
|
293
|
+
* no scheme, port or path). The API adds the last line `@app.example.com #123456`
|
|
294
|
+
* to the SMS, so that Chrome on Android offers to fill the code automatically.
|
|
295
|
+
*/
|
|
296
|
+
webOtpDomain?: string;
|
|
297
|
+
/** Replace accents and typographic characters with ASCII (GSM-7: often half the cost). Default false. */
|
|
298
|
+
stripAccents?: boolean;
|
|
299
|
+
/**
|
|
300
|
+
* Max 255 characters. Generated (UUID v4) when absent, and reused by the
|
|
301
|
+
* SDK's retries: a retried send never sends a second SMS. Pass your own to
|
|
302
|
+
* also protect against your own retries for 24 hours (a replay returns the
|
|
303
|
+
* same `otpId`, without a new SMS).
|
|
304
|
+
*/
|
|
305
|
+
idempotencyKey?: string;
|
|
306
|
+
}
|
|
307
|
+
interface VerifyOtpParams {
|
|
308
|
+
/** The `otpId` returned by `otp.send()`, kept server-side. */
|
|
309
|
+
otpId: string;
|
|
310
|
+
/** The code typed by the user (spaces and case are ignored by the API). */
|
|
311
|
+
code: string;
|
|
312
|
+
}
|
|
313
|
+
declare const INSPECT$1: unique symbol;
|
|
314
|
+
declare const DENO_INSPECT$1: unique symbol;
|
|
315
|
+
/**
|
|
316
|
+
* Result of an OTP send. Store `otpId` server-side (session, cache…) and pass
|
|
317
|
+
* it to `otp.verify()` with the code typed by the user.
|
|
318
|
+
*/
|
|
319
|
+
declare class OtpSendResult {
|
|
320
|
+
#private;
|
|
321
|
+
readonly otpId: string;
|
|
322
|
+
readonly expiresAt: Date;
|
|
323
|
+
readonly maxAttempts: number;
|
|
324
|
+
/** Number of characters of the code (4 to 8, account's OTP settings); null only with an API older than 1.2.0. */
|
|
325
|
+
readonly codeLength: number | null;
|
|
326
|
+
/** `numeric` (0-9) or `alphanumeric` (case-insensitive); null only with an API older than 1.2.0. */
|
|
327
|
+
readonly codeType: OtpCodeType | null;
|
|
328
|
+
/**
|
|
329
|
+
* Seconds before a new send to this number is accepted while this code is
|
|
330
|
+
* not verified: show a "Resend the code" countdown. Null only with an API older than 1.2.0.
|
|
331
|
+
*/
|
|
332
|
+
readonly resendAfterSeconds: number | null;
|
|
333
|
+
/** The Idempotency-Key of this send (yours, or the one generated by the SDK). */
|
|
334
|
+
readonly idempotencyKey: string;
|
|
335
|
+
/** True when the API replayed an earlier send with the same Idempotency-Key: no new SMS, no new charge. */
|
|
336
|
+
readonly replayed: boolean;
|
|
337
|
+
/** `X-Request-Id` of the response, for the support; null when absent. */
|
|
338
|
+
readonly requestId: string | null;
|
|
339
|
+
/**
|
|
340
|
+
* The code, ONLY with a test key (`lsk_test_…`): nothing is sent by SMS in
|
|
341
|
+
* test mode, so the API returns the code to let you test `verify()`. Always
|
|
342
|
+
* null with a live key. Never log it, never show it to end users. Hidden
|
|
343
|
+
* from `JSON.stringify()` and `console.log()`.
|
|
344
|
+
*/
|
|
345
|
+
get testCode(): string | null;
|
|
346
|
+
toJSON(): {
|
|
347
|
+
otpId: string;
|
|
348
|
+
expiresAt: string;
|
|
349
|
+
maxAttempts: number;
|
|
350
|
+
codeLength: number | null;
|
|
351
|
+
codeType: OtpCodeType | null;
|
|
352
|
+
resendAfterSeconds: number | null;
|
|
353
|
+
replayed: boolean;
|
|
354
|
+
requestId: string | null;
|
|
355
|
+
};
|
|
356
|
+
[INSPECT$1](): Record<string, unknown>;
|
|
357
|
+
[DENO_INSPECT$1](): string;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* One-time passwords by SMS (`client.otp`).
|
|
361
|
+
*
|
|
362
|
+
* 1. `send({ phone })` and keep `otpId` server-side (session);
|
|
363
|
+
* 2. the user types the code received by SMS (`codeLength`, `codeType`);
|
|
364
|
+
* 3. `verify({ otpId, code })`: log the user in when `verified` is true.
|
|
365
|
+
*
|
|
366
|
+
* Code length, alphabet, validity, attempts, anti-resend delay, sender and
|
|
367
|
+
* template come from the account's OTP settings (client area > OTP verification).
|
|
368
|
+
*/
|
|
369
|
+
declare class Otp {
|
|
370
|
+
#private;
|
|
371
|
+
/**
|
|
372
|
+
* Generates a code and sends it by SMS (billed like any SMS). A new send to
|
|
373
|
+
* the same number invalidates the previous code.
|
|
374
|
+
*
|
|
375
|
+
* An Idempotency-Key is always sent (generated when you give none), so the
|
|
376
|
+
* SDK retries network errors, 5xx, 429 TOO_MANY_REQUESTS and 409
|
|
377
|
+
* IDEMPOTENCY_REQUEST_IN_PROGRESS with the same key: a retry never sends a
|
|
378
|
+
* second SMS. The business limits (OTP_RESEND_TOO_SOON, OTP_RATE_LIMITED)
|
|
379
|
+
* are never retried. 503 FEATURE_DISABLED / OTP_SERVICE_DISABLED
|
|
380
|
+
* (`FeatureDisabledError`) is retried only after a `Retry-After` no longer
|
|
381
|
+
* than `maxRetryWaitMs`.
|
|
382
|
+
*/
|
|
383
|
+
send(params: SendOtpParams, options?: RequestOptions): Promise<OtpSendResult>;
|
|
384
|
+
/**
|
|
385
|
+
* Checks a code. A wrong code is NOT an error: the result says
|
|
386
|
+
* `verified: false` with a `reason` and `attemptsRemaining`, and one attempt
|
|
387
|
+
* is consumed. Free. Throws `NotFoundError` for an unknown `otpId` (or one
|
|
388
|
+
* from another account).
|
|
389
|
+
*
|
|
390
|
+
* Retried only on 429 TOO_MANY_REQUESTS and on 503 FEATURE_DISABLED with a
|
|
391
|
+
* `Retry-After` no longer than `maxRetryWaitMs` (both refused before
|
|
392
|
+
* processing): after a network error or another 5xx, an attempt may have
|
|
393
|
+
* been consumed.
|
|
394
|
+
*/
|
|
395
|
+
verify(params: VerifyOtpParams, options?: RequestOptions): Promise<OtpVerifyResult>;
|
|
396
|
+
toJSON(): Record<string, unknown>;
|
|
397
|
+
[INSPECT$1](): Record<string, unknown>;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** One page of a paginated list. Iterate it with `for await` to walk this page and the following ones. */
|
|
401
|
+
declare class Page<T> implements AsyncIterable<T> {
|
|
402
|
+
#private;
|
|
403
|
+
readonly data: T[];
|
|
404
|
+
readonly currentPage: number;
|
|
405
|
+
readonly lastPage: number;
|
|
406
|
+
readonly perPage: number;
|
|
407
|
+
readonly total: number;
|
|
408
|
+
get hasMore(): boolean;
|
|
409
|
+
/** The next page, or null on the last one. */
|
|
410
|
+
nextPage(): Promise<Page<T> | null>;
|
|
411
|
+
/** Every item from this page to the last one (pages are fetched lazily). */
|
|
412
|
+
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
413
|
+
toJSON(): Record<string, unknown>;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Result of a list call: `await` it for the first page, or iterate it with
|
|
417
|
+
* `for await` to walk every item of every page. The first request is sent
|
|
418
|
+
* when the result is awaited or iterated.
|
|
419
|
+
*/
|
|
420
|
+
declare class PagePromise<T> implements PromiseLike<Page<T>>, AsyncIterable<T> {
|
|
421
|
+
#private;
|
|
422
|
+
then<R1 = Page<T>, R2 = never>(onFulfilled?: ((page: Page<T>) => R1 | PromiseLike<R1>) | null, onRejected?: ((reason: unknown) => R2 | PromiseLike<R2>) | null): Promise<R1 | R2>;
|
|
423
|
+
catch<R = never>(onRejected?: ((reason: unknown) => R | PromiseLike<R>) | null): Promise<Page<T> | R>;
|
|
424
|
+
finally(onFinally?: (() => void) | null): Promise<Page<T>>;
|
|
425
|
+
[Symbol.asyncIterator](): AsyncIterator<T>;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
interface SendCommon {
|
|
429
|
+
/** Text, 1 to 1600 characters. Any non-ASCII character switches to UCS-2 (70 characters per SMS). */
|
|
430
|
+
body: string;
|
|
431
|
+
/** Approved Sender ID (see `senderIds.list()`). Defaults to the client's `defaultSender`, then the platform's. */
|
|
432
|
+
sender?: string;
|
|
433
|
+
/** Your own reference (order number…), max 64 characters, filterable with `sms.list({ reference })`. */
|
|
434
|
+
reference?: string;
|
|
435
|
+
/**
|
|
436
|
+
* Replace accents and typographic characters (curly quotes, long dashes…) with
|
|
437
|
+
* their ASCII equivalent before sending: the message switches to GSM-7, often
|
|
438
|
+
* half the cost. The transliteration depends on the server: do not rely on it
|
|
439
|
+
* for an exact text. Default false.
|
|
440
|
+
*/
|
|
441
|
+
stripAccents?: boolean;
|
|
442
|
+
/**
|
|
443
|
+
* Max 255 characters. Generated (UUID v4) when absent, and reused by the
|
|
444
|
+
* SDK's retries. Pass your own (e.g. "order-1042-ready") to also protect
|
|
445
|
+
* against your own retries for 24 hours.
|
|
446
|
+
*/
|
|
447
|
+
idempotencyKey?: string;
|
|
448
|
+
}
|
|
449
|
+
interface SendSmsParams extends SendCommon {
|
|
450
|
+
/** Phone number, international ("+243…", "243…", "00243…") or local ("0812…", "812…"). */
|
|
451
|
+
to: string;
|
|
452
|
+
}
|
|
453
|
+
interface SendManySmsParams extends SendCommon {
|
|
454
|
+
/** 1 to 1000 phone numbers. All or nothing: one refused number means nothing is sent. */
|
|
455
|
+
recipients: readonly string[];
|
|
456
|
+
}
|
|
457
|
+
interface EstimateSmsParams {
|
|
458
|
+
body: string;
|
|
459
|
+
/** Normalised and deduplicated like a send; invalid numbers are not counted (no error). */
|
|
460
|
+
recipients: string | readonly string[];
|
|
461
|
+
/** Estimate the text after `stripAccents` (see `sms.send()`). Default false. */
|
|
462
|
+
stripAccents?: boolean;
|
|
463
|
+
}
|
|
464
|
+
interface ListSmsParams {
|
|
465
|
+
status?: MessageStatus;
|
|
466
|
+
/** Any format, normalised before the search. */
|
|
467
|
+
recipient?: string;
|
|
468
|
+
/** Exact match. */
|
|
469
|
+
reference?: string;
|
|
470
|
+
/** Inclusive: a date ("2026-11-01") or a date-time. */
|
|
471
|
+
from?: Date | string;
|
|
472
|
+
/** Inclusive: a date covers the whole day. */
|
|
473
|
+
to?: Date | string;
|
|
474
|
+
/** Default 1. */
|
|
475
|
+
page?: number;
|
|
476
|
+
/** 1 to 100, default 15. */
|
|
477
|
+
perPage?: number;
|
|
478
|
+
}
|
|
479
|
+
/** Send and track SMS (`client.sms`). */
|
|
480
|
+
declare class Sms {
|
|
481
|
+
#private;
|
|
482
|
+
/**
|
|
483
|
+
* Sends one SMS. Asynchronous: the returned message is `queued`; follow its
|
|
484
|
+
* status with webhooks (`message.status_changed`) or `get()`.
|
|
485
|
+
*
|
|
486
|
+
* An Idempotency-Key is always sent, so network errors are retried safely:
|
|
487
|
+
* the SMS is never sent twice.
|
|
488
|
+
*/
|
|
489
|
+
send(params: SendSmsParams, options?: RequestOptions): Promise<Message>;
|
|
490
|
+
/**
|
|
491
|
+
* Sends the same SMS to several recipients (up to 1000), all or nothing: if
|
|
492
|
+
* a single recipient is refused, nothing is sent and a
|
|
493
|
+
* `RecipientNotSendableError` lists every refused number. Duplicates are
|
|
494
|
+
* merged by the API (one message per distinct number).
|
|
495
|
+
*/
|
|
496
|
+
sendMany(params: SendManySmsParams, options?: RequestOptions): Promise<MessageBatch>;
|
|
497
|
+
/** Computes encoding, segments and credits without sending anything. */
|
|
498
|
+
estimate(params: EstimateSmsParams, options?: RequestOptions): Promise<Estimate>;
|
|
499
|
+
/** One message of the account, by id. Throws `NotFoundError` when unknown. */
|
|
500
|
+
get(id: string, options?: RequestOptions): Promise<Message>;
|
|
501
|
+
/**
|
|
502
|
+
* Messages sent with this API key, newest first.
|
|
503
|
+
*
|
|
504
|
+
* const page = await client.sms.list({ status: 'failed' }); // one page
|
|
505
|
+
* for await (const message of client.sms.list()) { … } // every page
|
|
506
|
+
*/
|
|
507
|
+
list(params?: ListSmsParams, options?: RequestOptions): PagePromise<Message>;
|
|
508
|
+
toJSON(): Record<string, unknown>;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
declare const DEFAULT_BASE_URL = "https://api.labyrinthe-rdc.com/api/public/v1";
|
|
512
|
+
interface LabyrintheOptions {
|
|
513
|
+
/** `lsk_live_…` or `lsk_test_…`. Defaults to `process.env.LABYRINTHE_API_KEY`. Keep it server-side. */
|
|
514
|
+
apiKey?: string;
|
|
515
|
+
/**
|
|
516
|
+
* API root. Defaults to `process.env.LABYRINTHE_BASE_URL`, then production.
|
|
517
|
+
* A bare host ("http://localhost:8000") is completed with `/api/public/v1`.
|
|
518
|
+
*/
|
|
519
|
+
baseUrl?: string;
|
|
520
|
+
/** Timeout of each attempt, in milliseconds. Default 30000. */
|
|
521
|
+
timeoutMs?: number;
|
|
522
|
+
/** Automatic retries, only when they are safe (0 disables them). Default 2, max 10. */
|
|
523
|
+
maxRetries?: number;
|
|
524
|
+
/**
|
|
525
|
+
* Longest wait accepted before a retry (Retry-After), in milliseconds. Default 10000.
|
|
526
|
+
* A 503 FEATURE_DISABLED is retried only when its Retry-After fits in this wait.
|
|
527
|
+
*/
|
|
528
|
+
maxRetryWaitMs?: number;
|
|
529
|
+
/** Added to local numbers: "0812345678" → "243812345678". Default "243" (DR Congo). */
|
|
530
|
+
defaultCountryCode?: string;
|
|
531
|
+
/** Sender ID used when `sms.send()` gets none. */
|
|
532
|
+
defaultSender?: string;
|
|
533
|
+
/** Custom `fetch` implementation (defaults to the global `fetch`). */
|
|
534
|
+
fetch?: FetchLike;
|
|
535
|
+
/** Receives debug/info/warn/error logs: no key, no code, no body, masked phone numbers. */
|
|
536
|
+
logger?: Logger;
|
|
537
|
+
/**
|
|
538
|
+
* The API key gives full access to your account: it must never reach a
|
|
539
|
+
* browser. The client refuses to start in a browser unless this is true.
|
|
540
|
+
*/
|
|
541
|
+
dangerouslyAllowBrowser?: boolean;
|
|
542
|
+
}
|
|
543
|
+
declare const INSPECT: unique symbol;
|
|
544
|
+
declare const DENO_INSPECT: unique symbol;
|
|
545
|
+
/**
|
|
546
|
+
* Labyrinthe Connect API client.
|
|
547
|
+
*
|
|
548
|
+
* const client = new Labyrinthe({ apiKey: 'lsk_test_…' });
|
|
549
|
+
* const message = await client.sms.send({ to: '0812345678', body: 'Hello!' });
|
|
550
|
+
*
|
|
551
|
+
* Keys starting with `lsk_test_` use the sandbox: nothing is sent, no credit
|
|
552
|
+
* is used, responses have the same shape as in production.
|
|
553
|
+
*/
|
|
554
|
+
declare class Labyrinthe {
|
|
555
|
+
#private;
|
|
556
|
+
/** Send and track SMS. */
|
|
557
|
+
readonly sms: Sms;
|
|
558
|
+
/** Credit balance. */
|
|
559
|
+
readonly balance: BalanceResource;
|
|
560
|
+
/** Approved Sender IDs. */
|
|
561
|
+
readonly senderIds: SenderIds;
|
|
562
|
+
/** One-time passwords by SMS. */
|
|
563
|
+
readonly otp: Otp;
|
|
564
|
+
constructor(options?: LabyrintheOptions);
|
|
565
|
+
/** True with a sandbox key (`lsk_test_…`): nothing is sent, no credit is used. */
|
|
566
|
+
get isTestMode(): boolean;
|
|
567
|
+
/** True with a production key (`lsk_live_…`). */
|
|
568
|
+
get isLiveMode(): boolean;
|
|
569
|
+
/** The API root used by this client. */
|
|
570
|
+
get baseUrl(): string;
|
|
571
|
+
toJSON(): Record<string, unknown>;
|
|
572
|
+
[INSPECT](): Record<string, unknown>;
|
|
573
|
+
[DENO_INSPECT](): string;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/** SDK version, sent in the User-Agent header. Kept in sync with package.json (checked by the tests). */
|
|
577
|
+
declare const VERSION = "1.2.0";
|
|
578
|
+
|
|
579
|
+
/** Error codes documented by the API. The list is open: new codes may appear. */
|
|
580
|
+
type ApiErrorCode = 'UNAUTHENTICATED' | 'FORBIDDEN' | 'ACCOUNT_SUSPENDED' | 'NOT_FOUND' | 'VALIDATION_ERROR' | 'TOO_MANY_REQUESTS' | 'INSUFFICIENT_CREDITS' | 'IDEMPOTENCY_KEY_REUSED' | 'IDEMPOTENCY_KEY_UNRESOLVED' | 'IDEMPOTENCY_REQUEST_IN_PROGRESS' | 'RECIPIENT_NOT_SENDABLE' | 'SENDER_ID_NOT_APPROVED' | 'INVALID_PHONE' | 'INVALID_OTP_TEMPLATE' | 'OTP_RESEND_TOO_SOON' | 'OTP_RATE_LIMITED' | 'OTP_DISABLED_FOR_ACCOUNT' | 'OTP_SERVICE_DISABLED' | 'FEATURE_DISABLED' | 'HTTP_ERROR';
|
|
581
|
+
/** Codes of the errors raised by the SDK itself (no API response). */
|
|
582
|
+
type SdkErrorCode = 'CONNECTION_ERROR' | 'TIMEOUT' | 'REQUEST_ABORTED' | 'INVALID_ARGUMENT' | 'UNEXPECTED_RESPONSE' | 'SIGNATURE_VERIFICATION_FAILED' | 'INVALID_WEBHOOK_PAYLOAD';
|
|
583
|
+
/** Known codes, or any other string (the `string & {}` keeps autocompletion on the known ones). */
|
|
584
|
+
type ErrorCode = ApiErrorCode | SdkErrorCode | (string & {});
|
|
585
|
+
type ErrorDetails = Record<string, unknown>;
|
|
586
|
+
/**
|
|
587
|
+
* Base class of every error thrown by the SDK: catch it to handle any
|
|
588
|
+
* Labyrinthe Connect failure in one place.
|
|
589
|
+
*
|
|
590
|
+
* Always switch on `code` (stable), never on `message` (API messages are in
|
|
591
|
+
* French, for humans).
|
|
592
|
+
*/
|
|
593
|
+
declare class LabyrintheError extends Error {
|
|
594
|
+
/** Stable machine-readable code, e.g. "INSUFFICIENT_CREDITS" or "TIMEOUT". */
|
|
595
|
+
readonly code: ErrorCode;
|
|
596
|
+
/** HTTP status of the response; null when there was no response (network, timeout, local validation…). */
|
|
597
|
+
readonly status: number | null;
|
|
598
|
+
/** Error details, depending on the code (`{}` when there are none). */
|
|
599
|
+
readonly details: ErrorDetails;
|
|
600
|
+
constructor(message: string, code: ErrorCode, status?: number | null, details?: ErrorDetails);
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* The API answered with an error (`{ "error": { "code", "message", "details" } }`).
|
|
604
|
+
* Subclasses exist for the codes an integration usually handles; any other
|
|
605
|
+
* code (including codes added to the API later) is thrown as a plain ApiError.
|
|
606
|
+
*/
|
|
607
|
+
declare class ApiError extends LabyrintheError {
|
|
608
|
+
readonly status: number;
|
|
609
|
+
/** Response headers (lower-case names). */
|
|
610
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
611
|
+
constructor(message: string, code: ErrorCode, status: number, details?: ErrorDetails, headers?: Record<string, string>);
|
|
612
|
+
/**
|
|
613
|
+
* Request identifier (`X-Request-Id` response header): give it to the
|
|
614
|
+
* Labyrinthe support. Yours when you passed `requestId`, otherwise
|
|
615
|
+
* generated by the API; null only when the response had none.
|
|
616
|
+
*/
|
|
617
|
+
get requestId(): string | null;
|
|
618
|
+
/** Major API version that served the response (`X-Api-Version`, e.g. "1"); null when absent. */
|
|
619
|
+
get apiVersion(): string | null;
|
|
620
|
+
protected detailInt(key: string): number | null;
|
|
621
|
+
}
|
|
622
|
+
/** 401: missing, invalid or revoked API key. */
|
|
623
|
+
declare class AuthenticationError extends ApiError {
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* 403: IP not allowed for this key (`FORBIDDEN`), OTP disabled for the
|
|
627
|
+
* account (`OTP_DISABLED_FOR_ACCOUNT`) or account suspended
|
|
628
|
+
* (`ACCOUNT_SUSPENDED`, see `AccountSuspendedError`).
|
|
629
|
+
*/
|
|
630
|
+
declare class PermissionError extends ApiError {
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* 403 ACCOUNT_SUSPENDED: the account was suspended by the platform. The whole
|
|
634
|
+
* API is closed until it is reactivated: retrying does not help, contact the
|
|
635
|
+
* Labyrinthe support (with `requestId`).
|
|
636
|
+
*/
|
|
637
|
+
declare class AccountSuspendedError extends PermissionError {
|
|
638
|
+
}
|
|
639
|
+
/** 404: unknown message or `otpId` (or one from another account). */
|
|
640
|
+
declare class NotFoundError extends ApiError {
|
|
641
|
+
}
|
|
642
|
+
/** 422 VALIDATION_ERROR: one or more fields are missing or invalid. */
|
|
643
|
+
declare class ValidationError extends ApiError {
|
|
644
|
+
/** Error messages per field, e.g. `{ body: ["Le champ body est obligatoire."] }`. */
|
|
645
|
+
get fieldErrors(): Record<string, string[]>;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* 422 RECIPIENT_NOT_SENDABLE: at least one recipient is invalid, blacklisted
|
|
649
|
+
* or opted out. Sending is all-or-nothing: NOTHING was sent and no credit
|
|
650
|
+
* was used. Remove the `rejected` numbers and send again.
|
|
651
|
+
*/
|
|
652
|
+
declare class RecipientNotSendableError extends ApiError {
|
|
653
|
+
/** Reason of the first rejection. */
|
|
654
|
+
get reason(): RejectionReason;
|
|
655
|
+
/** Every rejected recipient (`sms.send` / `sms.sendMany`). Empty for OTP sends. */
|
|
656
|
+
get rejected(): RejectedRecipient[];
|
|
657
|
+
}
|
|
658
|
+
/** 422 SENDER_ID_NOT_APPROVED: `sender` is not an approved Sender ID of the account. */
|
|
659
|
+
declare class SenderIdNotApprovedError extends ApiError {
|
|
660
|
+
}
|
|
661
|
+
/** 422 INVALID_PHONE: the phone number cannot be used (OTP). */
|
|
662
|
+
declare class InvalidPhoneError extends ApiError {
|
|
663
|
+
}
|
|
664
|
+
/** 409 INSUFFICIENT_CREDITS: the balance does not cover the whole send. Nothing was sent. */
|
|
665
|
+
declare class InsufficientCreditsError extends ApiError {
|
|
666
|
+
/** Credits available on the account. */
|
|
667
|
+
get available(): number | null;
|
|
668
|
+
/** Credits needed for this send. */
|
|
669
|
+
get required(): number | null;
|
|
670
|
+
}
|
|
671
|
+
/** 409 IDEMPOTENCY_KEY_REUSED: the same Idempotency-Key was used for a different request. */
|
|
672
|
+
declare class IdempotencyKeyReusedError extends ApiError {
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* 409 IDEMPOTENCY_KEY_UNRESOLVED (POST /sms, POST /otp/send): the original
|
|
676
|
+
* request with this Idempotency-Key failed unexpectedly and its outcome is
|
|
677
|
+
* unknown: an SMS may have been sent. The SDK never retries it. Do not retry
|
|
678
|
+
* automatically: check first (`sms.list({ reference })`, or ask the user
|
|
679
|
+
* whether the code arrived), then send again with a NEW idempotency key.
|
|
680
|
+
*/
|
|
681
|
+
declare class IdempotencyKeyUnresolvedError extends ApiError {
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* 409 IDEMPOTENCY_REQUEST_IN_PROGRESS: the original request with this
|
|
685
|
+
* Idempotency-Key is still being processed. The SDK already retried it with
|
|
686
|
+
* the same key after `Retry-After`; when you get this error, wait
|
|
687
|
+
* `retryAfterMs` and call again with the same `idempotencyKey` (safe: nothing
|
|
688
|
+
* can be sent twice).
|
|
689
|
+
*/
|
|
690
|
+
declare class IdempotencyRequestInProgressError extends ApiError {
|
|
691
|
+
/** Milliseconds to wait (Retry-After header or `details.retry_after_seconds`); null when the API does not say. */
|
|
692
|
+
get retryAfterMs(): number | null;
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* 429: too many requests. The SDK already retried TOO_MANY_REQUESTS when it
|
|
696
|
+
* was safe to do so; when you get this error, wait `retryAfterMs`.
|
|
697
|
+
*/
|
|
698
|
+
declare class RateLimitError extends ApiError {
|
|
699
|
+
/**
|
|
700
|
+
* Milliseconds to wait before retrying, from the Retry-After header or
|
|
701
|
+
* `details.retry_after_seconds`; null when the API does not say.
|
|
702
|
+
*/
|
|
703
|
+
get retryAfterMs(): number | null;
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* 429 OTP_RESEND_TOO_SOON: a code was sent to this number too recently. Never
|
|
707
|
+
* retried; show a countdown of `retryAfterMs` (Retry-After header, equal to
|
|
708
|
+
* `details.retry_after_seconds`).
|
|
709
|
+
*/
|
|
710
|
+
declare class OtpResendTooSoonError extends RateLimitError {
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* 429 OTP_RATE_LIMITED: hourly cap reached for this phone number or for the
|
|
714
|
+
* whole account. Never retried; `retryAfterMs` is the time before the sliding
|
|
715
|
+
* one-hour window falls back under the cap.
|
|
716
|
+
*/
|
|
717
|
+
declare class OtpRateLimitedError extends RateLimitError {
|
|
718
|
+
get scope(): OtpRateLimitScope;
|
|
719
|
+
/** The hourly cap that was reached. */
|
|
720
|
+
get limit(): number | null;
|
|
721
|
+
}
|
|
722
|
+
/** 5xx, including 503 FEATURE_DISABLED and OTP_SERVICE_DISABLED (see `FeatureDisabledError`). */
|
|
723
|
+
declare class ServiceUnavailableError extends ApiError {
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* 503 FEATURE_DISABLED: a platform administrator temporarily shut down the
|
|
727
|
+
* whole API (`blockedBy` = "api") or one function ("api.sms_send",
|
|
728
|
+
* "api.otp_send", "api.otp_verify"). Also 503 OTP_SERVICE_DISABLED (whole OTP
|
|
729
|
+
* service off, `feature` = `blockedBy` = "otp"). Nothing was done: no SMS
|
|
730
|
+
* sent, no credit used.
|
|
731
|
+
*
|
|
732
|
+
* Never retried with blind backoff: the SDK retries only when the API gives a
|
|
733
|
+
* `Retry-After` no longer than `maxRetryWaitMs` (automatic re-enable
|
|
734
|
+
* scheduled), waiting exactly that long. Otherwise, treat the call as a
|
|
735
|
+
* temporary failure (queue it, tell the user, show `adminMessage`).
|
|
736
|
+
*/
|
|
737
|
+
declare class FeatureDisabledError extends ServiceUnavailableError {
|
|
738
|
+
/** Feature requested by the call ("api", "api.sms_send", "api.otp_send", "api.otp_verify", "otp"… open list). */
|
|
739
|
+
get feature(): string | null;
|
|
740
|
+
/** Feature actually shut down: `feature` itself or a parent (e.g. "api" when the whole API is off). */
|
|
741
|
+
get blockedBy(): string | null;
|
|
742
|
+
/** Message written by the administrator (`details.message`), null when there is none. */
|
|
743
|
+
get adminMessage(): string | null;
|
|
744
|
+
/** Scheduled automatic re-enable (`details.until`), null when unknown or invalid. */
|
|
745
|
+
get until(): Date | null;
|
|
746
|
+
/** Seconds until the scheduled re-enable (`Retry-After` header), null when the API does not say. */
|
|
747
|
+
get retryAfter(): number | null;
|
|
748
|
+
/** Same as `retryAfter`, in milliseconds (`Retry-After` header only), null when absent. */
|
|
749
|
+
get retryAfterMs(): number | null;
|
|
750
|
+
private detailString;
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* The API could not be reached (DNS, TLS, connection refused…).
|
|
754
|
+
*
|
|
755
|
+
* `sms.send()`, `sms.sendMany()` and `otp.send()` carry an Idempotency-Key:
|
|
756
|
+
* they were already retried, and calling again with the same `idempotencyKey`
|
|
757
|
+
* is safe. For `otp.verify()` the request may or may not have been processed
|
|
758
|
+
* (an attempt may have been consumed): the SDK never retries it after a
|
|
759
|
+
* network error.
|
|
760
|
+
*/
|
|
761
|
+
declare class ConnectionError extends LabyrintheError {
|
|
762
|
+
constructor(message: string, code?: ErrorCode);
|
|
763
|
+
}
|
|
764
|
+
/** An attempt took longer than `timeoutMs`. */
|
|
765
|
+
declare class TimeoutError extends ConnectionError {
|
|
766
|
+
constructor(message: string);
|
|
767
|
+
}
|
|
768
|
+
/** The call was cancelled through its `signal`. Never retried. */
|
|
769
|
+
declare class RequestAbortedError extends LabyrintheError {
|
|
770
|
+
constructor(message?: string);
|
|
771
|
+
}
|
|
772
|
+
/** Invalid input detected by the SDK, before any network call. */
|
|
773
|
+
declare class InvalidArgumentError extends LabyrintheError {
|
|
774
|
+
constructor(message: string);
|
|
775
|
+
}
|
|
776
|
+
/** A successful response that the SDK cannot read (not JSON, missing field…). */
|
|
777
|
+
declare class UnexpectedResponseError extends LabyrintheError {
|
|
778
|
+
constructor(message: string, status?: number | null);
|
|
779
|
+
}
|
|
780
|
+
/** A webhook failed verification: missing or wrong signature, or a timestamp outside the tolerance. Answer 400. */
|
|
781
|
+
declare class SignatureVerificationError extends LabyrintheError {
|
|
782
|
+
constructor(message: string);
|
|
783
|
+
}
|
|
784
|
+
/** A correctly signed webhook body is not a valid event envelope. Answer 400. */
|
|
785
|
+
declare class InvalidWebhookPayloadError extends LabyrintheError {
|
|
786
|
+
constructor(message: string);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/** Default country code added to local numbers (DR Congo). */
|
|
790
|
+
declare const DEFAULT_COUNTRY_CODE = "243";
|
|
791
|
+
/**
|
|
792
|
+
* Phone number helpers, consistent with the API's own normalisation.
|
|
793
|
+
*
|
|
794
|
+
* `normalize()` returns international digits without "+" (e.g. "243812345678"), the format of API
|
|
795
|
+
* responses. Requests carry the same number in E.164 with "+" ("+243812345678"), so the API keeps the
|
|
796
|
+
* country code of a short foreign number.
|
|
797
|
+
* Accepted input formats (with the default country code 243, DR Congo):
|
|
798
|
+
*
|
|
799
|
+
* - "+243 81 234 5678", "00243812345678" → "243812345678" (explicit international prefix kept)
|
|
800
|
+
* - "243812345678" → "243812345678"
|
|
801
|
+
* - "0812345678", "812345678" → "243812345678" (local format, default country code added)
|
|
802
|
+
* - "+33 6 12 34 56 78" → "33612345678" (other countries are never blocked)
|
|
803
|
+
* - "33612345678" → "33612345678" (no "+", 11 digits or more not starting
|
|
804
|
+
* with 0: already international)
|
|
805
|
+
*
|
|
806
|
+
* Idempotent, like the API: normalising an output again does not change it.
|
|
807
|
+
* For a short foreign number (10 digits or less), always use the "+".
|
|
808
|
+
*
|
|
809
|
+
* Validation is deliberately light (8 to 15 digits, E.164): the API remains
|
|
810
|
+
* the authority on whether a number can receive SMS.
|
|
811
|
+
*/
|
|
812
|
+
declare const PhoneNumber: Readonly<{
|
|
813
|
+
/** Normalises a number, or returns null when it cannot be a valid phone number. */
|
|
814
|
+
normalize(raw: string, defaultCountryCode?: string): string | null;
|
|
815
|
+
/** Normalises a number or throws an InvalidArgumentError (the number itself is never part of the message). */
|
|
816
|
+
normalizeOrThrow(raw: string, defaultCountryCode?: string, field?: string): string;
|
|
817
|
+
isValid(raw: string, defaultCountryCode?: string): boolean;
|
|
818
|
+
/** Masks a number for logs: "243812345678" → "24381*****78". */
|
|
819
|
+
mask(phone: string): string;
|
|
820
|
+
}>;
|
|
821
|
+
|
|
822
|
+
/** Name of the signature header. */
|
|
823
|
+
declare const SIGNATURE_HEADER = "X-Signature";
|
|
824
|
+
/** Default maximum age of a delivery, in seconds. */
|
|
825
|
+
declare const DEFAULT_TOLERANCE_SECONDS = 300;
|
|
826
|
+
/** Fields shared by every event. */
|
|
827
|
+
interface EventEnvelope {
|
|
828
|
+
/** Event identifier, identical across delivery attempts: deduplicate on it. */
|
|
829
|
+
id: string;
|
|
830
|
+
/** Event name exactly as sent by the API, e.g. "message.status_changed" (also for unknown events). */
|
|
831
|
+
type: string;
|
|
832
|
+
/** Time of this delivery attempt (signed). */
|
|
833
|
+
timestamp: Date;
|
|
834
|
+
/** Decoded envelope `{ id, event, data, timestamp }`, as sent by the API. */
|
|
835
|
+
raw: Json;
|
|
836
|
+
}
|
|
837
|
+
interface MessageStatusChangedEvent extends EventEnvelope {
|
|
838
|
+
event: 'message.status_changed';
|
|
839
|
+
data: {
|
|
840
|
+
messageId: string;
|
|
841
|
+
recipient: string;
|
|
842
|
+
status: MessageStatus;
|
|
843
|
+
providerMessageId: string | null;
|
|
844
|
+
campaignId: string | null;
|
|
845
|
+
/** The `reference` given at send time (`sms.send({ reference })`). */
|
|
846
|
+
reference: string | null;
|
|
847
|
+
/** Provider or platform error code when the message failed. */
|
|
848
|
+
errorCode: string | null;
|
|
849
|
+
errorMessage: string | null;
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
interface CampaignCompletedEvent extends EventEnvelope {
|
|
853
|
+
event: 'campaign.completed';
|
|
854
|
+
data: {
|
|
855
|
+
campaignId: string;
|
|
856
|
+
name: string;
|
|
857
|
+
recipientsCount: number;
|
|
858
|
+
/** Number of messages per final status, e.g. `{ delivered: 1150, failed: 30 }`. */
|
|
859
|
+
messagesByStatus: Partial<Record<MessageStatus, number>> & Record<string, number>;
|
|
860
|
+
creditsCharged: number;
|
|
861
|
+
completedAt: Date | null;
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
interface BalanceLowEvent extends EventEnvelope {
|
|
865
|
+
event: 'balance.low';
|
|
866
|
+
data: {
|
|
867
|
+
/** Balance after the operation that crossed the threshold. */
|
|
868
|
+
balance: number;
|
|
869
|
+
threshold: number;
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
/** Outcome of an OTP verification (`otp.verified`, `otp.failed`). Never contains the code. */
|
|
873
|
+
interface OtpWebhookData {
|
|
874
|
+
otpId: string;
|
|
875
|
+
/** Normalised number (international, no "+"). */
|
|
876
|
+
phone: string;
|
|
877
|
+
/** `verified` with `otp.verified`; `failed` or `expired` with `otp.failed`. */
|
|
878
|
+
status: OtpVerificationStatus;
|
|
879
|
+
/** Null when verified; `max_attempts_reached` (status `failed`) or `expired` (status `expired`). */
|
|
880
|
+
reason: 'max_attempts_reached' | 'expired' | 'unknown' | null;
|
|
881
|
+
/** Verification date; null for `otp.failed`. */
|
|
882
|
+
verifiedAt: Date | null;
|
|
883
|
+
}
|
|
884
|
+
/** A code was verified (sent once, after `otp.verify()` got the right code — API or client area console). */
|
|
885
|
+
interface OtpVerifiedEvent extends EventEnvelope {
|
|
886
|
+
event: 'otp.verified';
|
|
887
|
+
data: OtpWebhookData;
|
|
888
|
+
}
|
|
889
|
+
/** A verification failed for good: attempts exhausted or code expired (sent once). */
|
|
890
|
+
interface OtpFailedEvent extends EventEnvelope {
|
|
891
|
+
event: 'otp.failed';
|
|
892
|
+
data: OtpWebhookData;
|
|
893
|
+
}
|
|
894
|
+
interface TestPingEvent extends EventEnvelope {
|
|
895
|
+
event: 'test.ping';
|
|
896
|
+
data: Json;
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* An event type added to the API after this SDK version. Its signature was
|
|
900
|
+
* verified: `type` holds its name and `data` its content (as sent, snake_case).
|
|
901
|
+
* Acknowledge it with a 2xx. (`event` is the constant "unknown" so that
|
|
902
|
+
* `switch (event.event)` narrows the known events.)
|
|
903
|
+
*/
|
|
904
|
+
interface UnknownEvent extends EventEnvelope {
|
|
905
|
+
event: 'unknown';
|
|
906
|
+
data: Json;
|
|
907
|
+
}
|
|
908
|
+
/** A verified webhook event: switch on `event` to get the typed `data`. */
|
|
909
|
+
type WebhookEvent = MessageStatusChangedEvent | CampaignCompletedEvent | BalanceLowEvent | OtpVerifiedEvent | OtpFailedEvent | TestPingEvent | UnknownEvent;
|
|
910
|
+
interface ConstructEventOptions {
|
|
911
|
+
/** Max age of the signed timestamp, in seconds; 0 disables the check (not recommended). Default 300. */
|
|
912
|
+
toleranceSeconds?: number;
|
|
913
|
+
/** Current time (tests). */
|
|
914
|
+
now?: Date | number;
|
|
915
|
+
}
|
|
916
|
+
/** Raw request body: the exact bytes, or the text decoded as UTF-8. Never a re-encoded object. */
|
|
917
|
+
type RawBody = string | Uint8Array | ArrayBuffer;
|
|
918
|
+
/**
|
|
919
|
+
* Webhook verification, with Web Crypto (Node, edge runtimes, Deno, Bun).
|
|
920
|
+
*
|
|
921
|
+
* const event = await Webhooks.constructEvent(rawBody, request.headers.get('x-signature'), secret);
|
|
922
|
+
*
|
|
923
|
+
* The signature (`X-Signature: sha256=<hex HMAC-SHA256 of the raw body>`) is
|
|
924
|
+
* checked in constant time on the RAW body, before any JSON parsing; then the
|
|
925
|
+
* signed `timestamp` must be within the tolerance (replay protection).
|
|
926
|
+
*/
|
|
927
|
+
declare const Webhooks: Readonly<{
|
|
928
|
+
SIGNATURE_HEADER: "X-Signature";
|
|
929
|
+
DEFAULT_TOLERANCE_SECONDS: 300;
|
|
930
|
+
/**
|
|
931
|
+
* Verifies a delivery and returns the typed event.
|
|
932
|
+
*
|
|
933
|
+
* @throws SignatureVerificationError wrong/missing signature or timestamp out of tolerance: answer 400
|
|
934
|
+
* @throws InvalidWebhookPayloadError signed body that is not a valid envelope: answer 400
|
|
935
|
+
*/
|
|
936
|
+
constructEvent(rawBody: RawBody, signatureHeader: string | null | undefined, secret: string, options?: ConstructEventOptions): Promise<WebhookEvent>;
|
|
937
|
+
/** Constant-time check of the X-Signature header against the raw body. */
|
|
938
|
+
verifySignature(rawBody: RawBody, signatureHeader: string | null | undefined, secret: string): Promise<boolean>;
|
|
939
|
+
/** The X-Signature header value for a body: "sha256=<hex>". Useful to test your webhook endpoint. */
|
|
940
|
+
sign(rawBody: RawBody, secret: string): Promise<string>;
|
|
941
|
+
}>;
|
|
942
|
+
|
|
943
|
+
export { AccountSuspendedError, ApiError, type ApiErrorCode, AuthenticationError, type Balance, type BalanceLowEvent, BalanceResource, type CampaignCompletedEvent, ConnectionError, type ConstructEventOptions, DEFAULT_BASE_URL, DEFAULT_COUNTRY_CODE, DEFAULT_TOLERANCE_SECONDS, Encoding, type ErrorCode, type ErrorDetails, type Estimate, type EstimateSmsParams, FeatureDisabledError, type FetchInit, type FetchLike, type FetchResponse, IdempotencyKeyReusedError, IdempotencyKeyUnresolvedError, IdempotencyRequestInProgressError, InsufficientCreditsError, InvalidArgumentError, InvalidPhoneError, InvalidWebhookPayloadError, Labyrinthe, LabyrintheError, type LabyrintheOptions, type ListSmsParams, type Logger, type Message, type MessageBatch, MessageStatus, type MessageStatusChangedEvent, NotFoundError, Otp, OtpCodeType, type OtpFailedEvent, OtpFailureReason, type OtpLocale, OtpRateLimitScope, OtpRateLimitedError, OtpResendTooSoonError, OtpSendResult, OtpVerificationStatus, type OtpVerifiedEvent, type OtpVerifyResult, type OtpWebhookData, Page, PagePromise, PermissionError, PhoneNumber, RateLimitError, type RawBody, RecipientNotSendableError, type RejectedRecipient, RejectionReason, RequestAbortedError, type RequestOptions, type ResponseInfo, SIGNATURE_HEADER, type SdkErrorCode, type SendManySmsParams, type SendOtpParams, type SendSmsParams, type SenderId, SenderIdNotApprovedError, SenderIdStatus, SenderIds, ServiceUnavailableError, SignatureVerificationError, Sms, type TestPingEvent, TimeoutError, UnexpectedResponseError, type UnknownEvent, VERSION, ValidationError, type VerifyOtpParams, type WebhookEvent, WebhookEventType, Webhooks, isFailedStatus, isFinalStatus, isPendingStatus };
|