examples/apps. It needs Node.js 24 and uses the public jose and standardwebhooks packages. It does not depend on Convex or a Photon private package.
1. Run an HTTPS tunnel
Forward an HTTPS URL to your local port 8787. For example, with ngrok installed and configured:PUBLIC_ORIGIN.
2. Register a test App
Follow Register an App, using:
Save the App ID, key ID, downloaded PEM, and revealed webhook signing secret.
3. Configure and start your backend
From your checkout of the docs repository:.env with your own editor. Do not paste its contents into a public issue or screen recording.
The example requires HTTPS configuration. Use the target environment’s hosted Dashboard for this walkthrough. Its
.env.example intentionally contains placeholders so it cannot accidentally connect to production.
http://localhost:8787. The callback uses a secure session cookie bound to that public host.
4. Install as a customer
- Click Connect to Photon on the example homepage.
- In Photon Dashboard, sign in, select your test organization, review permissions, and choose project access.
- Approve. Photon redirects to
/photon/callbackon your example server. - The backend validates state, redeems the code, stores the credentials in memory, obtains a token, and calls
/v1/organizations/current. - The browser displays the installation ID, organization ID, and received-event count. It never receives client secrets or tokens.
/connection retries that lookup using the already-stored credentials without redeeming again.
5. Receive an event
Use a messaging channel already connected to one of the installation’s selected Photon projects. Send a test message into that channel. You do not install the App separately inside each project. Photon sendsmessage.received to /photon/webhooks. The backend verifies the signature, matches the installation and organization, checks the message project, and increments the count once per event. Open /connection to check the count; this deliberately simple example does not use polling or a WebSocket UI.
To add replies, follow Send a reply through the API, request project:write, and have an existing installation approve the permission update. The example never sends messages automatically.
Before adapting this into your product
- Replace the demo browser session with your product’s authenticated customer session; enforce tenant ownership on every installation and operation.
- Persist installation credentials encrypted at rest and save them before further setup calls. Keep App-level keys in a secret manager.
- Replace the in-memory event set and counter with durable, atomic deduplication and a processing queue. Handle valid events that arrive before installation binding completes.
- Validate every subscribed event’s schema and implement retries and lifecycle changes. Bound request size, sessions, queue growth, and rate limits for your service.
- Implement a user-visible disconnected/recovery flow. Restarting a production server must not require users to reinstall.
- Use a stable HTTPS domain. Remove tunnel and in-memory-demo assumptions.