npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@catalisa/panel-sdk

v0.1.5

Published

TypeScript SDK for Panel Client API - Complete type-safe client for CRM/Workflow automation

Readme

Panel SDK

TypeScript SDK for the Panel API (painel-cliente-api). Provides a fully-typed client library for all panel features including leads, workflows, WhatsApp devices, and more.

Installation

npm install @catalisa/panel-sdk

Quick Start

import { PanelAPIClient } from '@catalisa/panel-sdk';

// Create client
const client = new PanelAPIClient({
  baseURL: 'https://panel-api.example.com',
});

// Login with email/password
await client.login('[email protected]', 'password');

// Or use API token
const clientWithToken = new PanelAPIClient({
  baseURL: 'https://panel-api.example.com',
  auth: {
    type: 'apiToken',
    token: 'your-api-token',
  },
});

// Use resources
const leads = await client.leads.list();
const workflow = await client.workflows.create({ name: 'My Workflow' });

Authentication

The SDK supports two authentication methods:

JWT Authentication

// Login to get JWT token
const response = await client.login('[email protected]', 'password');
// Token is automatically set for subsequent requests

// Or set token manually
client.setAuth('your-jwt-token', 'jwt');

// Clear authentication
client.clearAuth();

// Logout (clears token and calls logout endpoint)
await client.logout();

API Token Authentication

// Initialize with API token
const client = new PanelAPIClient({
  baseURL: 'https://panel-api.example.com',
  auth: {
    type: 'apiToken',
    token: 'your-api-token',
  },
});

// Or set later
client.setAuth('your-api-token', 'apiToken');

Resources

Authentication (client.auth)

// Login
const response = await client.auth.login('email', 'password');

// Get current user
const user = await client.auth.me();

// Forgot password
await client.auth.forgotPassword('[email protected]');

// Reset password
await client.auth.resetPassword('token', 'newPassword');

// Logout
await client.auth.logout();

Users (client.users)

// Get current user profile
const me = await client.users.me();

// Update profile
await client.users.updateProfile({ name: 'New Name' });

// Update password
await client.users.updatePassword({
  currentPassword: 'old',
  newPassword: 'new',
});

Users Admin (client.usersAdmin)

// List all users
const users = await client.usersAdmin.list();

// Get user by ID
const user = await client.usersAdmin.get('user-id');

// Create user
const newUser = await client.usersAdmin.create({
  email: '[email protected]',
  password: 'password',
  role: 'CORRETOR',
});

// Update user
await client.usersAdmin.update('user-id', { role: 'COMERCIAL' });

// Delete user
await client.usersAdmin.delete('user-id');

Leads (client.leads)

// List leads with pagination
const leads = await client.leads.list({
  page: 1,
  limit: 20,
  status: 'NOVO',
});

// Get lead by ID
const lead = await client.leads.get('lead-id');

// Create lead
const newLead = await client.leads.create({
  name: 'John Doe',
  phone: '+5511999999999',
  email: '[email protected]',
  typeId: 'lead-type-id',
});

// Update lead
await client.leads.update('lead-id', { status: 'QUALIFICADO' });

// Delete lead
await client.leads.delete('lead-id');

Lead Types (client.leadTypes)

// List lead types
const types = await client.leadTypes.list();

// Create lead type
const type = await client.leadTypes.create({
  name: 'Real Estate',
  color: '#FF5733',
});

// Update lead type
await client.leadTypes.update('type-id', { name: 'Updated Name' });

// Delete lead type
await client.leadTypes.delete('type-id');

Chats (client.chats)

// List chats
const chats = await client.chats.list({ page: 1, limit: 20 });

// Get chat by ID
const chat = await client.chats.get('chat-id');

// Get chat messages
const messages = await client.chats.getMessages('chat-id', {
  page: 1,
  limit: 50,
});

Workflows (client.workflows)

// List workflows
const workflows = await client.workflows.list();

// Get workflow by ID
const workflow = await client.workflows.get('workflow-id');

// Create workflow
const newWorkflow = await client.workflows.create({
  name: 'Welcome Flow',
  description: 'Send welcome message to new leads',
});

// Update workflow
await client.workflows.update('workflow-id', { status: 'ACTIVE' });

// Delete workflow
await client.workflows.delete('workflow-id');

// Execute workflow manually
await client.workflows.execute('workflow-id', { leadId: 'lead-id' });

// Get workflow executions
const executions = await client.workflows.getExecutions('workflow-id');

// Get workflow statistics
const stats = await client.workflows.getStats('workflow-id');

Workflow Triggers (client.workflowTriggers)

import { WorkflowTriggerType } from '@catalisa/panel-sdk';

// List triggers for a workflow
const triggers = await client.workflowTriggers.listByWorkflow('workflow-id');

// Create event trigger
const trigger = await client.workflowTriggers.create({
  workflowId: 'workflow-id',
  type: 'event',
  config: {
    type: 'event',
    eventType: 'lead.created',
  },
});

// Create schedule trigger
const scheduleTrigger = await client.workflowTriggers.create({
  workflowId: 'workflow-id',
  type: 'schedule',
  config: {
    type: 'schedule',
    cron: '0 9 * * *', // Every day at 9 AM
    timezone: 'America/Sao_Paulo',
  },
});

// Update trigger
await client.workflowTriggers.update('trigger-id', {
  config: { type: 'event', eventType: 'lead.updated' },
});

// Delete trigger
await client.workflowTriggers.delete('trigger-id');

Workflow Actions (client.workflowActions)

import { WorkflowActionType, isActionType } from '@catalisa/panel-sdk';

// List actions for a workflow
const actions = await client.workflowActions.listByWorkflow('workflow-id');

// Create send text action
const action = await client.workflowActions.create({
  workflowId: 'workflow-id',
  type: 'send_text',
  order: 1,
  config: {
    type: 'send_text',
    message: 'Hello {{lead.name}}!',
    deviceId: 'device-id',
  },
});

// Create HTTP request action
const httpAction = await client.workflowActions.create({
  workflowId: 'workflow-id',
  type: 'http_request',
  order: 2,
  config: {
    type: 'http_request',
    url: 'https://api.example.com/webhook',
    method: 'POST',
    headers: [{ key: 'Content-Type', value: 'application/json' }],
    body: '{"lead": "{{lead.id}}"}',
  },
});

// Type-safe action handling
const someAction = await client.workflowActions.get('action-id');
if (isActionType(someAction, 'send_text')) {
  // TypeScript knows config is SendTextConfig
  console.log(someAction.config.message);
}

// Reorder actions
await client.workflowActions.reorder('workflow-id', [
  { id: 'action-1', order: 1 },
  { id: 'action-2', order: 2 },
]);

WhatsApp Devices (client.whatsappDevices)

// List devices
const devices = await client.whatsappDevices.list();

// Get device by ID
const device = await client.whatsappDevices.get('device-id');

// Connect device (generate QR code)
const qrResponse = await client.whatsappDevices.connect('device-id');
console.log(qrResponse.qr); // Base64 QR code

// Get QR code
const qr = await client.whatsappDevices.getQR('device-id');

// Disconnect device
await client.whatsappDevices.disconnect('device-id');

// Delete device
await client.whatsappDevices.delete('device-id');

WhatsApp Events (client.whatsappEvents)

// List events
const events = await client.whatsappEvents.list({
  deviceId: 'device-id',
  type: 'message',
  page: 1,
  limit: 50,
});

// Get event by ID
const event = await client.whatsappEvents.get('event-id');

Outgoing Webhooks (client.outgoingWebhooks)

// List webhooks
const webhooks = await client.outgoingWebhooks.list();

// Create webhook
const webhook = await client.outgoingWebhooks.create({
  url: 'https://api.example.com/webhook',
  events: ['lead.created', 'lead.updated'],
  secret: 'webhook-secret',
});

// Get webhook logs
const logs = await client.outgoingWebhooks.getLogs('webhook-id', {
  page: 1,
  limit: 20,
});

// Test webhook
await client.outgoingWebhooks.test('webhook-id');

// Delete webhook
await client.outgoingWebhooks.delete('webhook-id');

Messages (client.messages)

// List messages
const messages = await client.messages.list({
  deviceId: 'device-id',
  direction: 'outbound',
  page: 1,
  limit: 50,
});

// Get message by ID
const message = await client.messages.get('message-id');

Stats (client.stats)

// Get dashboard stats
const dashboard = await client.stats.getDashboard();

// Get message stats
const messageStats = await client.stats.getMessages({
  startDate: '2024-01-01',
  endDate: '2024-01-31',
});

// Get lead stats
const leadStats = await client.stats.getLeads({
  startDate: '2024-01-01',
  endDate: '2024-01-31',
});

// Get device stats
const deviceStats = await client.stats.getDevices();

Events (client.events)

// List events
const events = await client.events.list({
  type: 'lead.created',
  page: 1,
  limit: 50,
});

// Get event by ID
const event = await client.events.get('event-id');

API Tokens (client.apiTokens)

// List tokens
const tokens = await client.apiTokens.list();

// Create token
const newToken = await client.apiTokens.create({
  name: 'My API Token',
});
console.log(newToken.secret); // Only shown once!

// Delete token
await client.apiTokens.delete('token-id');

System Settings (client.systemSettings)

// Get settings
const settings = await client.systemSettings.get();

// Update settings
await client.systemSettings.update({
  defaultLanguage: 'pt-BR',
});

// Reset to defaults
await client.systemSettings.reset();

Routes (client.routes)

// List routes
const routes = await client.routes.list();

// Get route groups
const groups = await client.routes.getGroups();

// Create route
const route = await client.routes.create({
  path: '/api/custom',
  method: 'GET',
  roles: ['ADMIN'],
});

// Update route
await client.routes.update('route-id', { roles: ['ADMIN', 'COMERCIAL'] });

// Delete route
await client.routes.delete('route-id');

Health (client.health)

// Check API health
const status = await client.health.check();
console.log(status.status); // 'ok'

Workflow Action Types

The SDK provides complete type safety for all 22 workflow action types:

Messaging Actions

  • send_text - Send text message
  • send_media - Send image, video, audio, or document
  • send_reaction - Send emoji reaction
  • send_location - Send location
  • send_contact - Send contact card
  • send_buttons - Send message with buttons
  • send_list - Send list message

Group Actions

  • create_group - Create WhatsApp group
  • update_group - Update group info
  • add_to_group - Add participants
  • remove_from_group - Remove participants

HTTP Actions

  • http_request - Make HTTP request
  • webhook - Send webhook

Control Flow Actions

  • delay - Wait for duration
  • condition - If/else branching
  • switch - Switch/case branching
  • loop - Loop over items
  • wait_for_reply - Wait for user reply

Lead Actions

  • create_lead - Create new lead
  • update_lead - Update lead
  • assign_lead - Assign lead to user
  • change_lead_status - Change lead status

Workflow Actions

  • start_workflow - Start another workflow
  • stop_workflow - Stop workflow execution
  • set_variable - Set workflow variable

System Actions

  • log - Log message
  • notify - Send notification
  • schedule - Schedule action

AI Actions

  • ai_response - Generate AI response

Webhook Actions

  • trigger_webhook - Trigger advanced webhook
  • create_advanced_webhook - Create advanced webhook

Error Handling

import {
  APIError,
  AuthError,
  ValidationError,
  NotFoundError,
  RateLimitError,
  NetworkError,
} from '@catalisa/panel-sdk';

try {
  await client.leads.get('invalid-id');
} catch (error) {
  if (error instanceof NotFoundError) {
    console.log('Lead not found');
  } else if (error instanceof AuthError) {
    console.log('Authentication failed');
  } else if (error instanceof ValidationError) {
    console.log('Validation errors:', error.message);
  } else if (error instanceof RateLimitError) {
    console.log('Rate limited, retry after:', error.retryAfter);
  } else if (error instanceof NetworkError) {
    console.log('Network error:', error.message);
  } else if (error instanceof APIError) {
    console.log('API error:', error.status, error.message);
  }
}

Advanced Usage

Custom Headers

const client = new PanelAPIClient({
  baseURL: 'https://panel-api.example.com',
  headers: {
    'X-Custom-Header': 'value',
  },
});

Request/Response Interceptors

const client = new PanelAPIClient({
  baseURL: 'https://panel-api.example.com',
  onRequest: (config) => {
    console.log('Request:', config.method, config.url);
    return config;
  },
  onResponse: (response) => {
    console.log('Response:', response.status);
    return response;
  },
  onError: (error) => {
    console.error('Error:', error.message);
  },
});

Custom Timeout

const client = new PanelAPIClient({
  baseURL: 'https://panel-api.example.com',
  timeout: 60000, // 60 seconds
});

TypeScript Support

The SDK is written in TypeScript and provides full type definitions. All types are exported from the main package:

import {
  Lead,
  LeadStatus,
  Workflow,
  WorkflowAction,
  WorkflowActionType,
  isActionType,
  getActionConfig,
} from '@catalisa/panel-sdk';

// Type guards for action types
const action: WorkflowAction = await client.workflowActions.get('id');

if (isActionType(action, 'send_text')) {
  // action.config is now typed as SendTextConfig
  const message = action.config.message;
}

// Get typed config from action
const config = getActionConfig(action, 'http_request');
if (config) {
  // config is HttpRequestConfig
  console.log(config.url, config.method);
}

Testing

The SDK has two types of tests:

  1. Unit Tests - Uses Vitest + MSW (Mock Service Worker)
  2. E2E Tests - Runs against the real API

Quick Start

Run unit tests

npm test

Run E2E tests (local)

# Generate test token (if needed)
cd ../painel-cliente-api
JWT_SECRET="painel-jwt-secret-change-me" \
DATABASE_URL="postgresql://postgres:postgres@localhost:5434/painel_cliente_db" \
node scripts/generate-security-test-tokens.js

# Run E2E tests
cd ../panel-sdk
TEST_TOKEN="<token>" API_URL="http://localhost:3004" npx tsx src/e2e-tests.ts

E2E Tests - Environments

Local (development)

TEST_TOKEN="<token>" \
API_URL="http://localhost:3004" \
npx tsx src/e2e-tests.ts

Staging

# First, generate a valid token for staging
cd ../painel-cliente-api
JWT_SECRET="<staging-jwt-secret>" \
DATABASE_URL="postgresql://panel_staging_db_user:[email protected]:5432/panel_staging_db" \
node scripts/generate-security-test-tokens.js

# Run tests
cd ../panel-sdk
TEST_TOKEN="<token>" \
API_URL="https://panel-api.wpp.bb.staging.catalisa.app" \
npx tsx src/e2e-tests.ts

Production (use with caution!)

# Be careful - tests create and delete real data!
TEST_TOKEN="<production-token>" \
API_URL="https://panel-api.wpp.bb.catalisa.app" \
npx tsx src/e2e-tests.ts

Detailed Documentation

File Structure

apps/panel-sdk/
├── src/
│   ├── client.ts              # Main client
│   ├── cli.ts                 # CLI utilities (login, output)
│   ├── e2e-tests.ts           # E2E tests
│   ├── index.test.ts          # Unit tests
│   ├── resources/             # API resources
│   │   ├── leads.ts
│   │   ├── workflows.ts
│   │   ├── capture-links.ts
│   │   ├── branding.ts
│   │   └── ...
│   ├── types/                 # TypeScript types
│   │   ├── index.ts
│   │   ├── capture-links.ts
│   │   ├── branding.ts
│   │   └── ...
│   └── test/
│       └── mocks/
│           └── handlers.ts    # MSW handlers for mocks
├── package.json
└── README.md

Unit Tests (Vitest + MSW)

Unit tests use MSW to intercept HTTP requests and return mocked data.

Run tests:

npm test                    # Run once
npm run test:watch          # Watch mode
npm run test:coverage       # With coverage

Add new mocks:

Edit src/test/mocks/handlers.ts:

// Add mock data
export const mockMyResource = {
  id: 'test-123',
  name: 'Test',
  // ...
};

// Add handlers
http.get(`${BASE_URL}/my-resource`, () => {
  return HttpResponse.json([mockMyResource]);
}),

E2E Tests

E2E tests run against a real API (local, staging, or production).

Environment variables:

| Variable | Description | Example | |----------|-------------|---------| | TEST_TOKEN | Valid JWT | eyJhbGciOi... | | API_URL | API URL | http://localhost:3004 |

E2E test scripts:

# Basic test
npx tsx src/e2e-tests.ts

# Hierarchy tests (master/sub-user)
MASTER_TOKEN="..." SUBUSER_TOKEN="..." npx tsx src/e2e-hierarchy-tests.ts

# Chats/messages tests
TEST_TOKEN="..." npx tsx src/e2e-chats-messages-tests.ts

Interactive login (without token):

If you don't provide TEST_TOKEN, tests will start interactive login:

API_URL="http://localhost:3004" npx tsx src/e2e-tests.ts

# You will be prompted:
# Email: [email protected]
# Password: ***

Generating Test Tokens

For local development:

cd ../painel-cliente-api

JWT_SECRET="painel-jwt-secret-change-me" \
DATABASE_URL="postgresql://postgres:postgres@localhost:5434/painel_cliente_db" \
node scripts/generate-security-test-tokens.js

For staging:

cd ../painel-cliente-api

JWT_SECRET="<staging-jwt-secret>" \
DATABASE_URL="postgresql://panel_staging_db_user:[email protected]:5432/panel_staging_db" \
node scripts/generate-security-test-tokens.js

The script generates tokens for:

  • ADMIN_TOKEN - Admin user
  • MASTER_TOKEN - Master (COMERCIAL with sub-users)
  • SUBUSER_TOKEN - Sub-user of a master
  • SIBLING_TOKEN - Another sub-user of the same master
  • OTHER_TENANT_TOKEN - User from another tenant

Test Scenarios

Capture Links

  • Full CRUD
  • Toggle status (activate/deactivate)
  • Statistics
  • Public lead submission
  • assignedTo verification (leads created via capture link are always assigned to the link owner)

Branding

  • Get/Upsert/Delete
  • Permissions (canManage)
  • Public branding (default and by userId)

Workflows

  • Full CRUD
  • Triggers and Actions
  • Validation (required fields, JID format, etc.)

Hierarchy (Master/Sub-user)

  • Data isolation
  • Master sees all sub-users' data
  • Sub-user sees only their own data

Troubleshooting

Error "Token expired"

Generate a new token using the generate-security-test-tokens.js script.

Connection error

Check if:

  1. The API is running (docker compose up painel-cliente-api)
  2. The URL is correct
  3. The ports are correct (local: 3004, staging: HTTPS)

Tests failing due to existing data

Some tests create and delete data. If they fail midway, they may leave "orphan" data. Run the seed again or clean up manually.


CI/CD

Unit tests run automatically in CI:

# .github/workflows/test.yml
- name: Run tests
  run: |
    cd apps/panel-sdk
    npm test

For E2E tests in CI, configure secrets for staging:

- name: Run E2E tests
  env:
    TEST_TOKEN: ${{ secrets.STAGING_TEST_TOKEN }}
    API_URL: https://panel-api.wpp.bb.staging.catalisa.app
  run: |
    cd apps/panel-sdk
    npx tsx src/e2e-tests.ts

License

MIT