{"metadata":{"image":[],"title":"","description":""},"api":{"url":"","auth":"required","results":{"codes":[]},"settings":"","params":[]},"next":{"description":"","pages":[]},"title":"Roster Sync API","type":"basic","slug":"roster-sync-api","excerpt":"","body":"[block:api-header]\n{\n  \"title\": \"Authentication for APIs\"\n}\n[/block]\nAll Oneroster APIs are authenticated based on the client id and secret shared by Gooru with partners. Partner need to base64 encode the client id and secret in order to pass as Basic authorization header to APIs.\n\n**Example:** (To generate Base64 Encoded Credentials)\n\nClientId: 3d6bc3ea-2aff-4106-8762-9246d5d84813\nSecret: kteuobYdO1oRfqJ1+PZ5phMSeI=\n\nCombine client id and secret with ‘:’ -\n3d6bc3ea-2aff-4106-8762-9246d5d84813:kteuobYdO1oRfqJ1+PZ5phMSeI= \n\nAnd then encode it by Base64 encoder which will generate string like below. Use it to pass in request header.\nM2Q2YmMzZWEtMmFmZi00MTA2LTg3NjItOTI0NmQ1ZDg0ODEzOmt0ZXVvYllkTzFvUmZxSjErUFo1cGhNU2VJPQ==\n\n[block:api-header]\n{\n  \"title\": \"Data Requirements\"\n}\n[/block]\nAll csv files contained in the zip should comply to the [IMS Global OneRoster specification](https://www.imsglobal.org/lis/imsOneRosterv1p0/imsOneRosterCSV-v1p0.html)\n\n\n**CSV Overview:**\nTenants should provide the student information to Gooru as .csv formatted files.\nTenants must continue to use .csv files to exchange roster information with Gooru, below is an outline of the format for the roster data which corresponds to the OneRoster standard. \nTenants can choose to upload class rosters by preparing four (4) files in csv format outlined in this document.  \n1. orgs.csv\n2. users.csv\n3. classes.csv\n4. enrollments.csv\n\n**CSV Format:**\nThe file format MUST be comma separated values format (CSV) for the OneRoster profile.  Each field will be separated by commas and line breaks between each row. Double quotes MUST be used with a field that contains a comma.\n●        All files are required\n●        The header row is required.\n●        Some fields are required.\n●        The primary id is marked in red below, this must be unique per file.\nALL Header fields MUST be supplied in EXACTLY the same order as in the tables below. Optional fields with no data MUST simply be empty in the CSV. Header fields MUST be named the same as per the field header in the tables below. All filenames and header fields are case sensitive.\n\n\n**Organization Rostering**\nFirst step in the rostering to create the organization based on the details provided in the “organization.csv”. This file should be processed first if present\n  * New organization will be created if there is no organization present with the same “sourced_id” for the tenant.\n  * If an organization with the same “sourced_id” found for the tenant rostering the data, then the last modified date from the csv will be compared with the last modified date present in the database. If date from csv file is later than what present in date, class data will be updated otherwise no action will be taken\n\nIf this file is not present in the zip uploaded by the tenant, other csv files should contain references to existing organizations (orgSourcedId).\n\nIf there is no organization with orgSourcedId, no data will be rostered and error should be returned to the tenant.\n\nThere is one to one mapping between the organization and tenant. If any partner wants to roster the data for multiple school districts, all school districts should be first configured in Gooru as a sub tenant of the partner’s tenant. \n\nSchool districts will contain multiple schools having their own organization sourced ids. Data will be mapped in Gooru based on the tenant for which the data is getting rostered.\n\nPartner’s roster sync system will be responsible to roster the data against each school district separately.  \n\nSupport Fields:\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"file header\",\n    \"h-1\": \"required\",\n    \"h-2\": \"frormat\",\n    \"h-3\": \"description\",\n    \"0-3\": \"unique id for the organization. SourcedId is used in other files and must be unique across all organizations. (For example –schools in your district).\",\n    \"0-2\": \"UUID\",\n    \"0-1\": \"Yes\",\n    \"0-0\": \"sourcedId\",\n    \"1-0\": \"status\",\n    \"1-1\": \"No\",\n    \"1-2\": \"String\",\n    \"1-3\": \"“active”.\",\n    \"2-0\": \"dateLastModified\",\n    \"2-1\": \"No\",\n    \"2-2\": \"Date\",\n    \"2-3\": \"The date that this record was last modified. (format is YYYY-MM-DD)\",\n    \"3-0\": \"name\",\n    \"3-1\": \"Yes\",\n    \"3-2\": \"String\",\n    \"3-3\": \"Name of the Organization\",\n    \"4-0\": \"type\",\n    \"4-1\": \"Yes\",\n    \"4-2\": \"String\",\n    \"4-3\": \"“school”\",\n    \"5-0\": \"identifier\",\n    \"5-1\": \"No\",\n    \"5-2\": \"String\",\n    \"5-3\": \"NCES ID National Center for Education Statistics) for the school / district\",\n    \"6-0\": \"metadata.classification\",\n    \"6-1\": \"No\",\n    \"6-2\": \"String\",\n    \"6-3\": \"“charter” | “private” | “public”\",\n    \"7-0\": \"metadata.gender\",\n    \"8-0\": \"metadata.boarding\",\n    \"9-0\": \"parentSourcedId\",\n    \"7-1\": \"No\",\n    \"8-1\": \"No\",\n    \"9-1\": \"No\",\n    \"7-2\": \"String\",\n    \"8-2\": \"Boolean\",\n    \"9-2\": \"UUID\",\n    \"7-3\": \"“female” | “male” | “mixed”\",\n    \"8-3\": \"True if school is boarding school\",\n    \"9-3\": \"SourcedId of the Parent organization\"\n  },\n  \"cols\": 4,\n  \"rows\": 10\n}\n[/block]\n**Class Rostering**\nBased on the data present in the “classes.csv” file, classes are rostered into Gooru. \n  * New class will be created if there is no existing class present for the organization with same “sourced_id”\n  * If a class with the same “sourced_id” is found for the organization, the last modified date from the csv will be compared with the last modified date present in the database. If date from csv file is later than what present in date, class data will be updated otherwise no action will be taken\n\nSupport Fields:\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"file header\",\n    \"h-1\": \"required\",\n    \"h-2\": \"frormat\",\n    \"h-3\": \"description\",\n    \"0-0\": \"sourcedId\",\n    \"0-1\": \"Yes\",\n    \"0-2\": \"UUID\",\n    \"0-3\": \"Unique ID for the class. SourcedId is used in other files and must be unique across all classes.\",\n    \"1-0\": \"status\",\n    \"1-1\": \"No\",\n    \"1-2\": \"String\",\n    \"1-3\": \"“active”\",\n    \"2-0\": \"dateLastModified\",\n    \"2-1\": \"No\",\n    \"2-2\": \"Date\",\n    \"2-3\": \"The date that this record was last modified. (FORMAT IS YYYY-MM-DD)\",\n    \"3-0\": \"title\",\n    \"3-1\": \"Yes\",\n    \"3-2\": \"String\",\n    \"3-3\": \"Name of this class\",\n    \"4-0\": \"grade\",\n    \"4-1\": \"No\",\n    \"4-2\": \"String\",\n    \"4-3\": \"Grade (i.e. 9)\",\n    \"5-0\": \"courseSourcedId\",\n    \"5-1\": \"No\",\n    \"5-2\": \"UUID\",\n    \"5-3\": \"SourcedId of the course of which this class is an instance\",\n    \"6-0\": \"classCode\",\n    \"6-1\": \"No\",\n    \"6-2\": \"String\",\n    \"6-3\": \"Human readable code used to help identify this class\",\n    \"7-0\": \"classType\",\n    \"7-1\": \"Yes\",\n    \"7-2\": \"String\",\n    \"7-3\": \"“homeroom”, “scheduled”\",\n    \"8-0\": \"location\",\n    \"8-1\": \"No\",\n    \"8-2\": \"String\",\n    \"8-3\": \"Human readable description of where the class is physically located\",\n    \"9-0\": \"schoolSourcedId\",\n    \"9-1\": \"Yes\",\n    \"9-2\": \"UUID\",\n    \"9-3\": \"SourcedId of the organization which teaches this class\",\n    \"10-0\": \"termSourcedId\",\n    \"10-1\": \"No\",\n    \"10-2\": \"UUID\",\n    \"10-3\": \"SourcedId of the academicSessions(s) in which the class is taught. If more than one term is needed, use double quotes and delimit with commas, (per RFC 4180)\\nExamples:\\n“1,2”\\n1\\n“1,4,8”\",\n    \"11-0\": \"subjects\",\n    \"11-1\": \"Yes\",\n    \"11-2\": \"String\",\n    \"11-3\": \"Subject name(s). If more than one subject is needed, use double quotes, and delimit with commas (per RFC 4180):\\n\\nExamples:\\n“chemistry, physics”\\nphysics\\n“music, drama, poetry”\"\n  },\n  \"cols\": 4,\n  \"rows\": 12\n}\n[/block]\n**User Rostering**\nBased on the data present in the “users.csv” file, users data will be rostered into the Gooru system.\n  * New user will be created if there is no user present for the organization with same “sourced_id” and username\n  * If a user with the same “sourced_id” and username / user_id found for the organization, the last modified date from the csv will be compared with the last modified date present in the database. If date from csv file is later than what present in date, user data will be updated otherwise no action will be taken\n\n“username” is mandatory while rostering users data. This should be the login name or login id of the user in the client system.\n\nWhile rostering the record into Gooru, uniqueness of the records is based on the “username” and tenant. If there is already a user with “username” (i.e. reference_id in “users” table of Core DB) for the tenant, error (409 Conflict) will be returned.\n\nSupported Fields:\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"file header\",\n    \"h-1\": \"required\",\n    \"h-2\": \"format\",\n    \"h-3\": \"description\",\n    \"0-0\": \"sourcedId\",\n    \"0-1\": \"Yes\",\n    \"0-2\": \"UUID\",\n    \"0-3\": \"Unique ID for the user. SourcedId is used in other files and must be unique across all users,\",\n    \"1-0\": \"status\",\n    \"1-1\": \"No\",\n    \"1-2\": \"String\",\n    \"1-3\": \"“active”\",\n    \"2-1\": \"No\",\n    \"2-0\": \"dateLastModified\",\n    \"2-2\": \"Date\",\n    \"2-3\": \"The date that this record was last modified. (FORMAT IS YYYY-MM-DD)\",\n    \"3-0\": \"orgSourcedIds\",\n    \"3-1\": \"Yes\",\n    \"3-2\": \"UUID\",\n    \"3-3\": \"SourcedIds of the Organizations to which this user belongs. \\n\\n(Note in most cases, it is expected that users will belong to a single school).\",\n    \"4-0\": \"role\",\n    \"4-1\": \"Yes\",\n    \"4-2\": \"String\",\n    \"4-3\": \"“teacher”, “student”\",\n    \"5-0\": \"username\",\n    \"5-2\": \"String\",\n    \"5-1\": \"Yes\",\n    \"5-3\": \"Username\",\n    \"6-0\": \"userId\",\n    \"6-1\": \"No\",\n    \"6-2\": \"String\",\n    \"6-3\": \"external machine readable id (e.g. LDAP id, LTI id) for this user, to be used if the sourcedId should not be used.\",\n    \"7-0\": \"givenName\",\n    \"7-1\": \"Yes\",\n    \"7-2\": \"String\",\n    \"7-3\": \"User’s first name\",\n    \"8-0\": \"familyName\",\n    \"8-1\": \"Yes\",\n    \"8-2\": \"String\",\n    \"8-3\": \"User’s surname\",\n    \"9-0\": \"identifier\",\n    \"9-1\": \"No\",\n    \"9-2\": \"String\",\n    \"9-3\": \"Identifier for the user with a human readable meaning\",\n    \"10-0\": \"email\",\n    \"10-1\": \"No\",\n    \"10-2\": \"String\",\n    \"10-3\": \"Email address for the user\",\n    \"11-0\": \"sms\",\n    \"11-1\": \"No\",\n    \"11-2\": \"String\",\n    \"11-3\": \"SMS address for the user\",\n    \"12-0\": \"phone\",\n    \"12-1\": \"No\",\n    \"12-2\": \"String\",\n    \"12-3\": \"Phone number for the user\",\n    \"13-0\": \"agents\",\n    \"13-1\": \"No\",\n    \"13-2\": \"String\",\n    \"13-3\": \"SourcedIds of the users to which this user has a relationship.\\nNote: In most cases this will be for indicating parental relationships.\"\n  },\n  \"cols\": 4,\n  \"rows\": 14\n}\n[/block]\n**Enrollment Rostering**\nBased on the data present in the “enrollments.csv” file, enrollments will be rostered into the Gooru system.\nIt's mandatory to pass organization sourced id, class sourced id and user sourced id in order to enroll the users. Missing any of the details will not create enrollment and specific errors should be returned.\n\nSupport Fields:\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"file header\",\n    \"h-1\": \"required\",\n    \"h-2\": \"type\",\n    \"h-3\": \"description\",\n    \"0-0\": \"sourcedId\",\n    \"0-1\": \"Yes\",\n    \"0-2\": \"UUID\",\n    \"0-3\": \"Id of this enrollment\",\n    \"1-0\": \"classSourcedId\",\n    \"1-1\": \"Yes\",\n    \"1-2\": \"UUID\",\n    \"1-3\": \"Id of the class\",\n    \"2-1\": \"Yes\",\n    \"2-2\": \"UUID\",\n    \"3-1\": \"Yes\",\n    \"3-2\": \"UUID\",\n    \"2-0\": \"schoolSourcedId\",\n    \"2-3\": \"Id of the school\",\n    \"3-0\": \"userSourcedId\",\n    \"3-3\": \"Id of the user (teacher or student)\",\n    \"4-0\": \"role\",\n    \"4-1\": \"Yes\",\n    \"4-2\": \"String\",\n    \"4-3\": \"“student” | “teacher”\",\n    \"5-0\": \"status\",\n    \"5-1\": \"Yes\",\n    \"5-2\": \"String\",\n    \"5-3\": \"“active”\",\n    \"6-0\": \"dateLastModified\",\n    \"6-1\": \"No\",\n    \"6-2\": \"Date\",\n    \"6-3\": \"The date that this record was last modified. (FORMAT IS YYYY-MM-DD)\",\n    \"7-0\": \"primary\",\n    \"7-1\": \"Yes\",\n    \"7-2\": \"Boolean\",\n    \"7-3\": \"MUST only be set to true for ONE teacher for a class.\"\n  },\n  \"cols\": 4,\n  \"rows\": 8\n}\n[/block]\n\n[block:api-header]\n{\n  \"title\": \"Upload Roster\"\n}\n[/block]\nThis API is used to upload the roster files in zip format. All csv files contained in the zip should comply to the [IMS Global Oneroster specification](https://www.imsglobal.org/lis/imsOneRosterv1p0/imsOneRosterCSV-v1p0.html). Invalid or extra uploaded files are skipped from rostering. Current scope of this API is to process orgs, users, classes and enrollments only. Other files such as academicSessions, demographics and courses will be not be processed and rostered into system.\n\nAll data will be rostered against the client id (tenant) in Gooru. Client applications need to provide same client id and secret while performing SSO which id used while rostering the users.\n\nThis API will not return any details on error / exceptions for the failed records. Status API should be explicitly called with required parameters to get the status / error while rostering files.\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"Name\",\n    \"h-1\": \"Value\",\n    \"0-0\": \"HTTP Method\",\n    \"0-1\": \"POST\",\n    \"1-0\": \"End Point\",\n    \"1-1\": \"http://{host}/api/nucleus-oneroster/{version}/upload\",\n    \"2-0\": \"Sample\",\n    \"2-1\": \"http://oneroster.gooru.org/api/nucleus-oneroster/v1/upload\"\n  },\n  \"cols\": 2,\n  \"rows\": 3\n}\n[/block]\n###Headers\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"Header Name\",\n    \"h-1\": \"Value\",\n    \"0-0\": \"Authorization\",\n    \"0-1\": \"Basic <BASE64 ENCODED CREDENTIALS>\",\n    \"1-0\": \"Content-Type\",\n    \"1-1\": \"multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW\"\n  },\n  \"cols\": 2,\n  \"rows\": 2\n}\n[/block]\n  * Replace the correct value of encoded credentials in ‘Authorization’ header\n  * Boundary in the ‘Content-Type’ header is sample. Need to be replaced with actual boundary while calling the API.\n\n###Request Payload\nAttach the file with request.\n\n###Success Reponse\nHTTP Status Code: 201 Created\nLocation header of the response will contain the Upload Id which can be used to get the status of the upload.\n\n\n###Error Response\nIn case of any error while uploading the Roster, API will not provide any details of error / exceptions for records in the files. For Error response codes, refer to Reference section in this document.\n[block:api-header]\n{\n  \"title\": \"Get Upload Status\"\n}\n[/block]\nThis API is used to get the status of the upload for give upload id. For now this API will return total records and success record counts. It may be enhanced in future to return records level details.\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"Name\",\n    \"h-1\": \"Value\",\n    \"0-0\": \"HTTP Method\",\n    \"0-1\": \"GET\",\n    \"1-0\": \"End Point\",\n    \"1-1\": \"http://{host}/api/nucleus-oneroster/{version}/upload/{uploadId}/status\",\n    \"2-0\": \"Sample\",\n    \"2-1\": \"http://oneroster.gooru.org/api/nucleus-oneroster/v1/upload/62f3fbd3-4341-4e30-8efe-3b655074133d/status\"\n  },\n  \"cols\": 2,\n  \"rows\": 3\n}\n[/block]\n###Headers\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"Header Name\",\n    \"h-1\": \"Value\",\n    \"0-0\": \"Authorization\",\n    \"0-1\": \"Basic <BASE64 ENCODED CREDENTIALS>\"\n  },\n  \"cols\": 2,\n  \"rows\": 1\n}\n[/block]\n  * Replace the correct value of encoded credentials in ‘Authorization’ header. \n\n###Request Payload\nNo request payload required for this API\n\n###Success Response\nHTTP Status Code: 200 OK\n\n###Error Response\nRefer to reference section of this document below.\n\n###Possible Status\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"Status\",\n    \"h-1\": \"Purpose\",\n    \"0-0\": \"pending\",\n    \"0-1\": \"Denotes the zip upload is success and pending to be process. The uploads are queued at server and picked up one by one.\",\n    \"1-0\": \"accepted\",\n    \"1-1\": \"Denotes that the upload zip file has been accepted for rostering. This is the status after parsing and validation of all csv files, and still the sync with Gooru main database is pending.\",\n    \"2-0\": \"failed\",\n    \"2-1\": \"Denotes that the upload has been failed. In case of failure while rostering records, this status will be returned.\",\n    \"3-0\": \"completed\",\n    \"3-1\": \"Denotes that the upload has been complete. When all records are rostered successfully, this status will be returned.\"\n  },\n  \"cols\": 2,\n  \"rows\": 4\n}\n[/block]\n##Responses\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"{\\n\\t\\\"status\\\": \\\"pending\\\",\\n\\t\\\"total_records\\\": {\\n\\t\\t\\\"orgs\\\": 1,\\n\\t\\t\\\"users\\\": 10,\\n\\t\\t\\\"classes\\\": 2,\\n\\t\\t\\\"enrollments\\\": 12\\n\\t},\\n\\t\\\"success_records\\\": {\\n\\t\\t\\\"orgs\\\": 1,\\n\\t\\t\\\"users\\\": 8,\\n\\t\\t\\\"classes\\\": 2,\\n\\t\\t\\\"enrollments\\\": 10\\n\\t}\\n}\\n\",\n      \"language\": \"json\",\n      \"name\": \"Pending\"\n    },\n    {\n      \"code\": \"{\\n    \\\"status\\\": \\\"failed\\\",\\n    \\\"errors\\\": {\\n        \\\"orgs_errors\\\": [\\n            {\\n                \\\"error\\\": \\\"Field 'sourcedId' is mandatory but no value was provided.\\\",\\n                \\\"line_number\\\": 3\\n            }\\n        ],\\n        \\\"classes_errors\\\": [\\n            {\\n                \\\"error\\\": \\\"Field 'title' is mandatory but no value was provided.\\\",\\n                \\\"line_number\\\": 1\\n            }\\n        ],\\n        \\\"enrollments_errors\\\": [\\n            {\\n                \\\"error\\\": \\\"Field 'role' is mandatory but no value was provided.\\\",\\n                \\\"line_number\\\": 29\\n            }\\n        ],\\n        \\\"users_errors\\\": [\\n            {\\n                \\\"error\\\": \\\"Field 'username' is mandatory but no value was provided.\\\",\\n                \\\"line_number\\\": 2\\n            }\\n        ]\\n    }\\n}\\n\",\n      \"language\": \"json\",\n      \"name\": \"Failed\"\n    },\n    {\n      \"code\": \"{\\n\\t\\\"status\\\": \\\"accepted\\\",\\n\\t\\\"total_records\\\": {\\n\\t\\t\\\"orgs\\\": 1,\\n\\t\\t\\\"users\\\": 10,\\n\\t\\t\\\"classes\\\": 2,\\n\\t\\t\\\"enrollments\\\": 12\\n\\t},\\n\\t\\\"success_records\\\": {\\n\\t\\t\\\"orgs\\\": 0,\\n\\t\\t\\\"users\\\": 0,\\n\\t\\t\\\"classes\\\": 0,\\n\\t\\t\\\"enrollments\\\": 0\\n\\t}\\n}\\n\",\n      \"language\": \"json\",\n      \"name\": \"Accepted\"\n    },\n    {\n      \"code\": \"{\\n\\t\\\"status\\\": \\\"completed\\\",\\n\\t\\\"total_records\\\": {\\n\\t\\t\\\"orgs\\\": 1,\\n\\t\\t\\\"users\\\": 10,\\n\\t\\t\\\"classes\\\": 2,\\n\\t\\t\\\"enrollments\\\": 12\\n\\t},\\n\\t\\\"success_records\\\": {\\n\\t\\t\\\"orgs\\\": 1,\\n\\t\\t\\\"users\\\": 8,\\n\\t\\t\\\"classes\\\": 2,\\n\\t\\t\\\"enrollments\\\": 10\\n\\t}\\n}\\n\",\n      \"language\": \"json\",\n      \"name\": \"Completed\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"title\": \"Reference\"\n}\n[/block]\n## HTTP Response Codes\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"HTTP Status Code\",\n    \"h-1\": \"HTTP Method\",\n    \"h-2\": \"Response Body\",\n    \"h-3\": \"Description\",\n    \"0-0\": \"200\",\n    \"0-1\": \"GET\",\n    \"0-2\": \"Entity which is requested\",\n    \"0-3\": \"Operation successful without error\",\n    \"1-0\": \"201 Created\",\n    \"1-1\": \"POST\",\n    \"1-2\": \"No Entity in body except for the location of entity in header\",\n    \"1-3\": \"Entity creation successful\",\n    \"2-0\": \"204 No Content\",\n    \"2-1\": \"PUT, DELETE\",\n    \"2-2\": \"No Content\",\n    \"2-3\": \"Entity update or delete is successful\",\n    \"3-0\": \"400 Bad Request\",\n    \"3-1\": \"ALL\",\n    \"3-2\": \"Error messages\",\n    \"3-3\": \"Malformed, invalid or incorrect request or request parameters which are applicable to this Entity\",\n    \"4-0\": \"401 Unauthorized\",\n    \"4-1\": \"ALL\",\n    \"4-2\": \"No Content\",\n    \"4-3\": \"Action requires authentication or session token may have expired. Note that we don’t return www-authenticate headers as we do not want browser to participate in the auth flow.\",\n    \"5-0\": \"403 Forbidden\",\n    \"5-1\": \"ALL\",\n    \"5-2\": \"Error message\",\n    \"5-3\": \"Authentication failure, or invalid API key, or insufficient privileges\",\n    \"6-0\": \"f693bd0a-a67e-4e2d-92c7-0b75d0b0dd3e\",\n    \"6-1\": \"ALL\",\n    \"6-2\": \"No Content\",\n    \"6-3\": \"Entity not found\",\n    \"7-0\": \"405 Not Allowed\",\n    \"7-1\": \"ALL\",\n    \"8-1\": \"ALL\",\n    \"9-1\": \"POST, PUT\",\n    \"7-2\": \"No Content\",\n    \"8-2\": \"No Content\",\n    \"9-2\": \"No Content\",\n    \"10-2\": \"Error message\",\n    \"7-3\": \"Entity does not support the requested operation\",\n    \"8-0\": \"408 Request Timeout\",\n    \"8-3\": \"Request has timed out. The system was not able to complete the operation within specified time\",\n    \"9-0\": \"413 Request Entity Too Large\",\n    \"9-3\": \"The representation of payload is too large for server to handle, or is not allowed\",\n    \"10-0\": \"500 Internal Server Error\",\n    \"10-1\": \"ALL\",\n    \"10-3\": \"Some exception happened while processing the request. The message may be technical and may not be suitable for UI consumption.\"\n  },\n  \"cols\": 4,\n  \"rows\": 11\n}\n[/block]\n\n[block:api-header]\n{\n  \"title\": \"Do’s and Don’ts\"\n}\n[/block]\n  * Make sure to keep a track of all sourcedIds. These sourcedIds are required when you want to enrol the new user to the old class or old user to new class.\n  * CSV files should include all columns in the same sequence, including optional ones irrespective of whether there is data or not.\n  * One user can be associated to one organization only.\n  * Always class is associated with a teacher account. \n  * If you need to associate a Student to an existing class please use the same sourcedid of that respective class.\n  * ​Supported organization type : \"school\"​\n  * ​Supported Users Role : student, teacher​\n  * Enrollments : make sure teacher related data is available first then student.\n  * Update existing Users records: there is no change in the process. But if you need to delete any account, update the status column with appropriate value and sourcedId.\n  * Update existing Enrollments: \n  * If you want to add a new user to the existing class, make sure you provide the correct sourcedId in the enrollments.csv file","updates":[],"order":0,"isReference":false,"hidden":false,"sync_unique":"","link_url":"","link_external":false,"_id":"599bcb49c03fa2000f83dbaf","project":"56439dfe9eebf70d00490d54","version":{"version":"2","version_clean":"2.0.0","codename":"","is_stable":true,"is_beta":true,"is_hidden":false,"is_deprecated":false,"categories":["5864d2df79ce642d00f0fec8","5864d2df79ce642d00f0fec9","5864d2df79ce642d00f0feca","5864d2df79ce642d00f0fecb","5864d2df79ce642d00f0fecc","5864d2df79ce642d00f0fecd","5864d2df79ce642d00f0fece","5864d2df79ce642d00f0fecf","5864d2df79ce642d00f0fed0","5864d2df79ce642d00f0fed1","5864d2df79ce642d00f0fed2","5864d2df79ce642d00f0fed3","5864d2df79ce642d00f0fed4","5864d2df79ce642d00f0fed5","5864d2df79ce642d00f0fed6","5864d2df79ce642d00f0fed7","5864d2df79ce642d00f0fed8","5864d2df79ce642d00f0fed9","5864d2df79ce642d00f0feda","5864d2df79ce642d00f0fedb","5864d2df79ce642d00f0fedc","5864d2df79ce642d00f0fedd","5864d2df79ce642d00f0fede","598aa64f4b6e990019b7a2d2","599bc76bc03fa2000f83db2a","599bcc3c3c5bf7000f3434fc","5d427dc9fa56fa0011135058","5d429c616863d5003af785a7","5d429e0889418f00c5e95d3f","5d42b5f098b05e003acb08b4","5d43c16985775c00ebeede3b","5d43d15446d584003da91e6d","5d43d61a5bdac50011b6f234","5d43d7c2db365100640dbc58","5d43d954bffa8400ff7efa78","5d43e414cf4f03005944344c","5d43eb42db365100640dbe4a","5d43ee6c78121b0057bc1dbf","5d9a18b4afc33400126c4e6f","5dc4f96dbb5da3006c8f5660"],"_id":"5864d2df79ce642d00f0fec7","createdAt":"2016-12-29T09:09:51.074Z","project":"56439dfe9eebf70d00490d54","releaseDate":"2016-12-29T09:09:51.074Z","__v":18},"category":{"sync":{"isSync":false,"url":""},"pages":[],"title":"One Roster","slug":"one-roster","order":22,"from_sync":false,"reference":false,"_id":"5864d2df79ce642d00f0fedc","version":"5864d2df79ce642d00f0fec7","project":"56439dfe9eebf70d00490d54","createdAt":"2016-07-18T17:31:42.071Z","__v":0},"user":"567b9bf2b56bac0d0019d914","createdAt":"2017-08-22T06:12:25.434Z","githubsync":"","__v":0,"parentDoc":null}
[block:api-header] { "title": "Authentication for APIs" } [/block] All Oneroster APIs are authenticated based on the client id and secret shared by Gooru with partners. Partner need to base64 encode the client id and secret in order to pass as Basic authorization header to APIs. **Example:** (To generate Base64 Encoded Credentials) ClientId: 3d6bc3ea-2aff-4106-8762-9246d5d84813 Secret: kteuobYdO1oRfqJ1+PZ5phMSeI= Combine client id and secret with ‘:’ - 3d6bc3ea-2aff-4106-8762-9246d5d84813:kteuobYdO1oRfqJ1+PZ5phMSeI= And then encode it by Base64 encoder which will generate string like below. Use it to pass in request header. M2Q2YmMzZWEtMmFmZi00MTA2LTg3NjItOTI0NmQ1ZDg0ODEzOmt0ZXVvYllkTzFvUmZxSjErUFo1cGhNU2VJPQ== [block:api-header] { "title": "Data Requirements" } [/block] All csv files contained in the zip should comply to the [IMS Global OneRoster specification](https://www.imsglobal.org/lis/imsOneRosterv1p0/imsOneRosterCSV-v1p0.html) **CSV Overview:** Tenants should provide the student information to Gooru as .csv formatted files. Tenants must continue to use .csv files to exchange roster information with Gooru, below is an outline of the format for the roster data which corresponds to the OneRoster standard. Tenants can choose to upload class rosters by preparing four (4) files in csv format outlined in this document. 1. orgs.csv 2. users.csv 3. classes.csv 4. enrollments.csv **CSV Format:** The file format MUST be comma separated values format (CSV) for the OneRoster profile. Each field will be separated by commas and line breaks between each row. Double quotes MUST be used with a field that contains a comma. ● All files are required ● The header row is required. ● Some fields are required. ● The primary id is marked in red below, this must be unique per file. ALL Header fields MUST be supplied in EXACTLY the same order as in the tables below. Optional fields with no data MUST simply be empty in the CSV. Header fields MUST be named the same as per the field header in the tables below. All filenames and header fields are case sensitive. **Organization Rostering** First step in the rostering to create the organization based on the details provided in the “organization.csv”. This file should be processed first if present * New organization will be created if there is no organization present with the same “sourced_id” for the tenant. * If an organization with the same “sourced_id” found for the tenant rostering the data, then the last modified date from the csv will be compared with the last modified date present in the database. If date from csv file is later than what present in date, class data will be updated otherwise no action will be taken If this file is not present in the zip uploaded by the tenant, other csv files should contain references to existing organizations (orgSourcedId). If there is no organization with orgSourcedId, no data will be rostered and error should be returned to the tenant. There is one to one mapping between the organization and tenant. If any partner wants to roster the data for multiple school districts, all school districts should be first configured in Gooru as a sub tenant of the partner’s tenant. School districts will contain multiple schools having their own organization sourced ids. Data will be mapped in Gooru based on the tenant for which the data is getting rostered. Partner’s roster sync system will be responsible to roster the data against each school district separately. Support Fields: [block:parameters] { "data": { "h-0": "file header", "h-1": "required", "h-2": "frormat", "h-3": "description", "0-3": "unique id for the organization. SourcedId is used in other files and must be unique across all organizations. (For example –schools in your district).", "0-2": "UUID", "0-1": "Yes", "0-0": "sourcedId", "1-0": "status", "1-1": "No", "1-2": "String", "1-3": "“active”.", "2-0": "dateLastModified", "2-1": "No", "2-2": "Date", "2-3": "The date that this record was last modified. (format is YYYY-MM-DD)", "3-0": "name", "3-1": "Yes", "3-2": "String", "3-3": "Name of the Organization", "4-0": "type", "4-1": "Yes", "4-2": "String", "4-3": "“school”", "5-0": "identifier", "5-1": "No", "5-2": "String", "5-3": "NCES ID National Center for Education Statistics) for the school / district", "6-0": "metadata.classification", "6-1": "No", "6-2": "String", "6-3": "“charter” | “private” | “public”", "7-0": "metadata.gender", "8-0": "metadata.boarding", "9-0": "parentSourcedId", "7-1": "No", "8-1": "No", "9-1": "No", "7-2": "String", "8-2": "Boolean", "9-2": "UUID", "7-3": "“female” | “male” | “mixed”", "8-3": "True if school is boarding school", "9-3": "SourcedId of the Parent organization" }, "cols": 4, "rows": 10 } [/block] **Class Rostering** Based on the data present in the “classes.csv” file, classes are rostered into Gooru. * New class will be created if there is no existing class present for the organization with same “sourced_id” * If a class with the same “sourced_id” is found for the organization, the last modified date from the csv will be compared with the last modified date present in the database. If date from csv file is later than what present in date, class data will be updated otherwise no action will be taken Support Fields: [block:parameters] { "data": { "h-0": "file header", "h-1": "required", "h-2": "frormat", "h-3": "description", "0-0": "sourcedId", "0-1": "Yes", "0-2": "UUID", "0-3": "Unique ID for the class. SourcedId is used in other files and must be unique across all classes.", "1-0": "status", "1-1": "No", "1-2": "String", "1-3": "“active”", "2-0": "dateLastModified", "2-1": "No", "2-2": "Date", "2-3": "The date that this record was last modified. (FORMAT IS YYYY-MM-DD)", "3-0": "title", "3-1": "Yes", "3-2": "String", "3-3": "Name of this class", "4-0": "grade", "4-1": "No", "4-2": "String", "4-3": "Grade (i.e. 9)", "5-0": "courseSourcedId", "5-1": "No", "5-2": "UUID", "5-3": "SourcedId of the course of which this class is an instance", "6-0": "classCode", "6-1": "No", "6-2": "String", "6-3": "Human readable code used to help identify this class", "7-0": "classType", "7-1": "Yes", "7-2": "String", "7-3": "“homeroom”, “scheduled”", "8-0": "location", "8-1": "No", "8-2": "String", "8-3": "Human readable description of where the class is physically located", "9-0": "schoolSourcedId", "9-1": "Yes", "9-2": "UUID", "9-3": "SourcedId of the organization which teaches this class", "10-0": "termSourcedId", "10-1": "No", "10-2": "UUID", "10-3": "SourcedId of the academicSessions(s) in which the class is taught. If more than one term is needed, use double quotes and delimit with commas, (per RFC 4180)\nExamples:\n“1,2”\n1\n“1,4,8”", "11-0": "subjects", "11-1": "Yes", "11-2": "String", "11-3": "Subject name(s). If more than one subject is needed, use double quotes, and delimit with commas (per RFC 4180):\n\nExamples:\n“chemistry, physics”\nphysics\n“music, drama, poetry”" }, "cols": 4, "rows": 12 } [/block] **User Rostering** Based on the data present in the “users.csv” file, users data will be rostered into the Gooru system. * New user will be created if there is no user present for the organization with same “sourced_id” and username * If a user with the same “sourced_id” and username / user_id found for the organization, the last modified date from the csv will be compared with the last modified date present in the database. If date from csv file is later than what present in date, user data will be updated otherwise no action will be taken “username” is mandatory while rostering users data. This should be the login name or login id of the user in the client system. While rostering the record into Gooru, uniqueness of the records is based on the “username” and tenant. If there is already a user with “username” (i.e. reference_id in “users” table of Core DB) for the tenant, error (409 Conflict) will be returned. Supported Fields: [block:parameters] { "data": { "h-0": "file header", "h-1": "required", "h-2": "format", "h-3": "description", "0-0": "sourcedId", "0-1": "Yes", "0-2": "UUID", "0-3": "Unique ID for the user. SourcedId is used in other files and must be unique across all users,", "1-0": "status", "1-1": "No", "1-2": "String", "1-3": "“active”", "2-1": "No", "2-0": "dateLastModified", "2-2": "Date", "2-3": "The date that this record was last modified. (FORMAT IS YYYY-MM-DD)", "3-0": "orgSourcedIds", "3-1": "Yes", "3-2": "UUID", "3-3": "SourcedIds of the Organizations to which this user belongs. \n\n(Note in most cases, it is expected that users will belong to a single school).", "4-0": "role", "4-1": "Yes", "4-2": "String", "4-3": "“teacher”, “student”", "5-0": "username", "5-2": "String", "5-1": "Yes", "5-3": "Username", "6-0": "userId", "6-1": "No", "6-2": "String", "6-3": "external machine readable id (e.g. LDAP id, LTI id) for this user, to be used if the sourcedId should not be used.", "7-0": "givenName", "7-1": "Yes", "7-2": "String", "7-3": "User’s first name", "8-0": "familyName", "8-1": "Yes", "8-2": "String", "8-3": "User’s surname", "9-0": "identifier", "9-1": "No", "9-2": "String", "9-3": "Identifier for the user with a human readable meaning", "10-0": "email", "10-1": "No", "10-2": "String", "10-3": "Email address for the user", "11-0": "sms", "11-1": "No", "11-2": "String", "11-3": "SMS address for the user", "12-0": "phone", "12-1": "No", "12-2": "String", "12-3": "Phone number for the user", "13-0": "agents", "13-1": "No", "13-2": "String", "13-3": "SourcedIds of the users to which this user has a relationship.\nNote: In most cases this will be for indicating parental relationships." }, "cols": 4, "rows": 14 } [/block] **Enrollment Rostering** Based on the data present in the “enrollments.csv” file, enrollments will be rostered into the Gooru system. It's mandatory to pass organization sourced id, class sourced id and user sourced id in order to enroll the users. Missing any of the details will not create enrollment and specific errors should be returned. Support Fields: [block:parameters] { "data": { "h-0": "file header", "h-1": "required", "h-2": "type", "h-3": "description", "0-0": "sourcedId", "0-1": "Yes", "0-2": "UUID", "0-3": "Id of this enrollment", "1-0": "classSourcedId", "1-1": "Yes", "1-2": "UUID", "1-3": "Id of the class", "2-1": "Yes", "2-2": "UUID", "3-1": "Yes", "3-2": "UUID", "2-0": "schoolSourcedId", "2-3": "Id of the school", "3-0": "userSourcedId", "3-3": "Id of the user (teacher or student)", "4-0": "role", "4-1": "Yes", "4-2": "String", "4-3": "“student” | “teacher”", "5-0": "status", "5-1": "Yes", "5-2": "String", "5-3": "“active”", "6-0": "dateLastModified", "6-1": "No", "6-2": "Date", "6-3": "The date that this record was last modified. (FORMAT IS YYYY-MM-DD)", "7-0": "primary", "7-1": "Yes", "7-2": "Boolean", "7-3": "MUST only be set to true for ONE teacher for a class." }, "cols": 4, "rows": 8 } [/block] [block:api-header] { "title": "Upload Roster" } [/block] This API is used to upload the roster files in zip format. All csv files contained in the zip should comply to the [IMS Global Oneroster specification](https://www.imsglobal.org/lis/imsOneRosterv1p0/imsOneRosterCSV-v1p0.html). Invalid or extra uploaded files are skipped from rostering. Current scope of this API is to process orgs, users, classes and enrollments only. Other files such as academicSessions, demographics and courses will be not be processed and rostered into system. All data will be rostered against the client id (tenant) in Gooru. Client applications need to provide same client id and secret while performing SSO which id used while rostering the users. This API will not return any details on error / exceptions for the failed records. Status API should be explicitly called with required parameters to get the status / error while rostering files. [block:parameters] { "data": { "h-0": "Name", "h-1": "Value", "0-0": "HTTP Method", "0-1": "POST", "1-0": "End Point", "1-1": "http://{host}/api/nucleus-oneroster/{version}/upload", "2-0": "Sample", "2-1": "http://oneroster.gooru.org/api/nucleus-oneroster/v1/upload" }, "cols": 2, "rows": 3 } [/block] ###Headers [block:parameters] { "data": { "h-0": "Header Name", "h-1": "Value", "0-0": "Authorization", "0-1": "Basic <BASE64 ENCODED CREDENTIALS>", "1-0": "Content-Type", "1-1": "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" }, "cols": 2, "rows": 2 } [/block] * Replace the correct value of encoded credentials in ‘Authorization’ header * Boundary in the ‘Content-Type’ header is sample. Need to be replaced with actual boundary while calling the API. ###Request Payload Attach the file with request. ###Success Reponse HTTP Status Code: 201 Created Location header of the response will contain the Upload Id which can be used to get the status of the upload. ###Error Response In case of any error while uploading the Roster, API will not provide any details of error / exceptions for records in the files. For Error response codes, refer to Reference section in this document. [block:api-header] { "title": "Get Upload Status" } [/block] This API is used to get the status of the upload for give upload id. For now this API will return total records and success record counts. It may be enhanced in future to return records level details. [block:parameters] { "data": { "h-0": "Name", "h-1": "Value", "0-0": "HTTP Method", "0-1": "GET", "1-0": "End Point", "1-1": "http://{host}/api/nucleus-oneroster/{version}/upload/{uploadId}/status", "2-0": "Sample", "2-1": "http://oneroster.gooru.org/api/nucleus-oneroster/v1/upload/62f3fbd3-4341-4e30-8efe-3b655074133d/status" }, "cols": 2, "rows": 3 } [/block] ###Headers [block:parameters] { "data": { "h-0": "Header Name", "h-1": "Value", "0-0": "Authorization", "0-1": "Basic <BASE64 ENCODED CREDENTIALS>" }, "cols": 2, "rows": 1 } [/block] * Replace the correct value of encoded credentials in ‘Authorization’ header. ###Request Payload No request payload required for this API ###Success Response HTTP Status Code: 200 OK ###Error Response Refer to reference section of this document below. ###Possible Status [block:parameters] { "data": { "h-0": "Status", "h-1": "Purpose", "0-0": "pending", "0-1": "Denotes the zip upload is success and pending to be process. The uploads are queued at server and picked up one by one.", "1-0": "accepted", "1-1": "Denotes that the upload zip file has been accepted for rostering. This is the status after parsing and validation of all csv files, and still the sync with Gooru main database is pending.", "2-0": "failed", "2-1": "Denotes that the upload has been failed. In case of failure while rostering records, this status will be returned.", "3-0": "completed", "3-1": "Denotes that the upload has been complete. When all records are rostered successfully, this status will be returned." }, "cols": 2, "rows": 4 } [/block] ##Responses [block:code] { "codes": [ { "code": "{\n\t\"status\": \"pending\",\n\t\"total_records\": {\n\t\t\"orgs\": 1,\n\t\t\"users\": 10,\n\t\t\"classes\": 2,\n\t\t\"enrollments\": 12\n\t},\n\t\"success_records\": {\n\t\t\"orgs\": 1,\n\t\t\"users\": 8,\n\t\t\"classes\": 2,\n\t\t\"enrollments\": 10\n\t}\n}\n", "language": "json", "name": "Pending" }, { "code": "{\n \"status\": \"failed\",\n \"errors\": {\n \"orgs_errors\": [\n {\n \"error\": \"Field 'sourcedId' is mandatory but no value was provided.\",\n \"line_number\": 3\n }\n ],\n \"classes_errors\": [\n {\n \"error\": \"Field 'title' is mandatory but no value was provided.\",\n \"line_number\": 1\n }\n ],\n \"enrollments_errors\": [\n {\n \"error\": \"Field 'role' is mandatory but no value was provided.\",\n \"line_number\": 29\n }\n ],\n \"users_errors\": [\n {\n \"error\": \"Field 'username' is mandatory but no value was provided.\",\n \"line_number\": 2\n }\n ]\n }\n}\n", "language": "json", "name": "Failed" }, { "code": "{\n\t\"status\": \"accepted\",\n\t\"total_records\": {\n\t\t\"orgs\": 1,\n\t\t\"users\": 10,\n\t\t\"classes\": 2,\n\t\t\"enrollments\": 12\n\t},\n\t\"success_records\": {\n\t\t\"orgs\": 0,\n\t\t\"users\": 0,\n\t\t\"classes\": 0,\n\t\t\"enrollments\": 0\n\t}\n}\n", "language": "json", "name": "Accepted" }, { "code": "{\n\t\"status\": \"completed\",\n\t\"total_records\": {\n\t\t\"orgs\": 1,\n\t\t\"users\": 10,\n\t\t\"classes\": 2,\n\t\t\"enrollments\": 12\n\t},\n\t\"success_records\": {\n\t\t\"orgs\": 1,\n\t\t\"users\": 8,\n\t\t\"classes\": 2,\n\t\t\"enrollments\": 10\n\t}\n}\n", "language": "json", "name": "Completed" } ] } [/block] [block:api-header] { "title": "Reference" } [/block] ## HTTP Response Codes [block:parameters] { "data": { "h-0": "HTTP Status Code", "h-1": "HTTP Method", "h-2": "Response Body", "h-3": "Description", "0-0": "200", "0-1": "GET", "0-2": "Entity which is requested", "0-3": "Operation successful without error", "1-0": "201 Created", "1-1": "POST", "1-2": "No Entity in body except for the location of entity in header", "1-3": "Entity creation successful", "2-0": "204 No Content", "2-1": "PUT, DELETE", "2-2": "No Content", "2-3": "Entity update or delete is successful", "3-0": "400 Bad Request", "3-1": "ALL", "3-2": "Error messages", "3-3": "Malformed, invalid or incorrect request or request parameters which are applicable to this Entity", "4-0": "401 Unauthorized", "4-1": "ALL", "4-2": "No Content", "4-3": "Action requires authentication or session token may have expired. Note that we don’t return www-authenticate headers as we do not want browser to participate in the auth flow.", "5-0": "403 Forbidden", "5-1": "ALL", "5-2": "Error message", "5-3": "Authentication failure, or invalid API key, or insufficient privileges", "6-0": "f693bd0a-a67e-4e2d-92c7-0b75d0b0dd3e", "6-1": "ALL", "6-2": "No Content", "6-3": "Entity not found", "7-0": "405 Not Allowed", "7-1": "ALL", "8-1": "ALL", "9-1": "POST, PUT", "7-2": "No Content", "8-2": "No Content", "9-2": "No Content", "10-2": "Error message", "7-3": "Entity does not support the requested operation", "8-0": "408 Request Timeout", "8-3": "Request has timed out. The system was not able to complete the operation within specified time", "9-0": "413 Request Entity Too Large", "9-3": "The representation of payload is too large for server to handle, or is not allowed", "10-0": "500 Internal Server Error", "10-1": "ALL", "10-3": "Some exception happened while processing the request. The message may be technical and may not be suitable for UI consumption." }, "cols": 4, "rows": 11 } [/block] [block:api-header] { "title": "Do’s and Don’ts" } [/block] * Make sure to keep a track of all sourcedIds. These sourcedIds are required when you want to enrol the new user to the old class or old user to new class. * CSV files should include all columns in the same sequence, including optional ones irrespective of whether there is data or not. * One user can be associated to one organization only. * Always class is associated with a teacher account. * If you need to associate a Student to an existing class please use the same sourcedid of that respective class. * ​Supported organization type : "school"​ * ​Supported Users Role : student, teacher​ * Enrollments : make sure teacher related data is available first then student. * Update existing Users records: there is no change in the process. But if you need to delete any account, update the status column with appropriate value and sourcedId. * Update existing Enrollments: * If you want to add a new user to the existing class, make sure you provide the correct sourcedId in the enrollments.csv file