# Verification & subscription > Resend the verification email, resubscribe a contact you unsubscribed or imported as not subscribed, or unsubscribe one. The email-consent lifecycle, from your own backend. Source: https://getlaunchlist.com/help/docs/api/contacts-subscription --- The email-consent lifecycle, over HTTP. These are the same state changes your recipients trigger from a verification link or a one-click unsubscribe. Here you drive them from your own backend, for a preferences page or a CRM sync. ## Resend verification ``` POST /v1/contacts/{contact}/resend_verification ``` Requires `contacts:write`. Resends the verification email. Only meaningful while the contact is **waiting for verification**: they were asked to verify their email and have not yet (`verification: waiting` on the contact). A contact who has already verified, or who was never asked to, returns `422 contact_not_pending`. ```json { "object": "verification_resend", "contact": "con_4mKp9Wq2Rx", "sent": true, "reason": "sent" } ``` `sent` is honest: several guards can decline a send, and when one does you get `sent: false` rather than a claim that mail went out. `reason` names which one: | `reason` | What happened | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `sent` | A fresh link was minted and the mail was queued. | | `cooldown` | A verification email went to this contact moments ago, so this one was skipped. | | `mail_cap` | The address is over its hourly budget for verification mail across all projects. | | `undeliverable` | The address is known to bounce, or is a disposable inbox. Named once you have checked the address. | | `unavailable` | The address cannot be mailed. Check the contact's email in LaunchList to see why. | | `suppressed` | The address is on this project's do-not-mail list (a hard bounce, a complaint, or a block you set). | | `already_verified` | The contact had already verified. In practice this endpoint answers `422 contact_not_pending` before it gets here, so you will not normally see this reason. | | `no_email_channel` | The contact has no email identity to verify. | ## Unsubscribe ``` POST /v1/contacts/{contact}/unsubscribe ``` Requires `contacts:write`. Moves the contact out of your mailing audience: `status` becomes `unsubscribed`, and the campaign [send gate](/help/docs/email/broadcasts) skips them from then on. > [!NOTE] Unsubscribe stops updates, not every email > It stops your broadcasts and automations to that contact. Emails about their own spot, like an > invitation, still go. A spam complaint or a hard bounce stops every email. > [!NOTE] Unsubscribe is consent, not deletion > The contact, their queue position and their referral graph all stay. Only their mailing consent > changes. To remove or erase someone entirely, use [delete](/help/docs/api/contacts-delete). Returns the full [contact object](/help/docs/api/contacts-retrieve) in its new state. ## Resubscribe ``` POST /v1/contacts/{contact}/resubscribe ``` Requires `contacts:write`. Subscribes one contact so your broadcasts and automations can reach them, and returns the contact object plus an `outcome`. Use it for someone you unsubscribed, or an imported contact you added as not subscribed (`status: pending`). You do not need it for new signups: everyone who joins is subscribed already. > [!NOTE] Calling it is your statement that they asked to hear from you > It takes no body: the call itself is the attestation, the same one the dashboard asks for with a > **They asked to hear from me** checkbox. It is recorded on the contact: `consent` becomes `owner`, with `consented_at`, and `consented_by` > names the teammate who created the API key (empty if that teammate has since been removed). Only > call it for people who agreed to hear from you, for example by replying to an email. Subscribing > people who did not ask is the fastest way to earn spam complaints. What happens, by who the contact is: | The contact is | Result | | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | | Not subscribed (`pending`) | Subscribed. `outcome: subscribed`. | | Unsubscribed by you (the API, a bulk action, an import) | The unsubscribe is lifted and they are subscribed. `outcome: subscribed`. | | Already subscribed | Left as they are. `200` with `outcome: already`. | | Unsubscribed by themselves, or marked an email as spam | Refused with `409 contact_unsubscribed_themselves`. Only they can rejoin. | | Bounced, blocked, with no email address, or on a bounce or block list | Refused with `422 contact_cannot_be_emailed`. | | Held for review as a suspected fake signup | Refused with `409 contact_held`. Approve it in the dashboard first. | It sends no email, so nobody is welcomed or asked to verify. Lifting an unsubscribe sends the `email.resubscribed` webhook; subscribing a contact you imported as not subscribed sends none. It never undoes a person's own unsubscribe: a backend cannot quietly re-subscribe someone who left by clicking the link in an email. ```json { "object": "contact", "id": "con_4mKp9Wq2Rx", "status": "subscribed", "consent": "owner", "consented_at": "2026-10-27T09:30:00+00:00", "outcome": "subscribed" } ``` It is safe to retry, and honors `Idempotency-Key` like the other write calls. ## Who ended a subscription `unsubscribed_by` on the contact says who ended it, so you know whether you can undo it: | `unsubscribed_by` | Meaning | Can you resubscribe them? | | ----------------- | -------------------------------------------------------------------- | ----------------------- | | `owner` | You or a teammate: the API, the dashboard, an import or an agent. | Yes | | `contact` | They used the unsubscribe link, or their mail app's unsubscribe button. | No | | `complaint` | They marked an email as spam. | No | If you unsubscribed someone and they later unsubscribe themselves, it becomes theirs (`unsubscribed_by` changes to `contact`), and you can no longer undo it. A spam complaint outranks both. ## Next - [Broadcasts](/help/docs/email/broadcasts) for the send gate these states feed, and the Who in this audience choice - [Update a contact](/help/docs/api/contacts-update) for stage, attributes and your own id - [Delete a contact](/help/docs/api/contacts-delete) to remove or erase, when consent is not the point ## Common questions ### Does unsubscribing delete the contact? No. It moves them out of your mailing audience, so their status becomes unsubscribed and the send gate skips them, but the record, its position and its referral graph all stay. If you unsubscribed them, resubscribe puts them back. If they unsubscribed themselves, only they can come back. ### Do I need to subscribe new signups? No. Everyone who signs up through a form or POST /v1/contacts is subscribed when they join. Verifying their email is a separate fact, reported as verification on the contact. Resubscribe is for someone you unsubscribed, or an imported contact you added as not subscribed. ### Can I resubscribe someone who unsubscribed themselves? No. If a person left from the link in one of your emails, or marked an email as spam, only they can rejoin, by signing up again and verifying their email. Resubscribe refuses with a 409 and tells you so. ### Why did resend verification return sent false? A per-contact cooldown suppressed it, so you did not re-send the same mail seconds after the last one. It is reported honestly rather than pretending a mail went out.