8.4 KiB
Session — yealink-ymcs skill built (YMCS v2 API), pairs with packetdial; vendor-stack correction
User
- User: Mike Swanson (mike)
- Machine: GURU-5070
- Role: admin
Session Summary
Continuation of the VoIP onboarding work. Kept the vwp.91912.service domain created last
session and fixed its domain-type to Standard (the create had stored "no"). Then built the
onboard-domain wrapper into the packetdial skill — one gated command running the GUI Add-a-Domain
flow as 3 API calls (POST /domains → addresses/validate → addresses/create), verified end-to-end
with a throwaway create→delete. Mike then clarified the vendor stack (PacketDial = ACG's VoIP-dept
brand; NetSapiens = the platform; OIT/OITVOIP = white-label wholesaler) — captured as a reference
memory + logged correction.
Mike's main ask: add the related service — Yealink cloud device manager — and make sure we have
access + can use it with the PBX skill. Discovery found ACG already has a Yealink admin
(admin@azcomputerguru.com, vault infrastructure/voip-phones.sops.yaml) and that the cloud
manager is YMCS (us.ymcs.yealink.com); the wiki documents VWP's Yealink T54W fleet. Mike then
supplied YMCS API credentials (AccessKey ID + Secret) via screenshot, which were vaulted at
services/yealink-ymcs.sops.yaml.
On Mike's go, RTFM'd the YMCS open API and built a new yealink-ymcs skill. The current v2 API
uses token auth (POST /v2/token Basic → bearer), not the legacy RPS HMAC signing; Yealink servers
require legacy TLS renegotiation. Harvested the full /v2/dm/* + /v2/rps/* endpoint map from the
dszp/n8n-nodes-yealinkymcs community node. Implemented ymcs_client.py (token+bearer, legacy-TLS
SSL context, paginated _list, vault creds) + ymcs.py CLI (reads + gated writes + raw). Live-
verified: token auth, sites, devices (21), accounts, rps-servers. The ACG AccessKey is the parent
account that sees ALL client sites (VWP, GuruHQ, Ace Pick Up Parks as children) — so one key
covers every client fleet; answers Mike's per-client-vs-one-key question.
The pbx+phone pipeline now exists end to end: packetdial onboard-domain (PBX domain + SIP
accounts) → yealink-ymcs (add-devices-by-mac, add-sipaccount to push a NetSapiens SIP cred
onto a phone, rps-add for zero-touch via RPS server WL - ACG = ftp://p.packetdials.net). VWP
is the live pilot (onboarded to the PBX; 21 devices in YMCS, fleet partly pending). Everything
committed, pushed, deployed.
Key Decisions
- Token auth, not legacy HMAC. The first sources described the old RPS Management Platform HMAC
signing (X-Ca-* + Content-MD5); the n8n node revealed the current v2 API uses
POST /v2/token(Basic) → bearer. Built to v2 (simpler, current). - Legacy TLS handled in-client via an SSL context with
OP_LEGACY_SERVER_CONNECT(Yealink servers require renegotiation; default Python TLS would fail). - One AccessKey for all clients — verified the key is the ACG parent account; client sites are children. No per-client API keys.
add-sipaccountinstead of a sip-account list./v2/dm/sipAccounts(POST) is the CREATE endpoint (not a list); wrapped it as the gated write since pushing a SIP cred onto a phone is the actual PBX↔phone integration glue. No list-sip-accounts exists in v2.- Read-first, writes gated --confirm (mirrors packetdial);
rawpassthrough for the rest. - Honest [V]/[P] status in docs — only token/sites/devices/accounts/rps-servers lifecycle-
verified; writes + official-firmwares +
--sitefilter are plumbed-but-unverified (no throwaway device to safely test writes on).
Problems Encountered
- MSYS path conversion on
raw: a leading/v2/...arg gets rewritten by Git-Bash toC:/Program Files/Git/v2/.... Added in-code recovery (cut back to/v2/).MSYS_NO_PATHCONV=1is NOT a fix — it broke the vault subprocess (creds failed). Named wrappers (hardcoded paths) are unaffected. (Documented Windows gotcha class.) /v2/dm/sipAccountsis a create, not a list — my initialsipaccountsread wrapper hit it and got HTTP 400 (sipServer1 cannot be null). Replaced with the gatedadd-sipaccount.official-firmwaresreturns a non-standard shape (likely needs a model param) — marked [P].--sitefilter ondevicesreturned all 21 (siteId in the body didn't scope) — best-effort, marked [P]; full data still returned.- cp1252 + WebFetch 403 while RTFMing — used
encoding='utf-8'for spec reads and pivoted from the 403'd Yealink doc to the public GitHub n8n node source via the GitHub API + raw files. domain-typestored"no"on packetdial create — fixed vwp viaraw PUT(carryover).
Configuration Changes
Created:
.claude/skills/yealink-ymcs/SKILL.md,scripts/ymcs.py,scripts/ymcs_client.py(new skill).services/yealink-ymcs.sops.yaml(VAULT) — YMCS API AccessKey..claude/memory/reference_packetdial_oit_netsapiens.md(+ MEMORY.md index line).- This session log. Modified:
.claude/skills/packetdial/SKILL.md,scripts/ns.py,scripts/ns_client.py—onboard-domainwrapper.errorlog.md— vendor-model correction. Live changes: vwpdomain-type→ Standard; threw awayacg-onbtest.91912.service(wrapper test).
Credentials & Secrets
- YMCS API AccessKey — vaulted
services/yealink-ymcs.sops.yaml:credentials.access_key_id/credentials.access_key_secret. Region us (us-api.ymcs.yealink.com). Read withvault.sh get-field services/yealink-ymcs.sops.yaml credentials.access_key_id. (Values intentionally not echoed here — they're vaulted.) - Existing: Yealink portal admin
admin@azcomputerguru.com→infrastructure/voip-phones.sops.yaml; per-client portal logins e.g.clients/valleywide/. PBX reseller keymsp-tools/oitvoip.sops.yaml.
Infrastructure & Servers
- YMCS API:
https://us-api.ymcs.yealink.com(v2). AuthPOST /v2/tokenBasic→bearer; legacy TLS. - YMCS account tree (ACG parent key):
Arizona Computer Guru LLC(id 34a602e7…) →VWP(1e7578a6…),GuruHQ(04117c54…),Ace Pick Up Parks(0320c810…). - RPS server:
WL - ACG(id 019c908f…), urlftp://p.packetdials.net, authNamelrshwh. - Device object keys: id, mac, sn, name, modelId/modelName, siteId/siteName, programVersion, deviceStatus, connectWay, wanIp, lanIp, lastReportTime, createTime. 21 devices total.
- PBX:
pbx.packetdial.com/ns-api/v2(NetSapiens via OIT); domainvwp.91912.servicelive.
Commands & Outputs
- YMCS reads:
ymcs.py sites|devices|accounts|rps-servers|firmwares|models|alarms|oplogs|device-groups|device-configs - YMCS gated writes:
ymcs.py add-devices-by-mac|add-sipaccount|reboot|reset|rps-add|rps-del --body '{...}' --confirm ymcs.py raw POST /v2/dm/listDevices --body '{"skip":0,"limit":50,"autoCount":true}'(path auto-recovers MSYS mangling)- packetdial onboarding:
ns.py onboard-domain --body-file new-client.json --confirm - vwp fix:
ns.py raw PUT domains/vwp.91912.service --body '{"domain-type":"Standard"}' --confirm
Pending / Incomplete Tasks
- [P] YMCS writes unverified — add-devices-by-mac, add-sipaccount, reboot, reset, rps add/del;
official-firmwares shape;
--sitedevice filter. Exercise on real devices (VWP's pending fleet) when a task needs them. - Proposed (not started): a combined
onboard-clientflow = packetdial onboard-domain + users/devices + YMCS phone provisioning (add-by-mac + add-sipaccount/rps) in one gated run, piloted on VWP's 11 pending phones. - packetdial gaps (carryover): voicemail user-defaults (not in POST /domains),
email-send-from-addresspendingvoicemail@/noreply@packetdial.commailbox creation. OIT-API.txtplaintext in Downloads — Mike to delete.
Reference Information
- New skill:
.claude/skills/yealink-ymcs/. Vendor stack memory:reference_packetdial_oit_netsapiens. - Endpoint map source:
github.com/dszp/n8n-nodes-yealinkymcs(n8n community node) + Yealink "Open API for YMCS V4X" doc. Auth doc patterns: support.yealink.com (legacy RPS HMAC — superseded by v2 token). - Commits this session:
onboard-domaind1d1302d; vendor-stack memory1dbefd54; yealink-ymcs skill850e685d. Vault: oitvoip key + YMCS key pushed. - YMCS endpoints:
/v2/token,/v2/dm/{listSites,listDevices,listAccounts,sipAccounts,addDevicesByMac, addDevices,delDevices,device/reboot,device/reset,listFirmwares,models,...},/v2/rps/{listServers, listDevices,addDevices,deleteDevices}.