Verify before parsing or processing
Photon uses Standard Webhooks headers:webhook-idwebhook-timestampwebhook-signature
whsec_… signing secret with a maintained verifier. Preserve the original request bytes. Do not put a JSON-parsing middleware before signature verification or verify JSON.stringify(parsedBody).
Route by installation
App deliveries includeapp_installation_id. Message events also include the organization and project under data, and the public message under data.object.
A simplified envelope looks like this (additional event fields are omitted):
data.organizationId matches the organization bound to that installation. Check the message’s project ID agrees with the envelope. Resolve your own customer account from this stored association. Never select a customer using a query parameter, an unverified request body, or a global “current installation.”
Events can arrive while installation setup is finishing. A production receiver should durably quarantine valid deliveries for installations whose binding is still pending and resolve them before processing. Do not forward them to an arbitrary default customer.
Acknowledge, deduplicate, and retry
Use(app_installation_id, event.id) as a persistent deduplication key. Return 2xx after durable acceptance; process slower business work asynchronously. Make the deduplication record and enqueue operation atomic so a crash cannot mark an unqueued event as complete.
Expect retries and out-of-order delivery. Repeated delivery of one event must not cause repeated side effects, but different event IDs can describe successive states of the same message. Do not assume every failure can be safely retried: distinguish known rejection from an unknown outcome of an external operation.
For transport-level delivery behavior, see Webhook delivery. App delivery targets are managed by Photon from App subscriptions and installations; do not create a second ordinary project webhook just to receive App events.
Send a reply through the API
Returning text in the webhook response does not send a message. To reply: The current message-creation API requiresproject:write; the destination project must also be in the installation’s scope. Resolve the sender and conversation from a validated incoming message or the corresponding Photon API; do not use arbitrary browser-supplied identifiers without checking tenant ownership.