Job Story - Overview: High-level
New WME Dashboard (App) account sign ups should be added to CRM to aid with support and user experience. We'll only send requests of users that successfully verify their email and therefore gain access to the APIs and user dashboard login. 99% of the time the user will be new to the CRM and only one request to the CRM API will be needed. In the ~1% edge-case scenario we will 'patch' in the username via the CRM API along with a note to the CRM contact card; this takes two requests beyond the initial request.
Context Key:
- Dash || Dashboard || App == dashboard.enterprise.wikimedia.com
- HS || CRM || CRP API == Hubspot
- SAA == same as above
Sequencing - Overview: High-level
99% case:
Dashboard user signs up for WME, receives an email to verify, and with successful email verification a WME account is enabled, allowing login to Dashboard && access to free API.
- NEW WORK: Fire POST request to CRM API to create a contact card.
- NEW WORK: Catch response.
- NEW WORK: If 200, done.
1% case:
SAA with contingency. If CRM API returns 409 conflict when creating contact - user's email preexists in CRM:
- NEW WORK: Use payload data in response to fire new PATCH request to CRM API to update contact card.
- NEW WORK: Catch response.
- NEW WORK: Use response payload data to fire new POST request to CRM API to add a note to contact card.
- NEW WORK: Catch response.
- NEW WORK: If 200, done.
Job Story - Overview: Detail
Currently, when a new user signs up for WMEnterprise on the dashboard app, they receive an email with a verification code. When they successfully use that code to verify their email address on the dashboard app, their API & Dashboard login account is then active. This Phab ticket is requesting that when that successful verification event happens in the dashboard app, we fire an API request to add a contact to the HS CRM.
The only information being passed from our dashboard app to crm api are:
- email - email address the user verified (is UID (unique identifier) in CRM).
- username - username the user created.
There is other metadata used in the requests, but they are static with purpose to organize and/or add context to said contact in CRM. Instruction to follow.
There's a small chance a contingency happens, in which the UID (email) already exists in the CRM. In that case we have fallback requests to update that contact in the CRM with username and additional static metadata.
The CRM work has already been completed to accept requests and data:
- Custom app is setup and properly scoped in CRM.
- API Access Token is in Gitlab > dashboard repo > settings > ci/cd > DOTENV dv as VITE_HUBSPOT_API_AUTH var.
CRM API docs of note at bottom of this ticket for reference. But it's fairly standard Header Authorization: Bearer token with every request.
Job Story - Full Detail
When a Dashboard user account is verified; send a POST request to CRM API that creates contact card in CRM.
ENDPOINT: https://api.hubapi.com/crm/v3/objects/contacts
BODY: static metadata to stay EXACTLY as is below; variables from our Dashboard App are {{wme_user_email}} (the verified email of the user account), and {{wme_username}} (the username of the user account).
{
"properties": {
"email": "{{wme_user_email}}",
"username": "{{wme_username}}",
"hubspot_owner_id": "823648202",
"lifecyclestage": "lead",
"hs_lead_status": "NEW",
"lead_source_type": "WME Dashboard",
"team_ownership": "Enterprise"
}
}A successful contact card creation will return 200 Okay with a payload. For now we don't need to store anything from response.
DONE. EXIT().
Contingency Sequence:
An unsuccessful contact card creation might return 409 Conflict (See: HS error response codes for additional general fail potential; handle other errors as you normally would, i.e. retry and/or log errors, etc). If/when we get a 409 Conflict response, however, the response payload will look like this:
{
"status": "error",
"message": "Contact already exists. Existing ID: 91xxxxxx24",
"correlationId": "axxxxxxxx-e0b7-xxxx-xxxx-xxxxxxxxxxxx",
"category": "CONFLICT"
}In that case, where 409 && resp.category == "CONFLICT", we need to filter the resp.message string and store the integer from that "Existing ID: 91xxxxxx24" string for the next requests. That ID(int) is the ID of the pre-existing CRM contact card that uses the email as unique ID. We'll call this variable {{contactId}}.
We need to then fire a PATCH request to CRM API using that {{contactId}} variable in the endpoint.
ENDPOINT: https://api.hubapi.com/crm/v3/objects/contacts/{{contactId}} WHERE {{contactId}} === the ID(int) from previous response payload string.
BODY: static metadata to stay EXACTLY as is below; variables from our Dashboard App are {{wme_username}} (the username of the user account).
{
"properties": {
"username": "{{wme_username}}",
"team_ownership": "Enterprise"
}
}A successful contact card patch will return 200 Okay with a payload.
In that 200 payload we want to grab an item as a variable for the next request.
That field is: "updatedAt": "xxxx-xx-xxTxx:xx:xx.xxxZ". We could send a now() but I'd like to send the same datetime in the next request to keep parity with when the username was injected into the contact card. We'll call this variable {{updatedAt}}.
We need to then fire a POST request to CRM API that creates a note on the contact card explaining to CRM users why the contact card was updated.
ENDPOINT: https://api.hubapi.com/crm/v3/objects/notes
BODY: static metadata to stay EXACTLY as is below; variables are {{contactId}} (the contactId we used in the previous request), and {{updatedAt}} (the datetime from previous response.updatedAt payload field).
{
"properties": {
"hubspot_owner_id": "1245777529",
"hs_timestamp": "{{updatedAt}}",
"hs_note_body": "This contact created an account on Enterprise dashboard and already existed in Hubspot. Their Enterprise username has been added."
},
"associations": [
{
"to": {
"id": "{{contactId}}"
},
"types": [
{
"associationCategory": "HUBSPOT_DEFINED",
"associationTypeId": 202
}
]
}
]
}A successful contact card note creation will return 200 Okay with a payload. For now we don't need to store anything from response.
DONE. EXIT().
That's it. Don't deploy on friday. Thank you.
API Reference
- CRM Intro to Auth: https://developers.hubspot.com/docs/guides/apps/authentication/intro-to-auth#private-app-access-tokens
- CRM Objects Contacts: https://developers.hubspot.com/docs/reference/api/crm/objects/contacts#post-%2Fcrm%2Fv3%2Fobjects%2Fcontacts
- Create a Note: https://developers.hubspot.com/docs/guides/api/crm/engagements/notes#create-a-note
- CRM Error Codes: https://developers.hubspot.com/docs/reference/api/other-resources/error-handling
- Postman collection (if needed for dev testing): https://www.postman.com/hubspot/hubspot-public-api-workspace/overview