JavaScript SDK

Complete reference for the official @ixblix/sdk-js TypeScript package. Covers every exported class, function, type and constant — with signatures, parameters, return values and runnable examples.

Package

npm install @ixblix/sdk-js — requires Node.js 18+. Ships ESM + CJS with full TypeScript declarations.

Table of contents

IxblixClient

Typed HTTP client for the ixblix REST API. One instance per scope — integrator or company.

Constructor

IxblixClient
import { IxblixClient } from '@ixblix/sdk-js';

const client = new IxblixClient({
  baseUrl: 'https://api.ixblix.app',
  apiKey?: string,              // X-API-Key header (company scope)
  integratorId?: string,        // HTTP Basic user (integrator scope)
  integratorAccessToken?: string,
  operatorKey?: OperatorKeyPair, // required to encrypt outgoing messages
  fetch?: typeof fetch,         // optional custom fetch
});

Integrator methods

POST /integrators/register HTTP Basic
registerIntegrator
client.registerIntegrator({
  name: string,
  hostname: string,
  callbackUrl: string,
  force?: boolean,
  subscriptionId?: string,
}): Promise<RegisterIntegratorResult>

// Three-way handshake: ixblix POSTs a challenge to callbackUrl,
// your endpoint echoes the registration payload, then echoes the credentials.
GET /plans HTTP Basic
listPlans
client.listPlans(): Promise<Plan[]>

// Returns all active subscription plans (billingType, includedConversations,
// includedMessages, durationDays, priceCents, overagePriceCents).
GET /payment/providers HTTP Basic
listPaymentProviders
client.listPaymentProviders(): Promise<PaymentProvidersResult>

// { providers: string[] } — e.g. ['asaas', 'stripe'].
POST /companies/register HTTP Basic
registerCompany
client.registerCompany({
  name: string,
  planId?: string,
  confirmationWebhookUrl?: string,
  email?: string,
  taxId?: string,
}): Promise<RegisterCompanyResult>

// Returns { company, payment } where payment contains transactionId,
// checkoutUrl (for paid plans) and confirmationUrl.
POST /companies/{id}/activate/{transactionId} HTTP Basic
activateCompany
client.activateCompany(
  companyId: string,
  transactionId: string,
): Promise<ActivateCompanyResult>

// Returns { apiKey, status }. Store the apiKey in a secret manager.

Company methods — profile & customization

GET /companies/profile X-API-Key
getProfile
client.getProfile(): Promise<CompanyProfile>

// { id, name, status, websiteUrl, customizations }
GET /companies/balance X-API-Key
getBalance
client.getBalance(): Promise<CompanyBalance>

// { id, name, balanceCents, planId, planExpiresAt }
PUT /companies/customization X-API-Key
updateCustomization
client.updateCustomization({
  brandName?: string,
  logoUrl?: string,
  rectangularLogoUrl?: string,
  squareIconUrl?: string,
  primaryColor?: string,
  faviconUrl?: string,
  websiteUrl?: string,
  welcomeMessage?: string,
}): Promise<CompanyCustomization>
POST /companies/branding X-API-Key
uploadBranding
client.uploadBranding({
  squareIcon?: BrandingFile,
  rectangularLogo?: BrandingFile,
}): Promise<CompanyCustomization>

// BrandingFile = { data: Uint8Array, fileName: string, mimeType: string }
// Multipart/form-data upload. Returns the updated customization.
PUT /companies/webhook X-API-Key
updateWebhook
client.updateWebhook({
  webhookUrl?: string | null,
  rotateSecret?: boolean,
}): Promise<{ webhookUrl: string | null; webhookSecret: string | null }>

// The secret is returned ONCE. Store it to verify webhook signatures.

Company methods — encryption

PUT /companies/encryption-key X-API-Key
registerEncryptionKey
client.registerEncryptionKey({
  keyId: string,
  publicKey: string, // base64 SPKI DER
}): Promise<CompanyEncryptionKey>

// Rotates the active key. The previous key is retired but kept for
// decrypting existing conversations.
GET /companies/encryption-key X-API-Key
getEncryptionKey
client.getEncryptionKey(): Promise<CompanyEncryptionKey>

// Returns the currently active company encryption key.

Company methods — conversations

POST /conversations X-API-Key
createConversation
client.createConversation({
  contact: {
    externalId: string,
    name?: string,
    metadata?: Record<string, unknown>,
  },
  operator?: {
    uuid?: string,
    name?: string,
    image?: string,
    gravatarHash?: string,
  },
}): Promise<CreateConversationResult>

// Returns { conversation, contact, deeplink, company, operator }.
// Share the deeplink with the customer on the original channel.
PUT /conversations/{conversationId}/operator X-API-Key
updateOperator
client.updateOperator(
  conversationId: string,
  operator: OperatorInput,
): Promise<UpdateOperatorResult>

// OperatorInput = { uuid?, name?, image?, gravatarHash? }
// Pass null to clear a field. Can be called at any time.
GET /conversations/{conversationId}/keys X-API-Key
getConversationKeys
client.getConversationKeys(
  conversationId: string,
): Promise<ConversationKeys>

// { conversationId, keyStatus, operatorPublicKey, operatorKeyId, customerPublicKey }
// keyStatus: 'AWAITING_OPERATOR' | 'AWAITING_CUSTOMER' | 'ACTIVE'
// Poll until keyStatus === 'ACTIVE' and customerPublicKey is set,
// or wait for the CUSTOMER_JOINED webhook.

Company methods — messages

POST /messages/company X-API-Key
sendEncryptedMessage / sendCompanyMessage
// High-level: encrypt and send in one call (recommended)
// Handles text, media, or both with a single AES key
await client.sendEncryptedMessage(
  conversationId: string,
  options: {
    content?: string,        // plaintext (required for text, optional caption for media)
    file?: { data: Uint8Array, fileName: string, mimeType: string },
    operatorUuid: string,
    replyToId?: string | null,
    attachments?: { buttons?, vcard?, location?, linkPreview? },
    operatorIdentity?: { uuid, name, image?, gravatarHash? },
  },
): Promise<Message>

// Low-level: send pre-encrypted envelope
client.sendCompanyMessage(
  conversationId: string,
  envelope: MessageEnvelope,
  contentType?: 'text' | 'react', // default 'text'
  operatorUuid?: string,
  replyToId?: string | null,
  file?: MediaUpload, // optional: { data: Uint8Array, fileName, mimeType }
  contentIv?: string,      // caption IV (for media messages)
  contentAuthTag?: string, // caption auth tag (for media messages)
): Promise<Message>

// sendEncryptedMessage fetches keys, encrypts all parts (content/file/attachments)
// with a single AES key using distinct IVs, and sends.
// sendCompanyMessage requires a pre-built envelope.
// When file is provided, the message is sent as media (multipart/form-data).
// The operator identity is embedded in the encrypted attachments JSON.
GET /messages/{conversationId} X-API-Key
listMessages
client.listMessages(
  conversationId: string,
): Promise<Message[]>

// Returns all messages (content is always ciphertext when encrypted).
// Each message includes replyTo (one level) and reactions.
POST /messages/company X-API-Key
reactToMessage
client.reactToMessage(
  conversationId: string,
  messageId: string,
  emoji: string, // pass '' to clear
  operatorUuid?: string,
): Promise<Message>

// Encrypts the emoji and sends it as contentType='react' with replyToId.
// Requires operatorKey in the client constructor.
POST /messages/company/delivered X-API-Key
markMessageDeliveredByCompany
client.markMessageDeliveredByCompany(
  conversationId: string,
  messageId: string,
): Promise<{ messageId: string; deliveredAt: string }>

// Emits a 'message_delivered' Socket.io event to the customer.
// Call it as soon as your client receives the message.
POST /messages/company/read X-API-Key
markMessageReadByCompany
client.markMessageReadByCompany(
  conversationId: string,
  messageId: string,
): Promise<{ messageId: string; readAt: string }>

// Emits a 'message_read' Socket.io event to the customer.
// Only call it when the window is focused and the message is in the viewport.

Company methods — presence

POST /conversations/{conversationId}/presence X-API-Key
reportCompanyPresence
client.reportCompanyPresence(
  conversationId: string,
  type: 'typing' | 'stopped' | 'recording',
): Promise<{ status: string }>

// Relays operator presence to the customer's chat app via Socket.io.

Company methods — media

Media messages are sent using sendEncryptedMessage with the optional file parameter. The content field becomes the media caption. All parts (caption, file, attachments) share a single AES key with distinct IVs. The endpoint is the same as for text messages: POST /api/messages/company.

POST /messages/company X-API-Key
sendEncryptedMessage (media)
// Send media with optional caption and attachments
await client.sendEncryptedMessage(conversationId, {
  content: 'Here is your invoice', // optional caption
  file: { data: fileBytes, fileName: 'invoice.pdf', mimeType: 'application/pdf' },
  operatorIdentity: { uuid: 'agent-42', name: 'Maria Silva', gravatarHash: '...' },
  attachments: { buttons: [...] }, // optional rich attachments
});

// All parts (caption, file, attachments) are encrypted with a single AES key
// using distinct IVs so the nonce is never reused.
GET /media/{mediaId} X-API-Key
getMedia
client.getMedia(mediaId: string): Promise<Media>

// Returns metadata: { id, companyId, conversationId, messageId, mimeType,
// fileName, sizeBytes, iv, authTag, encryptedKey, selfEncryptedKey, keyId, createdAt }
GET /media/{mediaId}/content X-API-Key
downloadCompanyMedia
client.downloadCompanyMedia(
  mediaId: string,
): Promise<MediaDownload>

// Returns { data: Uint8Array, media: Media }.
// Decrypt with decryptMediaEnvelope.

Company methods — payments & credits

POST /companies/credits/purchase X-API-Key
purchaseCredits
client.purchaseCredits({
  amountCents: number,
  currency?: string,
  provider: string,
  metadata?: Record<string, unknown>,
}): Promise<PurchaseCreditsResult>

// Top up the company's prepaid balance.
GET /companies/credits/purchases X-API-Key
listCreditPurchases
client.listCreditPurchases(): Promise<CreditPurchase[]>

// Returns all credit purchases with status: PENDING | COMPLETED | FAILED | REFUNDED.
GET /companies/payment-methods X-API-Key
listPaymentMethods
client.listPaymentMethods(): Promise<PaymentMethod[]>

// Returns stored cards and Pix methods, default first.
// { id, provider, type, brand, last4, expMonth, expYear, holderName, isDefault, status }
GET /companies/payment-change/options X-API-Key
getPaymentChangeOptions
client.getPaymentChangeOptions(): Promise<PaymentChangeOptions>

// { card, pixAutomatic, pixAutomaticCycle, currentPlanExpiresAt,
//   stillActive, currentPaymentMethodType }
POST /companies/payment-change/start X-API-Key
startPaymentChange
client.startPaymentChange({
  planId?: string,
}): Promise<StartPaymentChangeResult>

// Returns { transactionId, provider, amountCents, currency,
// confirmationUrl, status, checkoutUrl }.
// Redirect the company owner to checkoutUrl.

Company methods — original channel relay

POST /webhooks/original-channel X-API-Key
relayOriginalChannelMessage
client.relayOriginalChannelMessage({
  conversationId: string,
  content: string, // base64 ciphertext
  iv: string,
  authTag: string,
  encryptedKey: string,
  selfEncryptedKey: string,
  keyId: string,
  metadata?: Record<string, unknown>,
}): Promise<Message>

// Relay a message from WhatsApp/Instagram/etc. into ixblix.
// The message must be encrypted to the operator's public key.

Crypto helpers

End-to-end encryption uses hybrid RSA-OAEP + AES-256-GCM. Each message gets a fresh AES key, wrapped to both the recipient and the sender (so you can read back your own messages).

generateOperatorKeyPair

generateOperatorKeyPair
import { generateOperatorKeyPair } from '@ixblix/sdk-js';

const keypair = generateOperatorKeyPair({
  modulusLength?: 2048 | 4096, // default 2048
}): OperatorKeyPair

// Returns { keyId, publicKeySpki, privateKey }.
// The private key never leaves your process.

importPublicKey

importPublicKey
import { importPublicKey } from '@ixblix/sdk-js';

const key = importPublicKey(spkiBase64: string): KeyObject

// Import a base64 SPKI DER RSA public key (e.g. the customer's key).

encryptToRecipient

encryptToRecipient
import { encryptToRecipient } from '@ixblix/sdk-js';

const envelope = encryptToRecipient(
  plaintext: string,
  recipientPublicKeySpki: string,
  senderKeyId: string,
  senderPublicKeySpki: string,
): MessageEnvelope

// Returns { content, iv, authTag, encryptedKey, selfEncryptedKey, keyId }.
// All fields are base64url-encoded.

decryptEnvelope

decryptEnvelope
import { decryptEnvelope } from '@ixblix/sdk-js';

const plaintext = decryptEnvelope(
  envelope: { content, iv?, authTag?, encryptedKey?, selfEncryptedKey? },
  privateKey: KeyObject,
): string | null

// Tries encryptedKey first, then selfEncryptedKey.
// Returns null when decryption fails.

encryptRichMessage

encryptRichMessage
import { encryptRichMessage } from '@ixblix/sdk-js';

const result = encryptRichMessage(
  content: string,
  attachments: MessageAttachments | null, // typed object — stringified internally
  recipientPublicKeySpki: string,
  senderKeyId: string,
  senderPublicKeySpki: string,
): {
  content: string,
  attachments: string | null, // encrypted blob
  iv: string,
  authTag: string,
  encryptedKey: string,
  selfEncryptedKey: string,
  keyId: string,
}

// MessageAttachments = {
//   operator?: { uuid?, name?, image?, gravatarHash? },
//   buttons?: MessageButton[],
//   vcard?: MessageVcard,
//   location?: MessageLocation,
//   linkPreview?: MessageLinkPreview,
// }

// Encrypts content and attachments with different IVs but the same AES key.
// The attachments blob embeds its own iv and authTag.

decryptAttachments

decryptAttachments
import { decryptAttachments } from '@ixblix/sdk-js';

const json = decryptAttachments(
  attachmentsBlob: string,
  envelope: { encryptedKey?, selfEncryptedKey? },
  privateKey: KeyObject,
): string | null

// Decrypts the attachments blob from a rich message.
// Returns the JSON string or null.

encryptMediaToRecipient

encryptMediaToRecipient
import { encryptMediaToRecipient } from '@ixblix/sdk-js';

const envelope = encryptMediaToRecipient(
  fileBytes: Uint8Array,
  recipientPublicKeySpki: string,
  senderKeyId: string,
  senderPublicKeySpki: string,
): MessageEnvelope

// Encrypts binary media (image/audio/document).
// Returns the envelope fields; the ciphertext is in envelope.content.

encryptMediaWithAttachments

encryptMediaWithAttachments
import { encryptMediaWithAttachments } from '@ixblix/sdk-js';

const result = encryptMediaWithAttachments(
  fileBytes: Uint8Array,
  attachments: MessageAttachments, // typed object — stringified internally
  recipientPublicKeySpki: string,
  senderKeyId: string,
  senderPublicKeySpki: string,
): {
  content: string,
  iv: string,
  authTag: string,
  encryptedKey: string,
  selfEncryptedKey: string,
  keyId: string,
  encryptedAttachments: string,
}

// Use this when sending media + rich attachments.
// Both share the same AES key so the recipient can decrypt both.

decryptMediaEnvelope

decryptMediaEnvelope
import { decryptMediaEnvelope } from '@ixblix/sdk-js';

const bytes = decryptMediaEnvelope(
  envelope: { content, iv?, authTag?, encryptedKey?, selfEncryptedKey? },
  privateKey: KeyObject,
): Uint8Array | null

// Decrypts a media file. Returns the plaintext bytes or null.

Keypair persistence

loadOrCreateOperatorKey

loadOrCreateOperatorKey
import { loadOrCreateOperatorKey } from '@ixblix/sdk-js';

const keypair = loadOrCreateOperatorKey(directory: string): OperatorKeyPair

// Loads the keypair from disk, or generates and persists it on first run.
// Files: operator-private.pem (mode 0600), operator-public.spki, operator-key-id.
// For production, use a secret manager or KMS instead.

Webhook verification

ixblix signs every webhook with HMAC-SHA256 using the company's webhook secret. Always verify the signature before acting on the event.

Constants

Webhook headers
import {
  WEBHOOK_SIGNATURE_HEADER,   // 'X-Ixblix-Signature'
  WEBHOOK_ID_HEADER,           // 'X-Ixblix-Event-Id'
  WEBHOOK_EVENT_HEADER,        // 'X-Ixblix-Event'
  WEBHOOK_COMPANY_ID_HEADER,   // 'X-Ixblix-Company-Id'
} from '@ixblix/sdk-js';

verifyWebhook

verifyWebhook
import { verifyWebhook } from '@ixblix/sdk-js';

const { event, raw } = verifyWebhook(
  body: unknown,
  secret: string,
  signature?: string,
): ParsedWebhook

// Computes HMAC-SHA256 of the raw body and compares it (constant-time)
// against the signature. Throws on mismatch.
// Returns the typed event (WebhookEvent union).

parseWebhook

parseWebhook
import { parseWebhook } from '@ixblix/sdk-js';

const { event, raw } = parseWebhook(body: unknown): ParsedWebhook

// Parses and validates the event type without checking the signature.
// Use only when you trust the source (e.g. internal relay).

Webhook event types

EventPayload
COMPANY_ACTIVATED{ companyId, transactionId, apiKey }
CUSTOMER_JOINED{ companyId, conversationId, customerPublicKey }
MESSAGE_RECEIVED{ companyId, conversationId, messageId, replyToId?, sentAt }
MESSAGE_DELIVERED{ companyId, conversationId, messageId, deliveredAt }
MESSAGE_READ{ companyId, conversationId, messageId, readAt }
CONVERSATION_CLOSED{ companyId, conversationId }
TYPING / STOPPED_TYPING / RECORDING / CHAT_CLOSED{ companyId, conversationId, type }
BALANCE_LOW{ companyId, balanceCents }

IxblixError

IxblixError
import { IxblixError } from '@ixblix/sdk-js';

class IxblixError extends Error {
  readonly status: number; // HTTP status (0 if request never reached the API)
  readonly code: string;   // e.g. 'UNAUTHORIZED', 'NOT_FOUND'
  readonly details?: Array<{ path: string; message: string }>;
}

// Thrown by every client method when the API returns a non-2xx response.
// Catch and inspect error.code for programmatic handling.

Shared types

All types are exported from the package root. Key types:

  • Contact, ContactInput — customer identity
  • Conversation, ConversationKeys — thread state
  • Message, MessageEnvelope, Media — encrypted content
  • Operator, OperatorInput — agent identity
  • CompanyProfile, CompanyCustomization — white-label
  • Plan, CompanyBalance, CreditPurchase — billing
  • PaymentMethod, PaymentChangeOptions — payment instruments
  • WebhookEvent — union of all webhook payloads