--- title: Sync unsubscribes to your database description: Mirror Plunk's subscription state into your own user table using a workflow + webhook icon: RefreshCw --- Every flip of a contact's `subscribed` state — manual edits, the hosted unsubscribe page, bounces, complaints — fires a `contact.unsubscribed` event. Wire a workflow with a `WEBHOOK` step to forward that to your backend. ## Setup import {Step, Steps} from 'fumadocs-ui/components/steps'; ### Build the receiving endpoint A public HTTPS endpoint that verifies a shared secret and updates the user row. Webhook requests time out after 10 seconds, so do the work async if it's slow. ```ts app.post('/plunk/unsubscribes', async (req, res) => { if (req.header('authorization') !== `Bearer ${process.env.PLUNK_WEBHOOK_SECRET}`) { return res.status(401).end(); } const { contact, event } = req.body; await db.user.update({ where: { email: contact.email }, data: { emailSubscribed: false, emailUnsubscribedReason: event.reason ?? 'user_action', }, }); res.status(204).end(); }); ``` `event.reason` is `"bounce"` or `"complaint"` for automatic unsubscribes, and absent for manual / self-service ones. ### Create the workflow **Workflows → New workflow**: - **Trigger**: `EVENT` on `contact.unsubscribed` - Add a `WEBHOOK` step: - **URL**: `https://api.example.com/plunk/unsubscribes` - **Headers**: `{ "Authorization": "Bearer your-shared-secret" }` - Leave the body blank to get the [default payload](/guides/webhooks#webhook-payload). Enable the workflow. ## Mirroring resubscribes Build a second workflow with the same shape, triggered by `contact.subscribed`. Keep it separate from the unsubscribe flow — two short workflows are easier to monitor than one branched one. ## The reverse direction If your product is the source of truth (a user toggles their email preference in your settings UI), call `PATCH /contacts/:id` from your backend: ```bash curl -X PATCH https://next-api.useplunk.com/contacts/cnt_abc \ -H "Authorization: Bearer sk_your_secret_key" \ -d '{"subscribed": false}' ``` That flip also fires `contact.unsubscribed`, meaning your own webhook will round-trip back into your handler. That's usually harmless because the update is idempotent — but be aware of it. ## What's next Webhook step reference, payload shape, and safety. The hosted pages and template URL variables.