Subscription recovery

Recover Pro access after a reinstall

If you already paid and only wiped your local setup, use the same billing email to open the portal. You do not need to buy Pro again for the same GitHub account or organization.

Local recovery is separate from billing recovery. Before reinstalling everything from scratch, run ecc list-installed, ecc doctor, and ecc repair to restore the local Claude setup.

Same billing email
Use the invoice email to reopen the existing subscription and avoid a new charge.
Same GitHub install
Reinstall ECC on the same repo or organization that already owns Pro.
No duplicate charge
Only start checkout if you're activating a brand-new install or a different organization.

Check if Pro is active

Confirm your paid plan is linked to the GitHub App install. Enter your GitHub login and the installation ID (shown on the GitHub App settings page URL, or in the bot's comments).

Prefer the terminal? Run npx ecc-tools status --account <login> or node scripts/ecc-local-daemon.mjs status --account <login> for the same PASS/FAIL report.

Cancel without the portal

If the billing portal is blocked (for example by duplicate billing records), cancel directly here. Enter the billing email and the last 4 digits of the card on file to prove ownership. All active subscriptions on the email are scheduled to cancel at the end of the billing period — no further charges.

Before you go

ECC 2.0.0 stable just shipped: 261 skills, the orch-* orchestrator family, and the control-pane substrate. If you're cancelling over a missing feature, it may already be here.

What you can do here

View invoices
Download receipts and payment history
Update payment
Change card or payment method
Cancel anytime
Cancels at end of billing period
Confirm access
Check the active subscription before you start a new checkout

If you were charged twice, stop here and use the existing portal session first. A new checkout is only for a new GitHub install or a different organization.

' + '
Tier: ' + escapeText(s.tier) + ' · Seats: ' + escapeText(s.seatCount) + ' · Analyses remaining: ' + escapeText(s.analysesRemaining == null ? 'unlimited' : s.analysesRemaining) + '
' for (var i = 0; i < (s.checks || []).length; i++) { var check = s.checks[i] lines += '
' + (check.ok ? 'PASS' : 'FAIL') + ' ' + escapeText(check.name) + ' — ' + escapeText(check.detail) + '
' } if (!s.proActive) { lines += '
Already paid? Relink the subscription below — do not start a new checkout.
' } showResult(resultEl, lines, !s.proActive) } else { showResult(resultEl, escapeText(data.error || 'Status check failed. Verify the GitHub login and installation ID.'), true) } } catch (err) { showResult(resultEl, 'Something went wrong. Try again or email affaan@ecc.tools.', true) } btn.disabled = false btn.textContent = 'Check Pro status' }) document.getElementById('relink-form').addEventListener('submit', async function(e) { e.preventDefault() var email = document.getElementById('relink-email').value.trim() var installationId = document.getElementById('relink-installation').value.trim() var btn = document.getElementById('relink-btn') var resultEl = document.getElementById('relink-result') btn.disabled = true btn.textContent = 'Relinking...' try { var res = await fetch(ECC_API_BASE + '/api/billing/relink', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: email, installationId: installationId }) }) var data = await res.json() if (data.success) { showResult(resultEl, '
Subscription relinked
' + escapeText((data.data && data.data.message) || 'Your paid plan is now linked to this installation.') + '
Use the status check above to confirm Pro is active.
', false) } else { showResult(resultEl, escapeText(data.error || 'Relink failed. Check the billing email and installation ID.'), true) } } catch (err) { showResult(resultEl, 'Something went wrong. Try again or email affaan@ecc.tools.', true) } btn.disabled = false btn.textContent = 'Relink subscription' }) document.getElementById('cancel-form').addEventListener('submit', async function(e) { e.preventDefault() var email = document.getElementById('cancel-email').value.trim() var cardLast4 = document.getElementById('cancel-card').value.trim() var btn = document.getElementById('cancel-btn') var resultEl = document.getElementById('cancel-result') if (!window.confirm('Schedule every active subscription on ' + email + ' to cancel at the end of the billing period?')) return btn.disabled = true btn.textContent = 'Cancelling...' try { var res = await fetch(ECC_API_BASE + '/api/billing/cancel', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: email, cardLast4: cardLast4, confirm: true }) }) var data = await res.json() if (data.success) { showResult(resultEl, '
Cancellation scheduled
' + escapeText((data.data && data.data.message) || 'All active subscriptions will cancel at the end of the billing period. No further charges.') + '
', false) } else { showResult(resultEl, escapeText(data.error || 'Cancellation failed. Check the email and card digits, or email affaan@ecc.tools.'), true) } } catch (err) { showResult(resultEl, 'Something went wrong. Try again or email affaan@ecc.tools.', true) } btn.disabled = false btn.textContent = 'Cancel at period end' }); (function() { var params = new URLSearchParams(window.location.search) var recoveryBanner = document.getElementById('account-recovery-banner') if (!recoveryBanner) return if (params.get('recovery') !== 'existing_subscription') return var account = params.get('account') var source = params.get('billingSource') || 'existing' recoveryBanner.textContent = account ? 'The ' + account + ' workspace already has an active ' + source + ' subscription. Do not start a new checkout. Use this portal flow or email affaan@ecc.tools if you suspect a duplicate charge.' : 'An active subscription already exists for this workspace. Do not start a new checkout. Use this portal flow or email affaan@ecc.tools if you suspect a duplicate charge.' recoveryBanner.style.display = 'block' })() document.getElementById('portal-form').addEventListener('submit', async (e) => { e.preventDefault() const email = document.getElementById('portal-email').value.trim() const btn = document.getElementById('portal-btn') const errEl = document.getElementById('portal-error') const loadEl = document.getElementById('portal-loading') errEl.style.display = 'none' loadEl.style.display = 'block' btn.disabled = true btn.textContent = 'Loading...' try { const res = await fetch('/api/billing/portal', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email }) }) const data = await res.json() if (data.success && data.data?.url) { window.location.href = data.data.url } else { errEl.textContent = data.error || 'No subscription found for this email. Check that you used the same email you subscribed with.' errEl.style.display = 'block' loadEl.style.display = 'none' btn.disabled = false btn.textContent = 'Open Billing Portal' } } catch (err) { errEl.textContent = 'Something went wrong. Please try again or email affaan@ecc.tools.' errEl.style.display = 'block' loadEl.style.display = 'none' btn.disabled = false btn.textContent = 'Open Billing Portal' } })
Start with ECC

Move from OSS discovery to repo-native rollout.

The ECC model stays additive: open-source distribution first, GitHub App automation when repository workflows matter, and enterprise support when the organization needs policy, rollout help, and governance.

210K+ stars Public repos free AgentShield protection layer