88 lines
2.6 KiB
Plaintext
88 lines
2.6 KiB
Plaintext
---
|
|
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';
|
|
|
|
<Steps>
|
|
|
|
<Step>
|
|
|
|
### 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.
|
|
|
|
</Step>
|
|
|
|
<Step>
|
|
|
|
### 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.
|
|
|
|
</Step>
|
|
|
|
</Steps>
|
|
|
|
## 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
|
|
|
|
<Cards>
|
|
<Card title="Webhooks" href="/guides/webhooks">
|
|
Webhook step reference, payload shape, and safety.
|
|
</Card>
|
|
<Card title="Unsubscribe pages" href="/guides/unsubscribe-pages">
|
|
The hosted pages and template URL variables.
|
|
</Card>
|
|
</Cards>
|