Failed to create subscription: LinkedIn Developer API real-time notification error
I’m working on enabling real-time notifications from LinkedIn. I can successfully retrieve access tokens, but when I try to create a real-time notification subscription, the API returns the following error. Could someone please help me understand what might be causing this issue?
Error Message { "message": "Failed to create subscription. RestException{_response=RestResponse[headers={Content-Length=13373, content-type=application/x-protobuf2; symbol-table="https://ltx1-app150250.prod.linkedin.com:3778/partner-entities-manager/resources|partner-entities-manager-war--60418946", x-restli-error-response=true, x-restli-protocol-version=2.0.0},cookies=[],status=400,entityLength=13373]}", "status": 400 }
My code is below
def linkedinCallBack(request):
"""Handle LinkedIn OAuth callback."""
code = request.GET.get('code')
state = request.GET.get('state')
if not code or not state:
return handle_redirect(request, message_key='missing_params')
try:
error, state_data = parse_state_json(state)
if error:
return handle_redirect(request, message_key='missing_params')
error, platform = get_social_platform(state_data['platform_id'])
if error:
return handle_redirect(request, message=error)
redirect_uri = request.build_absolute_uri(
reverse('social:linkedin_callback'))
# Exchange code for access token
token_url = 'https://www.linkedin.com/oauth/v2/accessToken'
data = {
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': redirect_uri,
'client_id': os.environ.get('LINKEDIN_APP_ID'),
'client_secret': os.environ.get('LINKEDIN_APP_SECRET'),
}
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
response = requests.post(token_url, data=data, headers=headers)
if response.status_code != 200:
return handle_redirect(request, message_key='token_failed')
token_data = response.json()
access_token = token_data.get('access_token', None)
refresh_token = token_data.get('refresh_token', None)
refresh_token_expires_in = token_data.get(
'refresh_token_expires_in', None)
expires_in = token_data.get('expires_in', 3600)
if not access_token:
return handle_redirect(request, message_key='token_failed')
LINKEDIN_API_VERSION = os.environ.get('LINKEDIN_API_VERSION')
org_url = "https://api.linkedin.com/v2/organizationalEntityAcls"
params = {
'q': 'roleAssignee',
'role': 'ADMINISTRATOR',
'state': 'APPROVED',
'projection': '(elements*(*,organizationalTarget~(id,localizedName)))'
}
headers = {
'Authorization': f'Bearer {access_token}',
'X-Restli-Protocol-Version': '2.0.0',
'LinkedIn-Version': LINKEDIN_API_VERSION
}
org_response = requests.get(org_url, headers=headers, params=params)
app_id = os.environ.get('LINKEDIN_APP_UID')
developer_urn = f'urn:li:developerApplication:{app_id}' # Update this
webhook_url = request.build_absolute_uri(
reverse('social:linkedin_wehbook'))
org_data = org_response.json()
page_names = []
for org in org_data.get("elements", []):
page_id = org.get("organizationalTarget~", {}).get("id")
page_name = org.get("organizationalTarget~",
{}).get("localizedName")
person_urn = org.get("roleAssignee")
role = org.get("role")
organization_urn = org.get("organizationalTarget")
platform_config = {
"role": role,
"roleAssignee": person_urn,
"organizationalTarget": organization_urn
}
subscription_key = (
f"(developerApplication:{urllib.parse.quote(developer_urn)},"
f"user:{urllib.parse.quote(person_urn)},"
f"entity:{urllib.parse.quote(organization_urn)},"
"eventType:ORGANIZATION_SOCIAL_ACTION_NOTIFICATIONS)"
)
subscription_url = f'https://api.linkedin.com/rest/eventSubscriptions/{subscription_key}'
subscription_data = json.dumps({'webhook': webhook_url})
headers['Content-Type'] = 'application/json'
response = requests.put(
subscription_url, headers=headers, data=subscription_data)
response_json = response.json()
if response.status_code not in (200, 201, 204):
return handle_redirect(request, message=f'Subscription failed for {page_name}: {response.text}')
error = save_social_account(
platform, page_id, page_name, access_token, expires_in, request.user,
refresh_token=refresh_token, rt_expires_at=refresh_token_expires_in,
platform_config=platform_config
)
if error is not None:
return handle_redirect(request, message=error)
page_names.append(page_name)
return handle_redirect(request, message=f"{platform.name} account(s) connected successfully. {response_json}", message_type='success')
except Exception as e:
return handle_redirect(request, message_key='error')
urls.py for webhook url
path('linkedin_wehbook/', views_webhooks.WebhookViewLinkedin.as_view(), name='linkedin_wehbook'),
views_webhooks.py
@method_decorator(csrf_exempt, name='dispatch')
class WebhookViewLinkedin(View):
def get(self, request, *args, **kwargs):
# Verification per docs
challenge = request.GET.get('challengeCode')
if challenge:
return JsonResponse({'challengeResponse': challenge})
return HttpResponseBadRequest('Invalid challenge')
def post(self, request, *args, **kwargs):
pass
Notes
- The developer urn is taken from the below url
- Is there a configuration option available for webhooks, similar to what Meta provides?
Reference
Organization Social Action Notifications