Compliance Events API

Important

Documentation Updates: We have made some structural changes to the left navigation to create a logical grouping of topics. Following are the changes:

  • The Integration section is now available under Compliance Snapshot Overview API.
  • Compliance Event API and Compliance Snapshot API are now separate sections containing the respective topics within it.

The Compliance Events API allows you to archive all LinkedIn activities from the past 30 days of a regulated, authenticated member. It returns each monitored activity as an individual event record to provide ease of archive and remediation if necessary.

Note

All use of the Microsoft Bing Maps location data is subject to Microsoft Bing Maps and MapPoint Web Service End User Terms of Use and Embedded Maps Service Terms of Use and the Microsoft Privacy Statement. By accessing any Microsoft Bing Maps location data, you are agreeing to be bound by these Microsoft terms.

Additional Decoration Activities

Usage

You must use an access token to make an authenticated call on behalf of a user.

Permissions

Permission Description
r_compliance Required to retrieve activities for compliance monitoring and archiving.

To use the following APIs, you will need to know your application ID. This can be found in your Developer Application Setting page. Select your application and go to the Mobile section to view your application ID. Alternatively, you can find the ID from the URL as well (ie. https://www.linkedin.com/developer/apps/{applicationID}/auth).

Compliance Authorization

To begin monitoring a member's activities, you will need to opt in the member into our Compliance system by calling the following API:

POST

https://api.linkedin.com/v2/memberComplianceAuthorizations

Note

You will need to supply an empty JSON object, {} to the request body to successfully opt the member into compliance.

Once the member has been opted in, all activities performed will be captured by the Compliance Events API . The API will not contain any preceding activities and will only capture activities performed following the authorization toggle. You can check the member's authorization status by requesting the following member FINDER API:

GET

https://api.linkedin.com/v2/memberComplianceAuthorizations?q=member&projection=(elements*(memberComplianceAuthorizationKey(developerApplication,member~(firstName,lastName,headline)),regulatedAt))

sample GET response

{
  "elements": [
    {
      "memberComplianceAuthorizationKey": {
        "developerApplication": "urn:li:developerApplication:123456",
        "member": "urn:li:person:123ABC",
        "member~": {
          "firstName": {...},
          "lastName": {...},
          "headline": {...}
        }
      },
      "regulatedAt": {
        "created": 1476375405722,
        "lastModified": 1476375405722
      }
    }
  ]
}

If the response contains your own developerApplicationUrn , then you are good to go!

Note

The sample request above is utilizing projection to decorate member with more information. This not needed as the member returned is always the one represented by the access token. However, this example represents decoration use if you choose to utilize it.

Compliance De-Authorization

If you wish to have LinkedIn stop monitoring a regulated member's activities i.e. opt out the member from our Compliance Platform, call the following and all subsequent member activities performed on LinkedIn will not be captured.

To allow developers to De-Authorize members for whom the access token might have expired, the following API can also be used with access tokens obtained via OAuth2.0 Client Credentials flow.

DELETE https://api.linkedin.com/v2/memberComplianceAuthorizations/developerApplication={developerApplicationURN}&member={personURN}

Note

A member will not opt out of Compliance authorization due to access token expiration. The only ways an opt out can happen are:

  • Application invokes the DELETE API mentioned above
  • The regulated member removes the application's authorization through LinkedIn's Privacy Setting page. This can be found under the Third Parties section.

Therefore, it is always a good idea to periodically check the member's authorization status using the GET API.

Compliance Events

After the member is authorized, you can start archiving the activities performed with the following request:

GET

https://api.linkedin.com/v2/complianceEvents?q=memberAndApplication&projection=(elements*(id,capturedAt,processedAt,configVersion,owner~(firstName,lastName,headline),actor~(firstName,lastName,headline),resourceName,resourceid,resourceUri,method,activity,processedActivity,siblingActivities,parentActivity,parentSiblingActivities),paging)

Query Parameters

Field Name Required Description
startTime No Represented as an inclusive timestamp in epoch milliseconds. If present, returns all the activities that are processed after the time.

Note

We recommend you specifying startTime by using the latest processedAt from the previous response. If there is no event from the previous response, keep the same startTime for the next request. If there is an event from the response, the next request should use the latest timestamp of the processedAt and that event will show up on the next request's response. This way, we will ensure no data loss in the next API request.

We currently return events up to 30 days. If you specify a startTime older than 30 days of the current timestamp, it will return events up to 30 days. If you specify an invalid timestamp, it will throw a 400 error.

In addition, we recommend querying at a count=10 to reduce latency and to query each member's activities once an hour. The upper limit is 50 . If you set a number not in the range [1,50] , it will result in a 400 and an error message containing the recommended count .

If you decide to increase number of activities returned via count , keep in mind it will slow down processing time and can lead to timeouts. For querying each member's activities, we recommend spreading out your requests over the hour such that your application is not having QPS spikes, which could lead to timeouts as well.

We also recommend using the capturedAt time returned by Compliance Events API to determine the time of the event activity. There might be instances within the specific activity that do not return the created/lastModified time.

The Compliance Events API will return an array of elements. Each element is an activity record with the following schema:

Compliance Events Schema

Field Name Description
id The unique identifier for the activity event.
capturedAt Time the event is captured.
processedAt Time the event is processed.
configVersion The configuration version used to process this event. This is unique to the activity's resourceName and method. It can change periodically as we update our configurations to better the API experience. It will mainly used for debugging purpose.
owner The member who owns the record. It is the same member who has retrieval and viewing access to this activity.
actor The member who performs the action of the activity.
resourceName Name of resource being acted upon. Please refer below for a complete list of the resources being captured.
resourceId The identifier of the resource.
resourceUri URI of the resource being modified. Used for remediation.
method The resource method. Can be one of the following write methods: *As of now, if a method is DELETE, the activity and processedActivity fields should be empty since we cannot capture the object after it has been deleted.
methodName The optional string representing the method's name. Only present in ACTION method.
activity The original activity data. Used for remediation.
processedActivity The decorated original activity containing relevant contextual information. For example, if the original activity is a comment, this will include the original share content. Used for archiving.
siblingActivities The activities on the same resource level. This is used for previous sibling comments on a share or previous messages in a conversation. Will return up to 10 most recent previous activities.
parentSiblingActivities The previous activities on the parent resource level. Example would be the previous sibling comments of a parent comment of a nested comment. Will return up to 10 most recent previous activities.
activityId A unique string identifier of a captured activity. If an activity is processed more than once, all generated records share same activityId.
activityStatus NEW! The status of the event. Can be one of the following:
  • SUCCESS - Event is successfully processed on the initial attempt.
  • FAILURE - Event has partial or complete processing failure on the initial attempt. The API will not surface multiple FAILURE events of the same activity.
  • SUCCESSFUL_REPLAY - Event is successfully reprocessed after one or many attempts.

Sample Get Response

{
  "elements": [
    {
      "id": 100,
      "activityId": "12356788990000",
      "capturedAt": 1476375751786,
      "processedAt": 1476375771769,
      "configVersion": 1,
      "owner": "urn:li:person:123ABC",
      "owner~": {
        "firstName": {
          "localized": {
            "en_US": "John"
          },
          "preferredLocale": {
            "country": "US",
            "language": "en"
          }
        },
        "lastName": {
          "localized": {
            "en_US": "Smith"
          },
          "preferredLocale": {
            "country": "US",
            "language": "en"
          }
        },
        "headline": {
          "localized": {
            "en_US": "Staff Software Engineer"
          },
          "preferredLocale": {
            "country": "US",
            "language": "en"
          }
        }
      },
      "actor": "urn:li:person:123ABC",
      "actor~": {
        "firstName": {
          "localized": {
            "en_US": "John"
          },
          "preferredLocale": {
            "country": "US",
            "language": "en"
          }
        },
        "lastName": {
          "localized": {
            "en_US": "Smith"
          },
          "preferredLocale": {
            "country": "US",
            "language": "en"
          }
        },
        "headline": {
          "localized": {
            "en_US": "Staff Software Engineer"
          },
          "preferredLocale": {
            "country": "US",
            "language": "en"
          }
        }
      },
      "resourceName": "people/positions",
      "resourceId": "123ABC",
      "resourceUri": "/people/id=123ABC/positions",
      "methods": "PARTIAL_UPDATE",
      "activity": {
        "patch": {
          "$set": {
            "startMonthYear": {
              "month": 3,
              "year": 2014
            },
            "title": {
              "localized": {
                "en_US": "Software Developer"
              }
            },
            "company": "urn:li:company:0000"
          }
        }
      },
      "processedActivity": {
        "patch": {
          "$set": {
            "startMonthYear": {
              "month": 3,
              "year": 2014
            },
            "title": {
              "localized": {
                "en_US": "Software Developer"
              }
            },
            "company": "urn:li:company:0000",
            "company~": {
              "name": "LinkedIn",
              "description": "Awesome Company!"
            }
          }
        }
      }
    },
    {
      "id": 101,
      "capturedAt": 1476375771786,
      "processedAt": 1476375971769,
      "configVersion": 1,
      "owner": "urn:li:person:123ABC",
      "actor": "urn:li:person:123ABC",
      "resourceName": "endorsement",
      "resourceId": "123456",
      "resourceUri": "/endorsements/123456",
      "methods": "CREATE",
      "activity": {
        "item": {
          "nonStandardEntity": {
            "entityType": "PROFILE_SKILL",
            "entityPhrase": "Java",

          }
        },
        "endorser": "urn:li:person:123ABC",
        "recipient": "urn:li:person:456DEF",
        "location": "skills.section"
      },
      "processedActivity": {
        "item": {
          "nonStandardEntity": {
            "entityType": "PROFILE_SKILL",
            "entityPhrase": "Java",

          }
        },
        "endorser": "urn:li:person:123ABC",
        "endorser~": {
          "lastName": {
            "localized": {
              "en_US": "Smith",

            },
            "preferredLocale": {
              "country": "US",
              "language": "en"
            }
          },
          "firstName": {
            "localized": {
              "en_US": "Bob",

            },
            "preferredLocale": {
              "country": "US",
              "language": "en"
            }
          },
          "headline": {
            "localized": {
              "en_US": "Software Engineer at LinkedIn",

            },
            "preferredLocale": {
              "country": "US",
              "language": "en"
            }
          }
        },
        "recipient": "urn:li:person:456DEF",
        "recipient~": {
          "lastName": {
            "localized": {
              "en_US": "Johnson",

            },
            "preferredLocale": {
              "country": "US",
              "language": "en"
            }
          },
          "firstName": {
            "localized": {
              "en_US": "Joe",

            },
            "preferredLocale": {
              "country": "US",
              "language": "en"
            }
          },
          "headline": {
            "localized": {
              "en_US": "Senior Software Engineer",

            },
            "preferredLocale": {
              "country": "US",
              "language": "en"
            }
          }
        },
        "location": "skills.section"
      }
    },
    ...
  ],
  "paging": {
    "count": 10,
    "start": 0,
    "links": [
      {
        "rel": "next",
        "href": "/v2/complianceEvents?count=10&projection=%28elements*%28id%2CcapturedAt%2CprocessedAt%2CconfigVersion%2Cowner%7E%28firstName%2ClastName%2Cheadline%29%2Cactor%7E%28firstName%2ClastName%2Cheadline%29%2CresourceName%2Cresourceid%2CresourceUri%2Cmethods%2Cactivity%2CprocessedActivity%2CsiblingActivities%2CparentActivity%2CparentSiblingActivities%29%2Cpaging%29&q=memberAndApplication&start=10",
        "type": "application/json"
      }
    ]
  }
}

Each activity record contains metadata such as resourceName , resourceId , resourceUri , and more to help you identify the activity performed so you can properly archive and remediate as needed.

As best practice, we recommend archiving all the data but specifically method , resourceName , resourceId , configVersion and processedActivity . For remediation, you will be able to use resourceUri and activity to easily update what is needed.

Resource References

The following resources are currently being captured and supported as part of Compliance Events:

Entity Monitoring

We added this feature to monitor activities that does not reference the regulated member directly. The activities returned will be related to the regulated member but not directly associated or performed by the regulated member. To know more, refer here.

Error Handling

In the event that a field or an activity is unable to be processed, we will still return the activity such that you are notified that an activity has occurred. The field that is not processed will have following message of "Unable_to_process_this_field." . In the processedActivity , the whole activity can be unprocessed, which will result in string of "message": "Unable to process this event." . If a particular field in the processedActivity is unprocessed, then it will have ! and the string "message": "Unable to process this field." . See below for examples.

Unable to process a field

{
    "activity": {}, 
    "activityId": "81fafbe1-ea4b-4454-a736-6b8a20d9aa90", 
    "actor": "urn:li:person:sFrA4B1w3F", 
    "capturedAt": 1494367986523, 
    "configVersion": 2, 
    "id": 23260, 
    "method": "UPDATE", 
    "owner": "urn:li:person:sFrA4B1w3F", 
    "processedActivity": {
        "message": "Unable to process this event."
    }, 
    "processedAt": 1494368009776, 
    "resourceId": "Unable_to_process_this_field.", 
    "resourceName": "mockSimpleKeyCollection", 
    "resourceUri": "Unable_to_process_this_field."
}

unable to process an activity

{
    "activity": {
        "simpleUnionField": {
            "int": 3
        }, 
        "urn": "urn:li:person:sFrA4B1w3F"
    }, 
    "activityId": "b77d27f4-b7c9-426c-a43e-204a2735c798", 
    "actor": "urn:li:person:sFrA4B1w3F", 
    "capturedAt": 1494368763168, 
    "configVersion": 2, 
    "id": 23716, 
    "method": "UPDATE", 
    "owner": "urn:li:person:sFrA4B1w3F", 
    "processedActivity": {
        "message": "Unable to process this event."
    }, 
    "processedAt": 1494369845019, 
    "resourceId": "4", 
    "resourceName": "mockSimpleKeyCollection", 
    "resourceUri": "/mockSimpleKeyCollection/4"
}

Unable to process an urn field in processedActivity

{
    "activity": {
        "simpleUnionField": {
            "int": 3
        }, 
        "urn": "urn:li:person:sFrA4B1w3F"
    }, 
    "activityId": "81fafbe1-ea4b-4454-a736-6b8a20d9aa90", 
    "actor": "urn:li:person:sFrA4B1w3F", 
    "capturedAt": 1494367986523, 
    "configVersion": 2, 
    "id": 23260, 
    "method": "UPDATE", 
    "owner": "urn:li:person:sFrA4B1w3F", 
    "processedActivity": {
        "simpleUnionField": {
            "int": 3
        }, 
        "urn": "urn:li:person:sFrA4B1w3F", 
        "urn!": {
            "message": "Unable to process this field."
        }
    }, 
    "processedAt": 1494368009776, 
    "resourceId": "4", 
    "resourceName": "mockSimpleKeyCollection", 
    "resourceUri": "/mockSimpleKeyCollection/4"
}

Replayer Feature

Replayer feature provides the ability to reprocess events that have already occurred and surfaced on the API response. This feature will provide more information on past activities that were not previously available during the initial compliance capture and process by surfacing them on new events. The Replayer feature will not modify existing events, only create new ones corresponding to the same activty. This feature will go live on February 12, 2020 with the goal of reprocessing all events that have missing and/or incomplete data.

Starting on February 12, 2020, you may see reprocessed events in the Compliance Events API response. To differentiate between initial and reprocessed events, we are introducing activityStatus field to denote each event's process state. It can be one of the following: SUCCESS, FAILURE, or SUCCESSFUL_REPLAY. To prevent archiving duplicate events, partners can filter out reprocessed events by filtering out SUCCESSFUL_REPLAY events until they are ready to utilize this feature.

Currently, reprocessed events will only appear in the API response if it's successfully reprocessed with no processing failures. This means the maximum number of Compliance Events for a unique activity is two. Therefore the three possible scenarios are:

  1. Event with SUCCESS activityStatus.
  2. Event with FAILURE activityStatus with no subsequent event.
  3. Event with FAILURE activityStatus that eventually is followed by another event with SUCCESSFUL_REPLAY activityStatus.

Some other ways to identify and differentiate these activities are to use the following fields: id, activityId, and processedAt. For more information on these fields, refer to the Compliance Events Schema. See below for an example:

Sample Replay Activities

{
  "elements": [
    {
      "id": 1000,
      "owner": "urn:li:person:yrZCpj2ZYQ",
      "resourceId": "urn:li:ugcPost:123456789",
      "configVersion": 36,
      "method": "CREATE",
      "activity": {
        "author": "urn:li:person:yrZCpj2ZYQ",
        "id": "urn:li:ugcPost:123456789",
        ...
      },
      "resourceName": "ugcPosts",
      "resourceUri": "/ugcPosts/urn:li:ugcPost:123456789",
      "actor": "urn:li:person:yrZCpj2ZYQ",
      "activityId": "8561f816-517e-49fb-901d-29c589e3b09f",
      "processedAt": 1572831764582,
      "capturedAt": 1573079762090,
      "activityStatus": "FAILURE",
      "processedActivity": {
        "author": "urn:li:person:yrZCpj2ZYQ",
        "author!": {
          "message": "Unable to process this field."
        },
        "id": "urn:li:ugcPost:123456789",
        ...
      }
    },
    {
      "id": 1001,
      "activityStatus": "SUCCESS",
      ...
    },
    {
      "id": 1002,
      "activityStatus": "SUCCESS",
      ...
    },
    {
      "id": 1003,
      "activityStatus": "SUCCESS",
      ...
    },
    {
      "id": 1004,
      "owner": "urn:li:person:yrZCpj2ZYQ",
      "resourceId": "urn:li:ugcPost:123456789",
      "configVersion": 37,
      "method": "CREATE",
      "activity": {
        "author": "urn:li:person:yrZCpj2ZYQ",
        "id": "urn:li:ugcPost:123456789",
        ...
      },
      "resourceName": "ugcPosts",
      "resourceUri": "/ugcPosts/urn:li:ugcPost:123456789",
      "actor": "urn:li:person:yrZCpj2ZYQ",
      "activityId": "8561f816-517e-49fb-901d-29c589e3b09f",
      "processedAt": 1573759549000,
      "capturedAt": 1573079762090,
      "activityStatus": "SUCCESSFUL_REPLAY",
      "processedActivity": {
        "author": "urn:li:person:yrZCpj2ZYQ",
        "author~": {
          "firstName": {
            "localized": {
              "en_US": "Bob"
            },
            "preferredLocale": {
              "country": "US",
              "language": "en"
            }
          },
          ...
        },
        "id": "urn:li:ugcPost:123456789",
        ...
      }
    }
  ]
}

Note

Notice the first and last event in the above sample response and their specific fields: activityStatus, id, activityId, and processedAt. The activityStatus field helps determine whether or not it's a reprocessed event and the last event denotes a reprocessed event. In addition, the two events correspond to the same activity by having the same activityId. However, they have different unique id and the first activity has the previous processedAt timestamp while the last one has the latest timestamp. The last activity was not able to process the author decoration in processedActivity and has author! whereas the first activity reprocessed the field with author~ in processedActivity. For more information on response decoration, please refer here.