Início Rápido
Um guia completo, passo a passo, de toda a integração ixblix construído sobre o pacote oficial
@ixblix/sdk-js. Siga-o de cima a baixo para ir de um projeto vazio a uma
conversa de overflow criptografada de ponta a ponta em produção.
Um backend de operador (seu CRM/help desk) que registra empresas, entrega um deeplink seguro aos clientes e troca mensagens criptografadas de ponta a ponta, mídia e mensagens ricas com eles — enquanto recebe eventos em tempo real via webhooks assinados.
Fluxo de integração
- Instalar e configurar o SDK — um cliente para chamadas de integrador, outro para chamadas de empresa.
- Registrar sua plataforma integradora — prove que você é dono do seu hostname via um desafio de callback.
- Registrar uma empresa — onboarding sem chaves que retorna instruções de pagamento.
- Ativar a empresa — após o pagamento, você recebe o
X-API-Keyda empresa. - Configurar o endpoint de webhook — armazene o segredo HMAC para verificar eventos.
- Registrar a chave de criptografia do operador — sua chave pública RSA para E2EE.
- Criar uma conversa — obtenha um deeplink para compartilhar com o cliente.
- Trocar mensagens criptografadas — texto, mídia, conteúdo rico, reações, presença.
Pré-requisitos
- Node.js 18+ (o SDK usa o módulo embutido
node:crypto). - Uma conta de integrador com um
callbackUrlpublicamente acessível (HTTPS). - Um endpoint de webhook para receber eventos (pode ser o mesmo host).
Passo 1 — Instalar o SDK
npm install @ixblix/sdk-js Passo 2 — Criar os clientes de API
Chamadas no escopo do integrador (registrar empresas, listar planos) usam HTTP Basic Auth, enquanto cada
chamada no escopo da empresa usa o header X-API-Key. Crie dois clientes a partir da mesma URL base.
import { IxblixClient } from '@ixblix/sdk-js';
const BASE_URL = 'https://api.ixblix.app';
// Integrator client — company onboarding & catalog.
export const integrator = new IxblixClient({
baseUrl: BASE_URL,
integratorId: process.env.IXBLIX_INTEGRATOR_ID,
integratorAccessToken: process.env.IXBLIX_INTEGRATOR_TOKEN,
});
// Company client — conversations, messages, media, keys.
// The API key is obtained after activation (Step 5).
export const company = new IxblixClient({
baseUrl: BASE_URL,
apiKey: process.env.IXBLIX_API_KEY,
}); Passo 3 — Registrar sua plataforma integradora
/integrators/register HTTP Basic
O registro é um handshake de três vias: ixblix faz POST de um challenge para o seu
callbackUrl, seu endpoint deve ecoar o payload de registro original de volta, e então
ixblix faz POST das credenciais que seu endpoint deve ecoar com um 200.
await integrator.registerIntegrator({
name: 'My CRM Platform',
hostname: 'mycrm.example.com',
callbackUrl: 'https://mycrm.example.com/ixblix/callback',
});
// The callback handler on your side must answer the challenge,
// then acknowledge the credentials payload.
// POST { type: "challenge" } -> respond with { name, hostname, callbackUrl }
// POST { integratorId, accessToken } -> respond 200 Passo 4 — Registrar uma empresa
/companies/register HTTP Basic Empresas são criadas sem chaves. Você recebe o registro da empresa mais instruções de pagamento para redirecionar o cliente para o checkout hospedado.
const { company: created, payment } = await integrator.registerCompany({
name: 'Acme Support',
planId: 'plan_starter',
email: 'billing@acme.example',
confirmationWebhookUrl: 'https://mycrm.example.com/ixblix/company-activated',
});
console.log(payment.transactionId, payment.checkoutUrl); Passo 5 — Ativar a empresa
/companies/{id}/activate/{transactionId} HTTP Basic
Uma vez que o pagamento é confirmado, ative a empresa para obter seu X-API-Key. Armazene-o
no seu gerenciador de segredos — ele concede acesso total a todos os recursos da empresa.
const { apiKey } = await integrator.activateCompany(
created.id,
payment.transactionId,
);
// Persist the key and build the company-scoped client.
const company = new IxblixClient({ baseUrl: BASE_URL, apiKey }); Passo 6 — Configurar o endpoint de webhook
/companies/webhook X-API-Key
Defina (ou rotacione) a URL onde ixblix entrega eventos. Cada mudança de URL — ou
rotateSecret: true — gera um novo segredo HMAC que é retornado uma única vez.
Armazene-o; você precisa dele para verificar assinaturas.
const { webhookUrl, webhookSecret } = await company.updateWebhook({
webhookUrl: 'https://mycrm.example.com/ixblix/webhook',
});
// webhookSecret looks like: whsec_xxxxxxxx...
// Store it in your secret manager. It is never returned again. Passo 7 — Gerar e registrar a chave do operador
/companies/encryption-key X-API-Key Mantenha um par de chaves RSA persistente. A chave privada nunca sai do seu processo; apenas a chave pública é registrada. Novas conversas tiram um snapshot da chave ativa, e rotacioná-la mantém a chave anterior para ler conversas antigas.
import {
IxblixClient,
loadOrCreateOperatorKey,
} from '@ixblix/sdk-js';
// Load an existing keypair or create one on first run (stored with 0600 perms).
const operatorKey = loadOrCreateOperatorKey('./.ixblix-keys');
await company.registerEncryptionKey({
keyId: operatorKey.keyId,
publicKey: operatorKey.publicKeySpki,
});
// Build a client that can encrypt outgoing messages.
const companyWithKey = new IxblixClient({
baseUrl: BASE_URL,
apiKey: process.env.IXBLIX_API_KEY,
operatorKey,
}); Passo 8 — Receber eventos
ixblix envia cada evento para seu endpoint com uma assinatura HMAC-SHA256 no header
X-Ixblix-Signature. Sempre verifique-a contra o body bruto antes de agir. Use o
middleware raw-body abaixo para que os bytes exatos estejam disponíveis para verificação.
import express from 'express';
import { verifyWebhook, WEBHOOK_SIGNATURE_HEADER } from '@ixblix/sdk-js';
const app = express();
const webhookSecret = process.env.IXBLIX_WEBHOOK_SECRET!;
// Keep the raw body so the signature can be verified byte-for-byte.
app.post(
'/ixblix/webhook',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.header(WEBHOOK_SIGNATURE_HEADER);
const body = JSON.parse(req.body.toString('utf8'));
let event;
try {
({ event } = verifyWebhook(body, webhookSecret, signature));
} catch (err) {
return res.status(401).send('invalid signature');
}
switch (event.event) {
case 'CUSTOMER_JOINED':
return void onCustomerJoined(event);
case 'MESSAGE_RECEIVED':
return void onMessageReceived(event);
case 'MESSAGE_DELIVERED':
case 'MESSAGE_READ':
return void onMessageRead(event);
case 'CONVERSATION_CLOSED':
return void onConversationClosed(event);
case 'BALANCE_LOW':
return void onBalanceLow(event);
default:
return void res.status(200).send('ok');
}
},
); | Evento | Quando dispara |
|---|---|
COMPANY_ACTIVATED | Uma empresa concluiu o onboarding e recebeu sua chave de API. |
CUSTOMER_JOINED | O cliente abriu o deeplink e registrou sua chave pública. |
MESSAGE_RECEIVED | O cliente enviou uma mensagem (texto ou mídia). |
MESSAGE_DELIVERED | O dispositivo do cliente recebeu uma mensagem que você enviou. |
MESSAGE_READ | O cliente leu uma mensagem que você enviou. |
TYPING / STOPPED_TYPING / RECORDING / CHAT_CLOSED | Presença reportada pela janela de chat do cliente. |
CONVERSATION_CLOSED | A conversa foi fechada. |
BALANCE_LOW | O saldo pré-pago da empresa caiu abaixo do limite. |
Passo 9 — Criar uma conversa
/conversations X-API-Key
Uma conversa representa o thread de overflow para um contato. Anexe a identidade do operador para que
o cliente possa mostrar quem está atendendo o chat, então compartilhe o deeplink retornado.
const { conversation, contact, deeplink } = await companyWithKey.createConversation({
contact: {
externalId: 'whatsapp_5511999999999',
name: 'Jane Doe',
metadata: { phone: '+55 11 99999-9999', ticket: 'T-1234' },
},
operator: {
uuid: 'agent-42',
name: 'Maria Silva',
image: 'https://cdn.example.com/avatars/maria.png',
// or: gravatarHash: md5('maria@example.com')
},
});
// Send the deeplink back on the original channel (WhatsApp, Instagram, ...). Passo 10 — Aguardar o cliente entrar
/conversations/{conversationId}/keys X-API-Key
O navegador do cliente gera um par de chaves quando o deeplink abre e registra a chave pública
com ixblix. Prefira reagir ao webhook CUSTOMER_JOINED; só faça polling se necessário.
sendEncryptedMessage busca as chaves internamente — você só precisa saber que o cliente
entrou antes de chamá-lo.
async function waitForCustomer(conversationId: string) {
for (;;) {
const keys = await companyWithKey.getConversationKeys(conversationId);
if (keys.keyStatus === 'ACTIVE' && keys.customerPublicKey) {
return; // customer joined — safe to call sendEncryptedMessage
}
await new Promise((r) => setTimeout(r, 2000));
}
}
await waitForCustomer(conversation.id); Passo 11 — Enviar uma mensagem criptografada
/messages/company X-API-Key
Use sendEncryptedMessage — um único método de conveniência que busca as chaves da conversa,
criptografa o conteúdo (e/ou mídia e/ou anexos) com uma chave AES, e envia a
mensagem. O SDK envolve a chave AES tanto para o cliente quanto para sua própria chave pública para que você possa
ler de volta o que enviou.
// Text message with operator identity embedded in the encrypted attachments.
await companyWithKey.sendEncryptedMessage(conversation.id, {
content: 'Hello Jane! How can we help you today?',
operatorUuid: '550e8400-e29b-41d4-a716-446655440000',
operatorIdentity: {
uuid: '550e8400-e29b-41d4-a716-446655440000',
name: 'Maria Silva',
gravatarHash: md5('maria@example.com'),
},
}); Passo 12 — Receber mensagens criptografadas
/messages/{conversationId} X-API-Key
Mensagens são sempre armazenadas como ciphertext. Descriptografe-as com sua chave privada. Prefira lidar com
webhooks MESSAGE_RECEIVED em vez de polling, então busque o thread e descriptografe.
import { decryptEnvelope } from '@ixblix/sdk-js';
const messages = await companyWithKey.listMessages(conversation.id);
for (const message of messages) {
if (!message.contentEncrypted) continue; // system/legacy plaintext
const plaintext = decryptEnvelope(message, operatorKey.privateKey);
console.log(message.senderType, message.sentAt, plaintext);
} Passo 13 — Enviar mídia e mensagens ricas
/media/company X-API-Key sendEncryptedMessage lida com texto, mídia e anexos ricos através da mesma chamada.
Passe content para texto, file para mídia, ou ambos para uma mensagem de mídia com
legenda. Adicione attachments para botões, vCard, localização ou previews de link — tudo
compartilha uma única chave AES com IVs distintos.
import { readFile } from 'node:fs/promises';
// 1. Plain media (image/audio/document).
const bytes = await readFile('./invoice.pdf');
await companyWithKey.sendEncryptedMessage(conversation.id, {
file: { data: bytes, fileName: 'invoice.pdf', mimeType: 'application/pdf' },
operatorUuid: '550e8400-e29b-41d4-a716-446655440000',
});
// 2. Rich text message (quick replies, buttons, ...).
await companyWithKey.sendEncryptedMessage(conversation.id, {
content: 'Your ticket is ready.',
operatorUuid: '550e8400-e29b-41d4-a716-446655440000',
attachments: {
buttons: [{ type: 'reply', label: 'Open ticket', value: 'open' }],
},
});
// 3. Media + caption + rich attachments in one call.
await companyWithKey.sendEncryptedMessage(conversation.id, {
content: 'Here is your invoices.',
file: { data: bytes, fileName: 'invoice.pdf', mimeType: 'application/pdf' },
operatorUuid: '550e8400-e29b-41d4-a716-446655440000',
attachments: {
buttons: [{ type: 'reply', label: 'Open ticket', value: 'open' }],
},
}); Passo 14 — Presença, recibos de entrega/leitura e reações
// Tell the customer you are typing / recording.
await companyWithKey.reportCompanyPresence(conversation.id, 'typing');
await companyWithKey.reportCompanyPresence(conversation.id, 'stopped');
// Mark a customer message as delivered as soon as your client receives it.
await companyWithKey.markMessageDeliveredByCompany(conversation.id, messageId);
// Mark it as read once the operator actually sees it (window focused and the
// message inside the viewport). The customer sees the receipt live.
await companyWithKey.markMessageReadByCompany(conversation.id, messageId);
// React with an emoji (encrypted to the customer, sent as contentType "react").
await companyWithKey.reactToMessage(conversation.id, messageId, '👍', '550e8400-e29b-41d4-a716-446655440000');
// Pass an empty string to clear your reaction:
// await companyWithKey.reactToMessage(conversation.id, messageId, '', '550e8400-e29b-41d4-a716-446655440000'); Passo 15 — Fechar e apagar uma conversa
Fechar encerra o thread. Apagar remove o conteúdo criptografado para satisfazer regras de LGPD/retenção de dados. Ambos são acionados por token do cliente ou pela empresa dependendo do endpoint — veja a referência da API.
// Report a customer-side close/presence using the conversation token.
// (Company side uses the conversation id with its API key.)
// Erasing encrypted content is a company action:
// POST /api/conversations/{token}/erase Exemplo completo
Juntando todo o fluxo em um único arquivo:
import {
IxblixClient,
loadOrCreateOperatorKey,
decryptEnvelope,
} from '@ixblix/sdk-js';
import { createHash } from 'crypto';
const md5 = (s: string) => createHash('md5').update(s).digest('hex');
const BASE_URL = 'https://api.ixblix.app';
async function main() {
// 1. Clients
const integrator = new IxblixClient({
baseUrl: BASE_URL,
integratorId: process.env.IXBLIX_INTEGRATOR_ID,
integratorAccessToken: process.env.IXBLIX_INTEGRATOR_TOKEN,
});
// 2. Register + activate a company
const { company: created, payment } = await integrator.registerCompany({
name: 'Acme Support',
planId: 'plan_starter',
});
const { apiKey } = await integrator.activateCompany(created.id, payment.transactionId);
// 3. Operator keypair + company client
const operatorKey = loadOrCreateOperatorKey('./.ixblix-keys');
const company = new IxblixClient({ baseUrl: BASE_URL, apiKey, operatorKey });
await company.registerEncryptionKey({
keyId: operatorKey.keyId,
publicKey: operatorKey.publicKeySpki,
});
await company.updateWebhook({ webhookUrl: 'https://mycrm.example.com/ixblix/webhook' });
// 4. Open a conversation and share the deeplink
const { conversation, deeplink } = await company.createConversation({
contact: { externalId: 'whatsapp_5511999999999', name: 'Jane Doe' },
operator: { uuid: '550e8400-e29b-41d4-a716-446655440000', name: 'Maria Silva' },
});
console.log('Share this link:', deeplink);
// 5. Wait for the customer, then talk
let keys = await company.getConversationKeys(conversation.id);
while (keys.keyStatus !== 'ACTIVE' || !keys.customerPublicKey) {
await new Promise((r) => setTimeout(r, 2000));
keys = await company.getConversationKeys(conversation.id);
}
// sendEncryptedMessage fetches keys, encrypts and sends in one call.
await company.sendEncryptedMessage(conversation.id, {
content: 'Hello! How can we help?',
operatorUuid: '550e8400-e29b-41d4-a716-446655440000',
operatorIdentity: {
uuid: '550e8400-e29b-41d4-a716-446655440000',
name: 'Maria Silva',
gravatarHash: md5('maria@example.com'),
},
});
// 6. Read the reply
const messages = await company.listMessages(conversation.id);
for (const message of messages) {
if (message.contentEncrypted) {
console.log(decryptEnvelope(message, operatorKey.privateKey));
}
}
}
main().catch(console.error); Solução de problemas
| Sintoma | Causa e correção |
|---|---|
401 UNAUTHORIZED | X-API-Key ou credenciais de integrador ausentes/inválidas. |
403 FORBIDDEN | A empresa existe mas ainda não está ativada. |
409 KEYS_NOT_ACTIVE | Você tentou criptografar antes do cliente entrar. Aguarde o CUSTOMER_JOINED. |
402 INSUFFICIENT_BALANCE | Recarregue com purchaseCredits e tente novamente. |
| Falha na assinatura do webhook | Verifique contra os bytes brutos do body, não um objeto re-serializado. |
Próximos passos
- Referência do SDK JavaScript — cada método e auxiliar de criptografia.
- Referência da API REST — cada endpoint exposto.
- Criptografia de Ponta a Ponta — como o envelope funciona.
- Webhooks — payloads de eventos e verificação.