Vicunia: from user feedback to shipped change
Last updated October 28, 2026
Vicunia turns a request into a pull request and ships it. Connected to Camelus, a theme your users asked for can go straight to Vicunia, and when the change is deployed, the people who asked hear about it.
Set it up
- In Vicunia, open the project, add a source with public trust (theme text comes from your end users, so Vicunia always asks for approval and never auto-merges it), and copy its key and signing secret (Show secret).
- In Camelus, go to Settings → Destinations → Add destination → Vicunia:
- URL: Vicunia's requests endpoint,
https://actions.vicunia.si/api/requests. A self-hosted Vicunia serves it on its HTTP actions origin (CONVEX_SITE_ORIGIN), not on the API origin. - Auth header value:
Bearer <the source key>. - Vicunia signing secret: the source's signing secret.
- URL: Vicunia's requests endpoint,
- Camelus shows a callback URL once the destination is created:
https://api.camelus.si/v1/callbacks/vicunia/<id>. Paste it into the Vicunia source's callback URL.
What happens
- Send to… → Vicunia on a theme sends one Vicunia request: the theme's title, its summary, up to 5 representative summaries and the counts, with
external_id: camelus_theme_<theme id>_<n>. Each send has its own number, so each gets its own Vicunia job. No requester emails are sent; Camelus emails requesters itself. - Vicunia reports every job change to the callback URL, signed with the source's secret. Camelus maps it:
| Vicunia | Camelus |
|---|---|
queued, running, pr_open, awaiting_merge_approval, merged |
Theme → In progress |
deployed |
Theme → Shipped, with the deploy URL, and a "shipped" email drafted for requesters |
rejected |
A note, and the theme page asks whether to decline the theme |
failed, deploy_failed, blocked_by_policy |
A note on the theme |
pending_approval |
Nothing yet |
The pull request link and deploy URL show under Linked work on the theme.
Locally
Both apps refuse private addresses by default. To connect two local stacks, set ALLOW_PRIVATE_DESTINATIONS=true in Camelus's .env and ALLOW_PRIVATE_CALLBACKS=true in Vicunia's (both only work when the site URL is local), and use http://host.docker.internal:<port> addresses between the containers.