All GoTo setup for a tenant is handled by a single artisan command (app/Console/Commands/Telephony/GoToSetup.php) run locally by a Curitics admin. The setup flow itself — including the OAuth callback — is handled entirely within the command via an embedded TCP socket listener. No Laravel routes or controllers are required during setup. (The webhook route and controller are separate and exist for the ongoing call event integration, not the setup.)
How environments are handled: The command is always run locally, pointed at the target environment's database. Once run against production, the tenant's GoTo config (tokens, channel ID, account key) lives in the production DB and flows down to UAT and local via the standard DB backfill — those environments never need to run goto:setup independently.
php artisan goto:setup {tenant_id}-
Starts a temporary TCP socket listener on a random local port. This acts as the OAuth
redirect_uri(http://127.0.0.1:{port}/callback) — GoTo redirects the browser back to it after the admin authorizes. No Laravel route is involved; the command handles the callback entirely within itself. -
Prints the authorization URL for the admin to open in a browser:
https://authentication.logmeininc.com/oauth/authorize?response_type=code&client_id={client_id}&redirect_uri=http://127.0.0.1:{port}/callback&state={state}The admin visits this URL and logs in with the tenant's GoTo account credentials. GoTo then redirects the browser to the local socket listener. The command captures the authorization code from that HTTP request, sends a "you can close this tab" response, and immediately closes the listener — no polling loop, no pasting, no waiting.
-
Exchanges the code for tokens and saves them to the
Tenantrecord. -
Creates a webhook notification channel:
POST https://api.goto.com/notification-channel/v1/channels/{tenant_name}-screen-pop Authorization: Bearer {access_token} Body: { "channelType": "Webhook", "webhookChannelData": { "webhook": { "url": "{webhook_base_url}/goto/webhook" } } }Webhook channel lifetime is permanent (~68 years). Stores the
channelIdon theTenant.Local env — ngrok required: GoTo's Web Application Firewall (WAF) — a security layer on their API — rejects channel creation requests where the webhook URL body contains
localhostor127.0.0.1, returning a 403. Since GoTo's servers can't reach your local machine directly, you need a public tunnel. On alocalenvironment the command requires a running ngrok tunnel and errors out if none is detected. Start ngrok before running:brew install ngrok # install once ngrok http 8000 # gives you https://abc123.ngrok.io
The ngrok URL is detected automatically via
http://127.0.0.1:4040/api/tunnels. On non-local environments,APP_URLis used directly (it's a real public URL).Free tier caveat: The ngrok URL changes on every restart, requiring
goto:setupto be re-run to update the webhook channel URL. A paid ngrok account with a reserved domain avoids this. -
Subscribes to call events:
POST https://api.goto.com/call-events/v1/subscriptions Authorization: Bearer {access_token} Body: { "channelId": "{goto_channel_id}", "accountKeys": [ { "id": "{goto_account_key}", "events": ["STARTING", "ENDING"] } ] }Verifies the response has
accountKeys[0].status === 200(the status is nested per-account-key, not at the top level).