Contacts are optional for transactional sending
Send a message never requires acontact_id — you can pass to directly for one-off transactional messages like password resets or receipts. Create contacts when you want Pulsewave to track subscription state and unsubscribes on your behalf, which matters most for marketing or digest-style sends to a list.
Subscription state
Every contact has asubscribed boolean. It starts true and flips to false when:
- The contact clicks an unsubscribe link in a message
- The contact marks a message as spam (a
message.complainedevent also fires) - You explicitly update the contact
contact.unsubscribed event so you can mirror the state in your own database.
Unsubscribes are channel-wide. There’s no separate opt-out for email vs. SMS on the same contact — if you need independent consent per channel, model that with
metadata and check it in your own application before sending.