Documentation

Complete user guide for CellHub Billing — every menu, every feature, explained.

System Overview

CellHub Billing is a complete VoIP billing and softswitch platform built on Laravel 12, Asterisk 20 (PJSIP), and Janus WebRTC. It is a multi-tenant SaaS application that supports admin, reseller, and client roles, suitable for ITSPs, call centers, and businesses requiring VoIP infrastructure with real-time billing.

Key capabilities

  • Real-time per-second billing with rate decks and tariffs
  • Trunk groups and Least Cost Routing (LCR) with sequential failover
  • Built-in WebRTC web phone (Janus) — no external softphone needed
  • Campaign manager (Manual / Auto Agent / IVR / IVR + Live Transfer)
  • Click-to-Call widget for client websites
  • DIDs, IVR builder, queues, holidays, audio files
  • SIP Trace, CDR reports, summary reports by day/month/trunk/user/DID
  • Auto update system with one-click upgrades

User Roles

CellHub Billing has four roles, each with different access levels:

RoleAccess
SUPERADMINFull system access, including resellers, group users, billing settings, and infrastructure.
ADMINManages clients, SIP users, rates, trunks, DIDs, and most settings. Cannot manage other admins or system-level configuration.
RESELLERManages own clients only (scoped). Has its own rate decks/margins. Sees its own customer base, CDRs, and revenue.
CLIENTEnd user of VoIP service. Manages own SIP user(s), uses Web Phone, accesses Click-to-Call widget. Cannot create/delete SIP users.
Tip: Role determines what menu items are visible in the sidebar. The same URL may return 403 for unauthorized roles.

Multi-Tenancy

The system separates data by ownership:

  • Admins see all data across the entire system.
  • Resellers see only their own users and their users' data (CDRs, SIP users, balances).
  • Clients see only their own data.

Internally this is enforced through:

  • Database user_id / reseller_id foreign keys
  • Eloquent scope User::forCurrentUser() applied to most queries
  • Controller-level abort(403) checks for sensitive operations

First Steps After Installation

  1. Login — Use admin credentials provided during installation.
  2. Configure SMTP — Settings → SMTP. Required for password reset, low credit alerts, system notifications.
  3. Add a Trunk — Routes → Trunks. This is the upstream provider you route calls through.
  4. Create a Rate Deck — Rates → Rate Decks. Or upload a CSV of prefix/rate pairs.
  5. Create a Plan — Rates → Plans. Assigns the rate deck and any per-minute markup.
  6. Create a User (Client) — Clients → Users. Assign the plan and add credit.
  7. Create a SIP User — Clients → SIP Users. Owned by the client. Test with a softphone.
  8. Make a test call — Use a softphone or the built-in Web Phone.

Dashboard

The first page after login. Displays key statistics tailored to the current role.

Sections

  • Active calls — How many calls are happening right now.
  • Today\'s totals — Calls, duration, revenue.
  • Top destinations — Most-called countries today.
  • Recent CDRs — Last 10 calls.
  • System health (admin) — Asterisk, Janus, MySQL, queue worker status.

Users

ADMIN RESELLER

Users are customer accounts. Each user can own multiple SIP users.

Fields

FieldDescription
Name / EmailLogin credentials and display name
Roleadmin, reseller, or client
PlanPricing plan (rates) the user is billed against
CreditCurrent prepaid balance — calls deduct from this
SIP account limitMaximum SIP users this account can have (0 = unlimited)
Statusactive / suspended / blocked
ResellerIf client belongs to a reseller, set here

How to add credit

Open user → "Add Credit" → enter amount and reference note. Balance increases immediately, recorded in the credits table.

SIP Users

SIP users are the actual SIP accounts that register to Asterisk and place calls. Each SIP user belongs to a User (owner).

Important fields

FieldDescription
Username / PasswordSIP authentication credentials
Caller IDOutbound caller ID, format: "Name" <number>
Hostdynamic for register-based, or a fixed IP for IP-to-IP downstream peer
Strip PrefixRemove this prefix from incoming numbers (normalization)
Add PrefixPrepend this to incoming numbers (normalization)
NATNAT traversal mode (default: force_rport,comedia)
CodecsAllowed codecs (e.g., alaw,ulaw,g729)
Trunk Group / Single TrunkOverride default routing — if set, calls from this SIP user use this trunk/group instead of rate-based selection
Rate DeckOverride the user\'s plan rate deck for this SIP user only (rare)
Max CallsConcurrent call limit per SIP user
Record CallsIf enabled, audio is recorded to /var/spool/asterisk/monitor
Important: Clients cannot create or delete SIP users — only admin/reseller can. Clients can edit their SIP user\'s password and Caller ID only.

Status indicator

The "SIP Status" column shows real-time registration: Online (green) means registered; Offline (red) means not registered. Updated via AJAX call to AMI.

Web Phone Access

ADMIN RESELLER — Located under Clients menu

Controls which SIP users have access to the built-in Web Phone (Janus WebRTC).

Toggle the "Web Phone Enabled" switch per SIP user. When enabled, that SIP user can log into the Dialer → Web Phone page and place calls from the browser.

Tip: Disabling web phone access does not disconnect existing browser sessions; it prevents new logins.

Calls Online

ADMIN RESELLER

Real-time list of ongoing calls. Shows source, destination, trunk, duration so far, and SIP user.

You can force-hangup any call from this page (red trash icon).

Auto-refreshes every 5 seconds.

CallerID

Caller ID whitelist or blacklist (optional). Use cases:

  • Whitelist: only allow calls from specific numbers
  • Blacklist: block calls from specific numbers

If empty, no caller ID filtering is applied.

Restricted Numbers

Per-SIP-user destination blacklist. If a SIP user attempts to call a number matching a restricted prefix, the call is rejected at the AGI billing layer with BILLINGRESULT=blocked.

Two layers

  • Per-SIP-user: Restricted Numbers entries are tied to specific SIP users
  • Global: The restrict_phones table applies to all calls regardless of SIP user

Match is prefix-based: dst LIKE CONCAT(prefix, '%').

User History

Audit log of changes to user accounts: credit additions, plan changes, status changes, etc.

IAX Users

For older clients using IAX2 protocol (Inter-Asterisk eXchange). Most modern setups use SIP only.

Sipuras

Provisioning records for Linksys/Sipura ATA devices. Used to auto-generate config files for physical ATAs.

CDR Reports

Call Detail Records — every completed call has a CDR row. The main reporting interface.

Columns

ColumnMeaning
DateCall start time
SIP UserThe SIP user who made the call
SourceCaller ID number
DestinationDialed number
Dest. NameDestination country/network (looked up from prefix)
DurationTotal call duration including ringing
Bill SecBillable seconds (after answer)
StatusANSWERED / NO ANSWER / BUSY / FAILED / CONGESTION
CodeSIP hangup cause code
CostCharged amount in account currency

Filters

Filter by date range, SIP user, destination prefix, status. Click "Filter" to apply.

Export

"Export" button downloads filtered CDRs as CSV.

Bulk delete

ADMIN RESELLER

Each row has a checkbox. Select multiple rows individually or hold Shift and click to select a range. The bulk action bar appears at the top with a "Delete Selected" button. Reseller scope: only own users' CDRs are deletable.

Failed Calls

Filtered view of CDRs where disposition != ANSWERED. Helpful for diagnosing routing issues, busy destinations, or trunk failures.

Trunk Errors

Aggregated view of CONGESTION/CHANUNAVAIL responses by trunk. If a trunk has an unusually high error rate, this is where you spot it.

Call Archive

Recorded call audio files. Each row shows the recording, with an inline player and download link.

Recording is enabled per SIP user (the "Record Calls" checkbox). Files are stored in /var/spool/asterisk/monitor.

Summary by Day

Aggregated CDR data per day: total calls, total minutes, total cost. Useful for daily revenue tracking.

Summary by Month

Same as Summary by Day but grouped per month. Use for monthly billing and revenue reports.

Summary by User

Per-user usage and cost breakdown for the selected period.

Summary by Trunk

Per-trunk usage and cost. Helps identify which providers handle the most traffic and revenue.

DID History

Inbound call history for DID numbers. Shows which DID was called, who answered, and what happened.

Offer CDR

CDRs filtered to calls covered by an active Offer (unlimited country package). These calls have cost=0 because they fall under the user\'s offer.

Offer Use

Offer activity report. For each user with an active offer, shows progress bar (days remaining), total calls made, and total minutes consumed.

Tariffs (Rates)

Pricing per destination prefix. Each rate has:

FieldDescription
PrefixDestination prefix (e.g., 1 for USA, 44 for UK)
DestinationHuman-readable name (e.g., "USA Mobile")
RatePrice per minute (sell rate)
Initial BlockMinimum billed seconds (e.g., 60 = first minute charged in full)
Billing BlockGranularity after initial block (e.g., 1 = per second, 60 = per minute)
Connect ChargeFlat fee added per call
TrunkOptional — force this rate to use a specific trunk (LCR override)
How matching works: Longest prefix wins. If destination is 905551234567 and you have prefixes 9, 90, and 905, the rate with prefix 905 is selected.

Plans

A pricing plan groups one or more rate decks together. Users are assigned a plan, which determines what rates they pay.

You can have one plan with all rates, or different plans for different markets (e.g., "USA Plan", "Europe Plan").

Rate Decks

A rate deck is a collection of rates (typically imported from a CSV from your wholesale provider). Plans reference rate decks.

Import CSV

Upload a CSV with columns: prefix,destination,rate,initblock,billingblock,connectcharge. The system auto-creates rate entries.

Prefixes

Reference table of country/destination prefixes (e.g., 1=USA, 44=UK). Used in dropdowns when creating rates so you don\'t need to remember prefixes manually.

User Custom Rates

Per-user prefix override above the user\'s plan rates. Use cases:

  • Give one specific customer a discount on a specific country
  • Charge premium for a specific user

Match priority: User Custom Rate > Plan Rate > Default Rate Deck.

Offers

Unlimited-country packages: a user can call a specific prefix for free for N days. Sold as add-on packages.

Fields

FieldDescription
UserThe user the offer applies to
PrefixDestination prefix covered (e.g., 1 for USA)
DestinationDisplay name (e.g., "USA Unlimited")
DaysValidity period in days from start
Starts at / Expires atAuto-calculated dates

While active, calls matching the offer prefix are billed at cost=0 and tracked in CDR with offer_id.

Reseller Margin

Defines the markup % a reseller adds on top of the wholesale rate. Reseller\'s clients see the marked-up rate.

Example: wholesale rate $0.01/min, reseller margin 50% → reseller\'s clients pay $0.015/min. The 50% difference is reseller revenue.

Trunks

Upstream SIP providers used to terminate outbound calls (and optionally receive inbound).

Fields

FieldDescription
NameInternal identifier (used in dialplan as trunk-{name})
Host / PortProvider SIP server
Username / PasswordIf provider requires Digest auth (REGISTER mode)
CodecsAllowed codecs
Max ChannelsConcurrent call cap
Trunk Prefix (Add)Prepend to dialed number before sending to provider
Strip Prefix (Remove)Remove from dialed number before sending
RegisterToggle outbound REGISTER (provider authentication)
Statusactive / inactive
PriorityLower number = higher preference (used in trunk groups)

Two authentication modes

  • IP-to-IP: Provider whitelists your server IP. No username/password needed. Leave register off.
  • Username + Password (REGISTER): Toggle "Register" on; system sends REGISTER with Digest auth.

Register Status badge

Trunks list shows a "Register" column with OK (green) if registered, NO (red) if registration failed. Updated via AJAX every page load.

Trunk Groups

Group multiple trunks together for failover. If the first trunk responds with CONGESTION or CHANUNAVAIL, the dialplan automatically tries the next trunk in the group.

Order matters — set priority on each trunk_group_trunk row to control failover sequence.

Providers

Trunks can be optionally tagged with a provider name (your wholesale company). Used for reporting and grouping.

Trunk Prefix Manipulation

Some providers require number reformatting before they accept the call:

  • Strip Prefix: If number starts with this string, remove it. Example: strip +90 from +9055512345675551234567.
  • Add Prefix: Prepend to result. Example: add 00005551234567.

Both can be set together; strip is applied first, then add.

For multi-trunk routing (failover), each trunk applies its own prefix manipulation.

Trunk Register

When "Register" is enabled, Asterisk sends a SIP REGISTER request to the provider with the configured username/password. Once accepted, the provider can route inbound calls to your server.

Status appears in the Trunks list as a colored badge.

Troubleshooting

  • Check provider gave you correct credentials
  • Check firewall allows outbound SIP (UDP 5060) and inbound responses
  • Check Asterisk full log: tail -f /var/log/asterisk/full | grep REGISTER

Payments

Manual payment records — when you receive money from a customer, log it here. Adds credit to the user.

Webhook integrations (cgw, heleket) auto-create payment records for crypto payments.

Vouchers

Prepaid voucher codes. Generate codes worth $X each, distribute to customers, they redeem to top up balance.

Invoices

Generate invoices for customers — useful for postpaid billing or accounting records.

Services

Recurring monthly services that customers can subscribe to (e.g., DID rental, premium support). Auto-billed monthly.

Web Phone

RESELLER CLIENT

Browser-based softphone using WebRTC (Janus SIP plugin). No software install needed.

How it works

  1. Open Dialer → Web Phone
  2. If you have multiple SIP users, select one
  3. System auto-registers with Asterisk via Janus
  4. Status shows "Registered - {username}"
  5. Type number, click Call

Features

  • Inbound call popup with answer/decline
  • DTMF dialpad during call
  • Call duration timer
  • Hangup, hold, transfer
  • Connected line update — when bridged to another party, the displayed number updates
Microphone access: Browser must allow microphone permission. HTTPS required for WebRTC.

DTMF Capture

Captures DTMF digits pressed by the remote party during in-call. Useful for monitoring IVR responses or recording PIN entries.

Click Calls (Click-to-Call Widget)

CLIENT

Embed a JavaScript snippet on any website. Visitors click a floating button, enter their phone number, and your operator\'s web phone rings instantly. Once answered, the visitor is automatically called and bridged.

Setup

  1. Sidebar: Dialer → Click Calls → "+ New Widget"
  2. Choose name, your SIP user (the operator who will answer), Caller ID, working hours, button color/text
  3. Save → Embed Code page shows the snippet
  4. Copy the script tag
  5. Paste into your website before </body>
  6. Floating button appears on every page of your site

Visitor flow

  1. Visitor clicks the floating button
  2. Popup opens — visitor enters phone and optional message
  3. Visitor clicks "Call Me"
  4. Your SIP user\'s web phone rings (caller ID = visitor\'s number)
  5. You answer → Asterisk bridges visitor (calls visitor\'s phone via your trunk)
  6. Both phones connected → conversation

Working hours

If a visitor clicks outside business hours, the request is queued as a callback. The system automatically calls them when business resumes.

Logs

Each widget has its own logs page showing all visitor requests, status (calling / answered / missed / callback queued), and timestamps.

Caller ID precedence: Trunk INVITE uses widget.callerid if set, otherwise sip_user.callerid. The visitor sees this number on their phone, not their own.

DIDs

Direct Inward Dial numbers — your inbound phone numbers. When someone calls your DID, the call is routed according to the DID Destination configuration.

DID Destinations

Defines what happens when a DID is called. Options:

  • SIP User: Ring a specific SIP user
  • IVR: Play an IVR menu
  • Queue: Place caller in a queue
  • External: Forward to an outside number (uses a trunk)
  • Voicemail: Send to voicemail

You can have time-of-day routing: different destinations during business hours vs. after hours.

IVR Builder

Drag-and-drop visual editor for building IVR menus. Each node is an action (play audio, get input, transfer, hang up, etc.) with branching based on caller input.

Node types

  • Play audio
  • Get DTMF input
  • Transfer to extension/queue
  • Conditional branch
  • Hangup

Save IVR and assign it to a DID Destination.

Queues

Asterisk queues for distributing calls to multiple agents. Configure ringtone, hold music, max wait, agent strategy (ringall, leastrecent, etc.).

Queue Members

Add SIP users to queues. Members can dynamically pause/unpause from the Queue Dashboard.

Holidays

Define holidays. DID Destinations can route differently on holidays (e.g., always to voicemail).

Audio Files

Upload audio files (.wav, .mp3) for IVR prompts, hold music, voicemail greetings. Stored in /var/lib/asterisk/sounds/cellhub.

Auto-converted to Asterisk-compatible format on upload.

Configuration

Global system settings:

  • Site name, default currency, timezone
  • Default low credit threshold (alert level)
  • Tax rate
  • Brand colors / logo

Group Users

Group accounts together for shared billing or quotas. Useful for departments or enterprise accounts.

Resellers

Manage reseller accounts — wholesale customers who resell your service to their own clients with markup.

SMTP

Configure outbound email server. Required for password reset, low credit alerts, payment notifications.

Test "Send Test Email" button to verify before going live.

Mail Templates

Edit email templates: welcome email, invoice notification, low credit alert, password reset, etc. Supports variables like @{{user_name}}, @{{credit}}.

System Status

Live health check: Asterisk, Janus, MySQL, queue worker. Each shows a green/red indicator.

API

API key management. Generate keys for external integrations (CRM, IVR scripts, etc.). Each key can be scoped to specific endpoints.

See API Documentation page for endpoint reference.

System Updates

One-click upgrade to the latest CellHub Billing release.

What happens during update

  1. License + IP verification with cellhubbilling.org
  2. Database backup (/var/backups/cellhub/db_pre_*.sql.gz)
  3. Code backup (/var/backups/cellhub/code_pre_*.tar.gz)
  4. Bundle download with SHA256 verification
  5. Code rsync (preserves .env, logs, uploads)
  6. composer install + database migration
  7. AGI sync + Asterisk reload + PHP-FPM reload
  8. Update version + cleanup

If a step fails, the previous version is preserved (rollback by restoring backup).

Notification: When a new release is available, an "UPDATE" badge appears next to "System Updates" in the sidebar.

Billing Engine (AGI Architecture)

The billing logic lives in /var/lib/asterisk/agi-bin/cellhub_billing.php. It runs on every outbound call.

Two modes

  • check — Runs at call start. Validates credit, finds rate, selects trunk, sets channel variables. Aborts call if no funds, no trunk, or restricted.
  • hangup — Runs at call end. Calculates cost from billsec × rate, deducts from balance, inserts CDR record.

Rate selection priority

  1. Active Offer matching destination prefix → cost = 0
  2. User Custom Rate matching prefix
  3. Plan rate matching prefix (longest prefix wins)
  4. Default rate deck fallback
  5. If none → CONGESTION (call aborted)

Dialplan & Sequential Failover

Outbound calls enter the [billing] context in /etc/asterisk/extensions.conf. Flow:

1. AGI(cellhub_billing.php,check) — validates + sets CELLHUB_TRUNK_CSV
2. trytrunk loop:
   - Pick trunk N from CSV
   - Apply per-trunk number prefix
   - Apply CALLERID override (if set)
   - Dial(PJSIP/{number}@trunk-X, 60, gTtoU(send_connected,s,1))
   - On CONGESTION/CHANUNAVAIL → next trunk
   - Otherwise → done
3. h extension: AGI(cellhub_billing.php,hangup) — finalizes CDR + balance

Trunk Selection Priority

#SourceWhen
1SIP user → trunk_group_idAlways wins if SIP user has a trunk group
2SIP user → trunk_id (single)If SIP user has only single trunk override
3Rate → trunk_idThe matching tariff specified a trunk (LCR)
4Default (first active trunk)If nothing else matched

The SIP-user-level assignment always overrides rate-level and default.

Troubleshooting

SIP user can\'t register

  • Check password matches (DB sip_users.password vs softphone)
  • Check Asterisk full log: tail -f /var/log/asterisk/full | grep "Registration"
  • Verify SIP transport is enabled: asterisk -rx "pjsip show transports"
  • Check firewall allows UDP 5060 (or TCP/WSS as needed)

Call fails immediately

  • Check user has credit: SELECT credit FROM users WHERE id = X
  • Check matching rate exists for destination
  • Check trunk is registered/reachable
  • Asterisk console: asterisk -rvvv while making the call

Web Phone won\'t register

  • Browser console: any WebRTC errors?
  • Check Janus is running: systemctl status janus
  • Check Janus reachable through nginx: curl https://yourdomain/janus
  • Verify HTTPS (WebRTC requires secure context)

Click Calls operator phone rings but visitor isn\'t called

  • Check SIP user is online (web phone open)
  • Check user credit and trunk availability
  • View click_to_call_logs for error message

Rate doesn\'t match destination

  • Verify prefix exists in rate deck assigned to user\'s plan
  • Check longest prefix matching: a rate with prefix 1 wins over 11 only if no 11 exists