@ixblix/sdk-js 0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ixblix Tecnologia Ltda
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,201 @@
1
+ # ixblix SDK
2
+
3
+ Official TypeScript SDK for integrating desk/CRM systems with **ixblix**.
4
+ ixblix lets customer-service platforms
5
+ transfer conversations from original channels (WhatsApp, Instagram, etc.) to a
6
+ white-labeled web/mobile experience — with **end-to-end encryption** so the ixblix
7
+ backend never sees message content.
8
+
9
+ This SDK is the **operator (desk/CRM) side** of the integration. It provides:
10
+
11
+ - A typed HTTP client for the ixblix REST API (conversations, messages, media,
12
+ keys, company onboarding, credits, webhook registration).
13
+ - End-to-end encryption helpers (RSA-OAEP + AES-256-GCM) so you can encrypt
14
+ messages to your customers and decrypt theirs.
15
+ - Webhook parsing/verification helpers (HMAC-SHA256) so you can receive
16
+ incoming messages and conversation events in real time without polling.
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @ixblix/sdk-js
22
+ ```
23
+
24
+ Requires Node.js >= 18.
25
+
26
+ ## Quick start
27
+
28
+ ```ts
29
+ import {
30
+ IxblixClient,
31
+ generateOperatorKeyPair,
32
+ loadOrCreateOperatorKey,
33
+ encryptToRecipient,
34
+ decryptEnvelope,
35
+ } from "@ixblix/sdk-js";
36
+
37
+ // 1. Configure the client with your company API key.
38
+ const ixblix = new IxblixClient({
39
+ baseUrl: "https://api.ixblix.app",
40
+ apiKey: process.env.IXBLIX_API_KEY,
41
+ });
42
+
43
+ // 2. Hold a persistent RSA keypair. The private key never leaves your side.
44
+ // Persist it once (e.g. loadOrCreateOperatorKey("./.keys")) and reuse it.
45
+ const operatorKey = generateOperatorKeyPair();
46
+
47
+ // 3. Create an overflow conversation for a contact.
48
+ const { conversation, deeplink } = await ixblix.createConversation({
49
+ contact: {
50
+ externalId: "whatsapp_5511999999999",
51
+ name: "John Doe",
52
+ phone: "+55 11 99999-9999",
53
+ },
54
+ channel: "whatsapp",
55
+ operatorPublicKey: operatorKey.publicKeySpki,
56
+ });
57
+
58
+ // 4. Share `deeplink` with the contact. When they open it, their browser
59
+ // generates a keypair and registers its public key with ixblix.
60
+
61
+ // 5. Wait for the customer to join. Prefer a webhook (`CUSTOMER_JOINED`) so
62
+ // you don't poll. If you must poll, use getConversationKeys:
63
+ let keys = await ixblix.getConversationKeys(conversation.id);
64
+ while (keys.keyStatus !== "ACTIVE" || !keys.customerPublicKey) {
65
+ await new Promise((r) => setTimeout(r, 2000));
66
+ keys = await ixblix.getConversationKeys(conversation.id);
67
+ }
68
+
69
+ // 6. Encrypt and send a message to the customer.
70
+ const envelope = encryptToRecipient(
71
+ "Hello! How can we help?",
72
+ keys.customerPublicKey,
73
+ operatorKey.keyId,
74
+ operatorKey.publicKeySpki,
75
+ );
76
+ await ixblix.sendCompanyMessage(conversation.id, envelope);
77
+
78
+ // 7. Read incoming messages (decrypt with your private key). Prefer receiving
79
+ // a `MESSAGE_RECEIVED` webhook and fetching the message, rather than
80
+ // polling listMessages.
81
+ const messages = await ixblix.listMessages(conversation.id);
82
+ for (const message of messages) {
83
+ if (!message.contentEncrypted) continue;
84
+ const plaintext = decryptEnvelope(message, operatorKey.privateKey);
85
+ console.log(plaintext);
86
+ }
87
+ ```
88
+
89
+ ## Company onboarding
90
+
91
+ Register a company (keyless), then activate it after payment to obtain the API
92
+ key:
93
+
94
+ ```ts
95
+ const { company, payment } = await ixblix.registerCompany({
96
+ name: "Acme CRM",
97
+ slug: "acme-crm",
98
+ paymentProvider: "dummy",
99
+ });
100
+
101
+ // After the payment is confirmed:
102
+ const { apiKey } = await ixblix.activateCompany(
103
+ company.id,
104
+ payment.transactionId,
105
+ );
106
+ ```
107
+
108
+ ## End-to-end encryption
109
+
110
+ ixblix uses a hybrid scheme:
111
+
112
+ - **RSA-OAEP** (2048-bit, SHA-256) to wrap a fresh per-message **AES-256-GCM**
113
+ key.
114
+ - Each message's AES key is wrapped **twice**: to the recipient
115
+ (`encryptedKey`) and to the sender's own public key (`selfEncryptedKey`), so
116
+ each side can read back its own sent messages.
117
+
118
+ The SDK exposes:
119
+
120
+ | Function | Purpose |
121
+ | ---------------------------------------------------------------------- | ------------------------------------------- |
122
+ | `generateOperatorKeyPair()` | Generate a fresh RSA keypair. |
123
+ | `loadOrCreateOperatorKey(dir)` | Load-or-create a persisted keypair on disk. |
124
+ | `encryptToRecipient(text, recipientPub, senderKeyId, senderPub)` | Encrypt an outgoing text message. |
125
+ | `decryptEnvelope(message, privateKey)` | Decrypt an incoming (or your own) message. |
126
+ | `encryptMediaToRecipient(bytes, recipientPub, senderKeyId, senderPub)` | Encrypt an outgoing media file. |
127
+ | `decryptMediaEnvelope(media, privateKey)` | Decrypt a media file. |
128
+
129
+ > **Security:** never send your private key to ixblix. Only the public key
130
+ > (`publicKeySpki`) is registered. Store the private key securely (the
131
+ > `loadOrCreateOperatorKey` helper writes it with mode `0600`).
132
+
133
+ ## Webhooks
134
+
135
+ ixblix pushes events to your webhook URL so you can receive incoming messages and
136
+ conversation events in real time **without polling**. Configure your endpoint
137
+ and verify incoming payloads:
138
+
139
+ ```ts
140
+ import { IxblixClient, verifyWebhook, WEBHOOK_SIGNATURE_HEADER } from "@ixblix/sdk-js";
141
+
142
+ // 1. Register your webhook URL. The returned secret is shown only once.
143
+ const ixblix = new IxblixClient({
144
+ baseUrl: "https://api.ixblix.app",
145
+ apiKey: process.env.IXBLIX_API_KEY,
146
+ });
147
+ const { webhookSecret } = await ixblix.updateWebhook({
148
+ webhookUrl: "https://desk.example.com/ixblix/webhook",
149
+ });
150
+ // Store webhookSecret securely (e.g. process.env.IXBLIX_WEBHOOK_SECRET).
151
+
152
+ // 2. In your Express webhook handler, verify the HMAC signature:
153
+ app.post("/ixblix/webhook", (req, res) => {
154
+ const signature = req.header(WEBHOOK_SIGNATURE_HEADER);
155
+ const { event } = verifyWebhook(
156
+ req.body,
157
+ process.env.IXBLIX_WEBHOOK_SECRET,
158
+ signature,
159
+ );
160
+
161
+ if (event.event === "MESSAGE_RECEIVED") {
162
+ // A contact sent a message. Fetch and decrypt it:
163
+ // const messages = await ixblix.listMessages(event.conversationId);
164
+ // const message = messages.find((m) => m.id === event.messageId);
165
+ // const plaintext = decryptEnvelope(message, operatorKey.privateKey);
166
+ } else if (event.event === "CUSTOMER_JOINED") {
167
+ // The customer registered its public key; you can now encrypt to them.
168
+ }
169
+ res.status(200).end();
170
+ });
171
+ ```
172
+
173
+ Webhook payloads are signed with HMAC-SHA256 using your webhook secret. Each
174
+ delivery also carries `X-Ixblix-Event-Id` (deduplicate on it — delivery is
175
+ at-least-once) and `X-Ixblix-Event`. See
176
+ [`docs/integrator/05-webhooks.md`](../docs/integrator/05-webhooks.md) for the
177
+ full event reference.
178
+
179
+ ## Read receipts and presence
180
+
181
+ When your agents have displayed a customer-sent message, mark it as read so the
182
+ customer's chat app shows a read receipt:
183
+
184
+ ```ts
185
+ await ixblix.markMessageReadByCompany(conversationId, messageId);
186
+ ```
187
+
188
+ When the customer reads one of your messages, ixblix pushes a `MESSAGE_READ`
189
+ webhook to your endpoint (see the webhook handler above).
190
+
191
+ Report operator typing/recording so the customer's chat app can show an
192
+ indicator, or send `stopped` to clear it immediately:
193
+
194
+ ```ts
195
+ await ixblix.reportCompanyPresence(conversationId, "typing"); // or "recording"
196
+ await ixblix.reportCompanyPresence(conversationId, "stopped");
197
+ ```
198
+
199
+ ## License
200
+
201
+ MIT
@@ -0,0 +1,132 @@
1
+ import type { ActivateCompanyResult, CompanyBalance, ContactInput, ConversationKeys, CreateConversationResult, CreditPurchase, Media, Message, MessageEnvelope, OriginalChannelMessageInput, PaymentProvidersResult, Plan, PurchaseCreditsResult, RegisterCompanyInput, RegisterCompanyResult } from "./types.js";
2
+ /** Options for constructing an {@link IxblixClient}. */
3
+ export interface IxblixClientOptions {
4
+ /** Base URL of the ixblix API, e.g. `https://api.ixblix.app`. */
5
+ baseUrl: string;
6
+ /** Company API key sent in the `X-API-Key` header. */
7
+ apiKey?: string;
8
+ /** Optional custom fetch implementation (e.g. for testing or proxies). */
9
+ fetch?: typeof fetch;
10
+ }
11
+ /** A media file to upload, with its plaintext bytes and metadata. */
12
+ export interface MediaUpload {
13
+ /** Plaintext file bytes. The SDK encrypts them before upload. */
14
+ data: Uint8Array;
15
+ fileName: string;
16
+ mimeType: string;
17
+ }
18
+ /** A media file downloaded from ixblix, still encrypted. */
19
+ export interface MediaDownload {
20
+ /** Encrypted (ciphertext) bytes as stored by ixblix. */
21
+ data: Uint8Array;
22
+ media: Media;
23
+ }
24
+ /**
25
+ * Client for the ixblix API.
26
+ *
27
+ * ```ts
28
+ * const ixblix = new IxblixClient({
29
+ * baseUrl: "https://api.ixblix.app",
30
+ * apiKey: process.env.IXBLIX_API_KEY,
31
+ * });
32
+ * ```
33
+ */
34
+ export declare class IxblixClient {
35
+ private readonly baseUrl;
36
+ private readonly apiKey?;
37
+ private readonly fetchImpl;
38
+ constructor(options: IxblixClientOptions);
39
+ private buildHeaders;
40
+ private request;
41
+ /**
42
+ * Register a company (keyless). Returns payment instructions; call
43
+ * {@link activateCompany} once the payment is confirmed.
44
+ */
45
+ registerCompany(input: RegisterCompanyInput): Promise<RegisterCompanyResult>;
46
+ /**
47
+ * Activate a company after payment confirmation and obtain its API key.
48
+ */
49
+ activateCompany(companyId: string, transactionId: string): Promise<ActivateCompanyResult>;
50
+ /** List available subscription plans. */
51
+ listPlans(): Promise<Plan[]>;
52
+ /** List available payment providers. */
53
+ listPaymentProviders(): Promise<PaymentProvidersResult>;
54
+ /** Fetch the authenticated company's balance and plan state. */
55
+ getBalance(): Promise<CompanyBalance>;
56
+ /** Purchase prepaid credits for the authenticated company. */
57
+ purchaseCredits(input: {
58
+ amountCents: number;
59
+ currency?: string;
60
+ provider: string;
61
+ metadata?: Record<string, unknown>;
62
+ }): Promise<PurchaseCreditsResult>;
63
+ /** List the authenticated company's credit purchases. */
64
+ listCreditPurchases(): Promise<CreditPurchase[]>;
65
+ /**
66
+ * Set or update the webhook endpoint where ixblix delivers events (incoming
67
+ * messages, customer joined, conversation closed, presence metadata). When
68
+ * the URL changes (or `rotateSecret` is true) a fresh HMAC secret is
69
+ * generated and returned once — store it to verify webhook signatures.
70
+ */
71
+ updateWebhook(input: {
72
+ webhookUrl?: string | null;
73
+ rotateSecret?: boolean;
74
+ }): Promise<{
75
+ webhookUrl: string | null;
76
+ webhookSecret: string | null;
77
+ }>;
78
+ /**
79
+ * Create an overflow conversation for a contact. Supply the operator's RSA
80
+ * public key (base64 SPKI DER) so the customer can encrypt messages to it.
81
+ */
82
+ createConversation(input: {
83
+ contact: ContactInput;
84
+ channel: string;
85
+ operatorPublicKey: string;
86
+ }): Promise<CreateConversationResult>;
87
+ /**
88
+ * Fetch the current E2EE key state of a conversation so the operator can
89
+ * detect when the customer has joined (`keyStatus === "ACTIVE"`).
90
+ */
91
+ getConversationKeys(conversationId: string): Promise<ConversationKeys>;
92
+ /**
93
+ * Send an encrypted message from the company to the contact. The message must
94
+ * already be encrypted to the customer's public key (see the crypto helpers).
95
+ */
96
+ sendCompanyMessage(conversationId: string, envelope: MessageEnvelope, contentType?: string): Promise<Message>;
97
+ /** List all messages of a conversation (content is always ciphertext). */
98
+ listMessages(conversationId: string): Promise<Message[]>;
99
+ /**
100
+ * Mark a contact-sent message as read by the operator. Emits a
101
+ * `message_read` Socket.io event to the customer's chat app so the customer
102
+ * sees the read receipt.
103
+ */
104
+ markMessageReadByCompany(conversationId: string, messageId: string): Promise<{
105
+ messageId: string;
106
+ readAt: string;
107
+ }>;
108
+ /**
109
+ * Relay an operator presence event (typing, stopped typing, or recording)
110
+ * to the customer's chat app via Socket.io.
111
+ */
112
+ reportCompanyPresence(conversationId: string, type: "typing" | "stopped" | "recording"): Promise<{
113
+ status: string;
114
+ }>;
115
+ /**
116
+ * Relay a message received on the original channel (WhatsApp, Instagram,
117
+ * etc.) into ixblix. The message must be encrypted to the operator's public key.
118
+ */
119
+ relayOriginalChannelMessage(input: OriginalChannelMessageInput): Promise<Message>;
120
+ /**
121
+ * Upload an encrypted media file from the company. `file.data` must be the
122
+ * ciphertext bytes produced by the media encryption helper.
123
+ */
124
+ sendCompanyMedia(conversationId: string, file: MediaUpload, envelope: MessageEnvelope): Promise<Message>;
125
+ /** Fetch the metadata (envelope + file info) of a media file. */
126
+ getMedia(mediaId: string): Promise<Media>;
127
+ /**
128
+ * Download the raw (encrypted) bytes of a media file as the operator, along
129
+ * with its metadata so it can be decrypted.
130
+ */
131
+ downloadCompanyMedia(mediaId: string): Promise<MediaDownload>;
132
+ }
package/dist/client.js ADDED
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Typed HTTP client for the ixblix public REST API.
3
+ *
4
+ * All methods that operate on behalf of a company authenticate with the
5
+ * `X-API-Key` header. Company onboarding (register/activate) and public
6
+ * endpoints (plans, payment providers) do not require an API key.
7
+ */
8
+ import { IxblixError } from "./errors.js";
9
+ /**
10
+ * Client for the ixblix API.
11
+ *
12
+ * ```ts
13
+ * const ixblix = new IxblixClient({
14
+ * baseUrl: "https://api.ixblix.app",
15
+ * apiKey: process.env.IXBLIX_API_KEY,
16
+ * });
17
+ * ```
18
+ */
19
+ export class IxblixClient {
20
+ baseUrl;
21
+ apiKey;
22
+ fetchImpl;
23
+ constructor(options) {
24
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
25
+ this.apiKey = options.apiKey;
26
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
27
+ }
28
+ buildHeaders(init) {
29
+ const headers = {};
30
+ const source = new Headers(init);
31
+ source.forEach((value, key) => {
32
+ headers[key] = value;
33
+ });
34
+ if (!headers["Content-Type"]) {
35
+ headers["Content-Type"] = "application/json";
36
+ }
37
+ if (this.apiKey) {
38
+ headers["X-API-Key"] = this.apiKey;
39
+ }
40
+ return headers;
41
+ }
42
+ async request(path, init = {}) {
43
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
44
+ ...init,
45
+ headers: this.buildHeaders(init.headers),
46
+ });
47
+ const contentType = response.headers.get("content-type") ?? "";
48
+ const isJson = contentType.includes("application/json");
49
+ const data = isJson ? (await response.json()) : null;
50
+ if (!response.ok) {
51
+ const body = data;
52
+ throw new IxblixError(body?.error ?? `ixblix ${path} failed with status ${response.status}`, {
53
+ status: response.status,
54
+ code: body?.code,
55
+ details: body?.errors,
56
+ });
57
+ }
58
+ return data;
59
+ }
60
+ // ---------------------------------------------------------------------------
61
+ // Company onboarding
62
+ // ---------------------------------------------------------------------------
63
+ /**
64
+ * Register a company (keyless). Returns payment instructions; call
65
+ * {@link activateCompany} once the payment is confirmed.
66
+ */
67
+ registerCompany(input) {
68
+ return this.request("/api/companies/register", {
69
+ method: "POST",
70
+ body: JSON.stringify(input),
71
+ });
72
+ }
73
+ /**
74
+ * Activate a company after payment confirmation and obtain its API key.
75
+ */
76
+ activateCompany(companyId, transactionId) {
77
+ return this.request(`/api/companies/${companyId}/activate/${transactionId}`, { method: "POST" });
78
+ }
79
+ /** List available subscription plans. */
80
+ listPlans() {
81
+ return this.request("/api/plans");
82
+ }
83
+ /** List available payment providers. */
84
+ listPaymentProviders() {
85
+ return this.request("/api/payment/providers");
86
+ }
87
+ /** Fetch the authenticated company's balance and plan state. */
88
+ getBalance() {
89
+ return this.request("/api/companies/balance");
90
+ }
91
+ /** Purchase prepaid credits for the authenticated company. */
92
+ purchaseCredits(input) {
93
+ return this.request("/api/companies/credits/purchase", {
94
+ method: "POST",
95
+ body: JSON.stringify(input),
96
+ });
97
+ }
98
+ /** List the authenticated company's credit purchases. */
99
+ listCreditPurchases() {
100
+ return this.request("/api/companies/credits/purchases");
101
+ }
102
+ /**
103
+ * Set or update the webhook endpoint where ixblix delivers events (incoming
104
+ * messages, customer joined, conversation closed, presence metadata). When
105
+ * the URL changes (or `rotateSecret` is true) a fresh HMAC secret is
106
+ * generated and returned once — store it to verify webhook signatures.
107
+ */
108
+ updateWebhook(input) {
109
+ return this.request("/api/companies/webhook", {
110
+ method: "PUT",
111
+ body: JSON.stringify(input),
112
+ });
113
+ }
114
+ // ---------------------------------------------------------------------------
115
+ // Conversations
116
+ // ---------------------------------------------------------------------------
117
+ /**
118
+ * Create an overflow conversation for a contact. Supply the operator's RSA
119
+ * public key (base64 SPKI DER) so the customer can encrypt messages to it.
120
+ */
121
+ createConversation(input) {
122
+ return this.request("/api/conversations", {
123
+ method: "POST",
124
+ body: JSON.stringify(input),
125
+ });
126
+ }
127
+ /**
128
+ * Fetch the current E2EE key state of a conversation so the operator can
129
+ * detect when the customer has joined (`keyStatus === "ACTIVE"`).
130
+ */
131
+ getConversationKeys(conversationId) {
132
+ return this.request(`/api/conversations/${conversationId}/keys`);
133
+ }
134
+ // ---------------------------------------------------------------------------
135
+ // Messages
136
+ // ---------------------------------------------------------------------------
137
+ /**
138
+ * Send an encrypted message from the company to the contact. The message must
139
+ * already be encrypted to the customer's public key (see the crypto helpers).
140
+ */
141
+ sendCompanyMessage(conversationId, envelope, contentType = "text") {
142
+ return this.request("/api/messages/company", {
143
+ method: "POST",
144
+ body: JSON.stringify({
145
+ conversationId,
146
+ content: envelope.content,
147
+ contentType,
148
+ iv: envelope.iv,
149
+ authTag: envelope.authTag,
150
+ encryptedKey: envelope.encryptedKey,
151
+ selfEncryptedKey: envelope.selfEncryptedKey,
152
+ keyId: envelope.keyId,
153
+ }),
154
+ });
155
+ }
156
+ /** List all messages of a conversation (content is always ciphertext). */
157
+ listMessages(conversationId) {
158
+ return this.request(`/api/messages/${conversationId}`);
159
+ }
160
+ /**
161
+ * Mark a contact-sent message as read by the operator. Emits a
162
+ * `message_read` Socket.io event to the customer's chat app so the customer
163
+ * sees the read receipt.
164
+ */
165
+ markMessageReadByCompany(conversationId, messageId) {
166
+ return this.request("/api/messages/company/read", {
167
+ method: "POST",
168
+ body: JSON.stringify({ conversationId, messageId }),
169
+ });
170
+ }
171
+ /**
172
+ * Relay an operator presence event (typing, stopped typing, or recording)
173
+ * to the customer's chat app via Socket.io.
174
+ */
175
+ reportCompanyPresence(conversationId, type) {
176
+ return this.request(`/api/conversations/${conversationId}/presence`, {
177
+ method: "POST",
178
+ body: JSON.stringify({ type }),
179
+ });
180
+ }
181
+ /**
182
+ * Relay a message received on the original channel (WhatsApp, Instagram,
183
+ * etc.) into ixblix. The message must be encrypted to the operator's public key.
184
+ */
185
+ relayOriginalChannelMessage(input) {
186
+ return this.request("/api/webhooks/original-channel", {
187
+ method: "POST",
188
+ body: JSON.stringify(input),
189
+ });
190
+ }
191
+ // ---------------------------------------------------------------------------
192
+ // Media
193
+ // ---------------------------------------------------------------------------
194
+ /**
195
+ * Upload an encrypted media file from the company. `file.data` must be the
196
+ * ciphertext bytes produced by the media encryption helper.
197
+ */
198
+ async sendCompanyMedia(conversationId, file, envelope) {
199
+ const form = new FormData();
200
+ form.append("conversationId", conversationId);
201
+ form.append("file", new Blob([file.data], { type: file.mimeType }), file.fileName);
202
+ form.append("iv", envelope.iv);
203
+ form.append("authTag", envelope.authTag);
204
+ form.append("encryptedKey", envelope.encryptedKey);
205
+ form.append("selfEncryptedKey", envelope.selfEncryptedKey);
206
+ form.append("keyId", envelope.keyId);
207
+ const headers = {};
208
+ if (this.apiKey) {
209
+ headers["X-API-Key"] = this.apiKey;
210
+ }
211
+ const response = await this.fetchImpl(`${this.baseUrl}/api/media/company`, {
212
+ method: "POST",
213
+ headers,
214
+ body: form,
215
+ });
216
+ const data = (await response.json());
217
+ if (!response.ok) {
218
+ const body = data;
219
+ throw new IxblixError(body?.error ?? "ixblix media upload failed", {
220
+ status: response.status,
221
+ code: body?.code,
222
+ details: body?.errors,
223
+ });
224
+ }
225
+ return data;
226
+ }
227
+ /** Fetch the metadata (envelope + file info) of a media file. */
228
+ getMedia(mediaId) {
229
+ return this.request(`/api/media/${mediaId}`);
230
+ }
231
+ /**
232
+ * Download the raw (encrypted) bytes of a media file as the operator, along
233
+ * with its metadata so it can be decrypted.
234
+ */
235
+ async downloadCompanyMedia(mediaId) {
236
+ const media = await this.getMedia(mediaId);
237
+ const headers = {};
238
+ if (this.apiKey) {
239
+ headers["X-API-Key"] = this.apiKey;
240
+ }
241
+ const response = await this.fetchImpl(`${this.baseUrl}/api/media/${mediaId}/content`, { headers });
242
+ if (!response.ok) {
243
+ throw new IxblixError(`ixblix media download failed with status ${response.status}`, {
244
+ status: response.status,
245
+ });
246
+ }
247
+ const arrayBuffer = await response.arrayBuffer();
248
+ return { data: new Uint8Array(arrayBuffer), media };
249
+ }
250
+ }
251
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}