Inspiration
The Problem That Costs Nigerian SMBs Millions
Every week, small business owners across Nigeria face the same invisible drain on their time and cash flow: chasing unpaid invoices.
The average Nigerian SMB owner spends 6-10 hours per week manually:
- Scanning their inbox for invoice emails
- Tracking who has paid and who hasn't
- Sending follow-up reminders that feel awkward and get ignored
- Manually generating payment links one by one
This isn't a productivity problem. It's an identity and authorization problem the business owner is the only person who can act on these invoices because only they have access to their Gmail, their Paystack account, and their calendar. There is no safe way to delegate these actions to anyone else.
Until now.
What it does
Remiti is an autonomous AI invoice management agent that acts on behalf of a business owner — reading their Gmail, extracting invoice details using Claude AI, sending Paystack payment links to clients, and booking follow-up meetings via Google Calendar.
The entire system is built around one core principle: the agent should only be able to do what the user has explicitly authorized — nothing more, nothing less.
This is the problem Auth0 Token Vault was designed to solve. And it is the foundation Remitti is built on.
How we built it
Remitti is an autonomous AI invoice management agent that acts on behalf of a business owner — reading their Gmail, extracting invoice details using Claude AI, sending Paystack payment links to clients, and booking follow-up meetings via Google Calendar.
The entire system is built around one core principle: the agent should only be able to do what the user has explicitly authorized — nothing more, nothing less.
This is the problem Auth0 Token Vault was designed to solve. And it is the foundation Remitti is built on.
How Auth0 Token Vault Powers Remitti
The moment a user connects their Gmail or Google Calendar in Remitti, Auth0 takes over the OAuth dance:
User clicks "Connect Gmail"
↓
Auth0 redirects to Google OAuth consent screen
↓
User grants permission — Gmail read + send scopes
↓
Auth0 Token Vault securely stores the Google access
and refresh tokens, linked to the user's Auth0 identity
↓
Remitti's backend NEVER sees the raw tokens
↓
When the agent needs to scan Gmail, it calls:
GET /api/v2/users/{userId}/federated-connections/google-oauth2/access-token
↓
Token Vault returns a fresh Google access token
↓
Agent uses it for ONE operation, then discards it
This means:
- Tokens never touch our database — Auth0 owns the credential lifecycle
- Per-user isolation — every user's Gmail is scoped to their Auth0 identity
- Automatic refresh — Token Vault handles expiry silently
- User revocation — disconnecting a tool instantly revokes agent access
- Audit trail — every token exchange is logged against a real user identity
Step-Up Authentication — The Hackathon Money Shot
Not all invoice actions are equal. Sending a ₦5,000($3.3) reminder is routine. Sending a ₦500,000 ($333) payment request is a high-stakes action that should never happen automatically.
Remitti implements step-up authorization for every invoice above ₦150,000($100):
Agent detects invoice for ₦250,000
↓
Status set to PENDING_APPROVAL
↓
Agent Action logged: APPROVAL_REQUESTED (requiresApproval: true)
↓
Dashboard shows orange "Awaiting Approval" badge
↓
User sees: "Send payment request to Acme Ltd for ₦250,000?"
↓
User clicks Approve → Agent Action: USER_APPROVED
↓
ONLY NOW does the agent send the Paystack payment link
↓
Agent Action: PAYMENT_LINK_SENT → status: PAYMENT_SENT
This mirrors how human financial controls work — applied to AI agents. No amount of automation removes the human from high-value financial decisions.
The Full Agent Loop
Every 15 minutes (or on manual trigger):
1. SCAN — Agent calls Gmail API using Token Vault credentials
Filters: invoice OR payment OR "amount due"
2. EXTRACT — Claude AI (claude-sonnet-4) reads each email
Returns: { clientName, amount, currency, dueDate, confidence }
Skips LOW confidence extractions
3. ROUTE — Amount < ₦150,000($100) → auto-execute payment flow
Amount ≥ ₦150,000($100) → queue for human approval
4. EXECUTE — Paystack payment link generated using user's own API key
(money goes directly to the user's account, not ours)
Gmail sends payment email to client
Google Calendar books follow-up meeting in 3 business days
5. LOG — Every action written to audit log with timestamp,
user identity, and Token Vault scope used
Architecture
┌─────────────────────────────────────────────────────┐
│ Remitti User │
│ (Authenticated via Auth0 Universal Login) │
└──────────────────────┬──────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────┐
│ Vue 3 Frontend (Railway) │
│ Connect Tools │ Invoice Dashboard │ Audit Log │
└──────────────────────┬──────────────────────────────┘
│ HTTP + Session Cookie
▼
┌─────────────────────────────────────────────────────┐
│ Spring Boot Backend (Railway) │
│ │
│ AgentOrchestrator (15-min scheduled loop) │
│ ├── GmailService │
│ ├── InvoiceExtractorService (Claude API) │
│ ├── PaystackService (user's own key) │
│ └── GoogleCalendarService │
│ │
│ Auth0TokenVaultService │
│ └── GET /api/v2/users/{id}/federated-connections │
│ /google-oauth2/access-token │
└──────┬──────────────────────┬───────────────────────┘
│ │
▼ ▼
┌──────────────┐ ┌─────────────────────────────────┐
│ PostgreSQL │ │ Auth0 Token Vault │
│ (Railway) │ │ │
│ │ │ google-oauth2 connection │
│ invoices │ │ ├── Gmail access token │
│ agent_acti- │ │ ├── Gmail refresh token │
│ ons │ │ └── Calendar access token │
│ connected_ │ │ │
│ tools │ │ Per-user isolation │
└──────────────┘ │ SOC 2 encrypted storage │
│ Automatic token refresh │
└─────────────────────────────────┘
A Key Insight: Per-User Paystack Keys
Early in development we faced a critical architectural decision. If Remitti generated Paystack payment links using a single platform API key, all payments would flow to our account — making Remitti an accidental fintech platform with regulatory and trust implications.
Instead, each user connects their own Paystack account to Remitti. The agent creates payment links using the user's key, meaning:
- Money goes directly to the business owner's Paystack account
- No funds ever touch Remitti's infrastructure
- Zero financial custodianship or compliance burden
- Clean SaaS model that scales without legal complexity
This decision was inspired directly by Auth0 Token Vault's philosophy: credentials belong to the user, not the platform.
Challenges we ran into
Challenge 1: Token Vault Early Access
Token Vault was initially in Early Access and required manual enablement per tenant. We discovered this when our /federated-connections/ endpoint returned 404. After contacting Auth0 Community support, we learned Token Vault is now generally available on the free plan and covers up to 2 connections exactly what we needed.
Lesson: When a feature returns 404, check community forums before assuming it's paywalled.
Challenge 2: Wrong OAuth Scopes
Our initial Auth0 connection flow requested openid profile email offline_access standard login scopes, but missing the Gmail API scopes. The user would connect Gmail, auth would succeed, but Token Vault stored a token with no Gmail permissions. Every Gmail API call returned 401.
Lesson: Token Vault stores exactly what Google grants. If you want Gmail access, you must request Gmail scopes at the connection level not just at login.
Challenge 3: The access_type=offline Problem
Without access_type=offline and prompt=consent configured in the Auth0 Google connection upstream params, Google never issued a refresh token. Token Vault had an access token that expired in 1 hour with no way to refresh it.
Lesson: For background AI agents that need to act asynchronously, offline access is not optional — it is the foundation of delegated agent authorization.
Challenge 4: Claude API Key with Newline Character
A subtle bug caused Unexpected char 0x0a at 171 in Authorization value — our Claude API key had a hidden newline character from copy-pasting. OkHttp correctly rejected it.
Lesson: Always call .trim().replace("\n", "") on injected API keys before using them in HTTP headers.
Accomplishments that we're proud of
- A real business problem solved — not a toy demo. Nigerian SMBs actually lose money chasing invoices.
- Step-up auth that judges will remember — the ₦150,000 approval threshold with a visual confirmation flow is a tangible demonstration of agent authorization boundaries.
- Full audit trail — every agent action is logged with user identity, action type, timestamp, and whether approval was required. Users always know what their agent did.
- Clean payment architecture — we deliberately chose a model where Remitti never touches the user's money.
- Production deployment — both frontend and backend deployed on Railway, not just running on localhost.
What We Learned
Auth0 Token Vault is the right abstraction for AI agents. Before Token Vault, building an agent that could act on behalf of multiple users across multiple services required custom OAuth implementations, encrypted token databases, refresh logic, and scope management all bespoke, all fragile. Token Vault collapses this into a single API call per action. The agent just asks "give me a token for this user and this service" — and Token Vault handles everything else.
Agent authorization is not the same as user authorization. When a human logs in, they authorize themselves. When an AI agent acts, it needs to authorize each action not just prove identity. Step-up auth, explicit approval flows, and audit logging are not nice-to-haves for AI agents. They are the minimum viable security model.
Scope is everything. The most important configuration decision in the entire system was which OAuth scopes to request. Get this wrong and the whole system silently fails.
What's next for Remiti — AI Invoice Agent
- WhatsApp integration — respond to client messages and confirm bookings
- Multi-currency support — USD invoices for international clients
- Bulk approval — approve multiple pending invoices at once
- Remitti for Teams — multiple team members, role-based agent permissions using Auth0 FGA
- Webhook notifications — real-time Slack/email alerts when an invoice is paid
🛠️ Built With
Backend: Java 21, Spring Boot 3, PostgreSQL
Frontend: Vue 3, Tailwind CSS, Pinia, Vue Router
AI: Anthropic Claude API (claude-sonnet-4-20250514)
Auth & Security: Auth0 Universal Login, Auth0 Token Vault, Auth0 Connected Accounts, AES-256-GCM encryption
Payments: Paystack API (payment links + webhooks)
Email: Gmail API via Auth0 Token Vault
Calendar: Google Calendar API via Auth0 Token Vault
Infrastructure: Railway (backend + frontend + PostgreSQL)
HTTP Client: OkHttp3
Other: Lombok, Jackson, Spring Scheduler, Spring Security
BONUS BLOG POST SECTION
Bonus Blog Post: What Building Remiti Taught Me About Agent Authorization
When I started building Remiti, I thought the hard part would be the AI, getting Claude to reliably extract invoice details from messy email threads. I was wrong. The hard part was authorization.
Not authentication. Authorization. Specifically: how do you let an AI agent do things on a user's behalf, securely, without the user having to be present for every action?
This is a problem every AI agent builder hits within the first week. And before Auth0 Token Vault, the answer was always the same: build it yourself. Custom OAuth flows. Encrypted token databases. Refresh logic. Scope management. Error handling for expired tokens. It was hundreds of lines of infrastructure code before you'd written a single line of agent logic.
Token Vault changes the mental model.
Instead of thinking "how do I store and manage this user's Gmail token?", you think "how do I tell Auth0 which services this user has authorized, and then ask Auth0 for a token when I need one?" The credential lifecycle becomes Auth0's problem. Your agent just asks for what it needs, uses it, and moves on.
Here is the concrete moment this clicked for me. In Remitti's AgentOrchestrator, the line that fetches a Google access token to scan Gmail is:
String googleAccessToken =
auth0TokenVaultService.getGoogleAccessToken(userId);
That's it. One line. Under the hood, Auth0 Token Vault checks if the token is still valid, refreshes it if needed using the stored refresh token, and returns a fresh access token scoped exactly to what the user consented to. The agent never sees a refresh token. The database never stores credentials. If the user revokes access, the next call throws a VaultException, and the scan stops immediately.
The step-up auth insight.
The second thing Token Vault forced me to think clearly about was: what actions should an agent never take without explicit human approval, regardless of what the user previously authorized?
For Remitti, the answer is financial actions above a threshold. Sending a ₦5,000($3.3) invoice reminder is routine. Sending a ₦500,000($333) payment request is a different category of action, one where the cost of an error is real and immediate.
This led to the PENDING_APPROVAL status and the approval flow in the dashboard. The agent detects and extracts automatically. It acts financially only with explicit confirmation. This is not a UX decision; it is an authorization boundary. And drawing that boundary clearly, in code, was one of the most important architectural decisions in the project.
The per-user credential insight.
The third insight was about payment infrastructure. Our first design had a single Paystack API key for the whole platform. Every payment link would go through our account. We would collect the money and pay it out.
We stopped building that the moment we realized what it meant: we would have become a financial intermediary. Regulatory compliance. Payout infrastructure. Dispute handling. Trust issues.
The fix was to let each user connect their own Paystack account. The agent generates payment links using the user's key. Money flows directly to the user. Remiti never touches it.
This is the same principle Token Vault is built on: credentials belong to the user, not the platform. Auth0 stores Google tokens on behalf of the user, not on behalf of Remitti. The platform is a conduit, not a custodian. Once we applied that same principle to payment credentials, the architecture became much cleaner, and the legal exposure went to zero.
What I would tell every AI agent builder.
If you are building any agent that needs to access third-party APIs on behalf of users, integrate Auth0 Token Vault before you write any other infrastructure code. Not after. Before.
The time you save on OAuth flows, token refresh logic, and credential storage is time you can spend on what actually matters: the agent's capabilities, the user experience, and the problem you are actually trying to solve.
For Remiti, that problem is real: Nigerian business owners spending hours chasing invoices instead of running their businesses. Every hour saved by the agent is a direct return on the authorization infrastructure that makes it trustworthy enough to run in the first place.
Build the trust layer first. Everything else follows.
TRY IT OUT LINKS
- Live App: https://remiti.up.railway.app
- GitHub: https://github.com/yourusername/remitti-agent
Built With
- auth0
- css
- docker
- google-calendar
- google-gmail-oauth
- java
- javascript
- lombok
- paystack
- pinia
- postgresql
- railway
- springboot
- taiwind
- tokenvault
- vue
Log in or sign up for Devpost to join the conversation.