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

@scell/sdk

v1.2.0

Published

Official TypeScript SDK for Scell.io - Electronic invoicing (Factur-X) and signatures (eIDAS) API

Readme

@scell/sdk

Official TypeScript SDK for Scell.io - Electronic invoicing (Factur-X/UBL/CII) and eIDAS-compliant electronic signatures API.

Features

  • Full TypeScript support with strict types
  • Two authentication modes: Bearer token (dashboard) and API key (external API)
  • Automatic retries with exponential backoff and jitter
  • Webhook signature verification
  • Zero external dependencies (uses native fetch)
  • ESM and CommonJS support
  • Node.js 18+ and browser compatible

Installation

npm install @scell/sdk
# or
yarn add @scell/sdk
# or
pnpm add @scell/sdk

Quick Start

Authentication

import { ScellClient, ScellApiClient, ScellAuth } from '@scell/sdk';

// 1. Login to get a token
const auth = await ScellAuth.login({
  email: '[email protected]',
  password: 'your-password',
});

// 2. Dashboard operations (Bearer token)
const client = new ScellClient(auth.token);

// 3. API operations (X-API-Key)
const apiClient = new ScellApiClient('your-api-key');

Create an Invoice

const { data: invoice } = await apiClient.invoices.create({
  invoice_number: 'FACT-2024-001',
  direction: 'outgoing',
  output_format: 'facturx',
  issue_date: '2024-01-15',
  due_date: '2024-02-15',
  currency: 'EUR',
  total_ht: 1000.0,
  total_tax: 200.0,
  total_ttc: 1200.0,
  seller_siret: '12345678901234',
  seller_name: 'My Company SAS',
  seller_address: {
    line1: '1 Rue de la Paix',
    postal_code: '75001',
    city: 'Paris',
    country: 'FR',
  },
  buyer_siret: '98765432109876',
  buyer_name: 'Client Company',
  buyer_address: {
    line1: '2 Avenue des Champs',
    postal_code: '75008',
    city: 'Paris',
    country: 'FR',
  },
  lines: [
    {
      description: 'Consulting services - January 2024',
      quantity: 10,
      unit_price: 100.0,
      tax_rate: 20.0,
      total_ht: 1000.0,
      total_tax: 200.0,
      total_ttc: 1200.0,
    },
  ],
});

console.log('Invoice created:', invoice.id);

Create a Signature Request

import { readFileSync } from 'fs';

const pdfContent = readFileSync('contract.pdf');

const { data: signature } = await apiClient.signatures.create({
  title: 'Service Agreement 2024',
  description: 'Annual service contract',
  document: pdfContent.toString('base64'),
  document_name: 'contract.pdf',
  signers: [
    {
      first_name: 'John',
      last_name: 'Doe',
      email: '[email protected]',
      auth_method: 'email',
    },
    {
      first_name: 'Jane',
      last_name: 'Smith',
      phone: '+33612345678',
      auth_method: 'sms',
    },
  ],
  ui_config: {
    logo_url: 'https://mycompany.com/logo.png',
    primary_color: '#3b82f6',
    company_name: 'My Company',
  },
  redirect_complete_url: 'https://myapp.com/signed',
  redirect_cancel_url: 'https://myapp.com/cancelled',
});

// Send signing URLs to signers
signature.signers?.forEach((signer) => {
  console.log(`${signer.email}: ${signer.signing_url}`);
});

Webhook Verification

import { ScellWebhooks } from '@scell/sdk';

// Express.js example
app.post('/webhooks/scell', async (req, res) => {
  const signature = req.headers['x-scell-signature'] as string;
  const payload = JSON.stringify(req.body);

  // Verify the webhook signature
  const isValid = await ScellWebhooks.verifySignature(
    payload,
    signature,
    process.env.WEBHOOK_SECRET!
  );

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  // Parse and handle the event
  const event = ScellWebhooks.parsePayload(payload);

  switch (event.event) {
    case 'invoice.validated':
      console.log('Invoice validated:', event.data);
      break;
    case 'signature.completed':
      console.log('Signature completed:', event.data);
      break;
    case 'balance.low':
      console.log('Low balance alert!');
      break;
  }

  res.status(200).send('OK');
});

API Reference

ScellClient (Dashboard)

For user/dashboard operations with Bearer token authentication.

const client = new ScellClient(token, {
  baseUrl: 'https://api.scell.io/api/v1', // optional
  timeout: 30000, // optional, in ms
  retry: { maxRetries: 3 }, // optional
});

// Resources
client.auth          // User authentication
client.companies     // Company management
client.apiKeys       // API key management
client.balance       // Balance and transactions
client.webhooks      // Webhook management
client.invoices      // Invoice listing (read-only)
client.signatures    // Signature listing (read-only)

ScellApiClient (External API)

For creating invoices and signatures with X-API-Key authentication.

const apiClient = new ScellApiClient(apiKey, {
  baseUrl: 'https://api.scell.io/api/v1', // optional
  timeout: 30000, // optional
});

// Resources
apiClient.invoices    // Create, download, convert invoices
apiClient.signatures  // Create, download, remind, cancel signatures

Companies

// List companies
const { data: companies } = await client.companies.list();

// Create company
const { data: company } = await client.companies.create({
  name: 'My Company',
  siret: '12345678901234',
  address_line1: '1 Rue Example',
  postal_code: '75001',
  city: 'Paris',
});

// Update company
await client.companies.update(companyId, { email: '[email protected]' });

// Delete company
await client.companies.delete(companyId);

// KYC
const kyc = await client.companies.initiateKyc(companyId);
const status = await client.companies.kycStatus(companyId);

Invoices

// List invoices (dashboard)
const { data, meta } = await client.invoices.list({
  direction: 'outgoing',
  status: 'validated',
  from: '2024-01-01',
  per_page: 50,
});

// Get invoice
const { data: invoice } = await client.invoices.get(invoiceId);

// Create invoice (API key required)
const { data: newInvoice } = await apiClient.invoices.create({...});

// Download
const { url, expires_at } = await apiClient.invoices.download(invoiceId, 'pdf');

// Audit trail
const { data: trail, integrity_valid } = await apiClient.invoices.auditTrail(invoiceId);

// Convert format
await apiClient.invoices.convert({ invoice_id: invoiceId, target_format: 'ubl' });

Incoming Invoices (Supplier Invoices)

// List incoming invoices
const { data: incoming, meta } = await apiClient.invoices.incoming({
  status: 'pending',
  seller_siret: '12345678901234',
  from: '2024-01-01',
  min_amount: 100,
  per_page: 50,
});

console.log(`Found ${meta.total} incoming invoices`);

// Accept an incoming invoice
const { data: accepted } = await apiClient.invoices.accept(invoiceId, {
  payment_date: '2024-02-15',
  note: 'Approved by accounting department',
});

// Reject an incoming invoice
const { data: rejected } = await apiClient.invoices.reject(invoiceId, {
  reason: 'Invoice amount does not match purchase order #PO-2024-001',
  reason_code: 'incorrect_amount',
});

// Dispute an incoming invoice
const { data: disputed } = await apiClient.invoices.dispute(invoiceId, {
  reason: 'Billed amount exceeds agreed price by 50 EUR',
  dispute_type: 'amount_dispute',
  expected_amount: 950.00,
});

// Mark an invoice as paid (mandatory in French e-invoicing lifecycle)
const { data: paidInvoice } = await apiClient.invoices.markPaid(invoiceId, {
  payment_reference: 'VIR-2026-0124',
  paid_at: '2026-01-24T10:30:00Z',
  note: 'Payment received via bank transfer',
});

// Download invoice file as PDF (Factur-X with embedded XML)
const pdfBuffer = await apiClient.invoices.downloadFile(invoiceId);
// In Node.js:
import { writeFileSync } from 'fs';
writeFileSync('invoice.pdf', Buffer.from(pdfBuffer));

// Download XML version (UBL/CII standalone)
const xmlBuffer = await apiClient.invoices.downloadFile(invoiceId, 'xml');
writeFileSync('invoice.xml', Buffer.from(xmlBuffer));

Signatures

// List signatures (dashboard)
const { data, meta } = await client.signatures.list({
  status: 'pending',
  per_page: 25,
});

// Get signature
const { data: signature } = await client.signatures.get(signatureId);

// Create signature (API key required)
const { data: newSignature } = await apiClient.signatures.create({...});

// Download files
const { url: signedUrl } = await apiClient.signatures.download(signatureId, 'signed');
const { url: auditUrl } = await apiClient.signatures.download(signatureId, 'audit_trail');

// Send reminder
const { signers_reminded } = await apiClient.signatures.remind(signatureId);

// Cancel
await apiClient.signatures.cancel(signatureId);

Balance

// Get balance
const { data: balance } = await client.balance.get();
console.log(`${balance.amount} ${balance.currency}`);

// Reload balance
const { transaction } = await client.balance.reload({ amount: 100 });

// Update settings
await client.balance.updateSettings({
  auto_reload_enabled: true,
  auto_reload_threshold: 50,
  auto_reload_amount: 200,
  low_balance_alert_threshold: 100,
});

// List transactions
const { data: transactions } = await client.balance.transactions({
  type: 'debit',
  service: 'invoice',
  from: '2024-01-01',
});

Webhooks

// List webhooks
const { data: webhooks } = await client.webhooks.list();

// Create webhook
const { data: webhook } = await client.webhooks.create({
  url: 'https://myapp.com/webhooks/scell',
  events: ['invoice.validated', 'signature.completed', 'balance.low'],
  environment: 'production',
});
// IMPORTANT: Store webhook.secret securely!

// Update webhook
await client.webhooks.update(webhookId, { is_active: false });

// Test webhook
const result = await client.webhooks.test(webhookId);
console.log('Test successful:', result.success);

// Regenerate secret
const { data: updated } = await client.webhooks.regenerateSecret(webhookId);
// Update your stored secret!

// View logs
const { data: logs } = await client.webhooks.logs(webhookId);

// Delete webhook
await client.webhooks.delete(webhookId);

Error Handling

import {
  ScellError,
  ScellAuthenticationError,
  ScellValidationError,
  ScellRateLimitError,
  ScellNotFoundError,
  ScellInsufficientBalanceError,
} from '@scell/sdk';

try {
  await apiClient.invoices.create(data);
} catch (error) {
  if (error instanceof ScellValidationError) {
    console.log('Validation errors:', error.errors);
    error.getAllMessages().forEach(msg => console.log(msg));
  } else if (error instanceof ScellAuthenticationError) {
    console.log('Invalid credentials');
  } else if (error instanceof ScellRateLimitError) {
    console.log(`Rate limited. Retry after ${error.retryAfter}s`);
  } else if (error instanceof ScellInsufficientBalanceError) {
    console.log('Insufficient balance, please reload');
  } else if (error instanceof ScellNotFoundError) {
    console.log('Resource not found');
  } else if (error instanceof ScellError) {
    console.log(`API error: ${error.message} (${error.status})`);
  }
}

Retry Configuration

The SDK automatically retries failed requests for rate limits (429) and server errors (5xx).

import { withRetry, createRetryWrapper } from '@scell/sdk';

// Custom retry options
const client = new ScellClient(token, {
  retry: {
    maxRetries: 5,
    baseDelay: 1000,
    maxDelay: 30000,
    jitterFactor: 0.1,
  },
});

// Disable retry for specific request
const result = await client.companies.list({ skipRetry: true });

// Manual retry wrapper
const result = await withRetry(
  () => apiClient.invoices.create(data),
  { maxRetries: 5 }
);

Webhook Events

| Event | Description | |-------|-------------| | invoice.created | Invoice created | | invoice.validated | Invoice validated | | invoice.transmitted | Invoice transmitted to recipient | | invoice.accepted | Invoice accepted | | invoice.rejected | Invoice rejected | | invoice.error | Invoice processing error | | invoice.incoming.received | Incoming invoice received from supplier | | invoice.incoming.validated | Incoming invoice validated | | invoice.incoming.accepted | Incoming invoice accepted | | invoice.incoming.rejected | Incoming invoice rejected | | invoice.incoming.disputed | Incoming invoice disputed | | invoice.incoming.paid | Incoming invoice marked as paid | | signature.created | Signature request created | | signature.waiting | Waiting for signers | | signature.signed | A signer has signed | | signature.completed | All signers have signed | | signature.refused | Signature refused | | signature.expired | Signature expired | | signature.error | Signature processing error | | balance.low | Balance below low threshold | | balance.critical | Balance below critical threshold |

TypeScript Types

All types are exported and can be imported:

import type {
  Invoice,
  InvoiceStatus,
  InvoiceDirection,
  InvoiceFileFormat,
  CreateInvoiceInput,
  // Incoming invoices
  IncomingInvoiceParams,
  AcceptInvoiceInput,
  RejectInvoiceInput,
  DisputeInvoiceInput,
  MarkPaidInput,
  RejectionCode,
  DisputeType,
  // Signatures
  Signature,
  SignatureStatus,
  CreateSignatureInput,
  Signer,
  // Webhooks
  Webhook,
  WebhookEvent,
  WebhookPayload,
  InvoiceIncomingPaidPayload,
  // Other
  Company,
  Balance,
  Transaction,
} from '@scell/sdk';

Environment Variables

# API Configuration
SCELL_API_URL=https://api.scell.io/api/v1
SCELL_API_KEY=your-api-key
SCELL_WEBHOOK_SECRET=whsec_your-webhook-secret

Requirements

  • Node.js 18.0.0 or higher (for native fetch)
  • TypeScript 5.0 or higher (for development)

License

MIT