An AI-powered operations layer on top of Wallet-Automation-Engine.
Users interact with it using natural language:
- "What's my balance?"
- "Show me last week's transactions"
- "Transfer $100 to wallet 42"
The agent plans and executes the required operations through the Wallet API.
It never accesses SQL Server directly and never bypasses the wallet's own service layer.
A money-moving AI agent must handle the same concerns as a production backend system:
- Authentication and authorization
- Audit trails
- Confirmation before irreversible operations
- Preventing the model from controlling sensitive identifiers
A key design decision:
No tool accepts a walletId or userId parameter from the planner.
The agent never trusts identifiers generated by the LLM.
The user's wallet identity is always resolved from the authenticated JWT.
The only tool that receives an identifier is TransferMoney, where the identifier represents the recipient wallet.
The source wallet is always derived from the caller's identity.
The agent has no registration endpoint and no user store of its own.
POST /api/auth/login forwards credentials to the Wallet API's own login endpoint and returns the same JWT.
Example:
POST /api/auth/login{
"email": "user@example.com",
"password": "password"
}Response:
{
"token": "<jwt-token>"
}Anyone who can authenticate with the wallet system can use the agent with that token.
Use it as:
Authorization: Bearer <token>on every /api/agent/* request.
Run Wallet-Automation-Engine and note its base URL.
docker compose up -dThis starts:
- Redis
- Ollama
Update:
{
"WalletApi": {
"BaseUrl": "<wallet-api-url>"
}
}Make sure JWT configuration matches the Wallet API:
{
"Jwt": {
"Key": "<same-key>",
"Issuer": "<same-issuer>",
"Audience": "<same-audience>"
}
}dotnet run --project Wallet.Agent.ApiPOST /api/auth/login{
"email": "user@example.com",
"password": "password"
}POST /api/agent/chat{
"conversationId": "abc",
"question": "What's my balance?"
}For operations that require approval:
POST /api/agent/confirm{
"conversationId": "abc",
"approved": true
}