Task 5 one-line registry descriptions on the 8 biggest skills (remediation-tool,
gc-audit, packetdial, memory-dream, human-flow, self-check, impeccable,
mailprotector); skill-description injection ~3320 -> ~2123 tokens (~36%),
keyword triggers preserved, frontmatter valid.
Task 7 thinned /save + /sync bodies to point at sync.sh (single source) instead of
re-documenting internals; Phase 0 save-vs-sync, cross-user notes, exit-75
reporting kept verbatim; mechanical sync never depends on an LLM step.
Task 10 session-logs/YYYY-MM/ forward convention for new logs (scoped-grep recall,
no monolithic index); existing flat logs untouched (grep covers both).
Bash now-phoenix.sh helper (fixed UTC-7 epoch math; replaces unreliable
TZ=America/Phoenix date that silently returns UTC on Git-Bash).
P0 (1.2.0) + Task 6 CLAUDE split + Task 9 delegation (1.3.0) already shipped.
Spec: specs/claudetools-harness-optimization/plan.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.9 KiB
name, description
| name | description |
|---|---|
| packetdial | Manage the ACG PacketDial/OITVOIP hosted VoIP via the NetSapiens API (pbx.packetdial.com). List/inspect domains, users, devices, DIDs, resellers; pull CDRs; provision domains/users/SIP/numbers (writes gated --confirm; read-only default). Triggers: packetdial, oitvoip, netsapiens, voip domain/user/extension, provision phone, add did, CDR. Live production PBX. |
PacketDial / NetSapiens (OITVOIP) Skill
Standalone CLI client for the NetSapiens SNAPsolution API v2 that backs
ACG's hosted-VoIP offering through OITVOIP / PacketDial. Read-only by default;
every write (create / update / delete) is gated behind --confirm.
The two hostnames (important)
| Host | What it is | API? |
|---|---|---|
voip.packetdial.com |
Customer-facing white-label portal / UC & fax dashboard (e.g. Cascades fax account 28598). Login-gated UI. | No |
pbx.packetdial.com |
Reseller PBX platform — NetSapiens v44.4. | Yes — this skill targets it |
- API base:
https://pbx.packetdial.com/ns-api/v2 - Token endpoint:
https://pbx.packetdial.com/ns-api/v2/tokens - Live OpenAPI spec:
https://pbx.packetdial.com/ns-api/webroot/openapi/openapi.json - Live Swagger UI:
https://pbx.packetdial.com/ns-api/openapi - Vendor docs: https://docs.ns-api.com/ (login) and https://voipdocs.io/oitvoip-access-platform-apis
Credentials — ONE-TIME SETUP (not yet provisioned)
As of this skill's creation no API key exists yet — the vault entry
msp-tools/oitvoip.sops.yaml is empty/absent, so every command will fail with a
clear "No credentials found" error until you do this once:
- Log into
pbx.packetdial.com-> Admin > API Keys and create a reseller-scoped key (prefixnsr_). If self-service key creation is not available, reply to Darwin Escaro (OITVOIP) for reseller OAuth client credentials. - Store it in the SOPS vault. Preferred (static bearer key):
Or, for OAuth2 password-grant credentials:
# msp-tools/oitvoip.sops.yaml credentials: api_key: nsr_xxxxxxxxxxxxxxxxcredentials: client_id: <client id> client_secret: <client secret> username: <portal user@domain> password: <portal password> - That's it — the client auto-detects which shape is present.
The client never hardcodes secrets. Resolution order: PACKETDIAL_API_KEY env
-> PACKETDIAL_CLIENT_ID+friends env -> vault credentials.api_key -> vault
OAuth fields. Env overrides exist for quick testing without touching the vault.
Running the CLI
This machine's Python launcher is py (per identity.json); python / python3
also work. Run from the scripts dir so the two modules resolve.
cd C:/claudetools/.claude/skills/packetdial/scripts
py ns.py status # API version + authenticated key identity
py ns.py domains # list all domains
py ns.py domain <domain> # one domain's config
py ns.py users <domain> # users / extensions in a domain
py ns.py user <domain> <user>
py ns.py phones <domain> # SIP devices registered in a domain
py ns.py dids <domain> # phone numbers (DIDs) on a domain
py ns.py devices <domain> <user>
py ns.py cdrs --domain <domain> --start 2026-06-01 --end 2026-06-02
py ns.py resellers
Writes (gated)
Every mutating command prints a [DRY RUN] line and exits non-zero unless you
pass --confirm. Bodies are raw JSON matching the NetSapiens v2 schema.
py ns.py create-domain --body '{"domain":"acme","description":"Acme Inc"}' --confirm
py ns.py create-user acme --body '{"user":"101","name-first-name":"Jane"}' --confirm
py ns.py create-phone acme --body '{...}' --confirm
py ns.py create-did acme --body '{"phonenumber":"15205551234"}' --confirm
py ns.py update-user acme 101 --body '{"name-last-name":"Doe"}' --confirm
py ns.py delete-user acme 101 --confirm
Raw escape hatch (any of the 239 v2 paths)
The named commands cover the common surface; for anything else, hit the path
directly. Non-GET methods still require --confirm.
py ns.py raw GET domains/acme/users/101/answerrules
py ns.py raw POST domains/acme/users --body '{...}' --confirm
Standard provisioning flow (new customer)
create-domain-> dial plan auto-generatescreate-userper extensioncreate-phoneper SIP device (MAC-provisioned)create-didto attach DIDs and route them to users- Log the work back to the Syncro ticket
Notes
- This is the LIVE production reseller PBX. A bad
create-domainordelete-useraffects real customers — confirm the target domain first with a read command before any write. - CDR queries can be large; always pass
--start/--endand a--limit. - Reference detail (auth shapes, full endpoint inventory) lives in
references/api.md.