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

cargos-sdk

v0.2.0

Published

TypeScript SDK for Italian State Police CARGOS car rental contract submission service

Readme

CARGOS API TypeScript SDK

npm version Publish to npm

⚠️ Disclaimer: This SDK is for personal/internal use only. It is not production-ready, not officially supported, and comes with no guarantees. Use at your own risk.

TypeScript SDK for the Cargos Polizia di Stato service - a mandatory system for Italian car rental companies to report rental contracts to the Italian State Police for terrorism prevention and public safety purposes.

Legal Basis: Art. 17, Law Decree October 4, 2018, n. 113

Overview

The Ca.R.G.O.S. (Centro Anagrafe Rifornimento Gestione Operativa Sicurezza) service requires all Italian car rental companies to report rental contract data including:

  • Driver identification information
  • Vehicle details
  • Rental duration and locations
  • Agency information

The system verifies driver eligibility and checks for security concerns in real-time.

Features

  • ✅ Full TypeScript support with strong typing
  • ✅ AES-256-CBC encryption (3DES deprecated but supported)
  • ✅ Token management with automatic refresh
  • ✅ Contract validation before submission
  • ✅ Batch processing (automatic chunking for 100+ contracts)
  • ✅ Check contracts before sending
  • ✅ Bundled static coding tables with lookup helpers
  • ✅ Runtime table download methods for fresh snapshots
  • ✅ Comprehensive error handling

Installation

npm install cargos-sdk
# or
yarn add cargos-sdk

Or import directly:

import { CargosClient, RentalContract, PaymentType } from './cargos-sdk';

Bundled Reference Tables

Reference tables are bundled in the SDK and can be used without authentication:

import { LOCATIONS, getLocationCode, lookupLocation } from 'cargos-sdk';

const romeCode = getLocationCode('ROMA'); // 412058091
const cityName = lookupLocation(412058091); // "ROMA"
const italyCode = LOCATIONS['ITALIA']; // 100000100

Table snapshot metadata is published in TABLES_LAST_UPDATED.md. If the snapshot looks stale, please open an issue.

To regenerate the bundled snapshot after replacing CSV exports in scripts/tables-csv/:

pnpm run generate:tables

To download the latest CSVs from CARGOS first:

pnpm run download:tables

Quick Start

import { CargosClient, RentalContract, PaymentType, VehicleType, DocumentType } from './cargos-sdk';

const client = new CargosClient(
  'your_username',
  'your_password',
  'your_api_key_at_least_48_chars'
);

const contract: RentalContract = {
  id: 'RENT-2024-001234',
  createdDate: new Date('2024-02-01T10:30:00'),
  paymentType: PaymentType.CARD,
  checkoutDate: new Date('2024-02-01T14:00:00'),
  checkoutLocation: { code: 80 }, // Rome
  checkoutAddress: 'Via del Castro Pretorio 10, Roma',
  checkinDate: new Date('2024-02-08T18:00:00'),
  checkinLocation: { code: 80 },
  checkinAddress: 'Via del Castro Pretorio 10, Roma',
  operatorId: 'OP-001',
  agency: {
    id: 'AG-ROME-001',
    name: 'RentCar Roma Center',
    location: { code: 80 },
    address: 'Via del Castro Pretorio 10, Roma',
    phone: '+39-06-12345678'
  },
  vehicle: {
    type: VehicleType.CAR,
    brand: 'Fiat',
    model: '500',
    plate: 'AB123CD',
    color: 'Nero',
    hasGPS: true
  },
  mainDriver: {
    surname: 'Rossi',
    name: 'Mario',
    birthDate: new Date('1980-05-15'),
    birthPlace: { code: 80 },
    citizenship: { code: 380 }, // Italy
    documentType: DocumentType.ID_CARD,
    documentNumber: 'AA123456789',
    documentIssuePlace: { code: 80 },
    licenseNumber: 'IT1234567890',
    licenseIssuePlace: { code: 80 }
  }
};

// Send contract
const result = await client.sendContracts([contract]);

if (result.responses[0].esito) {
  console.log('✓ Sent:', result.responses[0].transactionid);
} else {
  console.error('✗ Error:', result.responses[0].errore);
}

Authentication

Get credentials from Questura (Provincial Police Station):

  1. Request CARGOS portal activation from your provincial police station
  2. Receive: username, password, and API_KEY (48+ characters for AES)

Important:

  • API Key must be at least 48 characters for AES encryption (recommended)
  • For AES: First 32 chars are the key, last 16 are the IV
  • 3DES (deprecated) requires minimum 24 characters
const client = new CargosClient(
  process.env.CARGOS_USERNAME,
  process.env.CARGOS_PASSWORD,
  process.env.CARGOS_API_KEY // min 48 chars
);

Core Methods

checkContracts()

Validate contracts before sending (recommended for testing):

const checkResult = await client.checkContracts([contract]);

for (const response of checkResult.responses) {
  if (!response.esito) {
    console.error('Validation error:', response.errore?.error_description);
  }
}

Returns row-by-row validation errors (syntax and semantics).

sendContracts()

Send up to 100 contracts in a single request:

const sendResult = await client.sendContracts([contract1, contract2]);

for (const response of sendResult.responses) {
  if (response.esito) {
    console.log('Transaction ID:', response.transactionid);
  } else {
    console.error('Error:', response.errore);
  }
}

Limits:

  • Maximum 100 contracts per request
  • Use batchSendContracts() for 100+

batchSendContracts()

Automatically batch large contract sets:

const contracts: RentalContract[] = [...]; // 250 contracts

const batchResults = await client.batchSendContracts(contracts);
// Automatically split into 3 requests (100, 100, 50)

getTable()

Download specific coding table:

import { TableId } from './cargos-sdk';

const paymentTypes = await client.getTable(TableId.PAYMENT_TYPE);
const buffer = paymentTypes.file;

if (paymentTypes.esito && buffer) {
  const csv = buffer.toString('utf-8');
  console.log(csv);
}

Available tables:

  • TableId.PAYMENT_TYPE (0)
  • TableId.LOCATIONS (1)
  • TableId.VEHICLE_TYPE (2)
  • TableId.DOCUMENT_TYPE (3)

getAllTables()

Download all coding tables at once (useful when you need a fresh runtime snapshot):

const tables = await client.getAllTables();

for (const [name, buffer] of tables) {
  console.log(`${name}: ${buffer.length} bytes`);
  
  // Parse as CSV with '#' separator
  const map = parseTableCSV(buffer);
  for (const [code, value] of map) {
    console.log(`  ${code} => ${value}`);
  }
}

Data Types

PaymentType

enum PaymentType {
  CASH = 'C',
  CARD = 'T',
  BANK = 'B',
  OTHER = 'A'
}

VehicleType

enum VehicleType {
  CAR = 'A',
  MOTORCYCLE = 'M',
  TRUCK = 'C',
  OTHER = 'A'
}

DocumentType

enum DocumentType {
  PASSPORT = 'P',
  ID_CARD = 'C',
  DRIVERS_LICENSE = 'P',
  VISA = 'V',
  RESIDENCE_PERMIT = 'S'
}

Location

Reference locations by police code (Questura/Comune):

interface Location {
  code: number;  // Police location code (e.g., 80 for Rome)
  name?: string; // Human-readable name
}

Common location codes:

  • ROMA: 412058091
  • MILANO: 403015146
  • NAPOLI: 415063049
  • TORINO: 401001272
  • FIRENZE: 409048017

Use bundled LOCATIONS for offline lookups, or getAllTables() if you need a fresh runtime export.

Driver

interface Driver {
  surname: string;                          // Max 50 chars
  name: string;                             // Max 30 chars
  birthDate: Date;                          // DD/MM/YYYY
  birthPlace: Location;                     // Comune or foreign country
  citizenship: Location;                    // Country code
  residencePlace?: Location;                // Optional location
  residenceAddress?: string;                // Optional, max 150 chars
  documentType: DocumentType;               // ID type
  documentNumber: string;                   // Max 20 chars
  documentIssuePlace: Location;             // Where ID was issued
  licenseNumber: string;                    // Driver's license, max 20 chars
  licenseIssuePlace: Location;              // Where license issued
  phone?: string;                           // Optional, max 20 chars
}

Important: Secondary driver must have ALL fields populated or omitted entirely.

Vehicle

interface Vehicle {
  type: VehicleType;                        // From enum
  brand: string;                            // Max 50 chars
  model: string;                            // Max 100 chars
  plate: string;                            // License plate, max 15 chars
  color?: string;                           // Optional, max 50 chars
  hasGPS?: boolean;                         // Optional, 0 or 1
  hasEngineBlock?: boolean;                 // Optional, 0 or 1
}

Agency

interface Agency {
  id: string;                               // Unique, max 30 chars
  name: string;                             // Max 70 chars
  location: Location;                       // Office location
  address: string;                          // Max 150 chars
  phone: string;                            // Max 20 chars, > 3 chars
}

RentalContract

interface RentalContract {
  id: string;                               // Unique, max 50 chars
  createdDate: Date;                        // Contract signature time
  paymentType: PaymentType;                 // From enum
  checkoutDate: Date;                       // Vehicle pickup time
  checkoutLocation: Location;               // Pickup location
  checkoutAddress: string;                  // Pickup address, max 150 chars
  checkinDate: Date;                        // Vehicle return time
  checkinLocation: Location;                // Return location
  checkinAddress: string;                   // Return address, max 150 chars
  operatorId: string;                       // Employee ID, max 50 chars
  agency: Agency;                           // Agency details
  vehicle: Vehicle;                         // Vehicle details
  mainDriver: Driver;                       // Primary driver
  secondaryDriver?: Driver;                 // Optional second driver
}

Validation

Use isValidContractData() to validate before sending:

import { isValidContractData } from './cargos-sdk';

const errors = isValidContractData(contract);

if (errors.length > 0) {
  console.error('Validation errors:');
  errors.forEach(err => console.error(`  - ${err}`));
}

Checks:

  • Contract ID length (1-50)
  • Agency ID length (1-30)
  • Address minimum length (3)
  • Document number minimum length (5)
  • License number minimum length (5)
  • Vehicle plate minimum length (3)
  • Secondary driver completeness

Workflow

Recommended Process

  1. Prepare contract data from your rental management system
  2. Validate with isValidContractData()
  3. Check with checkContracts() (optional but recommended)
  4. Use bundled tables (LOCATIONS, getLocationCode(), etc.) for local lookups
  5. Send with sendContracts() or batchSendContracts()
  6. Store transaction IDs for audit trail

Code Example

const client = new CargosClient(username, password, apiKey);

// Prepare contracts
const contracts = readContractsFromDatabase();

// Validate
for (const contract of contracts) {
  const errors = isValidContractData(contract);
  if (errors.length > 0) {
    logger.error(`Contract ${contract.id} invalid:`, errors);
    continue; // Skip invalid contracts
  }
}

// Optional: Check before sending
const checkResult = await client.checkContracts(contracts);
for (const response of checkResult.responses) {
  if (!response.esito) {
    logger.warn(`Check failed: ${response.errore?.error_description}`);
  }
}

// Send in batches (auto-chunked to 100 max)
try {
  const results = await client.batchSendContracts(contracts);

  for (let i = 0; i < results.length; i++) {
    const result = results[i];
    for (let j = 0; j < result.responses.length; j++) {
      const response = result.responses[j];
      if (response.esito) {
        // Store transaction ID
        storeTransactionId(contracts[i * 100 + j].id, response.transactionid);
      } else {
        logger.error(`Failed: ${response.errore?.error_description}`);
      }
    }
  }
} catch (error) {
  logger.error('Batch send failed:', error);
}

Error Handling

All responses include error information:

interface ErrorResponse {
  error: string;
  error_description: string;
  error_code: number;
  timestamp: string;
}

Common error codes:

  • 1: Invalid token
  • 2: Invalid API key
  • 3: Syntax error in contract data
  • 4: Semantic error in contract data
  • 5: Contract already sent
  • 6: Invalid location code
  • 7: Invalid payment type

Example handling:

try {
  const result = await client.sendContracts([contract]);
  
  if (result.error) {
    console.error(`API Error (${result.error.error_code}):`, result.error.error_description);
    return;
  }

  for (const response of result.responses) {
    if (!response.esito) {
      console.error(`Contract Error (${response.errore?.error_code}):`, 
                    response.errore?.error_description);
    }
  }
} catch (error) {
  console.error('Network or parsing error:', error);
}

Security

Token Management

Tokens are:

  • Encrypted with your API Key using AES-256-CBC
  • Cached in memory with 5-minute safety margin before expiry
  • Automatically refreshed when expired
// Token is automatically managed
const result = await client.sendContracts(contracts);
// Token obtained/refreshed internally as needed

API Key Security

Never:

  • Hardcode API keys in source
  • Commit API keys to version control
  • Log API keys or encrypted tokens
  • Share credentials via email

Always:

  • Use environment variables
  • Rotate keys periodically
  • Use secure key management (vaults, KMS)
  • Log only transaction IDs, not tokens
// ✅ Good
const apiKey = process.env.CARGOS_API_KEY;
const client = new CargosClient(username, password, apiKey);

// ❌ Bad
const client = new CargosClient('user', 'pass', 'HARDCODED_KEY_HERE');

HTTPS

All communication with Cargos service is over HTTPS (TLS 1.2+).

Data Retention

  • Contract data is retained for maximum 7 days
  • Transaction IDs should be stored locally for audit
  • Implement your own retention policies per company requirements

Troubleshooting

"Invalid API Key" Error

  • Check API Key length (must be 48+ for AES)
  • Verify first 32 chars are the encryption key, last 16 are IV
  • Confirm API Key hasn't expired (request new from police station)

"Token not valid" Error

  • Token is automatically refreshed; retry the operation
  • Check username/password are correct
  • Verify credentials haven't been reset

"Invalid location code" Error

  • Check bundled LOCATIONS first for valid codes
  • If needed, refresh from API with getAllTables()
  • Location codes reference specific police jurisdictions
  • Use exact numeric code from the table

"Semantic error" in contract data

  • Use checkContracts() first to see detailed validation errors
  • Check all required fields are populated
  • Verify field lengths match specifications
  • Confirm date formats are DD/MM/YYYY

"Maximum 100 contracts per request"

  • Use batchSendContracts() instead of sendContracts()
  • Automatically handles batching

Examples

See cargos-example.ts for:

  • Basic single contract submission
  • Batch processing (100+ contracts)
  • Bundled table lookups
  • Downloading and parsing tables
  • Secondary driver handling
  • Error handling patterns

API Reference

CargosClient

class CargosClient {
  constructor(username: string, password: string, apiKey: string);
  
  // Methods
  async getToken(): Promise<string>;
  async checkContracts(contracts: RentalContract[]): Promise<CheckResponse>;
  async sendContracts(contracts: RentalContract[]): Promise<SendResponse>;
  async batchSendContracts(contracts: RentalContract[]): Promise<SendResponse[]>;
  async getTable(tableId: TableId): Promise<TableResponse>;
  async getAllTables(): Promise<Map<string, Buffer>>;
}

Utilities

// Validate contract data
function isValidContractData(contract: RentalContract): string[];

// Parse table CSV (# separated, UTF-8)
function parseTableCSV(data: Buffer): Map<string, string>;

// Bundled table lookups
function getLocationCode(locationName: string): number | undefined;
function lookupLocation(code: number): string | undefined;
const TABLES_LAST_UPDATED_AT: string;

Technical Details

Record Format

Contracts are formatted as fixed-width 1505-character records following CARGOS specifications:

  • Fields: 46 data elements
  • Total width: 1505 characters
  • Multiple records per batch (max 100)
  • Character set: UTF-8
  • Date format: DD/MM/YYYY or DD/MM/YYYY HH:MM

Encryption

AES Method (recommended, default):

  • Algorithm: AES-256-CBC
  • Mode: Cipher Block Chaining
  • Padding: PKCS7
  • Key size: 256 bits (32 bytes)
  • IV size: 128 bits (16 bytes)
  • Output: Base64
API Key (48+ chars):
├─ Chars 0-31: Encryption Key (32 bytes)
└─ Chars 32-47: Initialization Vector (16 bytes)

3DES Method (deprecated):

  • Algorithm: Triple DES
  • Mode: ECB
  • Padding: PKCS7
  • Key size: 192 bits (24 bytes)
  • Output: Base64

Authentication

Uses OAuth 2.0 with Bearer token:

Authorization: Bearer <encrypted_token>
Organization: <username>

Legal Compliance

This SDK implements the technical specifications from:

  • Decreto Legge 4 October 2018, n. 113 (Art. 17)
  • Decreto Ministeriale 29 October 2021
  • CARGOS Architectural Documentation (Rev. 01, 06/02/2024)

Support

For issues or questions:

  1. Check checkContracts() output for validation details
  2. Check TABLES_LAST_UPDATED.md and open an issue if an update is needed
  3. Review CARGOS documentation: https://cargos.poliziadistato.it
  4. Contact your provincial Questura

License

MIT

Changelog

1.0.0

  • Initial release
  • Full CARGOS API implementation
  • AES-256 encryption
  • Batch processing
  • Contract validation
  • Table downloads