Integration-rot scans your repos for deprecated third-party API usage, drafts the migration with a contract test, and opens the pull request — so a vendor sunset never becomes your 2 a.m. incident.
$ integration-rot check demo/sample-app Integration-rot report for demo/sample-app critical=0 high=0 medium=2 low=0 [MEDIUM ] SendGrid: SendGrid v2 API deprecated — migrate to v3 (no sunset published) (no sunset date) [auto-fix available] - dependency `@sendgrid/mail` ^6.5.0 (npm, package.json) - app.py:28: `"https://api.sendgrid.com/api/mail.send.json",` (matches sendgrid-v2-api) [MEDIUM ] Stripe: Legacy Charges API superseded by PaymentIntents (SCA-ready) (no sunset date) [auto-fix available] - dependency `stripe` ^8.0.0 (npm, package.json) - app.py:16: `charge = stripe.Charge.create(` (matches stripe-charges-api)
Every third-party API you ship against is a liability with a timer on it. The blast radius hides across repos until a vendor flips the switch.
Sunset notices live in changelogs nobody reads. The deprecated call sits in
billing/worker.py for two years — until it starts returning 410s.
Each vendor migration is the same shape: find every call site, rewrite args, prove the new contract. It's mechanical work that eats senior-engineer weeks.
Vendors change fields and endpoints without a formal deprecation. Your integration keeps "working" while the data it returns quietly changes shape.
Four stages, one pipeline. Every transcript below is real output captured from running the tool — no mockups.
integration-rot scanInventory every third-party dependency and find hardcoded vendor API
calls. Parses package.json, requirements.txt,
go.mod, Gemfile and pom.xml, maps
76 SDK packages to vendors (plus 13 API hostnames), and heuristically spots vendor hostnames in source.
$ integration-rot scan demo/sample-app
Dependencies found in demo/sample-app:
npm @sendgrid/mail ^6.5.0 [SendGrid] (package.json)
npm stripe ^8.0.0 [Stripe] (package.json)
pypi requests 2.28.0 (requirements.txt)
pypi stripe 2.56.0 [Stripe] (requirements.txt)
Direct vendor API calls:
[SendGrid] app.py:28: "https://api.sendgrid.com/api/mail.send.json",
integration-rot checkCross-reference the inventory against a curated knowledge base of 17 deprecations β each linked to its vendor source, with dates only where the vendor published them. Risk is ranked by days-to-sunset and severity; exit code 1 on critical/high — built for CI gates and Markdown/JSON reporting.
$ integration-rot check demo/sample-app Integration-rot report for demo/sample-app critical=0 high=0 medium=2 low=0 [MEDIUM ] SendGrid: SendGrid v2 API deprecated — migrate to v3 (no sunset published) (no sunset date) [auto-fix available] - dependency `@sendgrid/mail` ^6.5.0 (npm, package.json) - app.py:28: `"https://api.sendgrid.com/api/mail.send.json",` (matches sendgrid-v2-api) [MEDIUM ] Stripe: Legacy Charges API superseded by PaymentIntents (SCA-ready) (no sunset date) [auto-fix available] - dependency `stripe` ^8.0.0 (npm, package.json) - app.py:16: `charge = stripe.Charge.create(` (matches stripe-charges-api)
integration-rot fixGenerate a real unified diff plus a contract test that proves the new
API contract. Eight migration drafters ship today: Stripe Charges→PaymentIntents,
Twilio Authy→Verify v2, SendGrid v2→v3, Plaid
/transactions/get→/transactions/sync, Slack RTM→Socket Mode,
GitHub ?access_token=→Authorization header, Salesforce retired API
versions→v59.0, and Mailchimp API 2.0→3.0 (migration draft — v3 operations
need manual endpoint mapping).
$ integration-rot fix demo/sample-app --entry stripe-charges-api --file app.py --- a/app.py +++ b/app.py @@ -13,11 +13,20 @@ def create_charge(amount_cents, currency, token, description=""): """Charge a card using the legacy Charges API (deprecated pattern).""" - charge = stripe.Charge.create( + # TODO(manual, required): `token` was a legacy Charges-API card token. + # It MUST now be a PaymentMethod ID (pm_...): convert it first β e.g. Stripe's + # Dashboard data migration tool for saved cards, or the Payment Element / + # Checkout for new cards. Stripe documents `payment_method` as a + # PaymentMethod, Card, or compatible Source ID β NOT a raw tok_... token. + # https://stripe.com/docs/api/payment_intents/create + # https://stripe.com/docs/payments/payment-methods/transitioning + charge = stripe.PaymentIntent.create( amount=amount_cents, currency=currency, - source=token, + payment_method=token, description=description, + confirm=True, + automatic_payment_methods={"enabled": True, "allow_redirects": "never"} ) return charge --- generated contract test: tests/test_stripe_payment_intent_contract.py --- """Contract test sketch ... asserts the call *shape* only β it does not prove Stripe accepts the value. payment_method must be a real pm_... ID.""" Notes: - rewrote 1 stripe.Charge.create call(s) to stripe.PaymentIntent.create (SCA-ready) - `source=token` was a legacy Charges-API card token: convert it to a PaymentMethod (pm_...) β manual step, e.g. Stripe's Dashboard data migration tool β then pass the pm_... ID. See https://stripe.com/docs/payments/payment-methods/transitioning - verify with stripe-mock or Stripe test clocks before merging
integration-rot verify + drift + proposeExecute the migration, don't just draft it: run the contract tests in
an isolated sandbox before anything touches your repo. Catch the changes vendors
don't announce: diff a pinned OpenAPI snapshot against the live spec and see
changed params and fields on the endpoints you pin — or track drift over time
with timestamped snapshot history. Then propose opens the PR from your
already-pushed branch, with the risk finding, diff and reviewer notes attached.
(It doesn't create the branch, commit, or push, and it doesn't run the contract test
— verify runs tests in a sandbox; test-gating propose is on
the v0.5 roadmap.)
$ integration-rot verify ./myrepo --entry stripe-charges-api --file app.py === evidence: stripe-charges-api on app.py === Files changed in sandbox: app.py, tests/test_stripe_payment_intent_contract.py Contract tests run: 1 (PASS) --- pytest output --- . [100%] 1 passed in 0.16s --- end pytest output --- $ integration-rot drift --vendor stripe --spec ./stripe-spec.json Schema drift for Stripe: Stripe: 0 endpoint(s) added, 0 removed, 1 changed [CHANGED] POST /v1/charges - request field `capture` removed - response field `amount` type changed: integer -> string $ export GITHUB_TOKEN=ghp_... # repo scope, read from env β never argv $ integration-rot propose ./myrepo --entry stripe-charges-api --file app.py \ --owner myorg --repo-name myrepo --head fix/stripe-payment-intents Pull request opened: https://github.com/myorg/myrepo/pull/42
A complete loop from detection to merged PR — not another dashboard that just tells you you're doomed.
17 curated deprecations across 13 vendors, each linked to its vendor source (Twitter's links a news report β no vendor announcement was published). Dates appear only where the vendor published them — entries without a published date say so. No invented sunsets.
Critical/high/medium/low by days-to-sunset and severity. Exit code 1 on serious findings — gate your CI.
Real unified-diff patches for 8 migrations: Stripe, Twilio Verify, SendGrid v3, Plaid sync, Slack Socket Mode, GitHub auth headers, Salesforce versions, Mailchimp v3 (draft).
Every draft ships a pytest contract test asserting the new API's parameter contract. Proof, not vibes.
Diff pinned OpenAPI snapshots against live vendor specs β or keep a timestamped drift history. Catches the changes nobody announced.
Opens a GitHub PR from your already-pushed branch, carrying the risk report, diff and reviewer notes. Draft-PR mode included. (It doesn't run the contract test — verify does.)
integration-rot mcp exposes scan, check, draft-fix, DB lookup and the fixer catalog as Model Context Protocol tools over stdio β stdlib only, no mcp package. Point any MCP client at it.
integration-rot serve serves a JSON API (stdlib http.server): GET /health /deprecations /fixers, POST /scan /check /fix. /fix drafts only β it never writes to disk.
integration-rot agent --path ./repo runs scan β draft β sandbox-test β keep-or-revert on a temp copy of your repo. Your repo is never modified; you get a report with diffs to review. The planner is a deterministic risk-first policy β no LLM, no API key, works offline.
The knowledge base today — dates only where the vendor published them. Eight ship with auto-fixers; the rest are detection-ready.
| Vendor | Entry | Title | Auto-fix |
|---|---|---|---|
| Stripe | stripe-charges-api | Legacy Charges API β PaymentIntents (SCA-ready) | fixer |
| Stripe | stripe-legacy-checkout | Legacy Checkout retired | detect |
| Twilio | twilio-authy-api | Authy API end-of-life β Verify v2 | fixer |
| Twilio | twilio-fax-api | Programmable Fax deprecated | detect |
| Slack | slack-rtm-api | RTM API deprecated β Socket Mode / Events API | fixer |
| SendGrid | sendgrid-v2-api | v2 API sunset β v3 | fixer |
| Plaid | plaid-legacy-transactions | /transactions/get β /transactions/sync | fixer |
| twitter-free-api-retirement | Free API v1.1/v2 β paid Basic tier | detect | |
| reddit-free-api-retirement | Free API β paid Data API | detect | |
| google-plus-api-shutdown | Google+ API shutdown | detect | |
| gcm-to-fcm | Google Cloud Messaging β Firebase Cloud Messaging | detect | |
| google-signin-gsi | gapi.auth2 β Google Identity Services | detect | |
| linkedin-api-v1-v2 | API v1 β v2 | detect | |
| Mailchimp | mailchimp-api-2-retirement | API 2.0 / Export API 1.0 β API 3.0 | fixer |
| instagram-legacy-api-shutdown | Legacy API Platform shutdown | detect | |
| GitHub | github-api-query-auth | Query-param auth β Authorization header | fixer |
| Salesforce | salesforce-api-v21-v30 | Platform API v21.0βv30.0 retirement | fixer |
Thirteen modules, zero runtime dependencies. The fetcher abstraction means live vendor feeds plug in without touching the analyzer.
agent Β· analyzer Β· api_server Β· cli Β· deprecations Β· fixer Β· mcp_server Β· models Β· proposer Β· scanner Β· schema_drift Β· vendor_map Β· verifier
v0.4.3 is the working core β verified, not just drafted: Windows-safe UTF-8 I/O, auto-detected test functions on every entry point, complete vendor map, honest Reddit framing. Next: deeper fixes, team workflow, vendor partnerships.
pip install entry point: a real integration-rot commandrot.toml)Python β₯ 3.10, zero runtime dependencies. Sixty seconds to your first report.
$ git clone https://github.com/Kayforkind/integration-rot.git $ cd integration-rot && python -m pip install -e ".[dev]" $ integration-rot demo # full pipeline on the bundled sample app $ integration-rot check /path/to/your/repo $ integration-rot agent --path /path/to/your/repo # autonomous fix loop on a temp copy
The open-source CLI is yours today. We're building a hosted service that watches your repos continuously, opens PRs with proven migrations, and pages you before a deprecation becomes an incident. Join the waitlist β β¦ developers already have.