Skip to main content
Jira progress: loading…

EWA-HS

Marketing Opt-in → HubSpot — Backend Spec

Overview

When a user registers on the Academy and ticks "Send me updates about new courses, ESG regulatory changes, and EcoWorld Academy news", this consent must be synced to HubSpot as a GDPR-compliant email subscription.

Frontend (already done)

The registration form sends marketingOptIn: true | false in the RegistrationRequest payload to POST /register.

Backend — Registration API Pipeline

When the Registration API creates or updates a HubSpot contact (which already happens in the registration pipeline), it should additionally set the marketing subscription status.

Step 1: Create/update HubSpot contact

Endpoint: POST https://api.hubapi.com/crm/v3/objects/contacts (or PATCH .../contacts/{contactId} if the contact already exists)

Properties to set:

{
"email": "jane@company.com",
"firstname": "Jane",
"lastname": "Pedersen",
"company": "Acme Corp",
"jobtitle": "Procurement Manager",
"country": "Norway",
"eco_number": "ECO-578-123-456-789"
}

eco_number is a custom HubSpot property — create it in HubSpot Settings → Properties → Contact properties as a Single-line text field.

Step 2: Set email subscription status (GDPR-compliant)

Endpoint: POST https://api.hubapi.com/communication-preferences/v3/subscribe

Payload:

{
"emailAddress": "jane@company.com",
"subscriptionId": "<YOUR_SUBSCRIPTION_TYPE_ID>",
"legalBasis": "CONSENT_WITH_NOTICE",
"legalBasisExplanation": "User opted in during EcoWorld Academy course registration at academy.ecoworld.ai"
}

Only call this if marketingOptIn === true. If false, don't subscribe — don't unsubscribe either (they may already be subscribed via another channel).

Step 3: Find your Subscription Type ID

  1. In HubSpot, go to Settings → Marketing → Email → Subscription types
  2. Either use an existing subscription type (e.g. "Marketing Information") or create a new one:
    • Name: EcoWorld Academy Updates
    • Description: "New courses, ESG regulatory changes, and EcoWorld Academy news"
  3. Get the subscription type ID from the URL or via the API:
GET https://api.hubapi.com/communication-preferences/v3/definitions

The response will include all subscription types with their IDs.

For GDPR compliance, store the consent audit trail in your own database alongside the HubSpot sync:

INSERT INTO academy.consent_records (
user_id,
consent_type, -- 'marketing_email'
consented, -- true/false
consent_source, -- 'academy_registration'
consent_text, -- 'Send me updates about new courses, ESG regulatory changes, and EcoWorld Academy news'
ip_address,
user_agent,
consented_at -- timestamp
) VALUES (...);

Architecture Summary

Frontend                    Backend                           HubSpot
──────── ─────── ───────
POST /register 1. Create eco.entity
marketingOptIn: true 2. Generate E-C-O™ Number
email, name, etc. 3. Create academy.users
4. Create Cognito user
┌► 5. Create/update HubSpot contact ← Properties
│ 6. IF marketingOptIn:
│ Subscribe to email list ← Subscription
│ 7. Store consent record (local DB)
└► 8. Create Stripe session
9. Return checkoutUrl → redirect

API Permissions Required

The HubSpot private app token needs these scopes:

  • crm.objects.contacts.write — create/update contacts
  • communication_preferences.write — manage subscriptions
  • communication_preferences.read — list subscription types

Notes

  • Idempotent: If a returning user registers for another course, check if the contact already exists (by email) and update, don't duplicate.
  • Unsubscribe: Always handled through HubSpot's built-in unsubscribe links in emails. Never programmatically unsubscribe a user just because marketingOptIn is false on a new registration — they may have opted in before.
  • Double opt-in: HubSpot supports optional double opt-in. If enabled, the subscribe API call will trigger a confirmation email. Recommended for GDPR but adds friction. Configure in HubSpot Settings → Marketing → Email → Subscription types.



GitHub RepoRequest for Change (RFC)