I have worked around this problem by using the Make an API call
Google Contacts module and now I can tell you what the problem is with Make’s code, or at least with the requests that they are sending behind the scenes.
The People API’s updateContact method documentation is pretty clear and so are my tests : you have to include an etag
in the request body (not HTTP headers, request body) or the request will fail with error
[400] Request must set person.etag or person.metadata.sources.etag for the source that is being updated.
Which tells me that they always include the etag
in the Update Contact
request, but somehow they are getting the wrong one. It is not clear how and how it might or might not be related to the browser’s cache, but you have to use the Make an API call
to be able to set the right etag
and the request to always succeed (minus the occasional 500 error as this entire API seems pretty wonky).
Here is an example working payload, with the required etag
field :
{
"etag": "%EiQBAgMFBgcICQoLD12341234BUWGR8hIiMkJSYnLjQ1Nz0+P0AaBAECBQciDG1jZ2g4UVM2cUhzPQ==",
"names": [
{
"givenName": "Blablabla SOMENAME",
"familyName": ""
}
],
"genders": [
{
"value": "male",
"addressMeAs": "he/him"
}
],
"occupations": [
{
"value": "Tayste Occupation"
}
],
"emailAddresses": [
{
"value": "blablabla@bla.com",
"type": "work",
"displayName": "Tayste Display Name"
}
],
"addresses": [
{
"streetAddress": "123 Some address 12345 SOMECITY", "country": "Italy",
"type": "work"
}
],
"organizations": [
{
"name": "Blablabla"
}
],
"urls": [
{
"value": "https://www.blablabla.it/en/",
"type": "work"
},
{
"value": "https://airtable.com/blablabla",
"type": "profile"
}
],
"userDefined": [
{
"key": "Gender",
"value": " ♂️ "
}
]
}
Here is an example of my own Update contact
node using Make an API call
, as an example that you can copy in your scenario.
{
"subflows": [
{
"flow": [
{
"id": 111,
"module": "google-contacts:makeApiCall",
"version": 4,
"parameters": {
"__IMTCONN__": 592193
},
"mapper": {
"qs": [
{
"key": "updatePersonFields",
"value": "names,genders,occupations,phoneNumbers,emailAddresses,addresses,organizations,urls,biographies,userDefined"
}
],
"url": "/v1/people/{{43.id}}:updateContact",
"body": "{\n \"etag\": \"{{43.etag}}\",\n \"names\": [\n {\n \"givenName\": \"{{1.`First name`}}\",\n \"familyName\": \"{{1.`Last name`}}\"\n }\n ],\n \"genders\": [\n {\n{{switch(1.Gender; \" ♂️ \"; \"\n \"\"value\"\": \"\"male\"\",\n \"\"addressMeAs\"\": \"\"he/him\"\"\n\"; \" ♀️ \"; \"\n \"\"value\"\": \"\"female\"\",\n \"\"addressMeAs\"\": \"\"she/her\"\"\n\"; \"LGBTQIA+\"; \"\n \"\"value\"\": \"\"N/A\"\",\n \"\"addressMeAs\"\": \"\"they/them\"\"\n\"; \"\n \"\"value\"\": \"\"unspecified\"\",\n \"\"addressMeAs\"\": \"\"they/them\"\"\n\")}} \n }\n ],\n \"occupations\": [\n {\n \"value\": \"Tayste Occupation\"\n }\n ]{{if(1.Mobile | 1.Phone; \",\n \"\"phoneNumbers\"\": [\n\" + if(1.Mobile; \"\n {\n \"\"value\"\": \"\"\" + 1.Mobile + \"\"\",\n \"\"type\"\": \"\"mobile\"\"\n }\" + if(1.Mobile & 1.Phone; \",\")) + if(1.Phone; \"\n {\n \"\"value\"\": \"\"\" + 1.Phone + \"\"\",\n \"\"type\"\": \"\"work\"\"\n }\n\") + \"\n ]\")}}{{if(1.Email; \",\n \"\"emailAddresses\"\": [\n {\n \"\"value\"\": \"\"\" + 1.Email + \"\"\",\n \"\"type\"\": \"\"work\"\",\n \"\"displayName\"\": \"\"Tayste Display Name\"\"\n }\n ]\")}}{{if(6.Address | 6.Country; \",\n \"\"addresses\"\": [\n {\n\" + if(6.Address; \" \"\"streetAddress\"\": \"\"\" + 6.Address + \"\"\",\") + if(6.Country; \" \"\"country\"\": \"\"\" + 6.Country + \"\"\",\") + \"\n \"\"type\"\": \"\"work\"\"\n }\n ]\")}},\n \"organizations\": [\n {\n \"name\": \"{{6.Name}}\"{{if(1.`Job title` + \",\n \"\"title\"\": \"\"\" + 1.`Job title` + \"\"\"\")}}\n }\n ],\n \"urls\": [\n{{if(6.Website; \" {\n \"\"value\"\": \"\"\" + 6.Website + \"\"\",\n \"\"type\"\": \"\"work\"\"\n },\")}}{{if(1.Linkedin; \"\n {\n \"\"value\"\": \"\"\" + 1.Linkedin + \"\"\",\n \"\"type\"\": \"\"linkedin\"\"\n },\")}}\n {\n \"value\": \"https://airtable.com/appNlv21RrpAGmsyV/tbl8dODvN066m2wIc/viwMZB2q25fAGka9v/{{1.id}}\",\n \"type\": \"profile\"\n }\n ]{{if(1.Notes; \",\n \"\"biographies\"\": [\n {\n \"\"value\"\": \"\"\" + 1.Notes + \"\"\",\n \"\"contentType\"\": \"\"TEXT_PLAIN\"\"\n }\n ]\")}}{{if(1.Gender; \",\n \"\"userDefined\"\": [\n {\n \"\"key\"\": \"\"Gender\"\",\n \"\"value\"\": \"\"\" + 1.Gender + \"\"\"\n }\n ]\")}}\n}",
"method": "PATCH",
"headers": [
{
"key": "Content-Type",
"value": "application/json"
}
]
},
"metadata": {
"designer": {
"x": 1200,
"y": 300,
"name": "Update Contact"
},
"restore": {
"expect": {
"qs": {
"mode": "chose",
"items": [
null
]
},
"method": {
"mode": "chose",
"label": "PATCH"
},
"headers": {
"mode": "chose",
"items": [
null
]
}
},
"parameters": {
"__IMTCONN__": {
"data": {
"scoped": "true",
"connection": "google"
},
"label": "Flowrates Thomas Google"
}
}
},
"parameters": [
{
"name": "__IMTCONN__",
"type": "account:google",
"label": "Connection",
"required": true
}
],
"expect": [
{
"name": "url",
"type": "text",
"label": "URL",
"required": true
},
{
"name": "method",
"type": "select",
"label": "Method",
"required": true,
"validate": {
"enum": [
"GET",
"POST",
"PUT",
"PATCH",
"DELETE"
]
}
},
{
"name": "headers",
"spec": [
{
"name": "key",
"type": "text",
"label": "Key"
},
{
"name": "value",
"type": "text",
"label": "Value"
}
],
"type": "array",
"label": "Headers"
},
{
"name": "qs",
"spec": [
{
"name": "key",
"type": "text",
"label": "Key"
},
{
"name": "value",
"type": "text",
"label": "Value"
}
],
"type": "array",
"label": "Query String"
},
{
"name": "body",
"type": "any",
"label": "Body"
}
]
}
}
]
}
],
"metadata": {
"version": 1
}
}
Note that if you want to add a profile picture it will have to be in a separate request, although this one is much more simple.