Initial skills documentation — 25 categories, all SKILL.md + references + scripts

This commit is contained in:
root
2026-07-15 17:42:20 -04:00
commit 1b4fcd4427
976 changed files with 188344 additions and 0 deletions
+360
View File
@@ -0,0 +1,360 @@
---
name: hermes-browser-setup
description: "Install, configure, and troubleshoot Hermes browser automation tools — local Chromium via Playwright, cloud providers (browser-use, Browserbase, Camofox), and CDP-based headless mode. Covers the full lifecycle from zero to working browser_* tools."
version: 1.2.0
author: Hermes Agent
license: MIT
platforms: [linux]
metadata:
hermes:
tags: [hermes, browser, automation, playwright, chromium, cdp, browser-use]
related_skills: [hermes-agent, hermes-migration, computer-use]
---
# Hermes Browser Setup
Hermes ships a `browser` toolset with ~10 tools (`browser_navigate`, `browser_snapshot`, `browser_click`, `browser_type`, `browser_scroll`, etc.) that can run via a **local Chromium CDP** or **cloud providers** (browser-use.com, Browserbase, Camofox).
This skill covers getting from zero to working browser tools, including the common failure mode where the toolset is enabled but no tools appear because no credential or CDP URL is configured.
## Quick Success Check
```bash
# Are browser tools enabled?
hermes tools list | grep browser
# Is a CDP URL or cloud key set?
grep -i 'browser\|cdp\|base\|fox' ~/.hermes/.env
grep -A5 'browser:' ~/.hermes/config.yaml
```
**Tools gated out with no error?** Check `.env` for `BROWSER_CDP_URL`, `BROWSER_USE_API_KEY`, `BROWSERBASE_API_KEY`, or `CAMOFOX_URL`. If none are set, the browser tools will be silently disabled regardless of `hermes tools list` showing them enabled.
## Mode A: Local Chromium via Playwright (free, recommended default)
### 1. Install Playwright + Chromium
```bash
pip3 install playwright
python3 -m playwright install chromium
python3 -m playwright install-deps chromium
```
Chromium binary lands at `~/.cache/ms-playwright/chromium-<version>/chrome-linux64/chrome`.
### 2. Start headless CDP server
**Quick test:**
```bash
~/.cache/ms-playwright/chromium-*/chrome-linux64/chrome \
--headless=new --no-sandbox --disable-gpu --disable-dev-shm-usage \
--remote-debugging-port=9222 --user-data-dir=/tmp/chromium-data &
# Verify
curl -s http://127.0.0.1:9222/json/version
# Expected: Chrome/<version> + webSocketDebuggerUrl
```
**Persistent systemd service:**
```bash
cat > /etc/systemd/system/hermes-browser.service << 'EOF'
[Unit]
Description=Hermes Headless Chromium (CDP Browser)
After=network.target
[Service]
Type=simple
User=root
ExecStart=/root/.cache/ms-playwright/chromium-<version>/chrome-linux64/chrome \
--headless=new \
--no-sandbox \
--disable-gpu \
--disable-dev-shm-usage \
--remote-debugging-port=9222 \
--user-data-dir=/tmp/chromium-data
Restart=always
RestartSec=5
Environment=DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable hermes-browser.service
systemctl start hermes-browser.service
```
### 3. Wire up Hermes config
```bash
# Set CDP URL in .env
echo 'BROWSER_CDP_URL=ws://127.0.0.1:9222' >> ~/.hermes/.env
# Set local mode in config
hermes config set browser.cloud_provider local
# Restart gateway (or /reset in chat)
hermes gateway restart
```
### 4. Verify
```bash
hermes chat -q "Use browser_navigate to visit http://whatsmyuseragent.com and report the browser string" --toolsets browser
```
Expected: successful navigation, browser reports `HeadlessChrome/<version>`.
## Mode B: browser-use.com Cloud (when local Chromium isn't enough)
Use cloud mode when sites detect headless Chromium, require real mobile/desktop fingerprints, or you need browser-use's anti-detection features.
```bash
# Get API key from https://browser-use.com/
# Then:
echo 'BROWSER_USE_API_KEY=<your-key>' >> ~/.hermes/.env
hermes config set browser.cloud_provider browser-use
hermes gateway restart
```
Cost: ~$0.02/hr per browser instance. No minimums.
## Mode C: Browserbase (alternative cloud)
```bash
echo 'BROWSERBASE_API_KEY=<key>' >> ~/.hermes/.env
echo 'BROWSERBASE_PROJECT_ID=<project>' >> ~/.hermes/.env
hermes config set browser.cloud_provider browserbase
hermes gateway restart
```
Has a free tier suitable for light usage.
## Mode D: Camofox (local anti-detection)
```bash
echo 'CAMOFOX_URL=http://localhost:<port>' >> ~/.hermes/.env
```
## Configuration Reference
| Config key / env var | Default | Mode | Description |
|---|---|---|---|
| `browser.cloud_provider` | `browser-use` | All | `local`, `browser-use`, `browserbase`, or `camofox` |
| `BROWSER_CDP_URL` | — | Local | WebSocket URL of local Chromium (e.g. `ws://127.0.0.1:9222`) |
| `BROWSER_USE_API_KEY` | — | Cloud | Key from browser-use.com |
| `BROWSERBASE_API_KEY` | — | Cloud | Key from browserbase.com |
| `BROWSERBASE_PROJECT_ID` | — | Cloud | Project ID from Browserbase |
| `CAMOFOX_URL` | — | Local | Camofox anti-detection URL |
| `browser.inactivity_timeout` | `120` | All | Seconds before idle browser session is closed |
## SPA Loading Behavior
JS-rendered API doc sites (Redoc, Swagger UI, Stoplight) often show "Loading..." in the page title and have an **empty accessibility tree** on first `browser_snapshot`. This is normal — the SPA bootstraps, then renders the sidebar tree, then lazy-loads the content pane. The `browser_snapshot` tool captures the current accessibility tree state and will grow as the page renders.
**Proven resolution sequence (netcup KVM, Chromium 149):**
1. `browser_navigate(url)` — returns `{title: "Loading...", element_count: 0}`. This is fine.
2. `browser_snapshot(full=true)` — after ~2-3s, the sidebar appears with 7,000+ elements. The content pane may still be sparse.
3. If the content pane is empty, use `browser_click(ref)` on a sidebar link to navigate to a specific endpoint, then `browser_snapshot` again. Redoc renders endpoint details on click.
4. For bulk extraction, use `browser_cdp` with `Runtime.evaluate` on the correct target (identified via `Target.getTargets`) targeting the page's title/URL, not the chrome://newtab page. Execute `document.body.innerText.substring(N, M)` to page through the full rendered text.
See `references/js-rendered-api-docs.md` for the full probe pattern.
## Auto-Provisioning Flow (any phone, any source)
Phones purchased from any retail source (Amazon, Best Buy, eBay, second-hand from a previous provider) can auto-provision on RingLogix/NetSapiens. The critical insight: **MAC registration is the only per-phone step**. Everything else (DHCP, provisioning server URL) is network-level config.
### The Flow
| Step | What happens | Who does it |
|---|---|---|
| **1. Factory reset** (used phones only) | Hold reset pin or use phone menu to wipe old config | Customer or you, one-time |
| **2. DHCP discovery** | Phone boots, requests IP. Router serves DHCP options pointing to provisioning server | Router/DHCP server (configured once) |
| **3. MAC registration** | Register the phone's MAC address in RingLogix with model info | API or portal — `?object=mac&action=create` |
| **4. Device linking** | Link MAC to a subscriber extension | API or portal — `?object=device&action=create` |
| **5. Provisioning request** | Phone contacts the NDP (NetSapiens Device Provisioning) server, sends its MAC | Phone does this automatically |
| **6. Config push** | Server matches MAC, pushes SIP credentials, extension, caller ID, features | RingLogix NDP server |
| **7. Auto-reboot** | Phone reboots with new config, registers to VoIPSimplicity | Automatic |
| **8. Verify** | Check subscriber status, registration, and call capability | API — `?object=subscriber&action=read` |
### DHCP Options Required (configure once on your router/MikroTik)
| DHCP Option | Provider/Standard | What it sets |
|---|---|---|
| **Option 156** | Yealink / Poly | Provisioning server URL |
| **Option 66** (TFTP server) | Generic SIP phones | TFTP server address for config files |
| **Option 160** | Grandstream | HTTP provisioning URL |
| **Option 43** (Vendor-specific) | Cisco | Cisco-specific provisioning |
The actual provisioning server URL (e.g., `prov.ringlogix.com`) is configured in the RingLogix portal UI — it is **not exposed** through the REST API. The API provides the building blocks (MAC registration, device model queries) but not the provisioning server config itself.
### MAC Endpoints (from RingLogix API docs)
All POST to `https://api.ringlogix.com/pbx/v1/?object=mac&action=<action>`.
**Create:** `?object=mac&action=create`
Permission: OMP (Office Manager / Reseller / Super User)
| Param | Required | Description |
|---|---|---|
| `mac` | Yes | MAC address, hex only, 12 chars, no `-` `:` `.` |
| `domain` | Yes | Domain of new device |
| `model` | Yes | Phone model (query via Device_Model endpoint) |
| `transport` | No | NDP transport type |
| `server`, `last_pull`, `date_created` | No | Metadata |
| `device1..device8` | No | Device field references |
| `territory`, `notes` | No | Organization |
| `line1_ext..line2_ext` | No | Extension lines |
| `line1_enable..line8_enable` | No | Per-line enable flags |
| `line1_share..line8_share` | No | Line sharing |
| `overrides`, `dir_inc`, `presence` | No | NDP overrides |
**Read:** `?object=mac&action=read` — Permission: Reseller
| Param | Required | Description |
|---|---|---|
| `mac` | Yes | MAC to look up (hex, colons removed) |
| `extension` | Yes | Numerical string, searches lines with this extension associated with the MAC |
| `checkExistance` | Yes | Boolean — check if MAC exists |
Response includes: mac, server, territory, dir_inc, presence, domain, model, device1-8, notes, line1-8_share, overrides, phone_ext, transport, fxs, sla, sidecar, resync, directory_support, user_agent, contact, registration information.
**Update:** `?object=mac&action=update` — Permission: Reseller
Requires `mac`; all other fields optional (model, line, device, transport, server, notes, overrides, etc.).
**Delete:** `?object=mac&action=delete` — Permission: Reseller
Requires `mac` (hex, colons removed) and `domain`.
**Count:** `?object=mac&action=count` — Permission: Reseller
Requires `domain`. Optional: `territory`, `mac`, `mac_LIKE` (partial match), `model`, `notes`.
### Device Model Query
```http
POST https://api.ringlogix.com/pbx/v1/?object=devicemodel&action=read
```
Permission: Reseller
Optional: `brand`, `model`, `portal_view`, `include_ndp_defs`, `ndp_syntax`
### Important API Notes
- All endpoints use **POST** — parameters in form body
- Routing via `?object=` and `?action=` query params (not RESTful paths)
- Reseller permission required for provisioning ops; `subscriber&action=list` needs only Basic User
- RingLogix provides a Postman test collection from the docs site
- The docs site uses Redoc JS viewer — see `references/js-rendered-api-docs.md` for SPA navigation tips
- **Client ID/Secret** from RingLogix support (separate from PBX user credentials) — this is required before any API call works
## Reference Files
| File | Contents |
|---|---|
| `references/js-rendered-api-docs.md` | Navigating JS-heavy API doc sites (Redoc, Swagger) — sidebar vs main pane behavior, probe sequence |
| `references/ringlogix-api.md` | RingLogix (NetSapiens/CPaaS) API reference — OAuth2 auth, subscriber management, device provisioning, MAC endpoints, CDRs, and full endpoint catalog |
## Available Browser Tools
| Tool | Purpose | Requires |
|---|---|---|
| `browser_navigate` | Load a URL | First call that initializes session |
| `browser_snapshot` | Get accessibility tree with ref IDs | `browser_navigate` first |
| `browser_click` | Click element by ref ID | `browser_snapshot` first |
| `browser_type` | Type into a field by ref ID | `browser_snapshot` first |
| `browser_scroll` | Scroll up/down/left/right | `browser_navigate` first |
| `browser_back` | Navigate backward in history | `browser_navigate` first |
| `browser_press` | Press a keyboard key | `browser_navigate` first |
| `browser_console` | Get JS console output | `browser_navigate` first |
| `browser_get_images` | List images on current page | `browser_navigate` first |
| `browser_vision` | Screenshot + vision analysis | `browser_navigate` first |
## Troubleshooting
### Tools enabled but not appearing in function list
```
# Check if this machine
hermes tools list | grep browser # should show "enabled"
grep BROWSER ~/.hermes/.env # must show CDP URL or API key
```
**Root cause:** The browser toolset checks for a configured backend at startup. If no key/CDP URL is found, all browser tools silently gate out. Setting `cloud_provider` alone is NOT sufficient — the corresponding env var must exist.
### Chromium exits with code 127 (binary not found) or path mismatch
```bash
$ ~/.cache/ms-playwright/chromium-1228/chrome-linux/chrome
bash: .../chrome: No such file or directory
```
**The Playwright Chromium binary is NOT at `chrome-linux/chrome`.** Since Playwright ~1.50, the path is `chrome-linux64/chrome`. This was confirmed on Playwright 1.61.0 / Chromium 1228 installed Jul 7, 2026 on Debian 13. Always use:
```bash
find ~/.cache/ms-playwright/chromium-*/ -name chrome -type f
```
Then use the full discovered path in the systemd unit's `ExecStart`. The incorrect `chrome-linux/chrome` path causes exit code 127 immediately with no CDP port responding.
### Chromium fails to start with exit code 133
```
Error: dbus/bus.cc failed to connect to the bus
```
**Fix 1:** Start the D-Bus daemon:
```bash
dbus-daemon --system --fork
```
**Fix 2:** Set the env var in the systemd unit:
```
Environment=DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket
```
**Fix 3:** Install dbus-x11 if missing:
```bash
apt-get install -y dbus dbus-x11
```
### Headless Chrome crashes on KVM VPS (Debian 13)
**Symptoms:** Process exits with 133 or 255 immediately, no CDP port responding.
**Known issue on netcup KVM (nested virtualization):** The system CPU scaling files (`/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq`) are missing. These errors are **non-fatal warnings** — the real cause is usually:
1. Missing `--no-sandbox` flag
2. D-Bus socket unavailable
3. Wrong path to the `chrome` binary (Playwright installs to `chrome-linux64/chrome`, not `chrome-linux/chrome`)
Always check the binary path with `find`:
```bash
find ~/.cache/ms-playwright/chromium-*/ -name chrome -type f
```
### Test: verify browser is working
From any Hermes session:
```bash
hermes chat -q "Open browser_navigate to http://example.com and report the page title" --toolsets browser
```
Expect: `Example Domain` title returned.
### SPA site returns "Loading..." and empty snapshot — that's normal
When browsing JS-rendered API docs (Redoc, Swagger), the first `browser_navigate` call returns `title: "Loading..."` with `element_count: 0`. This is expected behavior — the SPA has not finished rendering. Resolution sequence proven on netcup KVM with Chromium 149:
1. Call `browser_navigate(url)` — accept the "Loading..." title
2. Call `browser_snapshot(full=true)` — after ~2-3s, sidebar appears with 7,000+ elements
3. If content pane is empty, use `browser_click(ref)` on a sidebar link, then `browser_snapshot` again
4. For bulk extraction, use `browser_cdp` with `Runtime.evaluate` on the correct page target (identified via `Target.getTargets`) targeting the page's URL/title, not the `chrome://newtab` page. Execute `document.body.innerText.substring(N, M)` to page through rendered content
See `references/js-rendered-api-docs.md` for the full probe pattern.
### Multiple page targets exist — use Target.getTargets to find the right one
When `browser_cdp` calls with `Runtime.evaluate` fail with `method not found`, the issue is often that the browser has multiple open tabs (new tab page + your page). Use `Target.getTargets` to find the page target by URL, then pass `target_id` in subsequent CDP calls. The `browser_navigate` tool interacts with the most recent target, but raw CDP calls need the explicit id.
## Related Skills
- `computer-use` — desktop GUI driving (separate from browser tools; use `browser_*` for web automation)
- `server-architecture-plan` — service placement on this server (browser runs well on 8C/15G boxes)
- `reboot-with-health-check` — verify hermes-browser.service comes back after server reboot
@@ -0,0 +1,105 @@
# JS-Rendered API Documentation Pages
Pattern observed when browsing OpenAPI/Redoc-styled docs (like RingLogix, Swagger, Stoplight):
## What the Accessibility Tree Shows
- **Sidebar/nav tree** — fully accessible via `browser_snapshot(full=true)`. Category names, endpoint paths, and hierarchical structure appear as text nodes.
- **Main content pane** — the actual API spec (endpoint details, parameters, schemas, sample requests) renders in a JavaScript-driven viewer (Redoc, Swagger UI, Scalar). The accessibility tree may show these as opaque elements with `role="region"` or similar, with no rich text content.
- **Search/filter box** at the top — accessible for navigation.
## Recommended Probe Sequence
1. `browser_navigate(url)` — loads the page
2. `browser_snapshot(full=true)` — get the full sidebar nav structure
3. If sidebar content is present but main pane is empty:
- `browser_click(element)` on a sidebar link to navigate to a specific endpoint
- `browser_snapshot(full=true)` again after navigation (JS may then render that section)
- Or use `browser_vision` to screenshot and analyze visually
## CDP Bulk Extraction (for large Redoc/Swagger pages)
When the SPA loads a huge page (e.g. 238 endpoints, 186K chars of docs), `browser_snapshot` truncates at ~18K lines and `browser_console` JS selectors may find no `<nav>` element (Redoc renders into custom elements or shadow DOM). Use this CDP-based probe sequence instead:
### Step 1: Navigate and identify the correct target
```
browser_navigate(url)
// Wait ~2s for SPA bootstrap
browser_snapshot(full=true) // Sidebar should be visible
```
### Step 2: List all browser targets via CDP
```
browser_cdp(method="Target.getTargets", params={})
```
Look for the page with the expected title (NOT `chrome://newtab`). Returns `targetInfos` array — the target you navigated to has the real URL and `title` field.
### Step 3: Extract text via CDP on the correct target
```
browser_cdp(
target_id="<target ID from Step 2>",
method="Runtime.evaluate",
params={
expression: "document.body.innerText.substring(0, 8000)",
returnByValue: true
}
)
```
Page through sections by changing the substring range. For large docs (e.g. RingLogix: 186K chars), use intervals of 3000-4000 at a time.
### Step 4: Get full sidebar nav list via CDP
```
browser_cdp(
target_id="<target ID>",
method="Runtime.evaluate",
params={
expression: "Array.from(document.querySelectorAll('li a')).map(a => a.textContent.trim()).filter(t => t)",
returnByValue: true
}
)
```
Returns the complete sidebar as an ordered array of link text.
### Step 5: Confirm page identity
```
browser_cdp(
target_id="<target ID>",
method="Runtime.evaluate",
params={
expression: "document.title",
returnByValue: true
}
)
```
### Key differences from browser_console
| Aspect | `browser_console` | `browser_cdp` + `Runtime.evaluate` |
|---|---|---|
| Cap | Expression length | None (full function bodies ok) |
| Target | Current browser tab | Any target by ID |
| Shadow DOM | Limited | Limited (same as console) |
| Return size | Moderate | Large (substring approach) |
| Syntax errors | Reported inline | Fail silently on malformed arrow functions — use `(function(){...})()` not `()=>{...}` |
## Confirmed Case: RingLogix API Docs
URL: https://apidocs.ringlogix.com/
Status: Public, no login required
Content: ~186K characters of API documentation on page load
Structure: Sidebar with OAuth2, Agents, Agent Log, Answer Rule, Audio, CDR2, CDR Export, CDR Schedule, Call, Call Center Stats, Call Queue, Call Queue Stats, Subscriber, Device, Message, Emergency Address
~7,800+ elements in the DOM
## Confirmed Case: browser_console caveats
Two pitfalls observed:
1. `querySelector('nav')` returns null on Redoc pages — the sidebar renders as custom elements, not `<nav>`.
2. Arrow functions (`() => {...}`) with template literals can trigger `SyntaxError: Unexpected end of input` via `browser_console`. Use `(function(){...})()` syntax instead for complex expressions.
@@ -0,0 +1,207 @@
# RingLogix (NetSapiens/CPaaS) API Reference
White-label phone service provider (VoIPSimplicity backend). Docs at https://apidocs.ringlogix.com/ — public, no login required for browsing. Uses Redoc JS viewer.
## Base URL
```
https://api.ringlogix.com/pbx/v1/
```
## Authentication — OAuth2
**Endpoint:** `POST https://api.ringlogix.com/pbx/v1/oauth2/token/`
### Flow 1: Password grant (initial token)
```
POST /pbx/v1/oauth2/token/
Content-Type: application/x-www-form-urlencoded
grant_type=password
client_id=<from RingLogix>
client_secret=<from RingLogix>
username=<subscriber_login>
password=<subscriber_password>
```
### Flow 2: Refresh token
```
POST /pbx/v1/oauth2/token/
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
client_id=<from RingLogix>
client_secret=<from RingLogix>
refresh_token=<from prior auth>
```
### Success 200 Response
| Field | Type | Description |
|---|---|---|
| `access_token` | String | Bearer token |
| `expires_in` | Number | Seconds until expiry |
| `refresh_token` | String | For getting a new token |
| `domain` | String | Domain of the user |
| `scope` | String | Scope of access |
| `token_type` | String | Always "Bearer" |
| `legacy` | Boolean | In legacy API window? |
| `legacy_expires_days` | Number | Days remaining in legacy window |
| `recover` | Boolean | Was recovery email dispatched? |
**Important:** Client ID/Secret are obtained from RingLogix support — they are **separate** from PBX user credentials.
---
## Endpoint Catalog (42 sections, 238 endpoints)
All use POST. Routing via query string `?object=<object>&action=<action>`.
### Auth & Users
| Endpoint | Permission | Key Params |
|---|---|---|
| `?object=subscriber&action=count` | Reseller | `domain` (required) |
| `?object=subscriber&action=read` | Reseller | `domain`, `user`, `login`, `email`, `first_name`, `last_name`, `fields` |
| `?object=subscriber&action=list` | Basic User | `domain` |
### Device Provisioning
**`POST ?object=device&action=create`** — Permission: Reseller
| Param | Required | Description |
|---|---|---|
| `domain` | Yes | VoIPSimplicity domain |
| `device` | Yes | Name of the device |
| `user` | Yes | Owner (valid user extension) |
| `mac` | Yes | MAC address — hex only, 12 chars, no `:` or `-` |
| `model` | Yes | Phone model (query via Device/Model API) |
Also available: `count`, `read`, `update`, `delete` via the same object/action pattern.
### CDRs
| Endpoint | Description |
|---|---|
| `?object=cdr&action=read_by_domain` | Read CDRs by domain |
| `?object=cdr&action=read_by_user` | Read CDRs by user |
| `?object=cdr&action=read_by_group` | Read CDRs by group |
| `?object=cdr&action=report_by_domain` | Report CDRs by domain |
| `?object=cdr&action=read_by_id` | Read CDR by ID |
Also: CDR Export (create/read/download), CDR Schedule (create recurring exports), optimize/purge old CDRs.
### Call Control
| Section | Endpoints |
|---|---|
| Call | Count/Read/Report active calls, Answer, Disconnect, Make, Transfer, Park, Hold, Record, Playback |
| Call Queue | Create/Read/Update/Delete queues, Create queued calls |
| Call Queue Stats | Read queue stats |
| Call Center Stats | Agent log, DNIS stats, Queue stats, User stats |
### Phone Numbers & SMS
| Endpoint | Description |
|---|---|
| `?object=phone_number&action=count` | Count numbers in a dial plan |
| `?object=phone_number&action=read` | Read phone numbers |
| `?object=phone_number&action=update` | Update a phone number |
| `?object=sms_number&action=count` | Count SMS-capable numbers |
| `?object=sms_number&action=read` | Read SMS numbers |
| `?object=sms_number&action=update` | Update SMS number |
### Agents & Teams
| Section | Endpoints |
|---|---|
| Agent | Create/Count/Read/Update/Delete agents in a callqueue, Change status |
| Agent Log | Create/Read agent logs |
| Contact | Create/Count/Read/Update/Delete contacts |
| Department | Create/List departments |
| Presence | List presence in a domain |
### MAC Provisioning Endpoints
All POST to `https://api.ringlogix.com/pbx/v1/?object=mac&action=<action>`.
**Create:** `?object=mac&action=create` — Permission: OMP (Office Manager / Reseller / Super User)
| Param | Required | Description |
|---|---|---|
| `mac` | Yes | MAC hex only, 12 chars, no `-` `:` `.` |
| `domain` | Yes | Domain of new device |
| `model` | Yes | Phone model (query via Device_Model) |
| `transport` | No | NDP transport type |
| `server`, `last_pull`, `date_created` | No | Metadata |
| `device1..device8` | No | Device field references |
| `territory`, `notes` | No | Organization |
| `line1_ext..line2_ext` | No | Extension lines |
| `line1_enable..line8_enable` | No | Per-line enable flags |
| `line1_share..line8_share` | No | Line sharing |
| `overrides`, `dir_inc`, `presence` | No | NDP overrides |
Note: "Before using this module, the device should have already been instantiated."
**Read:** `?object=mac&action=read` — Permission: Reseller
| Param | Required | Description |
|---|---|---|
| `mac` | Yes | MAC to look up (hex, colons removed) |
| `extension` | Yes | Numerical string, searches lines with this extension |
| `checkExistance` | Yes | Boolean — check if MAC exists |
Response: mac, server, territory, dir_inc, presence, domain, model, device1-8, notes, line1-8_share, overrides, phone_ext, transport, fxs, sla, sidecar, resync, directory_support, user_agent, contact, registration_expires_time, registration_time
**Update/Delete/Count:** Same pattern via `?object=mac&action=update|delete|count`. Delete requires `mac` + `domain`. Count requires `domain` (optional: `territory`, `mac`, `mac_LIKE`, `model`).
**Device Model Query:** `?object=devicemodel&action=read` — Permission: Reseller — optional: `brand`, `model`, `portal_view`, `include_ndp_defs`, `ndp_syntax`
---
### Auto-Provisioning Flow (any phone, any source)
Phones from any vendor (retail, used, previous provider) work identically — MAC registration is the only per-phone step. The provisioning server URL is configured in the RingLogix portal UI, NOT via REST API.
1. **Factory reset** (used phones only) — wipe old provider config
2. **DHCP discovery** — phone boots, router serves DHCP options (156/66/160/43) pointing to provisioning server (configured once on your network gear)
3. **MAC registration**`POST ?object=mac&action=create` with MAC + model
4. **Device linking**`POST ?object=device&action=create` links MAC to subscriber extension
5. **Phone contacts NDP server** — RingLogix provisioning server matches MAC
6. **Config push** — Server pushes SIP credentials, extension, features. Phone auto-reboots configured
7. **Verify**`POST ?object=subscriber&action=read` checks account_status: active
DHCP Options (set once per network):
| Option | For | What it sets |
|---|---|---|
| 156 | Yealink / Poly | Provisioning server URL |
| 66 | Generic SIP | TFTP server address |
| 160 | Grandstream | HTTP provisioning URL |
| 43 | Cisco | Cisco provisioning |
---
## Common Header Pattern
All API calls after auth:
```
Authorization: Bearer <access_token>
```
## Key Observations
- **Not RESTful** — single base URL with `?object=&action=` routing
- **All POST** — parameters in form body
- **Reseller permission** required for provisioning operations
- **Basic User permission** sufficient for `subscriber&action=list`
- **Client ID/Secret** from RingLogix support (separate from PBX creds)
- **Postman collection** available from the docs site for testing
## Automation Opportunities
1. **Auto-provisioning**: Customer order → create subscriber → register MAC → link device → phone auto-provisioned on boot
2. **MAC-based provisioning**: Customer plugs in any phone — register MAC via API, phone picks up config from DHCP + NDP
3. **Second-hand phone handling**: Factory reset + MAC registration + device link = same flow as new phones
4. **CDR export automation**: Scheduled CDR export for billing
5. **DID management**: Phone number inventory, assignment, porting
6. **Customer lookup**: Query subscriber details by name/extension/domain
7. **DHCP config**: Option 156/66/160/43 set on customer's router once (not per-phone)