Search by

johnrivera7 / filament-issabel-click-to-call

Johnrivera7

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

Statistics

Installs: 16

Dependents: 0

Suggesters: 0

Stars: 2

Open Issues: 0

1.1.1 2026-09-08 19:13 UTC

This package is auto-updated.

Last update: 2026-09-08 19:18:22 UTC


README

Issabel Click-to-Call Filament plugin

FILAMENT 4.x FILAMENT 5.x

Latest Version on Packagist License

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

  1. User clicks Llamar / Call in Filament.
  2. Laravel connects to Issabel AMI (TCP 5038).
  3. Sends Originate: channel PJSIP/{anexo} → when answered, dials destination in from-internal.
  4. 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

  1. Log in to Issabel as admin.
  2. Open Settings → Advanced Settings → Asterisk Manager Users (wording may vary by Issabel version).
  3. Add user e.g. kitinicio_originate with a strong secret.
  4. Restrict permit to the private IP of your Laravel server only (not 0.0.0.0/0).
  5. Grant at least write = originate (and minimal read permissions).
  6. 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',
);

Avoiding extra call legs

A second Originate while the extension is still ringing or talking creates another leg: the agent phone rings twice and the CDR shows duplicated calls. Before every Originate the plugin now:

  1. Takes an atomic cache lock per extension (originate_lock_seconds, default 15 s), so two fast clicks cannot both reach AMI.
  2. Asks the PBX for the live extension state — ExtensionState on the hint context, falling back to CoreShowChannels when the extension has no hint. Ringing, talking and on-hold all block a new call; idle and unavailable do not.

The call is refused with a translated message instead of adding the leg. Because the check reads live PBX state, the lock clears as soon as the agent hangs up — no CDR sync or manual refresh needed.

ISSABEL_PBX_PREVENT_EXTRA_LEGS=true
ISSABEL_PBX_ORIGINATE_LOCK_SECONDS=15
ISSABEL_PBX_HINT_CONTEXT=ext-local

Your AMI user needs read permission for call/reporting (already included in the manager.conf sample above) so these two read actions are allowed.

Ask the PBX directly when you need the state elsewhere:

FilamentIssabelClickToCallPlugin::get()->clickToCall()->extensionInCall('2151');

It throws when the PBX cannot answer (AMI unreachable or the AMI user lacks read permission). Treat that as unknown, never as idle, or you are back to extra legs.

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.

Links