Pre-approved corporate credit, decided inside Exasol.
An ExaThon India 2026 project. A bank's pre-approved corporate lending, decided inside Exasol Personal, built on the Exasol Personal Local Starter Kit and deployed on AWS. Sapphire Bank is fictional and all data is synthetic.
Anamika · Product Manager @ Equitas Small Finance Bank
The bank already holds the data. Offers are still slow and manual.
Sapphire Bank's credit team can't make pre-approved offers to the corporate customers it already serves, because the evidence sits in six separate systems and is gathered by hand, one company at a time. If this worked, every corporate customer would be decided in one run, and the relationship manager would call with an offer and a negotiation limit already set.
Analyse the customers we have, then offer them credit first.
PDprobability of default
How likely the company is not to repay, from a model trained on the bank's own past loans. Graded A1 (under 1%) to D (12% and above).
RAROCrisk-adjusted return
(Rate − cost of funds − operating cost − PD × loss given default) ÷ capital held. Every offer must clear the 15% hurdle.
DSCRdebt service coverage
Cash the company generates ÷ what the loan costs it each year. At least 1.25 for the offer, 1.10 at the negotiation limit.
GSTgoods and services tax
Monthly sales each company files with the tax network, GSTN, for 2 to 10 years per company. Late filings and a gap to bank credits are warning signs.
CIBILIndia's credit bureau
A score for every partner and a commercial rank (CMR) for every company. A partner below 600 or a CMR of 9 or worse means no offer.
AAAccount Aggregator
With the customer's consent, the partners' accounts at other banks. The consent expires, so Exasol purges the data on time, with proof.
What changes for the bank.
The published run R20261102-1037 on 1,000 companies. Every figure below is read from Exasol, from that run.
of pre-approved offers to 531 approved companies, against a credit need on file of ₹1,156.6 crore
is the top of the negotiation room, the limits Exasol set per company for the relationship managers
APPROVE, REFER and DECLINE, decided in one call, each with its reasons
in the Top 50, at an average rate of 11.0% and a RAROC of 22.7%
per run, so the whole book is decided again after every policy change or data refresh
to a credit committee for a deal inside the limit. The relationship manager negotiates within it, and only a deal beyond it goes to loan operations
Everything meets in Exasol, and leaves as a decision.
Bank data is read in place, 3rd party data is fetched via API by UDFs, and every person and the AI read results as their own Exasol user.
One engine does seven jobs, and the data never leaves it.
Exasol Personal used to the full, with open-source extensions from both GitHub organisations, exasol and exasol-labs.
Virtual schemas push the work down. Nothing is copied.
Feature step 7 aggregates core banking by account and month. Exasol hands the join, the filter and the GROUP BY to Oracle.
Monthly totals per account come back. The ~850,000 transactions stay in Oracle, and the bank keeps one system of record.
Python UDFs fetch 3rd party data via API, in 16 batches.
A SET script groups the keys into 16 buckets by the first hex digit of an MD5 hash. Exasol runs one UDF instance per bucket, 4 at a time on the node's 4 cores, and each call makes 3 attempts before it fails naming the service.
One Lua call runs the whole analysis, step by logged step.
EXECUTE SCRIPT CREDORA.RUN_ANALYSIS('<run>') commits after every step, so the web app can watch the run as it happens.
Measured on the live deployment for 1,000 companies, on one r6i.xlarge node.
A failed step is logged with its error and stops the run. The last published run stays visible in the app.
The models are trained, stored and scored inside Exasol.
Past loans are read live from PostgreSQL. Two models train on the same loans and the same hold-out. Only the active one decides.
PREFERRING ranks on three goals at once, with no weights.
The skyline is every approved company that no other beats on return, risk and amount together. Peel it off, take the next skyline, until 50 are ranked.
On the measured run two layers held 63 companies by the time 50 were reached, in 0.03 s. The best 50 approved go to the relationship managers.
One certified definition serves the app, the analyst and the AI.
CREDORA_SEM.PORTFOLIO holds certified metrics. The SQL preprocessor compiles semantic queries per role, inside Exasol.
Exasol, not the app, decides who sees what.
Four people, four Exasol users, and an AI user for each with the same role. A relationship manager reads only the V_RM_* views, which return the companies assigned to CURRENT_USER.
The view runs with its owner's rights, so R_RM needs no grant on the tables underneath. A second manager, CREDORA_RM002, and the RM's AI user CREDORA_RM_AI run the same SELECT in the control room's proof panel. With one Exasol user per RM the views work unchanged.
Ask Credora logs in as you, so the AI cannot read more than you.
Claude reaches Exasol only through the official Exasol MCP server (exasol/mcp-server), started for each question on the app host as the signed-in person's own AI user, which holds the same role and sits in a low-priority consumer group.
From a published run to a disbursed loan, in four roles.
Each hand-off is a row in Exasol or the CRM, so admin, data scientist and loan operations can track every company.
Started on the starter kit, deployed to AWS with one command.
Realistic volumes for a large book of corporate customers.
The data is synthetic, but the volumes are not small. Rows per source on a log scale, and what one run costs.
the full analysis of 1,000 companies, 13 steps, one Exasol call
AUC of the champion. The challenger scorecard reaches
batches of keys per 3rd party fetch, which Exasol runs 4 at a time on the node's 4 cores
companies flagged by the Isolation Forest, and approvals became referrals
to regenerate all the synthetic data, with 1,000 companies, ~3,500 partners, every source rebuilt consistently. Semantic routing adds ~4 ms per statement.
The same run at 1,000, 10,000 and 50,000 companies.
Measured on the live single node, nothing tuned between runs. Figures from RUN_LOG and EXA_DBA_AUDIT_SQL.
1,000 companies. 13 steps, peak temp RAM 425 MiB
10,000 companies, 8.2 million transactions grouped in Oracle, peak temp RAM 625 MiB
50,000 companies, 40.7 million transactions, 3.6 million GST filings, peak temp RAM 1.3 GiB
features at 50,000. Oracle groups 40.7 million rows per account and month and only the monthly totals return
to score 50,000 companies in the Python UDF, then 0.7 s to decide and 0.1 s for the PREFERRING skyline
of the 320 s is the Account Aggregator fetch, 287,633 partner accounts from one small mock container. The 3rd party APIs set the clock, not Exasol
Narrow doors, and failures that stay contained.
Only 80 and 443 are public. Exasol's ports open to admin IPs only, and RDS sits in private subnets.
A user per role. A refused query reaches the app as 403 Exasol denied access for your role.
MCP writes off, an AI user per person in a low-priority consumer group, a topic gate before any query, every SQL statement shown with the answer.
Generated per deployment into Secrets Manager. API tokens live in CONNECTION objects. None in the repo.
3rd party replies carry a purge time. PURGE_EXPIRED deletes them and keeps a SHA-256 proof per 1,000-row chunk.
| If this fails | What happens | Recovery |
|---|---|---|
| A bank database | That step fails and stays in RUN_LOG with its error, and the last published run stays visible | Re-run when it is back |
| A 3rd party API | The UDF makes 3 attempts, then the step fails naming the service, stays in RUN_LOG, and nothing half-done is published | Re-run, the relays are stateless |
| A preprocessor module | The framework skips it (fail-open), and plain SQL keeps working | Fix or remove its rule |
| The Exasol node | The app shows the last results it can reach, and analysis and AI pause | Restart, and deploy recreates objects |
| The app host | The site is down, but Exasol and the databases are unaffected | credora.py ship |
| The MCP server | Ask Credora returns 503, and the rest of the app works | Ask again for a fresh process |
| A question outside grants | Exasol refuses, and Claude says the role cannot see it | None, this is access control working |
Every screen in the demo proves an Exasol feature.
Explain mode shows it live, with an Exasol console showing the SQL behind each screen, an ⓘ glossary, and a control room.
Built with Exasol open source. Thank you.
Every project below runs in the live deployment, from both GitHub organisations, exasol and exasol-labs. Thank you to the Exasol team and the contributors.
What I learnt about Exasol Personal.
Six things the build taught me. Each one is now in the code or the docs.
The Oracle adapter does not push TRUNC(date, 'MM'), so step 7 groups by TO_CHAR(date, 'YYYY-MM'). That took the run from 24.7 s to 17.5 s.
The Sources screen shows the SQL each database receives, so the claim is Exasol's own. It is also how the TRUNC gap was found.
It has no /etc/resolv.conf or /etc/hosts. exasol/hotfix/slc_dns_fix.sh links both to the node's /conf files again after every Exasol restart.
One run's GST rows are more than that, so the purge fingerprint is built per 1,000-row chunk, and the proof is the hash of the chunk hashes.
So the AI gets the person's own rights and nothing more. A prompt cannot widen what Exasol grants.
So the AI got its own users, one per person with the same role, in a low-priority group with a 60 s limit and 2 GiB of memory.
Contributions welcome. Want to build on Credora, adapt it, or buy or license it?
Raise an issue at github.com/anamika-iit-kgp/credora. Credora is an ExaThon India 2026 project, a bank's pre-approved corporate lending, decided inside Exasol Personal, built on the Exasol Personal Local Starter Kit and deployed on AWS.
Project by Anamika, Product Manager @ Equitas Small Finance Bank. Sapphire Bank is fictional and all data is synthetic.