Skip to main content

modern-collections

The official Python client, command-line tool and MCP server for the Modern Collections API. With a Modern Collections API key you, a script, or your AI assistant can do what you do in the creditor dashboard: place overdue invoices, find and follow accounts, record payments, work disputes, attach documents, and pull what has been collected.

  • AI assistants (Claude, Cursor, anything that speaks MCP) connect to the hosted server at https://api.moderncollections.io/mcp. No install.
  • Command line: modern-collections.
  • Python: from mc_api import MCClient.

Connect your AI assistant

No install: the hosted server

Claude.ai, Claude Desktop, ChatGPT: connect and sign in. In ChatGPT, connect Modern Collections from the ChatGPT plugin directory (a Business or Enterprise workspace can also add the URL below as a custom MCP server). In Claude, add https://api.moderncollections.io/mcp as a custom connector. The app opens our sign-in page; sign in with your dashboard login and choose what the app may do. Starting, stopping or changing debtor outreach (placing invoices, recording payments, resolving disputes) is off unless you tick it. No API key is involved. A partner user connects the partner once; the app lists the linked creditors with partner_creditor_list and names one on each call. Disconnect any time under Settings → Developer access → Connected apps.

Without a credential, /mcp answers 401 with a WWW-Authenticate challenge that points OAuth clients at /.well-known/oauth-protected-resource/mcp.

Any other MCP client that can send a custom header can use an API key. Point it at:

  • URL: https://api.moderncollections.io/mcp (streamable HTTP)
  • Header: Authorization: Bearer <your API key>
{
  "mcpServers": {
    "modern-collections": {
      "url": "https://api.moderncollections.io/mcp",
      "headers": { "Authorization": "Bearer ca_..." }
    }
  }
}

Then ask your assistant to run account_whoami. It names the account the key reaches and says what, if anything, blocks placing (verification, the agreement, the payout account).

One command: Claude Code, Cursor, Claude Desktop

uv tool install modern-collections      # or: pip install modern-collections
modern-collections mcp install --client claude-code      # or cursor, claude-desktop

It asks for your key without echoing it, checks it, and only then writes the client's configuration:

--client What it writes
claude-code runs claude mcp add --transport http against the hosted server (--scope user by default; --scope project writes ${MC_API_KEY} into the project's .mcp.json instead of the key, so set MC_API_KEY where Claude Code runs)
cursor ~/.cursor/mcp.json
claude-desktop Claude Desktop's config file: a local modern-collections-mcp entry, or with --transport remote the hosted server through npx mcp-remote (a third-party npm package, pinned to one version, that holds your key; stdio is recommended)

Other servers already in those files are left alone. Restart Claude Desktop after installing.

Local server

The same tools run as a local stdio server, with the key in its environment:

MC_API_ENV=prod MC_API_KEY=ca_... modern-collections-mcp

modern-collections-mcp --http serves the same thing over streamable HTTP on http://127.0.0.1:8765/mcp; the key then comes from each request's Authorization header.

Getting a key

Dashboard → Settings → Developer access → API Key. The key is shown once, when it is created or rotated; Modern Collections keeps only a one-way hash, so a lost key is rotated, not recovered. The setup snippets are on the same page.

Creditor keys look like ca_<prefix>_<secret>. Partner keys (pa_<prefix>_<secret>) act for every creditor linked to the partner; see Partner keys.

What your assistant can do

A default set of about 30 tools covers everyday work:

Area Tools
Setup account_whoami, getting_started, account_setup_update, guide, partner_creditor_list
Placing and following placement_create, placement_list (search by company or invoice number), placement_status, audit_query, placement_context
Changing a placement placement_amend, placement_update_debtor, placement_set_posture, placement_recall
Payments payment_record, payment_list
Disputes dispute_list, dispute_get, dispute_acknowledge, dispute_resolve
Documents document_upload, document_upload_link, placement_documents, document_status
Reporting analytics_summary, analytics_series, remittance_list, statement_list, statement_download_url

Everything else (settlement floors, contact choices, CSV and file intake, evidence scoring, the recovery workspace, notifications, webhooks, QuickBooks) is in the full set. Turn it on with ?tools=all on the hosted URL (or the header X-MC-Tools: all), MC_API_MCP_TOOLS=all for the local server, or --all-tools on mcp install.

document_upload_link is for files on your computer that a chat assistant cannot read: it returns a link, valid for 30 minutes, that you open in a browser to upload the file onto the placement.

Confirmations

Tools that contact a debtor, move money, change who is contacted, or make a legal statement are marked destructive (placement_create, payment_record, placement_amend, placement_update_debtor, dispute_resolve, placement_recall, intake commits, and a few more). Your assistant asks before using them, and most MCP clients ask you too. When you want your assistant to act without asking, tell it so, or choose "always allow" for those tools in your MCP client.

placement_create places a real invoice against a real company. We review each new placement before outreach starts. placement_recall stops a placement that should not have been opened. To test, sign up for the sandbox at https://app.demo.moderncollections.io. It simulates outreach and moves no money. A platform that places for its own customers picks "Start a partner sandbox" there instead of creating a company, then mints a pa_ key under Settings, API & webhooks. Its API and MCP base URL is https://api.demo.moderncollections.io.

Install

uv tool install modern-collections      # CLI + local MCP server
pip install modern-collections          # or as a library

Python 3.11 or later. The PyPI project named mc-api is unrelated.

Configure

Pick an environment and supply a key. There is no default environment, so a forgotten setting can never send a key to the wrong place. The key is read from MC_API_KEY and is never printed or logged.

Variable Values
MC_API_ENV prod
MC_API_BASE_URL an explicit API URL (overrides MC_API_ENV)
MC_API_KEY ca_... (creditor) or pa_... (partner)
MC_API_CREDITOR partner keys only: the default creditor to act for

Command line

export MC_API_ENV=prod MC_API_KEY=ca_...

modern-collections whoami                                  # check your setup
modern-collections placements list --q "Acme"              # find by company or invoice number
modern-collections placements list --limit 100 --offset 100 # the second page of 100
modern-collections placements create --company "Acme Freight" --amount 7500.00 --email ap@acme.co \
  --invoice-number INV-4411 --invoice-date 2026-01-01 --due-date 2026-02-01 --state OH
modern-collections placements get <placement_id>
modern-collections placements audit <placement_id>         # what has happened on the account
modern-collections payments record <placement_id> --amount 500.00 --idempotency-key <bank-ref>
modern-collections disputes list --status open
modern-collections documents upload invoice-4411.pdf --placement <placement_id> --type invoice
modern-collections documents upload-link <placement_id> --type invoice
modern-collections analytics summary
modern-collections remittances list

-o json gives machine-readable output. It is a global option, so it goes before the subcommand (modern-collections -o json placements list), as do --env, --base-url, --api-key, --creditor and --timeout. Exit codes: 0 success, 1 API or runtime error, 2 configuration or usage error.

File an invoice, read it back, correct it

P=$(modern-collections -o json placements create --company "Acme Freight" --amount 7500.00 \
      --invoice-number INV-4411 --invoice-date 2026-01-01 --due-date 2026-02-01 \
      | jq -r .placement_id)

modern-collections documents upload invoice-4411.pdf --placement $P --type invoice
modern-collections placements context $P                    # what was read off the invoice
modern-collections placements amend $P --po-number PO-8871 --reason "read off the signed BOL"
modern-collections placements amend $P --amount 7250.00 --reason "credit memo applied"
modern-collections placements debtor $P --email ap@acmefreight.com

Amendments apply while outreach is running: the next call or email uses the corrected placement. Every edit is kept as an append-only, audited record. One amendment kind per call: --amount, --notes/--clear-notes, or any mix of the invoice fields (--invoice-number, --invoice-date, --due-date, --po-number, --service-date, --currency, --written-contract). Status is changed through payments record, disputes resolve or placements recall.

Python

from mc_api import MCClient, NotFoundError

with MCClient(env="prod", api_key="ca_...") as api:
    print(api.whoami()["blocking"])

    placement = api.create_placement(
        {
            "invoice_amount": "7500.00",
            "invoice_number": "INV-4411",
            "invoice_date": "2026-01-01",
            "due_date": "2026-02-01",
            "debtor": {"company_name": "Acme Freight", "primary_email": "ap@acme.co"},
        },
        idempotency_key="INV-4411",
    )

    for row in api.iter_placements(q="Acme", status="in_outreach"):
        print(row["id"], row["invoice_amount"])

    try:
        api.get_placement("00000000-0000-0000-0000-000000000000")
    except NotFoundError:
        pass

Errors are ApiError subclasses: AuthError (401/403), NotFoundError, ValidationError (400/422, with field errors flattened to one line) and RateLimitedError (429, with retry_after). Every ApiError carries code, the API's machine-readable error.code (for example duplicate_invoice or not_found), so branch on that instead of the message. iter_placements, iter_documents, iter_payments, iter_remittances, iter_statements, iter_exports, iter_audit and iter_partner_creditors walk every page lazily. Every paginated list takes limit and offset (--limit / --offset on the CLI) and returns {"items": [...], "limit": ..., "offset": ..., "total": ...}, where total counts every row matching the filters:

page = api.list_placements(status="in_outreach", limit=100, offset=200)
print(len(page["items"]), "of", page["total"])

list_payments returns two such pages, commitments and attempts. To catch up after a missed webhook, pass updated_since= (a timezone-aware timestamp) to list_placements / iter_placements, or --updated-since on the CLI; rows come back oldest change first. Resume from the last row you hold: its updated_at as updated_since and its id as after_id (--after-id), so rows changed at the same instant are not skipped. Webhook settings accept webhook_events to subscribe to a subset of event types (unset means all). Every create the API dedupes takes idempotency_key= (--idempotency-key on the CLI): placements, recall, amend, dispute resolve, exports, document upload, intake and partner creditor create and invites. A retry with the same key gets the first response back. The package ships type hints (py.typed).

Partner keys

A partner key reaches every creditor linked to the partner through one connection, one creditor per call:

  • MCP: run partner_creditor_list, then pass creditor= on each call (the creditor's id, its external reference, or its exact name).
  • CLI: modern-collections partner creditors, then --creditor <id or ref>, or set MC_API_CREDITOR.
  • Python: MCClient(api_key="pa_...", creditor="<id or ref>"), or client.for_creditor("<id or ref>") for one call on a client shared across creditors.

Two calls cover the whole partner at once, no creditor needed: remittances across every linked creditor (partner_remittance_list, modern-collections partner remittances, list_partner_remittances) and statements (partner_statement_list, modern-collections partner statements, list_partner_statements), each row carrying the creditor it belongs to plus totals for the filtered set. Partner onboarding is scriptable too: create a creditor, submit KYB, invite a user, update a link, fulfil an evidence request, and read webhook events.

A call without a creditor gets a 400 from the API; the client never guesses. With a creditor key the creditor argument is ignored.

Changes in 0.5.0

Partner features added since 0.4.1. Each is a Python method, a CLI command and, unless it handles key material or the webhook destination, an MCP tool.

  • Placements across the whole book: list_partner_placements, with search, status and date filters and an updated_since catch-up.
  • CSV exports of placements, remittances and payments across the book: download_partner_export.
  • Bulk creditor import: import_partner_creditors, get_partner_creditor_import, list_partner_creditor_imports and the template.
  • Notification settings for the partner and per creditor: get_partner_notification_settings, replace_partner_notification_settings.
  • Open settlement reviews across the book: list_partner_settlement_reviews.
  • Keys: set the webhook URL and events with a full key (update_partner_webhook), and mint, rotate or revoke a read-only key (rotate_partner_read_only_key, revoke_partner_read_only_key).
  • Bulk placement import: import_partner_placements, get_partner_placement_import, list_partner_placement_imports and the template.
  • Creditor control: update_partner_creditor (name, address, industry), recall_all_partner_creditor_placements, pause_placement, resume_placement and unrecall_placement (within 7 days of a recall).
  • Reconciliation: get_partner_statement_lines and get_partner_audit.
  • Partner-level webhooks such as partner.import_completed: redeliver_partner_level_webhook.
  • creditor= per call: client.for_creditor(ref) returns a view that sends one creditor's header and shares the parent's connection.
  • MCP: a partner key sees every partner tool by default, and account_setup_update works for a partner as the REST route does.
  • Removed: invite_partner_creditor_user no longer takes purpose="msa". The partner addendum covers creditors a partner creates.

Agent skills

Two skills teach an assistant how to work the account: modern-collections (everyday operations) and collections-ar-triage (turn an AR aging export into a reviewed batch of placements). They ship in this package:

modern-collections skills install              # into ~/.claude/skills
modern-collections skills install --dir <project>/.claude/skills

Over MCP the same text is available as prompts, as resources (modern-collections://skills/<name>), and through the guide tool.

Kept out of reach on purpose

  • Accepting the master service agreement is a person's click in the dashboard. The API refuses it from an API key, and no tool or command offers it.
  • Rotating the full API key needs a signed-in dashboard session, so a leaked key cannot replace itself. A creditor key cannot set the webhook URL; a partner's full key can, and its read-only key cannot.
  • Signing secrets stay out of the model. The intake webhook secret can be rotated and used from the CLI and the Python client, not through MCP.

Safety properties

  • The API key lives only in the request Authorization header. It is kept out of repr(), exceptions and logs, and so is the creditor scope; neither is sent on the unauthenticated health() check.
  • Explicit connect, read and write timeouts.
  • Reads are retried on 429 (honouring Retry-After), 502, 503 and 504. A write is retried only when it carries an idempotency key, which the server replays; other writes are sent once, so a create or a payment is never silently duplicated. The MCP server derives a key for placement_create and payment_record when none is given, so a retried tool call replays instead of acting twice.
  • Client-side checks on amendment fields and enum-like arguments, so a typo fails locally.
  • Signed structured intake is HMAC'd over the exact bytes sent.

License

Apache License 2.0. See LICENSE.

Metadata

Release files for modern-collections 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for modern-collections 0.5.0
File Size Uploaded Provenance Actions
modern_collections-0.5.0.tar.gz 124.2 kB Without provenance Details for modern_collections-0.5.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for modern-collections 0.5.0
File Interpreter ABI Platform Actions
modern_collections-0.5.0-py3-none-any.whl Python 3 none any Details for modern_collections-0.5.0-py3-none-any.whl

Total release size: 278.9 kB

Release files / modern_collections-0.5.0.tar.gz

Download URL modern_collections-0.5.0.tar.gz
Size 124.2 kB
Tags Source
SHA-256 checksum
How to use checksums
08ba6fc2ce1b42337806af0ae4a7527627a3423735060ed81a94060cbae4dd48
BLAKE2b-256 checksum
How to use checksums
c518aa30f53c36913428f2129658cce523931d6db1369144102d298296f87df4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / modern_collections-0.5.0-py3-none-any.whl

Download URL modern_collections-0.5.0-py3-none-any.whl
Size 154.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f29ac0c84061087aec1eacc845a6c388c59c1a10181490828db8c992125eb7fd
BLAKE2b-256 checksum
How to use checksums
e33e0c15dc78671fa250bf299c618ceacf34e4dac3c5bd319101a1b630f372c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.8 {"installer":{"name":"uv","version":"0.12.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for modern-collections 0.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page