Hint: It is normally preferable to import people as a CSV file (possibly via the CSV Upload API), rather than directly interacting with the People API.
The Watershed People API is used by activity providers to associate multiple personas as a single person. This guide outlines the functional components of the API and provides an example.
Please note: This guide is designed to complement a conversation with us. If you're looking to implement the People API and need assistance, please let us know and we will be glad to help.
Concepts
Actors and Personas
Actors are the entities that are part of xAPI statements. They ultimately tie the xAPI data that powers Watershed to the people who are in your organization. As per the xAPI API specification, actors may be identified in several ways, from email addresses to accounts on any arbitrary system. These identifiers are referred to in this document as personas.
For example, the email addresses bob@example.com and bobby@example.net are both personas, which may refer ultimately to the same person, Bob. Another example might be bob12 at twitter.com, which would be considered an "account on a system".
People
People are collections of personas. They represent individuals in reporting data, and members that can be organized into groups. A person can be identified using any of the personas which have been related to them.
Important note about people behavior:
- Personas can be associated with people by creating or updating a person with that persona.
- When a persona is associated with a person, if that persona was already associated with another person, it will be removed from the original.
Relationship between personas and people
Each persona can only belong to one person, and each person must have one or more personas. If you attach a persona that is already in use to a new or existing person, that persona will no longer be associated to the person it was previously attached to. If this results in a person having no personas, that person will be deleted.
A person entity will be created automatically for each unique xAPI agent which is referenced in statements sent to the LRS. In such case, a single persona is associated with the person, which corresponds to the xAPI agent object present in the statement. Each persona created in this way will always belong to a person and cannot be deleted. If a persona that’s included in statement data is removed from a person, a new person will automatically be created for that persona.
API
This section outlines the allowed resources and methods of the People API. In order to interact with the people API, you will need authentication credentials and an organization id (org id).
Hint: The Using Watershed's Administration APIs article provides important information on using the People API such as details on collections, paging, searching and sorting results.
Batch Updates
Whenever changes to groups and people are made that affect reporting, Watershed updates it's record of interaction data to reflect these changes. If you are making a lot of changes in rapid succession, it is better to let Watershed accumulate these changes and process them all at once after all of the changes have been submitted.
To delay the processing of groups or people changes until after all changes have been applied, add the parameter batch
with a value of true
to each request in the entire set of changes. Then, to process all pending people and group changes, send a batch update request as outlined below. The finish-batch-updates
request should only be sent once, after all groups and people changes have been requested.
Request URL: /api/organizations/[org-id]/groups/finish-batch-updates
Method: POST
Expected response code: 204 No Content
Please note: For smaller continuous updates, batch updates should not be used. Only use batch updates when making bulk updates.
Get a list of people
Request URL: /api/organizations/[org-id]/people
Method: GET
Expected response code: 200 OK
Request Parameters
Parameter |
Type |
Description |
Required |
_limit |
Integer |
Determines how many results will be included in the response object. The default is 100 and the max is 1000. If there are more results, pagination is required. |
Optional |
_offset |
Integer |
The number of records to offset. For example, if you've fetched the first 100 records then the next 100 can be obtained with _offset=100. |
Optional |
is_not_null |
String |
This parameter allows filtering on the results to only retrieve records where a field is not blank. For example, if you only wanted persons where the custom ID was populated you would use is_not_null=customId. |
Optional |
is_null |
String |
Works the opposite of is_not_null. To obtain a list of everyone missing a customId you would use is_null=customId. |
Optional |
forceDBSearch |
N/A |
There is a 10,000 person limit on the search executed from the People API call. If you need to list passed the 10,000th person, append &forceDBSearch (without any values) to get around this limit. |
Optional |
Please note: The parentDepth parameter cannot be used with this request.
Response
Please note: There is a 10,000 person limit on the search executed from this API call. If you need to list passed the 10,000th person, append&forceDBSearch
to your query to force a different kind of search that can get around this limit.
A successful request will return a JSON object with count and results properties. results contains an array of person objects (see below); count contains the length of that array.
Get a person by their id
Request URL: /api/organizations/[org-id]/people/[person-id]
Method: GET
Expected response code: 200 OK
Hint: you can also get a person by their custom id using the request url /api/organizations/[org-id]/people/?customId=[custom-id]
Request Parameters
Parameter |
Type |
Description |
Required |
parentDepth |
Integer |
How many levels up the hierarchy tree of parent groups should be included in the response. Defaults to 0. Set to -1 to return all data. Please note: parent group ids will always be included in the request; use this parameter to get full group data without making additional calls to the Groups API. |
Optional |
Response
A successful request will return a single person object (see below) matching the person id provided in the URL.
Get a person by a matching persona
Request URL: /api/organizations/[org-id]/people/with-persona
Method: GET
Expected response code: 200 OK
Request Parameters
Parameter |
Type |
Description |
Required |
persona |
Persona Object |
Persona to match against. This does not need to be a complete persona object, so long as it contains an inverse functional identifier. In fact, the object must not include a created property. |
Required |
parentDepth |
Integer |
How many levels up the hierarchy tree of parent groups should be included in the response. Defaults to 0. Set to -1 to return all data. Please note: parent group ids will always be included in the request; use this parameter to get full group data without making additional calls to the Groups API. |
Optional |
Response
A successful request will return a single person object (see below) matching the persona provided in the persona parameter.
Add a person
Request URL: /api/organizations/[org-id]/people/
Method: POST
Expected response code: 201 Created
Request Content
The request should contain a JSON formatted person object (see below). This person object does not need to include ids or created timestamps.
Response
A successful request will return a complete person object including id and created timestamps.
Update a person
Request URL: /api/organizations/[org-id]/people/[person-id]
Method: PUT
Expected response code: 204 No Content
Request Parameters
Parameter |
Type |
Description |
Required |
customId |
Persona Object |
Use this parameter to update a person using their custom ID, rather than there Watershed ID |
Optional |
Response
No content.
Delete a person
Request URL: /api/organizations/[org-id]/people/[person-id]
Method: Delete
Expected response code: 204 No Content
Request Parameters
Parameter |
Type |
Description |
Required |
customId |
Persona Object |
Use this parameter to update a person using their custom ID, rather than there Watershed ID |
Optional |
deleteStatementData |
Boolean |
If true all statement data about the person will also be deleted. This supports the requirements under GDPR for removing all (learner) PII from Watershed. |
Optional |
Response
No content.
Entity model
This section outlines the objects used in interacting with the People API.
Persona
An object representing an individual persona of a person. This has the same properties as an Agent or Group object defined in the xAPI specification. When returned by Watershed in response to a GET result, the persona object has two additional properties:
Property |
Type |
Description |
id |
Integer |
Watershed’s id for the persona. |
created |
ISO 8601 timestamp |
When the persona was created. |
Person
Not to be confused with the Person object defined in the xAPI specification, the Watershed Person object has the following properties.
Property |
Type |
Description |
id |
Integer |
Watershed’s id for the person. Used in a number of API calls relating to the person. |
customId |
String |
A custom identifier for the person. Must be unique. This is used by some Watershed clients to store an id from another system that may or may not be represented as a persona. |
created |
ISO 8601 timestamp |
When the person was created. |
name |
String |
Name of the person to be used by Watershed. For example, a learner might have personas named “Mike” and “Michael”; the person name is what Watershed will use. |
imageUrl |
URI |
Link to a profile image for a person. If not provided and one of the personas contains an email address linked to a Gravatar account, the Gravatar image will be used. |
personas |
Array |
List of persona objects associated with the person. |
parentGroupIds |
Array |
List of integer ids of groups the person belongs to. |
parentGroupCustomIds |
Array |
List of custom ids of groups the person belongs to. Can be used instead of parentGroupIds when updating a person's memberships. |
parentGroups |
Array |
List of group objects for groups the person belongs to. This may not be a complete list; see the parentDepth request parameter above. |
Example
Below are some API interactions corresponding to an example scenario in which some personas are associated as people. In our example scenario, our organization has two learners, Bob and Sue. Both Bob and Sue have learning activities to complete where they will be identified by their SCORM Cloud account and their email address. Bob has already completed the learning and therefore has data already in Watershed; Sue has not.
First, let’s see what people are already listed in the organization:
GET /api/organizations/1234/people This returns: { "count": 2, "results": [ { "id": 1, "name": "Bob Smith", "personas": [ { "name": "Bob Smith", "id": 1, "mbox": "mailto:bob.smith@example.com", "created": "2015-02-26T18:29:22.000+0000" } ], "parentGroupIds": [], "parentGroupCustomIds": [], "created": "2015-02-26T18:29:18.000+0000" }, { "id": 2, "name": "Bob Smith", "personas": [ { "name": "Bob Smith", "id": 2, "account": { "homePage": "http://cloud.scorm.com/", "name": "W74TDQ7PZG|251479" }, "created": "2015-04-29T13:00:42.000+0000" } ], "parentGroupIds": [], "parentGroupCustomIds": [], "created": "2015-04-29T13:00:42.000+0000" } ] }
Bob is currently represented as two people and Sue is not present at all. Let’s edit person Bob Smith 1 to include both of Bob’s personas; this will cause person Bob Smith 2 to be removed.
PUT /api/organizations/1234/people/1 { "id": 1, "name": "Bob Smith", "personas": [ { "name": "Bob Smith", "id": 1, "mbox": "mailto:bob.smith@example.com" }, { "name": "Bob Smith", "id": 2, "account": { "homePage": "http://cloud.scorm.com/", "name": "W74TDQ7PZG|251479" } } ], "parentGroupIds": [], "parentGroupCustomIds": [] }
Our organization now has one person and two personas, but what about Sue? There’s no exciting person for Sue, so let’s create one.
POST /api/organizations/1234/people/ { "name": "Sue Jones", "personas": [ { "name": "Sue Jones", "mbox": "mailto:sue.jones@example.com" }, { "name": "Sue Jones", "account": { "homePage": "http://cloud.scorm.com/", "name": "W74TDQ7PZG|145897" } } ], "parentGroupIds": [], "parentGroupCustomIds": [] }
Great! Now everything’s loaded. Some time later we need to retrieve the data from Watershed. We know that Bob’s person id was 1 and that Sue’s email is sue.jones@example.com. Let’s fetch Bob’s record first. We’ll also retrieve any group data associated with Bob.
GET /api/organizations/1234/people/1?parentDepth=-1
That returns Bob’s record. Now Sue. This time we only want to know about groups Sue is directly in, not any groups containing those groups.
/api/organizations/1234/people/with-persona?parentDepth=1&persona={"mbox":"mailto:sue.jones@example.com"}
Perfect! We’ve now got Sue’s record too!
Now that Bob and Sue are represented as single persons in Watershed, find out how to use the Groups API to create and manage groups and how to use the Permissions API to control what data people can see.