diff --git a/billing/credit/mocks/transaction_repository.go b/billing/credit/mocks/transaction_repository.go
index 65b5d30dd..e954105b3 100644
--- a/billing/credit/mocks/transaction_repository.go
+++ b/billing/credit/mocks/transaction_repository.go
@@ -200,6 +200,65 @@ func (_c *TransactionRepository_GetBalanceForRange_Call) RunAndReturn(run func(c
 	return _c
 }
 
+// GetBalanceForRangeWithoutOverdraft provides a mock function with given fields: ctx, accountID, start, end
+func (_m *TransactionRepository) GetBalanceForRangeWithoutOverdraft(ctx context.Context, accountID string, start time.Time, end time.Time) (int64, error) {
+	ret := _m.Called(ctx, accountID, start, end)
+
+	if len(ret) == 0 {
+		panic("no return value specified for GetBalanceForRangeWithoutOverdraft")
+	}
+
+	var r0 int64
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, time.Time, time.Time) (int64, error)); ok {
+		return rf(ctx, accountID, start, end)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, string, time.Time, time.Time) int64); ok {
+		r0 = rf(ctx, accountID, start, end)
+	} else {
+		r0 = ret.Get(0).(int64)
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, string, time.Time, time.Time) error); ok {
+		r1 = rf(ctx, accountID, start, end)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// TransactionRepository_GetBalanceForRangeWithoutOverdraft_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBalanceForRangeWithoutOverdraft'
+type TransactionRepository_GetBalanceForRangeWithoutOverdraft_Call struct {
+	*mock.Call
+}
+
+// GetBalanceForRangeWithoutOverdraft is a helper method to define mock.On call
+//   - ctx context.Context
+//   - accountID string
+//   - start time.Time
+//   - end time.Time
+func (_e *TransactionRepository_Expecter) GetBalanceForRangeWithoutOverdraft(ctx interface{}, accountID interface{}, start interface{}, end interface{}) *TransactionRepository_GetBalanceForRangeWithoutOverdraft_Call {
+	return &TransactionRepository_GetBalanceForRangeWithoutOverdraft_Call{Call: _e.mock.On("GetBalanceForRangeWithoutOverdraft", ctx, accountID, start, end)}
+}
+
+func (_c *TransactionRepository_GetBalanceForRangeWithoutOverdraft_Call) Run(run func(ctx context.Context, accountID string, start time.Time, end time.Time)) *TransactionRepository_GetBalanceForRangeWithoutOverdraft_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(context.Context), args[1].(string), args[2].(time.Time), args[3].(time.Time))
+	})
+	return _c
+}
+
+func (_c *TransactionRepository_GetBalanceForRangeWithoutOverdraft_Call) Return(_a0 int64, _a1 error) *TransactionRepository_GetBalanceForRangeWithoutOverdraft_Call {
+	_c.Call.Return(_a0, _a1)
+	return _c
+}
+
+func (_c *TransactionRepository_GetBalanceForRangeWithoutOverdraft_Call) RunAndReturn(run func(context.Context, string, time.Time, time.Time) (int64, error)) *TransactionRepository_GetBalanceForRangeWithoutOverdraft_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // GetByID provides a mock function with given fields: ctx, id
 func (_m *TransactionRepository) GetByID(ctx context.Context, id string) (credit.Transaction, error) {
 	ret := _m.Called(ctx, id)
diff --git a/billing/credit/service.go b/billing/credit/service.go
index 8bb1557e9..00501b27f 100644
--- a/billing/credit/service.go
+++ b/billing/credit/service.go
@@ -19,6 +19,7 @@ type TransactionRepository interface {
 	List(ctx context.Context, flt Filter) ([]Transaction, error)
 	GetByID(ctx context.Context, id string) (Transaction, error)
 	GetBalanceForRange(ctx context.Context, accountID string, start time.Time, end time.Time) (int64, error)
+	GetBalanceForRangeWithoutOverdraft(ctx context.Context, accountID string, start time.Time, end time.Time) (int64, error)
 }
 
 type CustomerRepository interface {
@@ -164,6 +165,12 @@ func (s Service) GetBalanceForRange(ctx context.Context, accountID string, start
 	return s.transactionRepository.GetBalanceForRange(ctx, accountID, start, end)
 }
 
+// GetBalanceForRangeWithoutOverdraft returns the balance for the given accountID within the given time range
+// excluding credit transactions sourced from overdraft reconciliation
+func (s Service) GetBalanceForRangeWithoutOverdraft(ctx context.Context, accountID string, start time.Time, end time.Time) (int64, error) {
+	return s.transactionRepository.GetBalanceForRangeWithoutOverdraft(ctx, accountID, start, end)
+}
+
 func (s Service) GetByID(ctx context.Context, id string) (Transaction, error) {
 	return s.transactionRepository.GetByID(ctx, id)
 }
diff --git a/billing/invoice/service.go b/billing/invoice/service.go
index ce528ebef..39593afe1 100644
--- a/billing/invoice/service.go
+++ b/billing/invoice/service.go
@@ -55,6 +55,7 @@ type CustomerService interface {
 type CreditService interface {
 	Add(ctx context.Context, cred credit.Credit) error
 	GetBalanceForRange(ctx context.Context, accountID string, start time.Time, end time.Time) (int64, error)
+	GetBalanceForRangeWithoutOverdraft(ctx context.Context, accountID string, start time.Time, end time.Time) (int64, error)
 }
 
 type ProductService interface {
@@ -522,7 +523,7 @@ func (s *Service) GenerateForCredits(ctx context.Context) error {
 			continue
 		}
 
-		balance, err := s.creditService.GetBalanceForRange(ctx, c.ID, startRange, endRange)
+		balance, err := s.creditService.GetBalanceForRangeWithoutOverdraft(ctx, c.ID, startRange, endRange)
 		if err != nil {
 			errs = append(errs, fmt.Errorf("failed to get balance for customer %s: %w", c.ID, err))
 			continue
diff --git a/internal/store/postgres/billing_transactions_repository.go b/internal/store/postgres/billing_transactions_repository.go
index ee56d7b47..7f8d3c1c4 100644
--- a/internal/store/postgres/billing_transactions_repository.go
+++ b/internal/store/postgres/billing_transactions_repository.go
@@ -494,3 +494,64 @@ func (r BillingTransactionRepository) GetBalanceForRange(ctx context.Context, ac
 	}
 	return amount, nil
 }
+
+func (r BillingTransactionRepository) getCreditBalanceExcludingSource(ctx context.Context, tx *sqlx.Tx, accountID string,
+	start *time.Time, end *time.Time, excludeSource string) (*int64, error) {
+	stmt := dialect.Select(goqu.SUM("amount")).From(TABLE_BILLING_TRANSACTIONS).Where(goqu.Ex{
+		"account_id": accountID,
+		"type":       credit.CreditType,
+	}).Where(goqu.C("source").Neq(excludeSource))
+	if start != nil {
+		stmt = stmt.Where(goqu.Ex{
+			"created_at": goqu.Op{"gte": *start},
+		})
+	}
+	if end != nil {
+		stmt = stmt.Where(goqu.Ex{
+			"created_at": goqu.Op{"lt": *end},
+		})
+	}
+	query, params, err := stmt.ToSQL()
+	if err != nil {
+		return nil, fmt.Errorf("%w: %s", parseErr, err)
+	}
+
+	var creditBalance *int64
+	if err = r.dbc.WithTimeout(ctx, TABLE_BILLING_TRANSACTIONS, "GetCreditBalanceExcludingSource", func(ctx context.Context) error {
+		return tx.QueryRowxContext(ctx, query, params...).Scan(&creditBalance)
+	}); err != nil {
+		return nil, fmt.Errorf("%w: %s", dbErr, err)
+	}
+	return creditBalance, nil
+}
+
+// GetBalanceForRangeWithoutOverdraft returns the balance excluding credit transactions
+// with source 'system.overdraft'. This prevents reconciliation credits from inflating
+// the balance when calculating credit overdraft invoices.
+func (r BillingTransactionRepository) GetBalanceForRangeWithoutOverdraft(ctx context.Context, accountID string, start time.Time,
+	end time.Time) (int64, error) {
+	var amount int64
+	if err := r.dbc.WithTxn(ctx, sql.TxOptions{
+		Isolation: sql.LevelSerializable,
+	}, func(tx *sqlx.Tx) error {
+		debitBalance, err := r.getDebitBalance(ctx, tx, accountID, &start, &end)
+		if err != nil {
+			return fmt.Errorf("failed to get debit balance: %w", err)
+		}
+		creditBalance, err := r.getCreditBalanceExcludingSource(ctx, tx, accountID, &start, &end, credit.SourceSystemOverdraftEvent)
+		if err != nil {
+			return fmt.Errorf("failed to get credit balance: %w", err)
+		}
+		if creditBalance == nil {
+			creditBalance = new(int64)
+		}
+		if debitBalance == nil {
+			debitBalance = new(int64)
+		}
+		amount = *creditBalance - *debitBalance
+		return nil
+	}); err != nil {
+		return 0, fmt.Errorf("failed to get balance: %w", err)
+	}
+	return amount, nil
+}
