Set up email double opt-in
Start a confirmed email opt-in from your server, email the confirmation link yourself, and add an unsubscribe link to your messages.
Double opt-in proves that an email address belongs to the person who typed it. The consent is recorded as pending, and takes effect only when the person follows a link sent to that address.
You send the email
PIEDAP Consent does not send email. It gives you a confirmation link, and your own mail system sends the message.
Before you start#
- A published form and its key.
- A secret key. A publishable key is refused with
403 EMAIL_CONSENT_SERVER_ONLY. - The
clientfrom Record consent from your server.
Start the opt-in and send the link#
-
On your page, post the address and the ticked boxes to your own server.
-
On your server, call
startEmailConsentwith an answer for every purpose that is not always on.Node.jsJavaScript const pending = await client.startEmailConsent({ email: 'rohan.das@example.in', formKey: 'newsletter', decisions: [{ purposeKey: 'newsletter', granted: true }], locale: 'en', returnUrl: 'https://www.example.in/subscribed', idempotencyKey: `optin-${signupId}`, });Python from piedap_consent import Decision pending = client.start_email_consent( "rohan.das@example.in", "newsletter", [Decision("newsletter", True)], locale="en", return_url="https://www.example.in/subscribed", idempotency_key=f"optin-{signup_id}", )cURLShell curl -X POST https://console.dpconsent.com/consent/sdk/v1/email-consent \ -H "Authorization: Bearer $PIEDAP_SECRET_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: optin-7c41" \ -d '{ "email": "rohan.das@example.in", "formKey": "newsletter", "decisions": [{ "purposeKey": "newsletter", "granted": true }], "locale": "en", "returnUrl": "https://www.example.in/subscribed" }' -
Email
verificationUrlfrom the response to the address, exactly as returned. In Python the field ispending.verification_url. -
Send nothing else while the record's status is
PENDING. Treat it as no consent. -
Wait for the
consent.updatedwebhook, or read the record withgetConsent, before you treat the consent as given.
The call answers 202 with the consentId, the status PENDING, the verificationUrl and expiresOn. The link works for 48 hours. To issue a new one, call startEmailConsent again.
What the person sees#
Opening the link changes nothing. It shows a page with a Confirm button, because mail scanners follow links. Pressing Confirm gives the record the status its answers lead to, usually GRANTED.
The person then lands on your returnUrl with ?consent=confirmed added, or ?consent=failed when the link had expired or was not valid.
Do not trust the query
Anyone can type ?consent=confirmed into the address bar. Use it to choose what the page says, not to decide whether consent was given.
A consent.granted webhook is sent when the pending record is created, with status: "PENDING". Check status before you act on a webhook.
Add an unsubscribe link#
Ask for an unsubscribe link for the consent record a message relies on. The call needs a secret key and no scope.
const { url, headers } = await client.unsubscribeLink(consentId);curl -X POST https://console.dpconsent.com/consent/sdk/v1/pages/unsubscribe-links \
-H "Authorization: Bearer $PIEDAP_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "consentId": "cns_91b7d0c4a2e64f0f8d3c5a7b9e1f2c60" }'- Add both
headers,List-UnsubscribeandList-Unsubscribe-Post, to the message as they are. Mail clients then show their own unsubscribe button. - Put
urlin the message body. Opened in a browser, it asks the person to confirm. - Either way the record becomes
WITHDRAWNand aconsent.withdrawnwebhook is sent. Stop sending when it arrives.
The link does not expire, and it works for any consent record. The Python package has no helper for this call, so post to the endpoint.
Cases to know#
The address has answered before
If the address already confirmed a consent on this form, the new request changes nothing yet. The earlier consent applies until the new link is followed. If a request is still pending, its answers are replaced and a new link is issued.
A browser gets EMAIL_NEEDS_DOUBLE_OPT_IN
A form of the type Email and marketing opt-in has Require double opt-in turned on by default. While it is on, a publishable key cannot record consent for an EMAIL identifier on that form. Start the opt-in from your server instead. A recordConsent call with a secret key is recorded at once, so use it only for addresses you have already confirmed.
The address was erased
Starting the opt-in again for an erased address creates a new record. The erased one is never reused.
The link is never followed
The record stays PENDING until the form's consent term ends. It then becomes EXPIRED and a consent.expired webhook is sent.