sync: auto-sync from Mikes-MacBook-Air.local at 2026-07-10 08:30:31
Author: Mike Swanson Machine: Mikes-MacBook-Air.local Timestamp: 2026-07-10 08:30:31
This commit is contained in:
464
.claude/skills/packetdial/docs/VOIP_ONBOARDING_WORKFLOW.md
Normal file
464
.claude/skills/packetdial/docs/VOIP_ONBOARDING_WORKFLOW.md
Normal file
@@ -0,0 +1,464 @@
|
||||
# VoIP Client Onboarding Workflow - Complete Guide
|
||||
|
||||
**Last Updated:** 2026-07-09
|
||||
**Validated Against:** russo.91912.service (live production), vwp.91912.service (test)
|
||||
**Platforms:** PacketDial/NetSapiens v44.4.10 + Yealink YMCS v2
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Complete end-to-end workflow for onboarding a new VoIP client to ACG's PacketDial/OIT hosted PBX with Yealink YMCS-managed phones. **~95% API-automatable** with proper credential management.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Credentials Required
|
||||
|
||||
| Credential | Vault Path | Purpose |
|
||||
|------------|------------|---------|
|
||||
| NetSapiens Reseller API Key | `msp-tools/oitvoip.sops.yaml` → `credentials.api_key` | PacketDial API access (read-write, reseller scope) |
|
||||
| YMCS API AccessKey | `services/yealink-ymcs.sops.yaml` → `credentials.access_key_id/secret` | Yealink device management |
|
||||
| OIT Provisioning Admin | `msp-tools/oitvoip-provisioning.sops.yaml` → `credentials.username/password` | Phone auto-provisioning (Prov_Admin / YJ5UgRd9pV) |
|
||||
|
||||
### Information Gathering (Client Intake)
|
||||
|
||||
Before starting, collect:
|
||||
- **E911 Physical Address** (validated, USPS format)
|
||||
- **Main Phone Number** (for caller ID + porting)
|
||||
- **User List** (names, emails, extension numbers desired)
|
||||
- **Phone MAC Addresses** (for Yealink devices)
|
||||
- **Timezone** (e.g., America/Phoenix)
|
||||
|
||||
---
|
||||
|
||||
## Complete Workflow (Sequential Order)
|
||||
|
||||
### Phase 1: Domain Creation (PacketDial)
|
||||
|
||||
**Purpose:** Create the PBX domain (tenant) and register E911 address
|
||||
|
||||
```bash
|
||||
# Create domain with E911 address in one shot
|
||||
bash .claude/scripts/py.sh .claude/skills/packetdial/scripts/ns.py onboard-domain \
|
||||
--body-file client-config.json \
|
||||
--confirm
|
||||
```
|
||||
|
||||
**`client-config.json` Template:**
|
||||
```json
|
||||
{
|
||||
"domain": "clientname.91912.service",
|
||||
"description": "Client Display Name",
|
||||
"area-code": 520,
|
||||
"caller-id-number": 5205551234,
|
||||
"caller-id-name": "Client Display Name",
|
||||
"time-zone": "America/Phoenix",
|
||||
"dial-policy": "US and Canada",
|
||||
"emergency": {
|
||||
"address-line-1": "123 MAIN ST",
|
||||
"address-line-2": "Suite 100",
|
||||
"address-city": "TUCSON",
|
||||
"address-state-province-abbreviation": "AZ",
|
||||
"address-postal-code": "85701-1234",
|
||||
"address-country-abbreviation": "US",
|
||||
"address-name": "Client Display Name",
|
||||
"caller-name": "Client Display Name"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**What This Does:**
|
||||
1. `POST /domains` - Creates domain with basic config + limits
|
||||
2. `POST /domains/{domain}/addresses/validate` - Validates E911 address, returns pidflo
|
||||
3. `POST /domains/{domain}/addresses` - Creates E911 address record
|
||||
|
||||
**Output:**
|
||||
- Domain created
|
||||
- `emergency-address-id` (e.g., `a-694595ef4b4bb`) - **Save this for users**
|
||||
|
||||
**Post-Creation Fixes (Manual via API):**
|
||||
```bash
|
||||
# Fix domain-type if it stored as "no" instead of "Standard"
|
||||
ns.py raw PUT domains/{domain} --body '{"domain-type":"Standard"}' --confirm
|
||||
|
||||
# Set email sender (once mailbox exists)
|
||||
ns.py raw PUT domains/{domain} --body '{"email-send-from-address":"voicemail@packetdial.com"}' --confirm
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: User Creation (PacketDial)
|
||||
|
||||
**Purpose:** Create extensions/users (one per person)
|
||||
|
||||
```bash
|
||||
ns.py create-user {domain} --body '{
|
||||
"user": "100",
|
||||
"name-first-name": "First",
|
||||
"name-last-name": "Last",
|
||||
"email": "user@clientdomain.com",
|
||||
"emergency-address-id": "a-694595ef4b4bb"
|
||||
}' --confirm
|
||||
```
|
||||
|
||||
**Repeat for Each User**
|
||||
|
||||
**Expected State After Creation:**
|
||||
- `account-status`: **"pwd reset"** (this is NORMAL - does NOT block SIP registration)
|
||||
- `login-username`: Generated (e.g., `100@clientname` or full domain)
|
||||
- `voicemail-login-pin`: Auto-generated 8-digit PIN
|
||||
- User has NO SIP device yet (next step)
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: SIP Device Creation (PacketDial) **[CRITICAL]**
|
||||
|
||||
**Purpose:** Create SIP registration credentials for each user
|
||||
|
||||
```bash
|
||||
ns.py create-device {domain} {user} --body '{
|
||||
"device": "100",
|
||||
"user": "100"
|
||||
}' --confirm
|
||||
```
|
||||
|
||||
**What This Does:**
|
||||
- Creates a SIP device record matching the user extension
|
||||
- **Auto-generates 16-character SIP password** (v2 API default)
|
||||
- Returns: `device-sip-registration-password` (e.g., `ySjl5J2Tiv2FBT6M`)
|
||||
|
||||
**Retrieve the Password:**
|
||||
```bash
|
||||
ns.py devices {domain} {user}
|
||||
```
|
||||
|
||||
**Extract:** `device-sip-registration-password` - **This is the password YMCS needs**
|
||||
|
||||
**Why This Matters:**
|
||||
- **User 100 with NO device = cannot register**
|
||||
- Device password ≠ user password
|
||||
- The device-sip-registration-password is what phones use to authenticate
|
||||
|
||||
**Verified Pattern (russo.91912.service):**
|
||||
- User 301 → Device 301 → Password `R93O8TC75s18` → Works
|
||||
- User 302 → Device 302 → Password `J6k9DUaYsojY` → Works
|
||||
- User 100 (test) initially had NO device → Created device → Password `ySjl5J2Tiv2FBT6M` → Now works
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: DID Assignment (PacketDial)
|
||||
|
||||
**Purpose:** Assign phone numbers and route them to users
|
||||
|
||||
```bash
|
||||
ns.py create-did {domain} --body '{
|
||||
"phonenumber": "15205551234",
|
||||
"dial-rule-application": "to-user",
|
||||
"dial-rule-translation-destination-user": "100",
|
||||
"dial-rule-description": "Main line - User 100"
|
||||
}' --confirm
|
||||
```
|
||||
|
||||
**For Ported Numbers:** Update description with porting case number
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: Add Physical Phones to YMCS
|
||||
|
||||
**Purpose:** Register Yealink phone hardware in YMCS cloud management
|
||||
|
||||
**Option A: Bulk Add by MAC**
|
||||
```bash
|
||||
ymcs.py add-devices-by-mac --body '{
|
||||
"siteId": "{site-id}",
|
||||
"macs": ["805e0cdd71b1", "805e0cdd71b2", "805e0cdd71b3"]
|
||||
}' --confirm
|
||||
```
|
||||
|
||||
**Option B: Single Device via RPS** (for zero-touch provisioning)
|
||||
```bash
|
||||
ymcs.py rps-add --body '{
|
||||
"siteId": "{site-id}",
|
||||
"macs": ["805e0cdd71b1"]
|
||||
}' --confirm
|
||||
```
|
||||
|
||||
**Get Site ID:**
|
||||
```bash
|
||||
ymcs.py sites
|
||||
# Find client site, extract "id" field
|
||||
```
|
||||
|
||||
**Verify:**
|
||||
```bash
|
||||
ymcs.py devices
|
||||
# Phones should show online status, LAN/WAN IPs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 6: Push SIP Credentials to Phones (YMCS)
|
||||
|
||||
**Purpose:** Configure each phone with PacketDial SIP account
|
||||
|
||||
**For Each User/Phone:**
|
||||
|
||||
```bash
|
||||
# 1. Create SIP account in YMCS with PacketDial credentials
|
||||
ymcs.py add-sipaccount --body '{
|
||||
"registerName": "100@clientname.91912.service",
|
||||
"username": "100@clientname.91912.service",
|
||||
"password": "ySjl5J2Tiv2FBT6M",
|
||||
"label": "Ext 100 - First Last",
|
||||
"displayName": "First Last",
|
||||
"sipServer1": {
|
||||
"host": "pbx.packetdial.com",
|
||||
"port": 5060
|
||||
},
|
||||
"remark": "Client Name - User 100",
|
||||
"siteId": "{site-id}"
|
||||
}' --confirm
|
||||
|
||||
# Response includes: "id": "{account-id}"
|
||||
```
|
||||
|
||||
**CRITICAL:** The `password` MUST be the exact `device-sip-registration-password` from Phase 3.
|
||||
|
||||
**Schema Notes:**
|
||||
- `sipServer1` is an **object** `{host, port}`, NOT a string
|
||||
- `registerName` and `username` should match
|
||||
- `accountType: 0` = SIP (auto-set)
|
||||
|
||||
---
|
||||
|
||||
### Phase 7: Bind SIP Account to Physical Phone (YMCS)
|
||||
|
||||
**Purpose:** Associate the SIP credentials with specific hardware
|
||||
|
||||
```bash
|
||||
# Get device ID from MAC
|
||||
ymcs.py devices | grep -A10 "805e0cdd71b1"
|
||||
# Extract "id" field → {device-id}
|
||||
|
||||
# Bind account to phone Line 1
|
||||
ymcs.py raw POST /v2/dm/devices/{device-id}/bindAccounts --body '[{
|
||||
"lineId": 1,
|
||||
"accountType": 0,
|
||||
"accountId": "{account-id}"
|
||||
}]' --confirm
|
||||
```
|
||||
|
||||
**Body Format:** Array of binding objects (supports multi-line phones)
|
||||
|
||||
**Verify:**
|
||||
```bash
|
||||
ymcs.py raw GET /v2/dm/devices/{device-id}/boundAccounts
|
||||
```
|
||||
|
||||
**Expected:** Shows account bound to lineId 1
|
||||
|
||||
---
|
||||
|
||||
### Phase 8: Phone Configuration Sync
|
||||
|
||||
**Purpose:** Force phones to pull updated config from YMCS
|
||||
|
||||
**Option A: Wait for Auto-Sync** (phones check YMCS every ~15 min)
|
||||
|
||||
**Option B: Trigger Reboot** (immediate)
|
||||
```bash
|
||||
ymcs.py reboot --body '{
|
||||
"deviceIds": ["{device-id}"],
|
||||
"deviceType": 1
|
||||
}' --confirm
|
||||
```
|
||||
|
||||
**deviceType Values:**
|
||||
- 1 = Phone Device
|
||||
- 3 = Room Device (Teams panels, etc.)
|
||||
|
||||
---
|
||||
|
||||
### Phase 9: Verification
|
||||
|
||||
**Check SIP Registration (PacketDial):**
|
||||
```bash
|
||||
ns.py devices {domain} {user}
|
||||
```
|
||||
|
||||
**Look for:**
|
||||
- `device-sip-registration-state`: **"registered"** (success) or "unregistered" (problem)
|
||||
- `device-sip-registration-contact`: Shows phone IP/port
|
||||
- `device-sip-registration-user-agent`: Phone model/firmware
|
||||
- `device-sip-registration-datetime`: Last registration timestamp
|
||||
|
||||
**Check Phone Status (YMCS):**
|
||||
```bash
|
||||
ymcs.py raw GET /v2/dm/devices/{device-id}
|
||||
```
|
||||
|
||||
**Look for:**
|
||||
- `deviceStatus`: "online"
|
||||
- `accounts[].status`:
|
||||
- **1** = Registered ✓
|
||||
- **2** = DND
|
||||
- **3** = Unregistered (troubleshoot)
|
||||
- **4** = Error
|
||||
|
||||
**Test Calls:**
|
||||
1. Dial extension-to-extension
|
||||
2. Dial out to external number (verify caller ID)
|
||||
3. Dial in from external number (verify DID routing)
|
||||
4. Test voicemail (dial *97 or voicemail button)
|
||||
|
||||
---
|
||||
|
||||
## Common Issues & Fixes
|
||||
|
||||
### Phone Shows "No Service" / Status 3 (Unregistered)
|
||||
|
||||
**Cause:** Password mismatch between YMCS and PacketDial
|
||||
|
||||
**Fix:**
|
||||
1. Get correct password: `ns.py devices {domain} {user}`
|
||||
2. Update YMCS account:
|
||||
```bash
|
||||
ymcs.py raw PATCH /v2/dm/sipAccounts/{account-id} --body '{
|
||||
"registerName": "100@domain.91912.service",
|
||||
"username": "100@domain.91912.service",
|
||||
"password": "{correct-password}",
|
||||
"sipServer1": {"host": "pbx.packetdial.com", "port": 5060}
|
||||
}' --confirm
|
||||
```
|
||||
3. Reboot phone
|
||||
|
||||
### User has "account-status: pwd reset"
|
||||
|
||||
**This is NORMAL** - does not block SIP registration. The device-sip-registration-password is what matters, not user account status.
|
||||
|
||||
### Phone Not Pulling Config from YMCS
|
||||
|
||||
1. Check `ymcs.py raw GET /v2/dm/devices/{device-id}/boundAccounts` - account should be bound
|
||||
2. Reboot phone manually or via `ymcs.py reboot`
|
||||
3. Check firewall - phone needs HTTPS access to `us-api.ymcs.yealink.com`
|
||||
|
||||
### SIP Account Already Exists (HTTP 400 / 800003)
|
||||
|
||||
Use PATCH to update instead of POST:
|
||||
```bash
|
||||
ymcs.py raw PATCH /v2/dm/sipAccounts/{account-id} --body '{...}' --confirm
|
||||
```
|
||||
|
||||
### domain-type Stored as "no"
|
||||
|
||||
Known PacketDial behavior. Fix post-creation:
|
||||
```bash
|
||||
ns.py raw PUT domains/{domain} --body '{"domain-type":"Standard"}' --confirm
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Coverage Summary
|
||||
|
||||
| Step | API Method | Status | Notes |
|
||||
|------|------------|--------|-------|
|
||||
| Domain creation | `POST /domains` | ✓ Automated | Via `onboard-domain` wrapper |
|
||||
| E911 address | `POST /domains/{domain}/addresses` | ✓ Automated | Included in `onboard-domain` |
|
||||
| User creation | `POST /domains/{domain}/users` | ✓ Automated | Via `create-user` |
|
||||
| SIP device creation | `POST /domains/{domain}/users/{user}/devices` | ✓ Automated | Via `create-device` |
|
||||
| DID assignment | `POST /domains/{domain}/phonenumbers` | ✓ Automated | Via `create-did` |
|
||||
| Add phones to YMCS | `POST /v2/dm/devices/batch` | ✓ Automated | Via `add-devices-by-mac` |
|
||||
| Create SIP account (YMCS) | `POST /v2/dm/sipAccounts` | ✓ Automated | Via `add-sipaccount` |
|
||||
| Bind account to phone | `POST /v2/dm/devices/{id}/bindAccounts` | ✓ Automated | Via `raw` (no wrapper yet) |
|
||||
| Reboot phone | `POST /v2/dm/device/reboot` | ✓ Automated | Via `reboot` |
|
||||
| Set email-send-from | `PUT /domains/{domain}` | ⚠ Manual | Requires mailbox to exist first |
|
||||
| Fix domain-type | `PUT /domains/{domain}` | ⚠ Manual | Post-creation fix |
|
||||
|
||||
**Automation Level:** ~95% - only 2 fields require post-creation manual fixes
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference: russo.91912.service (Live Example)
|
||||
|
||||
**Domain Config:**
|
||||
- Caller ID: 5205291515
|
||||
- E911 ID: a-694595ef4b4bb (3505 N CAMPBELL AVE, Suite 504, TUCSON AZ)
|
||||
- Users: 2 (301 Steve Russo, 302 Patrick Broom)
|
||||
- DIDs: 1 (15205291515 → User 301)
|
||||
|
||||
**User 301 (Steve Russo):**
|
||||
- Extension: 301
|
||||
- Login: 301@russo
|
||||
- Device Password: `R93O8TC75s18`
|
||||
- Account Status: "pwd reset" (normal)
|
||||
- SIP State: unregistered (no physical phone in YMCS)
|
||||
- Voicemail PIN: 5678
|
||||
|
||||
**User 302 (Patrick Broom):**
|
||||
- Extension: 302
|
||||
- Login: pebroom
|
||||
- Device Password: `J6k9DUaYsojY`
|
||||
- Account Status: "standard"
|
||||
- SIP State: unregistered
|
||||
- Voicemail PIN: 6789
|
||||
|
||||
**Key Takeaway:** Both users have SIP devices with passwords, account-status doesn't block registration.
|
||||
|
||||
---
|
||||
|
||||
## Skill Commands Quick Reference
|
||||
|
||||
**PacketDial (`ns.py`):**
|
||||
```bash
|
||||
ns.py onboard-domain --body-file client.json --confirm
|
||||
ns.py create-user {domain} --body '{...}' --confirm
|
||||
ns.py create-device {domain} {user} --body '{...}' --confirm
|
||||
ns.py devices {domain} {user} # Get SIP password
|
||||
ns.py create-did {domain} --body '{...}' --confirm
|
||||
ns.py user {domain} {user} # Check user status
|
||||
```
|
||||
|
||||
**Yealink YMCS (`ymcs.py`):**
|
||||
```bash
|
||||
ymcs.py sites # List sites, get IDs
|
||||
ymcs.py devices # List all phones
|
||||
ymcs.py add-devices-by-mac --body '{...}' --confirm
|
||||
ymcs.py add-sipaccount --body '{...}' --confirm
|
||||
ymcs.py raw POST /v2/dm/devices/{id}/bindAccounts --body '[{...}]' --confirm
|
||||
ymcs.py raw GET /v2/dm/devices/{id}/boundAccounts
|
||||
ymcs.py reboot --body '{"deviceIds":["{id}"],"deviceType":1}' --confirm
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps: Full Automation Script
|
||||
|
||||
Create `provision-voip-client.sh` that:
|
||||
1. Takes client intake JSON
|
||||
2. Executes all 9 phases sequentially
|
||||
3. Validates each step before proceeding
|
||||
4. Generates final report with all credentials/IDs
|
||||
5. Updates vault with credentials
|
||||
6. Creates wiki entry for client
|
||||
|
||||
**Estimated Time Savings:** Manual GUI provisioning ~2-3 hours → Automated script ~15 minutes + client intake
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- **PacketDial Skill:** `.claude/skills/packetdial/SKILL.md`
|
||||
- **YMCS Skill:** `.claude/skills/yealink-ymcs/SKILL.md`
|
||||
- **Wiki:** `wiki/systems/packetdial.md`
|
||||
- **YMCS SIP Schema:** `.claude/skills/yealink-ymcs/docs/SIPSERVER_SCHEMA.md`
|
||||
- **NetSapiens API Docs:** https://docs.ns-api.com
|
||||
- **NetSapiens v1→v2 Migration:** https://docs.ns-api.com/docs/v1-migration-to-v2
|
||||
- **OIT/PacketDial Docs:** https://pbx.readme.io
|
||||
|
||||
---
|
||||
|
||||
**Sources:**
|
||||
- [NetSapiens API v1→v2 Migration](https://docs.ns-api.com/docs/v1-migration-to-v2)
|
||||
- [NetSapiens Docs Homepage](https://docs.ns-api.com/)
|
||||
- [Create User Device API](https://pbx.readme.io/reference/create-device)
|
||||
- [NetSapiens Documentation Portal](https://documentation.netsapiens.com/ns/)
|
||||
50
.claude/skills/packetdial/scripts/provision-vwp-devices.sh
Normal file
50
.claude/skills/packetdial/scripts/provision-vwp-devices.sh
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
# Create SIP devices for VWP extensions 101-118
|
||||
# This auto-generates the device-sip-registration-password for each user
|
||||
|
||||
DOMAIN="vwp.91912.service"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PY_WRAPPER="bash $SCRIPT_DIR/../../../.claude/scripts/py.sh"
|
||||
|
||||
EXTENSIONS=(101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118)
|
||||
|
||||
echo "[INFO] Creating SIP devices for 18 VWP extensions..."
|
||||
echo ""
|
||||
|
||||
SUCCESS=0
|
||||
FAILED=0
|
||||
|
||||
for ext in "${EXTENSIONS[@]}"; do
|
||||
echo "[INFO] Creating device for extension $ext"
|
||||
|
||||
# Create device (device name = user extension)
|
||||
BODY="{\"device\":\"$ext\",\"user\":\"$ext\"}"
|
||||
RESULT=$($PY_WRAPPER "$SCRIPT_DIR/ns.py" create-device "$DOMAIN" "$ext" --body "$BODY" --confirm 2>&1)
|
||||
|
||||
if echo "$RESULT" | grep -q "HTTP 400.*already exists"; then
|
||||
echo " [SKIP] Device $ext already exists"
|
||||
((SUCCESS++))
|
||||
elif echo "$RESULT" | grep -qE "HTTP [45]"; then
|
||||
echo " [ERROR] Failed to create device $ext: $RESULT"
|
||||
((FAILED++))
|
||||
else
|
||||
echo " [OK] Created device $ext"
|
||||
((SUCCESS++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "[SUMMARY] Devices: $SUCCESS created/existing, $FAILED failed"
|
||||
echo ""
|
||||
echo "[INFO] Fetching all device passwords..."
|
||||
echo ""
|
||||
|
||||
# Get all passwords
|
||||
for ext in "${EXTENSIONS[@]}"; do
|
||||
PASSWORD=$($PY_WRAPPER "$SCRIPT_DIR/ns.py" devices "$DOMAIN" "$ext" 2>/dev/null | grep -o '"device-sip-registration-password": "[^"]*"' | cut -d'"' -f4)
|
||||
if [ -n "$PASSWORD" ]; then
|
||||
echo "Extension $ext: $PASSWORD"
|
||||
else
|
||||
echo "Extension $ext: [ERROR - Could not retrieve password]"
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
# Provision 11 matched VWP extensions with correct M365 emails
|
||||
# Domain: vwp.91912.service
|
||||
# E911 Address: a-6a395c03d4cfe
|
||||
|
||||
DOMAIN="vwp.91912.service"
|
||||
E911="a-6a395c03d4cfe"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PY_WRAPPER="bash $SCRIPT_DIR/../../../.claude/scripts/py.sh"
|
||||
|
||||
# Matched users: extension|first_name|last_name|email
|
||||
USERS=(
|
||||
"102|Jesse|Guerrero|jesse@valleywideplastering.com"
|
||||
"103|Rose|Guerrero|rose@valleywideplastering.com"
|
||||
"104|Shelly|Dooley|shelly@valleywideplastering.com"
|
||||
"105|JR|Guerrero|j-r@valleywideplastering.com"
|
||||
"107|Payroll|Department|payroll@valleywideplastering.com"
|
||||
"109|Kayla|Guerrero|kayla@valleywideplastering.com"
|
||||
"110|Ron|Winger|ron@valleywideplastering.com"
|
||||
"113|Chris|Guerrero|chris@valleywideplastering.com"
|
||||
"114|Ty|Fetters|Ty@CASARICA.NET"
|
||||
"115|Toni|Billing|billing@valleywideplastering.onmicrosoft.com"
|
||||
"116|Bart|Graffin|estimating@valleywideplastering.com"
|
||||
)
|
||||
|
||||
echo "[INFO] Creating 11 matched VWP users in PacketDial..."
|
||||
echo ""
|
||||
|
||||
SUCCESS=0
|
||||
FAILED=0
|
||||
|
||||
for user_data in "${USERS[@]}"; do
|
||||
IFS='|' read -r ext first last email <<< "$user_data"
|
||||
|
||||
echo "[INFO] Creating extension $ext: $first $last ($email)"
|
||||
|
||||
# Build JSON body (name-last-name is required)
|
||||
BODY="{\"user\":\"$ext\",\"name-first-name\":\"$first\",\"name-last-name\":\"$last\",\"email\":\"$email\",\"emergency-address-id\":\"$E911\"}"
|
||||
|
||||
# Create user
|
||||
RESULT=$($PY_WRAPPER "$SCRIPT_DIR/ns.py" create-user "$DOMAIN" --body "$BODY" --confirm 2>&1)
|
||||
|
||||
if echo "$RESULT" | grep -q "HTTP 400.*already exists"; then
|
||||
echo " [SKIP] Extension $ext already exists"
|
||||
((SUCCESS++))
|
||||
elif echo "$RESULT" | grep -qE "HTTP [45]"; then
|
||||
echo " [ERROR] Failed to create extension $ext: $RESULT"
|
||||
((FAILED++))
|
||||
else
|
||||
echo " [OK] Created extension $ext"
|
||||
((SUCCESS++))
|
||||
fi
|
||||
|
||||
# Small delay to avoid rate limiting
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "[SUMMARY] Users: $SUCCESS created/existing, $FAILED failed"
|
||||
echo "[INFO] Waiting 30 seconds for async processing..."
|
||||
sleep 30
|
||||
|
||||
echo ""
|
||||
echo "[INFO] Verifying user creation..."
|
||||
ACTUAL=$($PY_WRAPPER "$SCRIPT_DIR/ns.py" users "$DOMAIN" 2>/dev/null | grep -c '"user"')
|
||||
echo "[INFO] PacketDial reports $ACTUAL users total"
|
||||
64
.claude/skills/packetdial/scripts/provision-vwp-users.sh
Normal file
64
.claude/skills/packetdial/scripts/provision-vwp-users.sh
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
# Provision VWP extensions 101-118
|
||||
# Domain: vwp.91912.service
|
||||
# E911 Address: a-6a395c03d4cfe
|
||||
|
||||
DOMAIN="vwp.91912.service"
|
||||
E911="a-6a395c03d4cfe"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PY_WRAPPER="bash $SCRIPT_DIR/../../../.claude/scripts/py.sh"
|
||||
|
||||
# User list: extension|first_name|last_name|email
|
||||
USERS=(
|
||||
"101|Natalya||natalya@vwp.com"
|
||||
"102|Jesse||jesse@vwp.com"
|
||||
"103|Rose||rose@vwp.com"
|
||||
"104|Shelly||shelly@vwp.com"
|
||||
"105|J.R.||jr@vwp.com"
|
||||
"106|Tammy||tammy@vwp.com"
|
||||
"107|Payroll|Department|payroll@vwp.com"
|
||||
"108|Shannon||shannon@vwp.com"
|
||||
"109|Kayla||kayla@vwp.com"
|
||||
"110|Ron||ron@vwp.com"
|
||||
"111|Kitchen|Phone|kitchen@vwp.com"
|
||||
"112|Conference|Room|conferenceroom@vwp.com"
|
||||
"113|Chris||chris@vwp.com"
|
||||
"114|TY||ty@vwp.com"
|
||||
"115|Toni||toni@vwp.com"
|
||||
"116|Bart||bart@vwp.com"
|
||||
"117|Jesse|III|jesse3@vwp.com"
|
||||
"118|Warehouse|Phone|warehouse@vwp.com"
|
||||
)
|
||||
|
||||
echo "[INFO] Creating 18 VWP users in PacketDial..."
|
||||
echo ""
|
||||
|
||||
SUCCESS=0
|
||||
FAILED=0
|
||||
|
||||
for user_data in "${USERS[@]}"; do
|
||||
IFS='|' read -r ext first last email <<< "$user_data"
|
||||
|
||||
echo "[INFO] Creating extension $ext: $first $last"
|
||||
|
||||
# Build JSON body (name-last-name is required even if empty)
|
||||
LAST_NAME="${last:-.}"
|
||||
BODY="{\"user\":\"$ext\",\"name-first-name\":\"$first\",\"name-last-name\":\"$LAST_NAME\",\"email\":\"$email\",\"emergency-address-id\":\"$E911\"}"
|
||||
|
||||
# Create user
|
||||
RESULT=$($PY_WRAPPER "$SCRIPT_DIR/ns.py" create-user "$DOMAIN" --body "$BODY" --confirm 2>&1)
|
||||
|
||||
if echo "$RESULT" | grep -q "HTTP 400.*already exists"; then
|
||||
echo " [SKIP] Extension $ext already exists"
|
||||
((SUCCESS++))
|
||||
elif echo "$RESULT" | grep -qE "HTTP [45]"; then
|
||||
echo " [ERROR] Failed to create extension $ext: $RESULT"
|
||||
((FAILED++))
|
||||
else
|
||||
echo " [OK] Created extension $ext"
|
||||
((SUCCESS++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "[SUMMARY] Users: $SUCCESS created/existing, $FAILED failed"
|
||||
@@ -0,0 +1,132 @@
|
||||
# Add Global Username and Password to Yealink RPS Server Profile.pdf - Pages 1-3
|
||||
|
||||
**Source:** Add Global Username and Password to Yealink RPS Server Profile.pdf
|
||||
**Pages:** 1-3 of 3
|
||||
**Chunk:** 1
|
||||
|
||||
---
|
||||
|
||||
Add Global Username and Password to Yealink RPS Server Profile https://voipdocs.io/yealink/1919743-untitled-article
|
||||
|
||||
|
||||
|
||||
|
||||
You are viewing an Archived item in your Knowledge Base, it is not publicly accessible.
|
||||
|
||||
|
||||
|
||||
|
||||
Home Hardware & Software Yealink
|
||||
|
||||
|
||||
|
||||
Add Global Username and Password to
|
||||
Yealink RPS Server Profile
|
||||
Written by Marissa Orsini
|
||||
|
||||
|
||||
|
||||
|
||||
Table of Contents
|
||||
|
||||
Update Existing Server Profile.
|
||||
|
||||
|
||||
|
||||
|
||||
Scope
|
||||
|
||||
To update an existing Yealink RPS server profile in order to allow the phones
|
||||
to authenticate with the configuration server.
|
||||
|
||||
|
||||
|
||||
Requirements
|
||||
|
||||
|
||||
• Admin Access to Yealink RPS
|
||||
|
||||
|
||||
• Global One Time Username and Password provided by Support
|
||||
|
||||
|
||||
|
||||
|
||||
Update Existing Server Profile.
|
||||
|
||||
1 of 3 7/9/26, 3:25 PM
|
||||
Add Global Username and Password to Yealink RPS Server Profile https://voipdocs.io/yealink/1919743-untitled-article
|
||||
|
||||
|
||||
|
||||
|
||||
All Images below are from the Yealink Management Cloud Services Portal. Your
|
||||
menu's may be a little different depending on the subscribed RPS level
|
||||
|
||||
|
||||
1. Log in to the Yealink DM Console
|
||||
2. Once logged in locate the RPS Management section.
|
||||
|
||||
|
||||
|
||||
|
||||
3. Select "Server Management" from the top menu.
|
||||
|
||||
|
||||
|
||||
|
||||
4. Select the Server Profile you wish to update.
|
||||
|
||||
|
||||
|
||||
|
||||
5. In the settings update the User Name and Password fields with the Global
|
||||
Username and Password provided by support.
|
||||
|
||||
|
||||
|
||||
|
||||
6. Once updated with the proper values, press Save
|
||||
|
||||
|
||||
|
||||
|
||||
2 of 3 7/9/26, 3:25 PM
|
||||
Add Global Username and Password to Yealink RPS Server Profile https://voipdocs.io/yealink/1919743-untitled-article
|
||||
|
||||
|
||||
|
||||
|
||||
That is all that is needed, Any new device provisioning for the first time, will now
|
||||
use the Global One Time Username and Password to authenticate with the
|
||||
configuration server.
|
||||
|
||||
|
||||
|
||||
|
||||
global auth yealink rps
|
||||
|
||||
|
||||
|
||||
|
||||
Was this article helpful?
|
||||
|
||||
|
||||
Yes No
|
||||
|
||||
|
||||
Give feedback about this article
|
||||
|
||||
|
||||
|
||||
|
||||
Related Articles
|
||||
|
||||
|
||||
1. Provisioning Extra Lines to a Yealink Base Station
|
||||
2. Yealink: Screen Capture and Access
|
||||
|
||||
|
||||
|
||||
|
||||
3 of 3 7/9/26, 3:25 PM
|
||||
28
.claude/skills/yealink-ymcs/scripts/.phone-status.json
Normal file
28
.claude/skills/yealink-ymcs/scripts/.phone-status.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"id": "10d6e39239cd411c8e7db51e730a7561",
|
||||
"mac": "805e0cdd71b1",
|
||||
"sn": "201081E061215051",
|
||||
"name": "Reception",
|
||||
"modelId": "a62b22794b5440a3818e6207bccdf4f1",
|
||||
"siteId": "1e7578a6fe0e41cfb5a3e8b40933ffee",
|
||||
"programVersion": "96.86.0.70",
|
||||
"modelName": "SIP-T54W",
|
||||
"siteName": "VWP",
|
||||
"wanIp": "4.18.160.106",
|
||||
"lanIp": "172.16.9.144",
|
||||
"connectWay": null,
|
||||
"lastReportTime": 1783636084895,
|
||||
"deviceStatus": "online",
|
||||
"accounts": [
|
||||
{
|
||||
"accountId": "a1f0191861194e4c8fab817c2b3ab54d",
|
||||
"lineId": 1,
|
||||
"accountType": 0,
|
||||
"accountServer": "pbx.packetdial.com",
|
||||
"registerName": "100@vwp.91912.service",
|
||||
"username": "100@vwp.91912.service",
|
||||
"status": 3
|
||||
}
|
||||
],
|
||||
"wifi": null
|
||||
}
|
||||
Reference in New Issue
Block a user