Case study
GrantOne: AI screening for grant applications full of personal data
Replacing a legacy OpenAI Assistants integration with one EU-hosted AI service that screens grant applications for eligibility, scans for personal data and answers caseworker questions, with every decision auditable.
- Client
- Grant ApS
- Period
- 2026
- Role
- AI solutions architect and technical advisor
3 min read

Sensitive data, manual screening
GrantOne, built by Grant ApS, helps Danish foundations handle grant applications. Those applications are full of personal data: CPR numbers, tax returns, medical certificates.
Caseworkers screened eligibility by hand, application by application. The AI that did exist was a legacy OpenAI Assistants integration: synchronous, single-tenant, and with applicant data leaving the platform's own control.
I was brought in to work out how AI could take load off the caseworkers without putting that data at risk, and then to build it.
Discovery: hosting first, then the rules
I started with hosting. I benchmarked four architectures, from private edge on local hardware through consumer-local and hybrid cloud to private cloud, and modelled what each would cost. The result was a decision the client could make on its own risk appetite, not on technical hype.
Then I worked with fund administrators on the rules themselves. Each programme's written eligibility criteria, and the way reviewers actually apply them, became a versioned, machine-checkable assessment configuration. The prompts that encode those rules live in a versioned, schema-validated library and are treated as code.
One service, four capabilities
I replaced the legacy integration with a single async FastAPI service offering four capabilities:
- Categorisation: screens each application against its programme's eligibility rules.
- Conversational RAG chat: a LangGraph agent lets caseworkers ask questions across applications, with a sandboxed code tool so model-generated arithmetic runs safely.
- PII scanning: finds Danish personal data across documents and free-text fields, with page-level attribution for redaction workflows.
- Document embedding: feeds the vector search behind the chat.
A few design decisions carry most of the weight.
OCR as foundation. Each document is read once, and that reading is reused by every downstream pipeline. It cuts model cost and guarantees that screening, chat and PII scanning all see the same text.
A smaller job for the LLM. The model's role is shrunk to two narrow tasks with strict, schema-validated output. That replaced a brittle free-form prose layer.
Deterministic, auditable decisions. The final label comes from deterministic, rule-driven logic, together with a natural-language justification. Every automated decision can be audited by a caseworker.
Isolation and sovereignty. Each customer organisation gets its own database schema, a physically separate data store within one service. A provider-agnostic model layer lets the platform move between hosted and self-hosted models without vendor lock-in, and everything runs in the EU.
Most of the implementation was written by Claude Code agents under human diff review and a full regression run on every change. That method is its own story, told in Shipping production code with coding agents.
Hardened against real outcomes
A screening service is only as good as its behaviour on real applications. To validate it, I replayed a corpus of applications that had already been granted. That cut false automatic rejections from 24 to 0 and manual escalations from 28 to 4.
To keep it that way, 644 automated tests plus 80 live end-to-end scenarios over 130+ synthetic, PII-free fixtures tell us when a prompt or model change alters the behaviour.
In production
The service is in production. Screening has handled more than 4,200 real applications for the first customer, freeing caseworkers from manual eligibility screening. A second grant programme with 300+ applications is live, and a third is built and awaiting go-live.
The outcome is an AI service that a foundation can trust with sensitive data: the data stays in the EU and in its own schema, the model does only what it can do reliably, and every decision comes with a reason a caseworker can check.