{"id":50519199,"url":"https://github.com/rithinch/cursor-briefcase-hack","last_synced_at":"2026-06-03T02:30:36.680Z","repository":{"id":354896516,"uuid":"1225818900","full_name":"rithinch/cursor-briefcase-hack","owner":"rithinch","description":null,"archived":false,"fork":false,"pushed_at":"2026-04-30T20:11:26.000Z","size":311,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-30T20:16:30.350Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rithinch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-30T17:12:40.000Z","updated_at":"2026-04-30T20:11:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rithinch/cursor-briefcase-hack","commit_stats":null,"previous_names":["rithinch/cursor-briefcase-hack"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/rithinch/cursor-briefcase-hack","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rithinch%2Fcursor-briefcase-hack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rithinch%2Fcursor-briefcase-hack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rithinch%2Fcursor-briefcase-hack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rithinch%2Fcursor-briefcase-hack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rithinch","download_url":"https://codeload.github.com/rithinch/cursor-briefcase-hack/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rithinch%2Fcursor-briefcase-hack/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33845770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-03T02:30:31.931Z","updated_at":"2026-06-03T02:30:36.671Z","avatar_url":"https://github.com/rithinch.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"./images/banner.png\" alt=\"Pulp\" width=\"400\" /\u003e\n\u003c/div\u003e\n\n\n# Pulp — Autonomous Accounts Payable\n\nPulp is a **full-stack autonomous accounts payable** prototype:\n\n- A **React dashboard** for finance/ops teams to create payment intents and track invoice → approval → payment.\n- A **FastAPI backend** that ingests invoices, parses them with an LLM, **verifies the vendor company details via Specter**, then runs a **LangGraph** workflow to validate, route approvals, pick a payment rail, execute payment, and reconcile.\n\nThis repo contains both the frontend (`frontend/`) and backend (`backend/`).\n\n## What it’s meant to do\n\n- **Create an intent**: “Pay vendor X up to Y for Z”.\n- **Receive an invoice** (PDF upload) tied to that intent.\n- **Extract invoice fields** using an LLM (vendor name/email, amount, currency, dates, line items, confidence).\n- **Company verification step (Specter)**:\n  - Uses Specter company search to sense-check the invoice vendor domain against the intent vendor email/domain.\n  - If it does **not** match (or can’t be verified), the flow pauses at **`company_verification_pending`** so a human can review.\n- **LangGraph pipeline** continues only after verification:\n  - Validation agent checks invoice vs intent (amount tolerance, vendor trust, etc.)\n  - Approval routing either auto-pays or pauses for human approval\n  - Payment rail selection (Mock / Wise / Revolut / Yapily)\n  - Payment execution and reconciliation\n\n## Pulp for AI Agents (SMEs building their own agents)\n\nPulp is designed to be an **execution layer for agentic workflows** that need to safely move money.\nInstead of letting an AI agent call a bank API directly, your agent can delegate financial operations to Pulp,\nwhich enforces **guardrails + policies + auditability**.\n\n- **Bring your own agent**: an SME can build domain-specific agents (e.g., procurement, facilities, logistics, retail ops)\n  that decide *what* should be paid and *why*.\n- **Pulp adds the financial operations layer**: Pulp handles the *how* safely:\n  - **Policies**: thresholds, tolerance checks, known vendor rules, approval routing.\n  - **Guardrails**: domain/company verification (Specter), validation agents, idempotency, rails allow-lists.\n  - **Human-in-the-loop pauses**: `company_verification_pending`, `pending_approval`.\n  - **Audit trail**: append-only `events` timeline + persisted intent/invoice/payment state.\n  - **Pluggable execution**: choose rails (`mock`, `wise`, `revolut`, `yapily`) without changing agent logic.\n\nConceptually:\n\n```mermaid\nflowchart LR\n  Agent[SME AI Agent\u003cbr/\u003edecides what/why] --\u003e|create intent + upload invoice| Pulp[Pulp\u003cbr/\u003epolicies + guardrails]\n  Pulp --\u003e|pause for review when needed| Human[Approver / Finance]\n  Pulp --\u003e|execute| Money[Payment rails]\n  Pulp --\u003e|events + state| Audit[(Audit + history)]\n```\n\n### Quickstart (AI agent tool calling via MCP)\n\nUse Pulp as a **tool server** for your agent: your agent decides *what/why*, and Pulp enforces *how* safely (policies, guardrails, approvals, audit trail).\n\n```ts\nimport { query } from \"@anthropic-ai/claude-agent-sdk\";\nimport Pulp from \"@pulp/sdk\";\n\nconst pulp = await Pulp.connect({ apiKey: \"YOUR_API_KEY\" });\n\nfor await (const msg of query({\n  prompt: \"Pay Acme Supplies £2,400 for the October invoice, ref INV-2024-089. Validate against our open intent and auto-pay if confidence is above 90%.\",\n  options: {\n    mcpServers: {\n      pulp: pulp.mcp(),\n    },\n  },\n})) {\n  console.log(msg);\n}\n```\n\n### Quickstart (direct API client)\n\nIf you’re not using an agent framework, you can call Pulp directly via `@pulp/sdk`.\n\n```ts\nimport Pulp from \"@pulp/sdk\";\n\nconst client = new Pulp({ apiKey: \"YOUR_API_KEY\" });\n\n// Register a payment intent\nconst intent = await client.intents.create({\n  vendor: { name: \"Acme Supplies\" },\n  amount: { expected: 2400.0, currency: \"GBP\" },\n  reference: \"INV-2024-089\",\n});\n\nconsole.log(\"Intent registered:\", intent.id);\n```\n\n## Architecture (system diagram)\n\n```mermaid\nflowchart TB\n  %% ---------- Clients ----------\n  User[Finance / Ops user]\n  FE[Frontend\u003cbr/\u003eReact + TS (Vite)]\n\n  %% ---------- Backend ----------\n  API[Backend API\u003cbr/\u003eFastAPI]\n  MW[Auth middleware\u003cbr/\u003eAPI key]\n\n  %% ---------- Persistence ----------\n  DB[(Postgres\u003cbr/\u003eSQLAlchemy models)]\n  LG[(LangGraph checkpointer\u003cbr/\u003ePostgres)]\n  Blob[(Azure Blob Storage\u003cbr/\u003einvoice PDFs)]\n\n  %% ---------- External services ----------\n  Claude[LLM\u003cbr/\u003eInvoice parsing + agents]\n  Specter[Specter API\u003cbr/\u003eCompany search]\n\n  %% ---------- Pipeline / rails ----------\n  Graph[LangGraph pipeline\u003cbr/\u003evalidate → approval → select rail → pay → reconcile]\n  Rails[Payment rails\u003cbr/\u003emock / wise / revolut / yapily]\n\n  %% ---------- Flows ----------\n  User --\u003e FE\n  FE --\u003e|/v1/*| API\n\n  API --\u003e MW\n  MW --\u003e API\n\n  API --\u003e|create intent| DB\n  API --\u003e|receive invoice| DB\n  API --\u003e|events + status updates| DB\n\n  API --\u003e|upload PDF (best-effort)| Blob\n  API --\u003e|parse invoice| Claude\n  API --\u003e|company verification search| Specter\n\n  API --\u003e|start pipeline| Graph\n  Graph --\u003e|checkpoint| LG\n  Graph --\u003e|read/write status + payment rows| DB\n  Graph --\u003e|validation + approval agents| Claude\n  Graph --\u003e Rails\n  Rails --\u003e|execute payment| Graph\n  Graph --\u003e|reconcile| DB\n```\n\n## Invoice processing timeline (state machine)\n\n```mermaid\nstateDiagram-v2\n  [*] --\u003e pending_invoice\n  pending_invoice --\u003e invoice_received: POST /v1/invoices\n  invoice_received --\u003e company_verification_pending: Specter mismatch/unknown\n  company_verification_pending --\u003e invoice_verified: Verified (manual or automatic)\n  invoice_received --\u003e invoice_verified: Verified automatically\n  invoice_verified --\u003e validated: Validation agent pass/review\n  validated --\u003e pending_approval: Approval router requires human\n  validated --\u003e paying: Auto-pay\n  pending_approval --\u003e paying: Approved\n  paying --\u003e reconciled: Reconciliation success\n  paying --\u003e completed: Reconciliation skipped/failed\n```\n\n## Key backend concepts\n\n- **Intent**: the unit of work (who/what/how much). Status is what the UI primarily tracks.\n- **Invoice**: the uploaded document and parsed fields. Includes `company_verification` payload.\n- **Events**: append-only timeline records (`invoice.received`, `invoice.company_verification_pending`, `payment.completed`, …).\n- **LangGraph**: orchestrates the multi-step AP pipeline with persistence via Postgres checkpointer.\n\n## Repository layout\n\n```text\n.\n├── backend/   # FastAPI + LangGraph + SQLAlchemy + rails\n└── frontend/  # React dashboard (Vite)\n```\n\n## Running locally\n\n### Backend\n\n```bash\ncd backend\ncp .env.example .env\n# fill: DATABASE_URL, ANTHROPIC_API_KEY, JWT_SECRET, SPECTER_API_KEY\n\npython3 -m venv .venv\nsource .venv/bin/activate\npip install -e .\n\nuvicorn api.main:app --reload --port 8000\n```\n\n- **API**: `http://localhost:8000`\n- **Docs**: `http://localhost:8000/docs`\n\nIf you’re adding the new invoice verification storage column:\n\n```bash\npython3 db/migrate_add_company_verification.py\n```\n\n### Frontend\n\n```bash\ncd frontend\nnpm install\nnpm run dev\n```\n\nFrontend runs at `http://localhost:5173` and (by default) talks to the backend at `http://localhost:8000`.\n\n## Environment variables (high-signal)\n\n- **Backend**\n  - `DATABASE_URL`: Postgres connection string (async SQLAlchemy/asyncpg)\n  - `ANTHROPIC_API_KEY`: LLM for parsing + agents\n  - `SPECTER_API_KEY`: Specter company search for invoice vendor verification\n  - `JWT_SECRET`: approval token signing secret\n  - `RAILS_ENABLED`: `mock`, `wise`, `revolut`, `yapily` (comma-separated)\n- **Frontend**\n  - `VITE_API_URL`: override API base URL (defaults to local proxy)\n\n## Current “pause” points for humans\n\n- **Company verification**: `company_verification_pending` (domain mismatch/unknown)\n- **Payment approval**: `pending_approval` (approval router requires human)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frithinch%2Fcursor-briefcase-hack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frithinch%2Fcursor-briefcase-hack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frithinch%2Fcursor-briefcase-hack/lists"}