Build a working local URL for a Surge page. Base URL: https://<interface>.jason.surge-eng.com, where <interface> is hq, embed, or api.
Workflow
1. Identify the interface
Infer from the user's request when possible:
- embed - embedded widget, iframe, inbox, phone SDK, conversation embed
- hq - dashboard, admin, account settings, phone number management, calls list
- api - REST endpoint, OpenAPI, curl, programmatic access
If the interface is not clear, ask which one before continuing.
2. Find the route
Search lib/surge_web/router.ex for the matching host: scope (hq., embed., api.). Match the user's page description to a live, get, post, patch, or delete route.
Then read the target LiveView or controller to learn path segments and query params:
- LiveViews:
mount/3,handle_params/3 - Controllers: action function + OpenAPI spec entry
For embed routes, query params are route-specific - read mount/3 and handle_params/3 on that LiveView (and EmbedAuth for auth params) to see what that route accepts. Do not assume params from one embed page apply to another. See Embed for common routes and auth rules.
References (use as needed, not all every time):
lib/surge_web/router.ex- route paths per interfacelib/surge_web/embed_auth.ex- embed auth query paramslib/surge_web/api_auth.ex- API account scopinglib/surge_web/api_spec.ex- API request/response shapestest/surge_web/embed_auth_test.exs- embed URL examples with query strings
3. Determine requirements
Assume auth is already handled unless a specific credential belongs in the URL itself.
| Interface | Auth |
|---|---|
| hq | Session cookie from browser login |
| embed | token query param; user_id when token is atok_* |
| api | Authorization: Bearer $LOCAL_BEARER_TOKEN header |
For API requests, use the LOCAL_BEARER_TOKEN environment variable:
curl -H "Authorization: Bearer $LOCAL_BEARER_TOKEN" https://api.jason.surge-eng.com/accounts
If an API call returns 401 or 403, do not try other auth workarounds - ask the user to refresh LOCAL_BEARER_TOKEN.
Assume these values by default, unless the user says otherwise:
account_id-GET /accountsand match name "Artisan Pastries & Co"user_id- first user on the account (GET /accounts/:account_id/users), or "Default user id" on HQ account show (/accounts/:account_id)phone_number_id- account default phone number when the route allows omitting it; otherwiseGET /accounts/:account_id/phone_numbersplatform_id- from the logged-in user's platform, or from an account'splatform_id- Resource IDs in URLs (
pn_*,call_*,conv_*, etc.) - from API responses or by navigating HQ in the browser
For embed token, see Embed.
4. Gather missing values
API first using LOCAL_BEARER_TOKEN (run in parallel when independent):
GET /accounts- list accountsGET /accounts/:account_id/phone_numbers- phone number IDs (pn_*)GET /accounts/:account_id/users- user IDs
Browser via dev skill when API cannot supply what you need:
- Skip starting dev if port 4000 is already listening
- Log in with
playwright-cli(devskill credentials; headless is fine for lookups) - Navigate to the HQ page that shows the value (phone numbers list for
pn_*, etc.) - Read IDs from the page URL, visible text, or
playwright-cli eval - Clean up
.playwright-clibefore reporting back
For embed token lookup, see Publishable token lookup.
5. Report to the user
If anything is still unknown, output two bullet lists and wait for answers:
Needed - required fields you cannot derive.
Optional - query params or path segments the route accepts but has defaults.
Skip both lists when you already have everything.
6. Output the URL
Put the final URL in a code block. For example:
https://embed.jason.surge-eng.com/phone?token=atok_live_...&user_id=usr_...
End with a brief summary of what you used and what you looked up. Even when the URL is complete, note any optional params you omitted.
Embed
Embed routes use EmbedAuth with :mount_structure. Auth query params are shared; everything else is route-specific.
Defaults
Unless the user says otherwise:
token(atok_*) - always from HQ account show; no API endpoint
user_id and phone_number_id use the same defaults as other interfaces (see step 3). On embed routes they appear as query params; on API and HQ routes they may be path or body params instead.
Auth
token- always requireduser_id- required whentokenisatok_*; optional for JWT tokens (user is embedded in the JWT)
Route aliases
| User might say | Path |
|---|---|
| phone SDK, softphone, dialpad, phone call page | /phone |
| inbox, conversations list | /conversations |
| single conversation (inbox UI) | /conversations/:id |
| single conversation (standalone) | /conversation/:id or /conversation/new |
| unread badge count | /unread_count |
Always confirm params in the target LiveView - aliases are hints, not specs.
Publishable token lookup
On https://hq.jason.surge-eng.com/accounts/:account_id, the token is blurred by default:
playwright-cli eval "document.querySelector('#account-token')?.textContent?.trim()"
Known setup gates
Some embed pages load but show a not-configured or error state when account setup is incomplete. Check mount/3 on the LiveView and mention this in the summary. Example: /phone requires twiml_app_sid and a call-capable phone number.