johnrivera7 / filament-issabel-click-to-call
Filament plugin for Issabel PBX click-to-call via Asterisk AMI — originate from tables, forms, and a credentials UI.
Package info
github.com/Johnrivera7/filament-issabel-click-to-call
pkg:composer/johnrivera7/filament-issabel-click-to-call
Requires
- php: ^8.2
- filament/filament: ^4.0|^5.0
- illuminate/contracts: ^11.28|^12.0|^13.0
- illuminate/support: ^11.28|^12.0|^13.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- laravel/pint: ^1.18
- phpunit/phpunit: ^11.0
README
Filament plugin for Issabel PBX click-to-call via Asterisk AMI: ring the agent extension first, then dial the customer number from tables or forms.
Open source (MIT). Works with Issabel and other Asterisk/FreePBX deployments that expose AMI on port 5038.
Requirements
| Stack | Versions |
|---|---|
| PHP | 8.2+ |
| Laravel | 11.28+ / 12+ / 13+ |
| Filament | 4.x or 5.x |
| Issabel | AMI enabled; agent softphone registered on extension |
How click-to-call works
- User clicks Llamar / Call in Filament.
- Laravel connects to Issabel AMI (TCP 5038).
- Sends
Originate: channelPJSIP/{anexo}→ when answered, dials destination infrom-internal. - Agent answers on their softphone/IP phone; Issabel connects the customer.
Filament does not carry audio — the PBX does.
Agent phone display (required Issabel dialplan)
Issabel/FreePBX stores a CNAM per extension (e.g. Mariela Lopez on 2150). If you originate directly to SIP/2150, the agent phone shows the extension identity, not the customer number — AMI CallerID variables alone cannot override this on stock Issabel.
We added strategy custom_agent: originate to Local/{anexo}@filament-click-to-call, set display from CTC_DEST in dialplan, then dial outbound via from-internal/{destino}.
Why we need [filament-click-to-call] on the PBX
| Approach | Agent display | Outbound dial |
|---|---|---|
Direct SIP/anexo + AMI CallerID |
Extension CNAM (wrong) | May work |
Force CALLERID(num) = cell in AMI |
Cell on display | Often bad-number / wrong route |
custom_agent + custom context |
Cell on display | Normal outbound route |
One-time setup on Issabel
Add to /etc/asterisk/extensions_custom.conf:
[filament-click-to-call] exten => _X.,1,NoOp(Filament click-to-call anexo ${EXTEN} destino ${CTC_DEST}) same => n,Set(CALLERID(name)=${IF($["${CTC_NAME}"=""]?${CTC_DEST}:${CTC_NAME})}) same => n,Set(CALLERID(num)=${IF($["${CTC_NUM}"=""]?${CTC_DEST}:${CTC_NUM})}) same => n,Set(CALLERID(name-pres)=allowed_not_screened) same => n,Set(CALLERID(num-pres)=allowed_not_screened) same => n,Dial(SIP/${EXTEN},30,m) same => n,Hangup()
Use PJSIP instead of SIP in Dial() if your extensions are PJSIP. Then:
asterisk -rx "dialplan reload"
Publish the snippet from Laravel:
php artisan vendor:publish --tag=filament-issabel-click-to-call-dialplan
Full Spanish guide: docs/ISSABEL_VISOR_DESTINO.md
.env for correct display
ISSABEL_PBX_ORIGINATE_STRATEGY=custom_agent ISSABEL_PBX_AGENT_DIAL_CONTEXT=filament-click-to-call ISSABEL_PBX_CALLER_ID_DISPLAY=destination
Fallback without PBX dialplan (display stays extension CNAM):
ISSABEL_PBX_ORIGINATE_STRATEGY=application_dial
Where to create AMI credentials (Issabel)
Option A — Issabel web UI
- Log in to Issabel as admin.
- Open Settings → Advanced Settings → Asterisk Manager Users (wording may vary by Issabel version).
- Add user e.g.
kitinicio_originatewith a strong secret. - Restrict permit to the private IP of your Laravel server only (not
0.0.0.0/0). - Grant at least write = originate (and minimal read permissions).
- Apply config / reload Asterisk.
Option B — /etc/asterisk/manager.conf
[kitinicio_originate] secret = YOUR_STRONG_SECRET deny=0.0.0.0/0.0.0.0 permit=172.16.10.20/255.255.255.255 read = system,call,log,verbose,command,agent,user,config,dtmf,reporting,cdr,dialplan write = originate
Then: asterisk -rx "manager reload" (or restart Asterisk from Issabel).
.env in Laravel
ISSABEL_CLICK_TO_CALL_ENABLED=true ISSABEL_PBX_HOST=172.16.10.50 ISSABEL_PBX_AMI_PORT=5038 ISSABEL_PBX_AMI_USER=kitinicio_originate ISSABEL_PBX_AMI_SECRET=YOUR_STRONG_SECRET ISSABEL_PBX_CHANNEL_DRIVER=PJSIP ISSABEL_PBX_DIAL_CONTEXT=from-internal ISSABEL_PBX_ORIGINATE_STRATEGY=custom_agent ISSABEL_PBX_AGENT_DIAL_CONTEXT=filament-click-to-call ISSABEL_PBX_CALLER_ID_DISPLAY=destination
Network / Huawei Cloud
You do not need to expose port 5038 to the public internet.
| Scenario | Recommendation |
|---|---|
| Laravel + Issabel same VPC / LAN | Security group: inbound TCP 5038 only from Laravel VM private IP → Issabel private IP |
| Laravel outside, Issabel inside | VPN or private peering; avoid public AMI |
| Public AMI | Not recommended — AMI has full call control |
Issabel AMI listens on 5038/TCP by default. Open it only between app server and PBX.
Does Issabel have an API?
Yes, partially:
| API | Use for click-to-call? |
|---|---|
Issabel REST /pbxapi/ |
Admin (extensions, ring groups). No CDR/originate in core API. |
| Community PBXAPI v2 | Optional HTTP CDR + originate if installed on Issabel |
| AMI (this plugin) | Recommended for originate — native Asterisk, no extra modules |
This plugin uses AMI because it is stable, works on stock Issabel, and matches legacy cobranza integrations.
Installation
composer require johnrivera7/filament-issabel-click-to-call:^1.0
php artisan vendor:publish --tag=filament-issabel-click-to-call-config
Register in your PanelProvider:
use JohnRivera7\FilamentIssabelClickToCall\FilamentIssabelClickToCallPlugin; ->plugin( FilamentIssabelClickToCallPlugin::make() ->navigationGroup('Telefonía') )
Filament action (table / form)
use JohnRivera7\FilamentIssabelClickToCall\Actions\ClickToCallAction; ClickToCallAction::make( phoneResolver: fn (array $record) => $record['celular'], extensionResolver: fn () => auth()->user()->cobranzaEjecutivo?->anexo_pbx, )
Wire extension per user from your ejecutivo mantenedor (anexo_pbx column in your app).
Multi-tenant credentials
use JohnRivera7\FilamentIssabelClickToCall\Support\IssabelAmiCredentials; FilamentIssabelClickToCallPlugin::make() ->credentialsUsing(fn (): IssabelAmiCredentials => IssabelAmiCredentials::fromArray(/* DB */)) ->persistCredentialsUsing(function (IssabelAmiCredentials $c): void { /* save to DB */ })
Programmatic
use JohnRivera7\FilamentIssabelClickToCall\FilamentIssabelClickToCallPlugin; FilamentIssabelClickToCallPlugin::get()->clickToCall()->call( extension: '2151', phone: '957592274', );
Agent requirements
Each cobranza agent needs:
- Issabel extension (anexo) assigned in your mantenedor
- Softphone (Zoiper, MicroSIP, etc.) or IP phone registered to Issabel
Without a registered endpoint, Originate will fail or ring nowhere.
License
MIT © John Rivera. Issabel® is a trademark of its respective owners; this plugin is not affiliated with Issabel LLC.
