Production SaaS · 2020–2024 · Three generations

Amatista: Debt-Collection Management Platform

A Rails platform I designed and built for a Chilean collections firm. It ran their day-to-day operation for about a year, managing debt records in the thousands, before the company wound down. I later modernized it twice, which makes it a good record of how my engineering standards evolved over four years.

Role: Sole designer and builder

The problem

A collections operation lives or dies by its working queue: which debtors to contact today, what happened in every previous contact, and which debts a promise-to-pay actually covers. The firm was running this on spreadsheets: debtor data, debt instruments, and contact history scattered across workbooks, with no shared state between agents and no audit trail.

What I built

flowchart LR
XLS["Excel workbooks
5 sheet types"] --> IMP["All-or-nothing importer
per-row validation"] IMP --> RUT["Chilean RUT validator
mod-11 check digit"] RUT --> SII["Live SII lookup
official company names"] SII --> DB[("PostgreSQL
12-table domain")] DB --> Q["Agent work queues
22-state workflow"] Q --> OUT["Debtor outreach
self-logging actions"] OUT --> DB

Spreadsheets in, accountable collection workflow out: every import validated row by row, every outreach action logged automatically.

  • A 12-table domain model covering creditor companies and their contacts, debtors with multiple phones, addresses, and emails, debt instruments (principal, payments, due dates, installments, promise-to-pay dates, co-debtors), and a full collection-action history.
  • A 22-state collection workflow, from positive contact through field visits to write-off suggestions and judicial referral, encoding the firm's actual operating procedure. Every action recorded who acted, what happened, and when the next action was due.
  • Bulk Excel import as an all-or-nothing transaction: one workbook with five sheet types, validated row by row with per-row error reporting, so a typo on line 200 never silently corrupted the book.
  • A from-scratch Chilean RUT validator (the national tax ID): mod-11 check-digit verification, normalization, and formatting.
  • Live integration with Chile's SII (the national tax authority) to resolve official company names during import, so creditor records matched the legal registry.
  • Self-logging outreach: emailing a debtor automatically created the corresponding collection action with a follow-up date, so the contact history could not drift from reality.
  • Role-based access (admin, supervisor, agent), an admin back office, and Excel exports for reporting.

Three generations

The 2020 system (Rails 6, deployed with Capistrano to a VPS) ran the operation until the firm dissolved. In 2023 I ported it to Rails 7 with Hotwire to keep it current, and then chose to rebuild it clean: English domain names, service objects, a multi-stage Docker build running as a non-root user, and a hand-rolled replacement for the abandoned SII gem, including seeding the full catalog of 674 SII economic-activity codes. The schema had stabilized within seven months of production use and survived all three generations nearly unchanged, which I take as evidence the domain model was right.

Outcome

About a year of daily production use by a real collections operation, with debt records in the thousands. The company was dissolved for reasons unrelated to the software; the platform was working when it stopped being needed.

arrow_upward