Skip to main content

Vanty App: multi-tenant CRM (people, companies, activities, feedback).

Project description

vanty-crm

Lean, multi-tenant CRM toolkit for FastAPI + Tortoise ORM. Part of the Vanty ecosystem.

  • People (leads/contacts/customers), companies, activities, feedback
  • Tortoise models on top of OrganizationScopedModel for tenant isolation
  • FastAPI routers (public + admin) with cursor pagination
  • Domain events on the shared vanty-core event bus — subscribe from any package to react to CRM lifecycle moments
  • CSV import (preview + apply)

CRM Person is an external contact, never a platform user. An optional linked_user_id records the user account a Person was converted into.

Install

uv add vanty-crm

Quickstart

from fastapi import FastAPI

from vanty_crm import CrmApp, CrmSettings, mount_crm_router

app = FastAPI()
kit = mount_crm_router(app, CrmSettings(database_url="sqlite://./crm.db"))


@app.on_event("startup")
async def _startup() -> None:
    await kit.init_orm(generate_schemas=True)


@app.on_event("shutdown")
async def _shutdown() -> None:
    await kit.close_orm()


# Optional: admin router (mount behind your own RBAC)
from vanty_crm.fastapi.dependencies import require_superuser

def my_superuser_check() -> None:
    ...  # raise HTTPException if caller is not a superuser

app.dependency_overrides[require_superuser] = my_superuser_check
app.include_router(kit.admin_router)

Subscribe to vanty_auth.user.signed_up

The headline cross-package recipe — when a new user signs up via vanty-auth, upsert a CRM Person for them so marketing/CS workflows can pick them up:

from vanty_auth.events import UserSignedUp
from vanty_core.events import on

from vanty_crm.services.person_service import PersonService

person_service = PersonService()


@on(UserSignedUp)
async def upsert_person_on_signup(evt: UserSignedUp) -> None:
    if evt.organization_id is None:
        return
    await person_service.upsert_by_email(
        organization_id=evt.organization_id,
        email=evt.email,
        defaults={"linked_user_id": evt.user_id, "source": evt.source or "signup"},
    )

That handler is the contract proven by tests/test_signup_subscriber.py.

Public API

Mounted at /crm by default. All routes (except POST /crm/feedback) require the X-Organization-ID header (or an active vanty_auth AuthContext).

Method Path Notes
GET /crm/people Filter by search, owner_id, stage, company_id. Cursor pagination via cursor/limit.
POST /crm/people Create person
GET /crm/people/{id} Get person
PATCH /crm/people/{id} Partial update
DELETE /crm/people/{id} Hard delete
POST /crm/people/merge Merge two people
GET /crm/people/{id}/activities Activity timeline
POST /crm/people/{id}/activities Log a note/email/call/event
GET /crm/companies Filter by search, owner_id
POST /crm/companies Create company
GET /crm/companies/{id}
PATCH /crm/companies/{id}
DELETE /crm/companies/{id}
POST /crm/companies/{id}/people Attach person
DELETE /crm/companies/{id}/people/{person_id} Detach person
POST /crm/feedback Public — accepts unauthenticated submissions
GET /crm/feedback List (auth required)
PATCH /crm/feedback/{id} Update / mark resolved

Pagination uses an opaque cursor (the last id returned). Responses include next_cursor while more rows are available.

Admin API

Mounted by host at /admin/crm behind require_superuser:

  • GET /admin/crm/people — cross-organization list (filter by organization_id)
  • GET /admin/crm/companies
  • GET /admin/crm/feedback
  • GET /admin/crm/activities
  • POST /admin/crm/imports/people — upload a CSV file, returns preview
  • POST /admin/crm/imports/people/{import_id}/apply — apply a previewed import

CSV imports are held in process memory between preview and apply (v1).

Events

Every event is a frozen dataclass registered with @event(...):

Event name Class
vanty_crm.person.created PersonCreated
vanty_crm.person.updated PersonUpdated
vanty_crm.person.merged PersonMerged
vanty_crm.person.deleted PersonDeleted
vanty_crm.company.created CompanyCreated
vanty_crm.company.updated CompanyUpdated
vanty_crm.feedback.received FeedbackReceived
vanty_crm.feedback.resolved FeedbackResolved
vanty_crm.activity.logged ActivityLogged

Develop

uv sync
uv run pytest --no-cov -p no:cacheprovider

Publish to its own GitHub repo

This package lives inside the Vanty monorepo but ships as a standalone PyPI package + GitHub repo. The publish-github Make target uses git-filter-repo to extract the vanty-crm/ subdirectory with full history and push it to a fresh repo:

brew install git-filter-repo  # one-time
make publish-github REPO=git@github.com:advantch/vanty-crm.git

License

MIT — © 2026 Themba themba@advantch.com

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vanty_crm-0.3.0.tar.gz (106.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vanty_crm-0.3.0-py3-none-any.whl (25.8 kB view details)

Uploaded Python 3

File details

Details for the file vanty_crm-0.3.0.tar.gz.

File metadata

  • Download URL: vanty_crm-0.3.0.tar.gz
  • Upload date:
  • Size: 106.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for vanty_crm-0.3.0.tar.gz
Algorithm Hash digest
SHA256 4e4f5d0e58d0e8fa38c659b07bfff8f185221b081ec211cd46c292d3c52e231a
MD5 7ec6bc58dbcbaea956de58097b551e8e
BLAKE2b-256 39f888f0d6125241f2f7b3bc62ff482546f4860bdf7eff46aff24b80f731b356

See more details on using hashes here.

Provenance

The following attestation bundles were made for vanty_crm-0.3.0.tar.gz:

Publisher: release.yml on advantch/vanty-crm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vanty_crm-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: vanty_crm-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 25.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for vanty_crm-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 94bbb9bfa8f6d51474d7002f53c6dc0d1defdf5cd59e1da1627d286de0575055
MD5 cdec160922590a572edfb3af9e564ea3
BLAKE2b-256 b1b7be7ac6566ccf862d04546880034c779506bcbf07ac6404f25311963bec4c

See more details on using hashes here.

Provenance

The following attestation bundles were made for vanty_crm-0.3.0-py3-none-any.whl:

Publisher: release.yml on advantch/vanty-crm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page