This service powers two customer-facing features, both set up through a Zoho form — you normally don't need server access:
Both are keyed by a Config ID of the form <ns-domain>-<suffix>
(e.g. pressone.net-sms, pressone.net-sales). One NS domain can have many
configs.
/update-sms, Click-to-Call form → /update-domain).ucportal.pressone.net.ns-scripts.pressone.net is only needed for troubleshooting (password in 1Password).| Field | What it is | Example |
|---|---|---|
| Config ID | <ns-domain>-<suffix>, your unique key |
pressone.net-sms |
NS Domain (api_domain) |
the customer's real NS domain | pressone.net |
SMS Number Owner (user) |
extension that owns the SMS DID (see below) | 2000 |
NS System User (ns_system_user) |
the routing user to be auto-created | 8001 |
| From Number | the SMS DID, digits only | 19294873999 |
| Auto-Reply Message | the text the caller receives | Our hours are Mon–Fri 9am–5pm… |
Finding the SMS Number Owner (user): look up which extension owns the DID:
curl -s -H "Authorization: Bearer $(grep NS_TOKEN /opt/ns-scripts/webresponder/.env | cut -d= -f2)" \
"https://ucportal.pressone.net/ns-api/v2/domains/pressone.net/smsnumbers"
Find the row where number matches your from-number — the dest field is the user.
Pick a free
ns_system_userextension. It must not already be a real user. The auto-provisioning creates it as a Simple User dedicated to routing this responder (service-code=system-webresponder).
Fill the fields above and submit. On success the form returns:
{"status":"ok","id":"pressone.net-sms",
"provisioning":{"user":"ok","dial_rule":"ok","answer_rule":"ok"}}
provisioning values should be "ok"."error: ...", the config still saved but that NS step failed —
see Troubleshooting below.Behind the scenes this saves the config and builds the NS routing:
call → {ns_system_user}@{api_domain} → answer rule forward-always →
webresponder{ns_system_user} → dial rule (To Web) → webhook → SMS sent.
The auto-created dial rule already routes webresponder{ns_system_user} to the
webhook. In the customer's NS portal, route the inbound treatment (Auto
Attendant digit, etc.) to the NS System User extension (e.g. 8001).
Call the number/treatment and confirm you receive the SMS.
| Field | What it is | Example |
|---|---|---|
| Config ID | <ns-domain>-<suffix> |
pressone.net-sales |
NS Domain (api_domain) |
the customer's real NS domain | pressone.net |
| Queue | call queue in EXT@DOMAIN format |
9002@pressone.net |
| Button Text | label on the button | Call Us Now |
| Button Color | hex color | #1a73e8 |
| Company Name | shown in the widget | PressONE |
| Logo URL | optional | (blank) |
The queue MUST include
@domain(e.g.9002@pressone.net). A bare extension silently fails with "Call Queue not found".
On success it returns {"status":"ok","id":"pressone.net-sales"}.
<script src="https://ns-scripts.pressone.net/widget.js?id=pressone.net-sales"></script>
Load the page, click the button, enter a phone number, and confirm the callback rings and bridges into the queue.
Just re-submit the same form with the same Config ID and changed values.
- SMS re-submit: provisioning re-runs but is idempotent — existing NS objects
are left in place (a 409 / "already exists" counts as success), so editing the
message text won't error.
- The two forms preserve each other's fields, so an SMS submit won't wipe a
widget's queue/branding on the same id, and vice-versa.
The confirmation message callers hear (and other in-code strings) live in
webresponder/app.py (_XML_SUCCESS). To change it, follow the deploy
process in the reference doc (edit local → test → commit → push → pull on
server → restart). Do not hand-edit files on the server.
Edit domains.json on the server to delete the entry (it's re-read on every
request, no restart needed):
ssh root@ns-scripts.pressone.net
nano /opt/ns-scripts/webresponder/domains.json
For an SMS responder, also remove the NS objects (system user, dial rule, answer rule) in the NS portal if you want them gone — the app doesn't delete them.
Service status:
ssh root@ns-scripts.pressone.net
systemctl status ns-webresponder
Watch live logs during a test:
journalctl -u ns-webresponder -f
A successful SMS call logs:
INFO webresponder: caller=+19734321440 id=pressone.net-sms digits=1 call_id=...
INFO SMS sent to +19734321440 via 19294873999
Probe the SMS webhook from the command line:
curl -s "https://ns-scripts.pressone.net/webresponder?id=pressone.net-sms&NmsAni=%2B19734321440&Digits=1"
Should return XML containing texted you.
| Symptom | Likely cause | Fix |
|---|---|---|
Form returns 403 Forbidden |
Wrong/missing form secret | Check the hidden update_secret field matches the server's UPDATE_SECRET |
Form returns 400 Missing fields |
A required field was blank | Fill all required fields for that form |
provisioning.user = error |
NS API auth or bad api_domain |
Check NS_TOKEN; confirm the domain exists |
provisioning.dial_rule = error |
Dial plan / domain issue | Confirm the api-domain's dial plan; check logs |
provisioning.answer_rule = error |
System user not ready | Re-submit (idempotent); check logs |
| Call to SMS number does nothing | Treatment not pointed at ns_system_user |
Route the treatment to that extension |
| Caller hears "could not send" | Config ID not in domains.json |
Re-submit the form |
| Caller hears "could not send" | Wrong user/from_number |
Re-check SMS inventory |
| Widget shows "Something went wrong" | Bad/empty queue, or queue missing @domain |
Use EXT@DOMAIN |
| New endpoint 404s publicly | Caddy not routing it | Add the route to server-config/Caddyfile |
| Task | Where |
|---|---|
| Add/edit SMS responder | SMS Zoho form → /update-sms |
| Add/edit click-to-call | Click-to-Call Zoho form → /update-domain |
| Widget embed | <script src="https://ns-scripts.pressone.net/widget.js?id=<ID>"></script> |
| SMS webhook URL | https://ns-scripts.pressone.net/webresponder?id=<ID> |
| View logs | journalctl -u ns-webresponder -f |
| Restart service | systemctl restart ns-webresponder |
| Full deploy / config detail | docs/webresponder-reference.md and runbook |