Developing locally
You can build and debug your app on your own computer while launching it from your organization's synthetic sandbox. In most cases you do not need a tunnel.
Why loopback URIs work
In an EHR launch, Ava EMR never calls your app server directly. It sends the clinician's browser to your launch URI, and later redirects that same browser to your redirect URI. Your app then calls the token endpoint and the FHIR API itself — outbound requests from your machine.
So if the browser you use to sign in to the sandbox EMR runs on the same computer as your app, a loopback address reaches your app with no inbound network access.
Sandbox configurations accept plain HTTP only for loopback IP literals:
| Accepted | Rejected |
|---|---|
http://127.0.0.1:3000/launch |
http://localhost:3000/launch |
http://[::1]:3000/callback |
http://192.168.1.20:3000/launch |
Production configurations always require HTTPS.
Set it up
- Run your app locally on a fixed port, for example
http://127.0.0.1:3000. Serve a launch route (such as/launch) and a redirect route (such as/callback). - Point your sandbox configuration at it. On your app's page, open
Configuration for the sandbox environment and set:
- Launch URI:
http://127.0.0.1:3000/launch - Redirect URI:
http://127.0.0.1:3000/callback
- Launch URI:
- Redeploy. Press Update sandbox deployment and wait for the sandbox registration card to show your public client ID. Configuration changes do not reach Ava EMR until you redeploy.
- Launch from a chart. Sign in to the sandbox EMR in a browser on the
same machine, open a synthetic patient, and press your app's launch
button. Your local app receives
issandlaunchand continues the flow described in App launch & PKCE.
The hosted test app is a separate sandbox client. It appears in the patient chart as Your App (Ava test page) and never changes your own sandbox URIs, so it keeps working while you develop locally or point your sandbox at a QA environment.
Start from the example client
The repository includes a runnable single-file client in
examples/smart-test-app/. It listens on http://127.0.0.1:4567, performs
every step of the launch, and prints each intermediate result:
CLIENT_ID=<public client id from the portal> ruby app.rb
Set the sandbox launch URI to http://127.0.0.1:4567/launch and the redirect
URI to http://127.0.0.1:4567/callback, then redeploy.
When you need a tunnel
Use an HTTPS tunnel such as ngrok or Cloudflare Tunnel when:
- you launch from a different device than the one running your app (a tablet, a teammate's laptop, a phone),
- your app or a library you depend on requires HTTPS (secure cookies, some browser APIs), or
- you want the sandbox configuration to look like production.
With ngrok, for example:
ngrok http 3000
Register the https:// forwarding URL it prints — for example
https://example-123.ngrok-free.app/launch and .../callback — as your
sandbox launch and redirect URIs, then redeploy.
Keep in mind:
- Redirect URIs must match exactly. Free tunnel URLs usually change on every restart, so update your sandbox configuration and redeploy each time, or reserve a stable domain with your tunnel provider.
- A tunnel makes your local app reachable from the internet. Only expose it while you are testing, and never use one with production data.
Troubleshooting
| Symptom | Fix |
|---|---|
| Launch opens a page that cannot be reached | Your app isn't running, is on a different port, or you're launching from another device — use a tunnel. |
localhost URI rejected when saving |
Use 127.0.0.1 (or [::1]) instead. |
| Redirect URI mismatch at authorization | The redirect_uri your app sends must match the registered URI byte for byte, including port and path. |
| Changes have no effect | Press Update sandbox deployment after editing the configuration. |
See Errors & troubleshooting for token and FHIR errors.