Webhooks
Webhooks let Lychee call an external HTTP endpoint of yours whenever a photo is added, moved, or deleted, so another system can react to changes in the gallery without polling it.
They are off by default. Set WEBHOOK_ENABLED=true in the .env file to make the Webhooks entry appear in the admin panel; without it the page and its endpoints stay hidden. Only administrators can see or manage webhooks.
Events
Section titled “Events”| Event | Fires when |
|---|---|
photo.add | A photo is added to the gallery |
photo.move | A photo is moved to another album |
photo.delete | A photo is deleted |
One webhook subscribes to exactly one event. Subscribe to several events by creating several webhooks.
Configuring a webhook
Section titled “Configuring a webhook”Each webhook carries:
- a name, for your own reference;
- the event it listens to;
- the HTTP method:
GET,POST,PUT,PATCH, orDELETE; - the target URL;
- the payload format:
jsonorquery_string; - an enabled switch, so a webhook can be parked without being deleted;
- an optional secret and the header it is sent in, defaulting to
X-Webhook-Secret.
Every request is sent with User-Agent: Lychee/Webhooks and an X-Lychee-Event header naming the event.
What is sent
Section titled “What is sent”The payload is opt-in field by field, so you only ship what the receiving end actually needs:
| Field | Contents |
|---|---|
photo_id | The photo’s identifier |
album_id | The identifier of the album it belongs to |
title | The photo’s title |
size_variants | A list of {type, url} entries for the photo’s generated variants |
Fields you did not select are omitted from the payload entirely rather than sent as null. size_variants can additionally be narrowed to a chosen subset of variant types; leaving the selection empty sends them all.
With the json format the payload is sent as a JSON request body, whatever the HTTP method. With query_string it goes into the URL’s query parameters instead, and the size-variant URLs are base64-encoded so they survive the encoding.
Delivery behaviour
Section titled “Delivery behaviour”- Requests are dispatched asynchronously, as queued jobs, so a slow endpoint never delays an upload. A queue worker has to be running for them to actually go out.
- There is no automatic retry: each webhook is attempted once. A non-2xx response or a transport error is logged at
ERRORlevel and discarded; a success is logged atDEBUGlevel. WEBHOOK_TIMEOUT_SECONDS(default10) caps how long Lychee waits for the endpoint to answer.
Because failures are dropped rather than retried, treat webhooks as best-effort notifications, not as a guaranteed delivery channel.
Testing
Section titled “Testing”php artisan lychee:webhook-test <webhook-id>sends a test request to the configured endpoint using the same dispatch path as a real event, and tells you to check storage/logs/laravel.log for the outcome. The command refuses to run while WEBHOOK_ENABLED is not set.
Settings
Section titled “Settings”| Variable | Description | Default |
|---|---|---|
WEBHOOK_ENABLED | Master toggle for the whole feature. | false |
WEBHOOK_TIMEOUT_SECONDS | Seconds to wait for the endpoint before giving up. | 10 |
Both are read at boot, so a Docker deployment needs a container restart after changing them.
