Skip to main content

Extension Model

The Extension model is the central entity for call routing in OPBX. Extensions represent various types of PBX endpoints including users, conference rooms, ring groups, IVR menus, AI assistants, and more.

Overview

PropertyValue
NamespaceApp\Models
Tableextensions
Primary Keyid
Global ScopeOrganizationScope

Database Schema

ColumnTypeNullableDefaultDescription
idbigint unsignedNoautoPrimary key
organization_idbigint unsignedNo-Foreign key to organizations
user_idbigint unsignedYesnullAssigned user (USER type)
ai_assistant_idbigint unsignedYesnullAssigned AI assistant
extension_numbervarchar(50)No-Extension number (e.g., 1001)
passwordvarchar(255)YesnullSIP password (USER type only)
cloudonix_subscriber_idbigint unsignedYesnullCloudonix subscriber ID
cloudonix_uuidchar(36)YesnullCloudonix UUID
cloudonix_syncedbooleanNofalseSync status with Cloudonix
typevarchar(50)No-ExtensionType enum
statusvarchar(50)NoactiveUserStatus enum
voicemail_enabledbooleanNotrueVoicemail enabled flag
configurationjsonYesnullType-specific configuration
service_urlvarchar(500)YesnullService URL (AI assistants)
service_tokenvarchar(255)YesnullService authentication token
service_paramsjsonYesnullService parameters
created_attimestampNo-Creation timestamp
updated_attimestampNo-Last update timestamp

Indexes

  • PRIMARY on id
  • UNIQUE on organization_id + extension_number
  • INDEX on organization_id
  • INDEX on user_id
  • INDEX on type
  • INDEX on status
  • INDEX on ai_assistant_id

Attributes

Fillable

protected $fillable = [
'organization_id',
'user_id',
'extension_number',
'password',
'cloudonix_subscriber_id',
'cloudonix_uuid',
'cloudonix_synced',
'type',
'status',
'voicemail_enabled',
'configuration',
'service_url',
'service_token',
'service_params',
'ai_assistant_id',
];

Hidden

protected $hidden = [
'password', // SIP password protected from serialization
];

Security Note: The SIP password is hidden from API responses to prevent toll fraud and unauthorized access.

Casts

AttributeCastDescription
typeExtensionType::classExtensionType enum
statusUserStatus::classUserStatus enum
voicemail_enabledbooleanBoolean cast
cloudonix_syncedbooleanBoolean cast
configurationarrayJSON configuration
service_paramsarrayJSON service parameters

Constants

ConstantValueDescription
DEFAULT_USER_FIELDS'user:id,organization_id,name,email,role,status'Default eager load fields for user relationship

Extension Types

The ExtensionType enum defines the available extension types:

TypeDescriptionRequires User
userDirect extension for a userYes
conferenceConference roomNo
ring_groupRing multiple extensionsNo
ivrInteractive voice response menuNo
ai_assistantAI-powered virtual assistantNo
forwardCall forwardingNo
ai_load_balancerAI Assistant load balancerNo

Relationships

Belongs To

organization(): BelongsTo

The organization this extension belongs to.

$extension->organization; // Organization model

user(): BelongsTo

The user assigned to this extension (USER type only).

$extension->user; // User model or null

aiAssistant(): BelongsTo

The AI assistant assigned to this extension.

$extension->aiAssistant; // AiAssistant model or null

aiLoadBalancer(): BelongsTo

The AI Load Balancer assigned (via configuration).

$extension->aiLoadBalancer; // AiAssistantLoadBalancer or null

Methods

Status Checking

isActive(): bool

Check if the extension is active.

if ($extension->isActive()) {
// Can receive calls
}

isInactive(): bool

Check if the extension is inactive.

if ($extension->isInactive()) {
// Cannot receive calls
}

User Assignment

belongsToUser(int|string $userId): bool

Check if extension belongs to a specific user.

if ($extension->belongsToUser($userId)) {
// User owns this extension
}

SIP/URI Methods

getSipUri(): ?string

Get the SIP URI for this extension.

  • USER type: Returns extension number
  • Other types: Returns configuration['sip_uri'] or null
$sipUri = $extension->getSipUri(); // "1001" or "sip:service@example.com"

hasSipUri(): bool

Check if extension has a SIP URI configured.

if ($extension->hasSipUri()) {
// Can be dialed via SIP
}

Password Management

getSipPassword(): ?string

Get the SIP password (USER type only, audited).

$password = $extension->getSipPassword();

Security: All accesses are logged for security monitoring. Returns null for non-USER extensions.

regeneratePassword(): ?string

Generate and save a new SIP password (USER type only).

$newPassword = $extension->regeneratePassword();

generateSecurePassword(): string

Generate a cryptographically secure password (32-char hex).

$password = $extension->generateSecurePassword();
// Returns: "a1b2c3d4e5f6..." (32 characters)

Accessors

getFormattedNumberAttribute(): string

Get extension number padded to 4 digits.

$extension->extension_number = "101";
$extension->formatted_number; // "0101"

Query Scopes

scopeForOrganization(Builder $query, int|string $organizationId): Builder

Filter by organization.

$extensions = Extension::forOrganization(1)->get();

scopeWithType(Builder $query, ExtensionType $type): Builder

Filter by extension type.

$userExtensions = Extension::withType(ExtensionType::USER)->get();

scopeWithStatus(Builder $query, UserStatus $status): Builder

Filter by status.

$active = Extension::withStatus(UserStatus::ACTIVE)->get();

scopeForUser(Builder $query, int|string $userId): Builder

Filter by assigned user.

$userExtensions = Extension::forUser($userId)->get();

scopeSearch(Builder $query, string $search): Builder

Search by extension number.

$results = Extension::search('100')->get();

scopeActive(Builder $query): Builder

Filter to active extensions only.

$active = Extension::active()->get();

scopeUnassigned(Builder $query): Builder

Filter to unassigned extensions (no user).

$available = Extension::unassigned()->get();

Configuration Structure

Each extension type has specific configuration stored in the configuration JSON column:

USER Type

{
"forward_to": "+1234567890",
"forward_enabled": true
}

CONFERENCE Type

{
"conference_room_id": 5
}

RING_GROUP Type

{
"ring_group_id": 10
}

IVR Type

{
"ivr_id": 3
}

AI_ASSISTANT Type

{
"provider": "openai",
"phone_number": "+18001234567"
}

FORWARD Type

{
"forward_to": "+1234567890"
}

AI_LOAD_BALANCER Type

{
"ai_load_balancer_id": 7
}

Usage Examples

Creating Extensions

use App\Models\Extension;
use App\Enums\ExtensionType;
use App\Enums\UserStatus;

// Create user extension
$userExt = Extension::create([
'organization_id' => $orgId,
'user_id' => $user->id,
'extension_number' => '1001',
'type' => ExtensionType::USER,
'status' => UserStatus::ACTIVE,
'voicemail_enabled' => true,
'password' => $extension->generateSecurePassword(),
]);

// Create conference room extension
$confExt = Extension::create([
'organization_id' => $orgId,
'extension_number' => '8000',
'type' => ExtensionType::CONFERENCE,
'status' => UserStatus::ACTIVE,
'configuration' => ['conference_room_id' => $room->id],
]);

Querying Extensions

// Get all active user extensions
$extensions = Extension::active()
->withType(ExtensionType::USER)
->with(['user'])
->get();

// Search for extensions
$results = Extension::search('100')
->forOrganization($orgId)
->get();

// Get unassigned extensions
$available = Extension::unassigned()
->withType(ExtensionType::USER)
->get();

SIP Password Management

// Get password (with audit logging)
$password = $extension->getSipPassword();

// Regenerate password
$newPassword = $extension->regeneratePassword();

Cloudonix Sync Status

// Check sync status
if ($extension->cloudonix_synced) {
// Extension is synced with Cloudonix
}

// Get Cloudonix identifiers
$subscriberId = $extension->cloudonix_subscriber_id;
$uuid = $extension->cloudonix_uuid;