cargos-api
A small Python library to build and submit rental booking records to the Italian Police Ca.R.G.O.S. API.
Table of Contents
- cargos-api
- Table of Contents
- Install
- Quick start
- Formatting the data
- API usage notes
- Inspecting the record as a mapping
This repository was created to interact with the Ca.R.G.O.S. APIs for a client who needed to automate the submission of rental booking details to the Italian Police.
The official documentation (see docs.pdf) is difficult to follow and sparse on examples. The goal of this project is to provide a clean, documented Python module that makes the integration straightforward and consistent.
This module handles all the location -> id conversions, data formatting and submission to the Ca.R.G.O.S. API.
Install
pip install cargos-api
Quick start
from cargos_api import CargosAPI, CargosRecordMapper, models as m
# Credentials
api = CargosAPI(username="...", password="...", api_key="...48-chars...")
# Build a booking using codes from your catalogs (examples below use placeholder codes)
booking = m.BookingData(
contract_id="CONTR-001",
contract_datetime="2025-01-01T10:00:00", # ISO-like; mapper outputs dd/mm/YYYY HH:MM
payment_type_code="1", # per Ca.R.G.O.S. table
checkout_datetime="2025-01-01T11:00:00",
checkout_place=m.Address(location_code=403015146, street="Via X 1"), # Roma (example)
checkin_datetime="2025-01-02T11:00:00",
checkin_place=m.Address(location_code=402020327, street="Via Y 2"), # Verona (example)
operator=m.Operator(
id="SYSTEM",
agency_id="1",
agency_name="ACME",
agency_place_code=403015146,
agency_address="Via Z 3",
agency_phone="0000",
),
car=m.Car(
type_code="A", # vehicle type code (per table)
brand="Fiat",
model="Panda",
plate="AB123CD",
color="Bianco",
),
customer=m.Customer(
surname="Rossi",
name="Mario",
birth_date="1990-01-01",
birth_place_code=401028044, # Genova (example)
citizenship_code=100000001, # Italia (example)
id_doc_type_code="CI",
id_doc_number="XYZ12345",
id_doc_issuing_place_code=401028044,
driver_licence_number="LIC123456",
driver_licence_issuing_place_code=401028044,
contact="0000000000",
),
)
# Map to Ca.R.G.O.S. fixed-width record (1505 chars)
record = CargosRecordMapper().build_record(booking)
# Validate or send
api.check_contracts([record])
# api.send_contracts([record])
Formatting the data
This library exposes typed dataclasses in cargos_api.models that you fill with your normalized data and pass to the mapper.
BookingData:contract_id,contract_datetime,payment_type_code,checkout_datetime,checkout_place(Address),checkin_datetime,checkin_place(Address),operator(Operator),car(Car),customer(Customer),second_driver(optional)Customer:surname,name,birth_date,birth_place_code,citizenship_code,residence(Address, optional),id_doc_type_code,id_doc_number,id_doc_issuing_place_code,driver_licence_number,driver_licence_issuing_place_code,contactSecondDriver: same asCustomer(withoutresidence); if present, all fields are mandatoryCar:type_code,brand,model,plate,color(optional),has_gps(optional),has_immobilizer(optional)Address:location_code,streetOperator:id,agency_id,agency_name,agency_place_code,agency_address,agency_phone
Ca.R.G.O.S. Tracciato Record Parameters
| Name | Required | Description |
|---|---|---|
| CONTRATTO_ID | ✅ | Unique contract identifier |
| CONTRATTO_DATA | ✅ | Contract date (format gg/mm/aaaa hh:mm) |
| CONTRATTO_TIPOP | ✅ | Payment type (see TABELLA TIPO PAGAMENTI) |
| CONTRATTO_CHECKOUT_DATA | ✅ | Checkout date (format gg/mm/aaaa hh:mm) |
| CONTRATTO_CHECKOUT_LUOGO_COD | ✅ | Police location code (TABELLA LUOGHI POLIZIA) |
| CONTRATTO_CHECKOUT_INDIRIZZO | ✅ | Checkout address (length > 3) |
| CONTRATTO_CHECKIN_DATA | ✅ | Check-in date (format gg/mm/aaaa hh:mm) |
| CONTRATTO_CHECKIN_LUOGO_COD | ✅ | Police location code (TABELLA LUOGHI POLIZIA) |
| CONTRATTO_CHECKIN_INDIRIZZO | ✅ | Check-in address (length > 3) |
| OPERATORE_ID | ✅ | Operator identifier |
| AGENZIA_ID | ✅ | Agency identifier (unique) |
| AGENZIA_NOME | ✅ | Agency name |
| AGENZIA_LUOGO_COD | ✅ | Police location code (TABELLA LUOGHI POLIZIA) |
| AGENZIA_INDIRIZZO | ✅ | Agency address (length > 3) |
| AGENZIA_RECAPITO_TEL | ✅ | Agency phone number (length > 3) |
| VEICOLO_TIPO | ✅ | Vehicle type (TABELLA TIPO VEICOLO) |
| VEICOLO_MARCA | ✅ | Vehicle brand |
| VEICOLO_MODELLO | ✅ | Vehicle model |
| VEICOLO_TARGA | ✅ | Vehicle plate number (length > 3) |
| VEICOLO_COLORE | ❌ | Vehicle color |
| VEICOLO_GPS | ❌ | GPS presence flag |
| VEICOLO_BLOCCOM | ❌ | Immobilizer presence flag |
| CONDUCENTE_CONTRAENTE_COGNOME | ✅ | Driver’s surname |
| CONDUCENTE_CONTRAENTE_NOME | ✅ | Driver’s name |
| CONDUCENTE_CONTRAENTE_NASCITA_DATA | ✅ | Driver’s date of birth (gg/mm/aaaa) |
| CONDUCENTE_CONTRAENTE_NASCITA_LUOGO_COD | ✅ | Birthplace (COMUNE ITALIANO or STATO ESTERO) |
| CONDUCENTE_CONTRAENTE_CITTADINANZA_COD | ✅ | Citizenship code (TABELLA LUOGHI POLIZIA) |
| CONDUCENTE_CONTRAENTE_RESIDENZA_LUOGO_COD | ❌* | Residence (COMUNE ITALIANO or STATO ESTERO) |
| CONDUCENTE_CONTRAENTE_RESIDENZA_INDIRIZZO | ❌* | Residence address (e.g., “VIA DEL CASTRO PRETORIO 10, ROMA”) |
| CONDUCENTE_CONTRAENTE_DOCIDE_TIPO_COD | ✅ | Document type (TABELLA DOCUMENTI POLIZIA) |
| CONDUCENTE_CONTRAENTE_DOCIDE_NUMERO | ✅ | Document number (length > 4) |
| CONDUCENTE_CONTRAENTE_DOCIDE_LUOGORIL_COD | ✅ | Document issuing place (COMUNE ITALIANO or STATO ESTERO) |
| CONDUCENTE_CONTRAENTE_PATENTE_NUMERO | ✅ | Driver’s license number (length > 4) |
| CONDUCENTE_CONTRAENTE_PATENTE_LUOGORIL_COD | ✅ | License issuing place (COMUNE ITALIANO or STATO ESTERO) |
| CONDUCENTE_CONTRAENTE_RECAPITO | ❌ | Driver’s contact number |
| CONDUCENTE2_COGNOME | ❌** | Second driver surname |
| CONDUCENTE2_NOME | ❌** | Second driver name |
| CONDUCENTE2_NASCITA_DATA | ❌** | Second driver date of birth (gg/mm/aaaa) |
| CONDUCENTE2_NASCITA_LUOGO_COD | ❌** | Second driver birthplace (COMUNE ITALIANO or STATO ESTERO) |
| CONDUCENTE2_CITTADINANZA_COD | ❌** | Second driver citizenship (TABELLA LUOGHI POLIZIA) |
| CONDUCENTE2_DOCIDE_TIPO_COD | ❌** | Second driver document type |
| CONDUCENTE2_DOCIDE_NUMERO | ❌** | Second driver document number |
| CONDUCENTE2_DOCIDE_LUOGORIL_COD | ❌** | Second driver document issuing place |
| CONDUCENTE2_PATENTE_NUMERO | ❌** | Second driver license number (length > 4) |
| CONDUCENTE2_PATENTE_LUOGORIL_COD | ❌** | Second driver license issuing place |
| CONDUCENTE2_RECAPITO | ❌** | Second driver contact number |
Notes
*→ Both residence fields (LUOGO_COD and INDIRIZZO) must be filled together, otherwise the data is rejected.**→ If a second driver is present, all fields underCONDUCENTE2_become mandatory.- Each block of records must not contain more than 100 contracts (rows).
Required fields
The mapper validates inputs and raises InvalidInput if anything is missing:
- booking:
id,creation_date,from_date,to_date - customer:
birth_date,firstname,lastname,birth_place,citizenship, and one of (document_id,driver_licence_number) and one of (cellphone,email) - car:
brand,model,plate,color - delivery_place:
address_city,address - return_place:
address_city,address - operator:
id,agency_id,agency_name,city,address,phone
Optional fields
Customer.addressis emitted as free-text in the record when providedAddress.address_countryis currently not mapped to a dedicated field
Lookup tables (locations, documents, payments, vehicles)
- Packaged CSV catalogs under
cargos_api/data/are used at runtime:luoghi.csv(location/country codes)tipo_documento.csv(document types)tipo_pagamento.csv(payment types)tipo_veicolo.csv(vehicle types)
- Lookups are case-insensitive; expired locations (rows with
DataFineVal) are ignored - Use
CatalogLoaderif you need to resolve human-readable labels to Ca.R.G.O.S. codes in your app:from cargos_api.locations_loader import CatalogLoader code = CatalogLoader().document_type_code('CI')
- If a required code is not found, the mapper raises a
ValueError
Inspecting the record as a mapping
Sometimes you want to verify exactly what each field contains after padding/truncation. You can ask the mapper to return both the fixed-width string and a JSON-like mapping of field names to their exact slices by passing with_map=True:
from cargos_api import CargosRecordMapper, models as m
booking = m.BookingData(
contract_id="123", contract_datetime="2025-01-05T09:00:00", payment_type_code="1",
checkout_datetime="2025-01-06T10:00:00", checkout_place=m.Address(location_code=403015146, street="Via A 10"),
checkin_datetime="2025-01-07T10:00:00", checkin_place=m.Address(location_code=403015146, street="Via B 20"),
operator=m.Operator(id="SYS", agency_id="AG1", agency_name="ACME", agency_place_code=403015146, agency_address="Via C 30", agency_phone="06..."),
car=m.Car(type_code="A", brand="VW", model="Golf", plate="ZZ999ZZ"),
customer=m.Customer(
surname="Bianchi", name="Anna", birth_date="1985-05-20",
birth_place_code=403015146, citizenship_code=100000001,
id_doc_type_code="CI", id_doc_number="X1", id_doc_issuing_place_code=403015146,
driver_licence_number="L1", driver_licence_issuing_place_code=403015146,
),
)
record, mapping = CargosRecordMapper().build_record(booking, with_map=True)
print(len(record)) # 1505
print(mapping["CONTRATTO_ID"]) # 50-char padded slice for the contract ID
API usage notes
CargosAPI.get_token()fetches the token using HTTP Basic auth- The
api_keymust be exactly 48 characters: first 32 chars are the AES key and the last 16 chars are the IV used to encrypt the bearer token - Use
check_contracts(records)to validate records before submission; usesend_contracts(records)to submit them
Example: minimal end-to-end flow
from cargos_api import CargosAPI, CargosRecordMapper, models as m
api = CargosAPI(username="ORG", password="PASS", api_key="...48-chars...")
booking = m.BookingData(
contract_id="123",
contract_datetime="2025-01-05T09:00:00",
payment_type_code="1",
checkout_datetime="2025-01-06T10:00:00",
checkout_place=m.Address(location_code=403015146, street="Via A 10"),
checkin_datetime="2025-01-07T10:00:00",
checkin_place=m.Address(location_code=403015146, street="Via B 20"),
operator=m.Operator(id="SYS", agency_id="AG1", agency_name="ACME", agency_place_code=403015146, agency_address="Via C 30", agency_phone="06..."),
car=m.Car(type_code="A", brand="VW", model="Golf", plate="ZZ999ZZ", color="Nero"),
customer=m.Customer(
surname="Bianchi", name="Anna", birth_date="1985-05-20",
birth_place_code=403015146, citizenship_code=100000001,
id_doc_type_code="CI", id_doc_number="DOC1", id_doc_issuing_place_code=403015146,
driver_licence_number="LIC1", driver_licence_issuing_place_code=403015146,
contact="333...",
),
)
record = CargosRecordMapper().build_record(booking)
api.check_contracts([record])
Troubleshooting
ValueError: location not found → check the spelling of city/country namesInvalidInput: missing fields → read the error message for which fields are missingInvalidResponse: HTTP errors → network issues or server response witherrorefield
Building something with it?
If you nee dhelp implementing this in your project, please reach out.
Author
If you found this project helpful or interesting, consider starring the repo and following me for more security research and tools, or buy me a coffee to keep me up
Metadata
Release files for cargos-api 0.2.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| cargos_api-0.2.4.tar.gz | 127.4 kB | With provenance | Details for cargos_api-0.2.4.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| cargos_api-0.2.4-py3-none-any.whl | Python 3 | none | any | Details for cargos_api-0.2.4-py3-none-any.whl |
Total release size: 252.6 kB
Release files / cargos_api-0.2.4.tar.gz
| Download URL | cargos_api-0.2.4.tar.gz |
|---|---|
| Size | 127.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8c8d24ca6869c3c8f7d5c2677e1e765e3d12aa306d89e6f86808f5b1b2a1a1d3
|
|
BLAKE2b-256 checksum How to use checksums |
e60afc2c4e732f3f746a122f5c4c7057c68443ed0521a3deec464f2dc4d7c565
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 7, 2025.
Transparency logRelease files / cargos_api-0.2.4-py3-none-any.whl
| Download URL | cargos_api-0.2.4-py3-none-any.whl |
|---|---|
| Size | 125.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
42dc4a95c5beba001f7ffbb32e33e8bb6c5a2e504774bfdaad3461df8d67ec02
|
|
BLAKE2b-256 checksum How to use checksums |
118f304e8b53d7c1cd745f985ebb44f0a1dd453fcf6b7b194003ba950d4c7c57
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 7, 2025.
Transparency logSecurity for cargos-api 0.2.4
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for cargos_api-0.2.4.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 7, 2025.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.