Skip to main content
The generic example in the Photon docs repository lives in 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.
This is a local teaching example for a disposable test installation. Credentials, sessions, and deduplication records live only in memory and disappear on restart. It acknowledges events after accepting them into memory, not durable storage. Do not use it for production traffic or customer data.

1. Run an HTTPS tunnel

Forward an HTTPS URL to your local port 8787. For example, with ngrok installed and configured:
Keep it running and copy the HTTPS forwarding origin. If the URL changes, update both App registration URLs and the server’s 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:
Edit .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.
Open the public HTTPS tunnel URL, not http://localhost:8787. The callback uses a secure session cookie bound to that public host.

4. Install as a customer

  1. Click Connect to Photon on the example homepage.
  2. In Photon Dashboard, sign in, select your test organization, review permissions, and choose project access.
  3. Approve. Photon redirects to /photon/callback on your example server.
  4. The backend validates state, redeems the code, stores the credentials in memory, obtains a token, and calls /v1/organizations/current.
  5. The browser displays the installation ID, organization ID, and received-event count. It never receives client secrets or tokens.
A failure after credential delivery must be handled using the redemption recovery rules. Do not repeatedly refresh the callback. If organization lookup alone failed, opening /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 sends message.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.
These are backend responsibilities. Customers installing a hosted App should not handle PEM files, signing secrets, or environment variables.

Troubleshooting