---
title: "Bulk Tools"
description: "Index the panel's lines, link existing WHMCS services to them and re-apply the product configuration to every line at once. For migrations from another WHMCS module and for mass connection changes."
---

The addon's **Bulk tools** view (module 1.3.0 and newer, **Addons → Xtream AI Panel → Bulk tools**) covers the two situations where editing services one by one is not an option: you are moving thousands of services from another WHMCS module onto this one, or you changed a product option that should reach every customer line. It runs from the browser in batches, with a progress bar, a counter per outcome and one result row per service, and every operation can be run again without duplicating anything.

The **Panel** dropdown at the top selects the panel every card works on. Run the cards in order: index first, then link, then sync.

## 1. Index panel lines

Reads every line of the selected panel through `GET /panel-api/v1/lines` (100 per batch) and stores a local copy in the addon table `mod_xtreamai_line_index`: line id, username, expiry, enabled flag and the WHMCS service number parsed from the line's `notes`. The parser uses the **Line Notes Template** from General Settings (`WHMCS:{service_id}` by default): anything else in the template becomes a wildcard, and the tag can sit anywhere in the notes, so lines the panel created with the `Created via panel-api | ` prefix are recognised too.

This card only reads from the panel. The first batch of a run clears the previous index of that panel, so the index always reflects the panel as it is now. Around 100 batches for a 10,000-line panel.

> [!NOTE]
> The `notes` field of line objects exists on panels updated on or after 2026-09-14. On an older panel the index is built without tags and the link step falls back to username matching only.

## 2. Link existing services

Candidates are WHMCS services whose product uses the `xtreamai` server module and that have no panel line recorded yet (no `mod_xtreamai_services` row, or a row without a line id). By default only `Active` and `Suspended` services are considered; tick **Include Pending, Terminated and Cancelled services** to widen it. Batches of 100, ordered by service id.

For each candidate the module resolves the product's panel (its **Panel** config option, or the first active panel when unset) and skips it when it is not the selected one. Then it looks for the line in the index, first by tag (`service_tag = service id`), then by username (`tblhosting.username`, when set). When a tag is shared by several lines, the one with the same username wins, otherwise the one with the latest expiry.

On a match the module writes the `mod_xtreamai_services` row (panel, line id, username, the product's package id), records the status (`Active` or `Suspended`, from the WHMCS service status) and the expiry from the index, and, when the WHMCS service has no username yet, fetches the line once and stores its username and password on the service so the client area card works. Nothing is created, changed or deleted on the panel.

| Outcome | Meaning |
|---|---|
| `linked_by_tag` | A line's notes carry this service number. The most reliable match. |
| `linked_by_username` | No tag matched; the service username equals a line username. |
| `not_found` | No line matched. The service was left untouched. |
| `ambiguous_tag` | Several lines carry this service number and none has the service's username. Nothing is linked, so a wrong line (and its password) is never attached to the service: set the username on the WHMCS service or fix the notes on the panel, then run again. The message lists the candidate line ids. |
| `skipped_sub_reseller` | The product is a Sub-Reseller product; there is no line to link. |
| `skipped_other_panel` | The product points at another panel. Run the tool with that panel selected. |
| `error` | The message column carries the exception text. The batch continues with the next service. |

The card refuses to start when the index of the selected panel is empty or when the last indexing run did not reach the last page, so a half-built index can never turn thousands of services into `not_found`. When the service username is empty, the line is read from the panel before the link is written, so a line that disappeared in between leaves the service unlinked and reported as `error` instead of half-linked.

## 3. Sync all services

Runs, for every linked service of the selected panel, exactly what the **Sync line to panel** button does on one service: WHMCS's local API executes the module's `sync` command (`ModuleCustom`), which recomputes bouquets, notes and the connection count (Max Connections plus any connections configurable option, see [Configure](/docs/?page=whmcs-module-configure)) from the product and pushes them to the line with `POST /lines/{id}/update`. Batches of 5, one panel call each: on a panel where an update takes two seconds, that keeps every request around ten seconds.

**Parallel requests** (module 1.4.0 and newer, selector next to the Sync button, 1 to 4, default 3) runs that many batch requests at once, each on its own disjoint share of the services (`service_id` modulo the number of requests), so the run finishes roughly N times faster. Resume state is kept per request: a run that stopped must be resumed with the same value, otherwise it starts over and says so. The module releases the WHMCS admin session while a batch runs, so the parallel requests do not queue behind the session lock.

By default only `Active` services are synced; tick **Include Suspended services** to add those. Sub-Reseller products are reported as `skipped_sub_reseller`. Each other service ends as `synced` or `error` with the panel's message. The package list is fetched once per batch. If a request fails, the page retries it twice ten seconds apart and, if it still fails, stops and remembers where it was: clicking the button again resumes from that service instead of starting over.

Every card of the view keeps its resume position in the browser, one `localStorage` entry per panel and operation (module 1.4.2 and newer), so a run that stopped can be resumed after a page reload or after a new login: the card shows the services it stopped after and the button continues from there. A batch that answers `Invalid security token` (the admin session token rotated) is retried once with a refreshed token before the run stops, and while a run is active the page refreshes that token every four minutes, which also keeps the WHMCS admin session alive during runs that last hours.

> [!IMPORTANT]
> This writes to every linked active line of the panel with the configuration its product has right now. Finish the product setup (packages, bouquets, Max Connections, configurable options) before you run it. On a reseller key the connection count is dropped by the module and only bouquets and notes are pushed.

## Migrating from another WHMCS module

If the previous module wrote the WHMCS service number into the panel line notes (the common `WHMCS:<service id>` convention), the whole migration is: point the products at this module, index the panel, link the services, then sync. Existing lines keep their id, username, password and expiry; customers notice nothing. Services whose lines carry no tag are linked by username; the ones that end as `not_found` are the only manual work left, and the results table lists them.

## See also

- [Configure](/docs/?page=whmcs-module-configure) for the connections configurable option.
- [Provisioning Modes](/docs/?page=whmcs-module-provisioning-modes) for what a single Sync does on the panel.
- [Troubleshooting](/docs/?page=whmcs-module-troubleshooting) when a service answers `This service has no panel line yet`.
