Xano backend · Claude Code / MCP connection · data flows · branch & write-safety — snapshot 2026-07-17
Petra runs a private / hard-money loan-servicing platform on Xano (Postgres backend + business logic) with Retool as the internal UI. It models loans end-to-end: onboard → fund (by lenders/entities) → accrue interest → collect borrower payments → disburse to lenders → payoff, over a double-entry ledger with full trust accounting. Rebel Ops is the consultant: building a document-intake service (this repo) and, near-term, a payments API by month-end.
This is a mature, money-moving production system — not an early build. Interest accrual, payment application, ACH/NACHA file generation, disbursements, and a large reporting suite already exist and run daily via cron.
flowchart LR
CC["Claude Code"] -->|"Developer MCP"| CTX["XanoScript /<br/>docs context"]
CC -->|"CLI token (curl)"| META["Metadata API<br/>full CRUD"]
META --> WS["Xano workspace 2<br/>48 tables · 219 endpoints"]
WS --> RT["Retool<br/>internal UI"]
IW["Intake Worker<br/>(Cloudflare)"] -->|"POST parsed docs"| WS
classDef ext fill:#e7f5ff,stroke:#1971c2,color:#1971c2;
class RT,IW ext;
| Layer | What it is | What it does |
|---|---|---|
| Developer MCP | @xano/developer-mcp v2.2.3 | Docs, XanoScript validation, CLI-command & knowledge context. Does not read/write data. |
| Metadata API | Instance-level bearer token (via CLI) | The real read/write path — full CRUD on tables, endpoints, functions, tasks, branches. |
| Xano CLI | @xano/cli 1.0.5 | Wraps the Metadata API. Its network layer fails in our env — we use curl to the Metadata API instead. |
flowchart LR
A["Claude Code<br/>tool call"] --> B{"context or<br/>action?"}
B -->|context| MCP["Developer MCP<br/>(docs / validation)"]
B -->|action| CURL["curl → Metadata API"]
CURL --> AUTH["Bearer token<br/>(not branch-scoped)"]
AUTH --> WS["Xano workspace 2"]
WS --> LOGIC["Logic layer<br/>(per branch)"]
WS --> DB["Database<br/>(shared, all branches)"]
classDef danger fill:#fff5f5,stroke:#c92a2a,color:#c92a2a;
class DB danger;
flowchart LR
A["Onboard<br/>submission"] --> B["Approve →<br/>lender + entity"]
B --> C["Create loan<br/>+ loan_terms"]
C --> D["Funding<br/>(lenders / entities)"]
D --> E["Interest accrual<br/>(daily)"]
E --> F["Borrower<br/>payment"]
F --> G["Apply to<br/>ledger"]
G --> H["Disburse<br/>to lender"]
H --> I["Payoff"]
flowchart TD
BR["Borrower"] -->|"payment_request_public"| PR["payment<br/>status: scheduled"]
PR -->|"daily task 10:00"| PEND["pending"]
PEND -->|"+5 business days"| APPR["approved"]
APPR --> APPLY["apply_received_payment<br/>split interest / principal / fees"]
APPLY --> LT["loan_transaction<br/>(ledger)"]
LT --> REC["trust_transaction_rec"]
REC --> TT["trust_transaction"]
APPR --> ACH["create_ach_file<br/>(NACHA)"]
ACH --> BANK["Bank / ACH rail"]
classDef money fill:#ebfbee,stroke:#2b8a3e,color:#2b8a3e;
class LT,TT,ACH money;
flowchart LR
subgraph L["Loan ledger"]
LT["loan_transaction<br/>debit / credit"]
end
subgraph T["Trust (bank) ledger"]
TT["trust_transaction<br/>debit / credit"]
TS["trust_transaction_split"]
end
LT <-->|"trust_transaction_rec"| TT
TT --- TS
TA["trust_account<br/>entry vs reconciled balance"] --- TT
flowchart TD
DEV["Dev branch<br/>(v1.1.4)"]
LIVE["Live branch<br/>(v1.1.1)"]
subgraph BRANCHED["Branched — isolated"]
EP["API endpoints"]
FN["Functions"]
TK["Tasks / Addons"]
end
subgraph SHARED["Shared store — hits LIVE"]
SCH["Table schema"]
DAT["Records / data"]
end
DEV -->|"safe to iterate"| BRANCHED
DEV -.->|"NOT isolated"| SHARED
LIVE --> BRANCHED
LIVE --> SHARED
classDef danger fill:#fff5f5,stroke:#c92a2a,color:#c92a2a;
class SHARED,SCH,DAT danger;
classDef safe fill:#ebfbee,stroke:#2b8a3e,color:#2b8a3e;
class BRANCHED,EP,FN,TK safe;
| Change type | Branch-isolated? | Dev-branch-safe? |
|---|---|---|
| Endpoints / functions / tasks / addons | Yes | ✅ develop freely, Joe merges to live |
| Tables / fields / indexes (schema) | No — hits live | ❌ production op; needs Joe's sign-off |
| Table records (data) | No — shared prod data | ❌ test writes = real prod rows |
48 tables. FKs are plain int …_id columns with a table="…" annotation. user is the only auth table. Money movement is double-entry: loan_transaction ↔ trust_transaction.
erDiagram
lender ||--o{ loan : services
lender ||--o{ entity : owns
borrower ||--o{ loan : owes
loan ||--|{ loan_terms : "versioned"
loan ||--o{ loan_funding : "funded by"
loan ||--o{ payment : receives
loan ||--o{ disbursement : "pays out"
loan ||--o{ loan_security : "secured by"
loan ||--o{ loan_escrow : impounds
loan_funding ||--o{ loan_transaction : posts
trust_account ||--o{ trust_transaction : records
loan_transaction }o--o{ trust_transaction : reconciled
| Table | Purpose | Size |
|---|---|---|
| lender | Servicing client + default fee/interest/late/prepay policy | ~57 fields |
| loan_terms | Versioned economic terms (rate, fees, interest calc) | ~48 fields |
| onboard_submission | Raw new-lender intake staging | 32 fields |
| loan | Central servicing record | 29 fields |
| borrower | Party that owes the loan (+ guarantor) | 22 fields |
| Domain | Tables |
|---|---|
| Loan Core | loan, loan_terms, loan_status, loan_note |
| Borrowers & Entities | borrower, entity |
| Lenders & Servicers | lender, servicer, third_party |
| Disbursements & Funding | loan_funding, loan_funding_update, loan_reserve, disbursement, disbursement_setup |
| Transactions & Payments | loan_transaction(+_ref, _draft, _draft_ref), payment, lender_submission, payoff_addition, ach_file |
| Trust Accounting | trust_account, trust_transaction(+_split, _upload, _rec), petra_statement |
| Collateral / Escrow / Ins / Tax | loan_security, loan_escrow, loan_security_insurance, loan_security_tax, loan_tracking, loan_file, loan_file_signature |
| Onboarding | onboard_submission |
| Reporting & Ops | lender_statement, report_job, report_date, hybiscus_request, task |
| Reference / System | user, loan_counter, payoff_addition_text, payment_return_code, flood_zone, us_state, federal_holiday |
RPC-style, not RESTful — endpoints are named actions (post/payment_disbursement). Auth is per-endpoint (auth="user"); 210/219 authenticated, role gates in-body.
| Domain | Groups (endpoints) |
|---|---|
| Loans & lifecycle | loans(17), draws(5), security(5), borrowers(4), tracking(4), terms(3), updates(3), +3 ref — 44 |
| Onboarding | onboard(9), submissions(8), entities(6) — 23 |
| Payments & money movement | funding(17), payments(11), payoff(11), disbursements(5), ach(3) — 47 |
| Trust & accounting ledger | transactions(21), trust_accounts(12), transaction_drafts(7), reserves(5), escrow(2), insurance(1), tax(1) — 49 |
| Lenders | lenders(7), servicers(1) — 8 |
| Reporting & documents | files(25), report_dates(1) — 26 |
| Admin / internal | users(6), statuses(5), tasks(5), notes(2) — 18 |
| Integrations / webhooks | usio(1), sendgrid(1), slack(1), anthropic_snippet(1) — 4 |
Servicing logic lives in Xano: 94 functions (88 business + 6 connectors), 9 scheduled tasks, 35 addons (reusable query-enrichment joins).
| Task | Cadence | Does |
|---|---|---|
| payment_status_update ⭐ | Daily 10:00 | scheduled→pending; pending 5 biz-days→approved, calls apply_received_payment |
| month_end_servicing_fees ⭐ | Daily, day 1 | Accrues servicing fee per loan for prior month |
| update_current_due_dates ⭐ | Daily 10:00 | Recomputes due dates, days-past-due, late buckets |
| interest_due_projection | Daily 10:00 | Projects interest ~7 days out; month-start auto-calc |
| trust_account_activity | Daily 09:00 | Slack alert for trust activity |
| maturing_loans_notification | Daily 09:00 | Slack alert for loans maturing in 30 days |
| add_month_for_reports | Daily, day 1 | Inserts a report_date row |
| late_loan_check | Disabled | Would flag late loans — currently off |
| send_task_reminders | Disabled | Stubbed out |
| Service | Role |
|---|---|
| Hybiscus | Async PDF report rendering — the main outbound integration |
| ACH / NACHA | File-based, not an API. No Dwolla/Modern Treasury/Plaid/Stripe. The gap a real-time payments API would fill. |
| Postmark / SendGrid | Transactional email (Postmark primary) |
| Slack | Ops notifications ⚠️ webhook URL hardcoded in task blocks |
| Anthropic / Claude | In-platform LLM calls, pinned to old models. Doc-parsing AI is Rebel Ops' separate Worker. |
| ICE / OSC | Export files only, not live APIs |
Does Petra need real-time / originated ACH (a new processor — the usio group is an empty stub) or is hardening the existing file-based NACHA flow enough? This scopes the whole build.