API Access to ADAM
Introduction the ADAM API
An API allows for another computer program to connect to ADAM to either access data stored in its database or to make changes to that data. ADAM makes use of a “RESTful” API and returns data mostly in JSON format.
What is a RESTful system?
REST is a technique of communicating with an API which is very common amongst web-based applications - such as ADAM. Other programs will make one of four types of web request depending on what they are hoping to do in the database, and include the necessary information for ADAM. ADAM then responds to that request with the appropriate data or success code.
How does access control work?
Access to the ADAM database is controlled using a token system. These are essentially random passwords that are created which are shared only with the other program that wishes to access the API. Because these tokens are really only meant to be used by a computer, it is not important that they are memorable or easy to type. In fact, ADAM will generate a random set of characters to be used as the API key and you are strongly encouraged to make use of the suggested token.
It is vitally important that the token is kept confidential between the person issuing it and the person using it. Anyone who knows the token will be able to access the API. Please read the Best Practices section below for further information.
Each API token can be given access to one or more API resources. Anyone who knows the API token can access all the resources that have been allocated to that token.
API tokens can be revoked at any point and can have their resources changed and added to over time. Any changes you make to the token and its API resource assignments will take place immediately.
Managing API Tokens in ADAM
In ADAM: Navigate to Administration → Security Administration → Manage API Tokens.
A list of existing API tokens will be shown. Click on Add new API Token to begin the process of creating a new Token.
- A random 30-character token will be generated. This should be left unmodified unless you have a very specific reason to do so. Once set, token values cannot be changed. If you need to change the token, you must delete the existing endpoint and create a new one.
- Select the appropriate resources to allow the token to access. Hold down the “Ctrl” key on your keyboard to allow you to select multiple resources by then clicking on those resources while holding down that button.
- Add notes if required. It is a good idea to make note of what service is making use of the API token.
- Click on Save Token.
The random 30 character token must be kept secret since will will allow anyone who knows it access to the data stored in the ADAM database. It will need to be shared with the integration provider and great care should be taken with how they are provided the API key. We strongly recommend against sending this information via email or other unsecured means.
Managing Existing Tokens
In the table of existing API tokens, you have the option to edit, delete or regenerate the API token.
Edit allows you to change the Resources and Notes associated with an endpoint, but does not allow you to edit the Token.
Regenerating a token will create a new random value for the token but will keep your resources and notes the same. If you do regenerate a token, please remember to update your external systems with the new token.
Deleting the token will remove it from the list and it will no longer be available for use. Systems that rely on the resources will no longer have access to the data supplied by ADAM.
Best Practice Security Principals
The following are provided as best practice guidelines for managing API tokens.
- Treat API Tokens as top secret. Do not send them via email and do not publish them in a place where they could be accessed by unauthorized personnel. API access can expose sensitive data. Allowing an API tokens to fall into the wrong hands could expose personal information about the users of your system and this would be considered an offence under the “Protection of Personal Information Act” (POPIA). Access to such personal information could become a security and safety issue.
- Each entity (e.g. a program that is accessing data from ADAM) should have its own token or tokens. Tokens should never be shared between entities. If another entity requires access to the same data, give it a new token. This allows you to revoke each token individually and stop one program from accessing the data without affecting any other programs.
- Programmers integrating with the ADAM API should not, under any circumstances, include API tokens in their source code and should always fetch them from some form of access controlled data storage.
API Interactions
Authentication
All API requests should be authenticated using a Bearer Token.
Example:
Authenticating with the token value svyzBvkuuXLdJwV7YcdYsQFVX5ha54 would yield the following header:
Authorize: Bearer svyzBvkuuXLdJwV7YcdYsQFVX5ha54
ADAM also will accept a Basic Authorization header with an arbitrary username. Authenticating with the username apitoken token value svyzBvkuuXLdJwV7YcdYsQFVX5ha54 would yield the following header:
Authorize: Basic YXBpdG9rZW46c3Z5ekJ2a3V1WExkSndWN1ljZFlzUUZWWDVoYTU0
API Requests
All API requests, as listed below, should be prefixed with the “apicore” folder to make the URL end-point:
https://adam.example.com/apicore/
The remainder of the URL generally consists of a module name, a data set and zero or more parameters. For example:
https://demo.adam.co.za/apicore/apirequests/test/parameter
Parameters should be “percent encoded”. Spaces should be encoded as %20 and not as a “+”.
A GET request to this API end-point would require token access to the resource: APIRequests/test/get. Note that without specifically assigned access, access to the resource - even this test resource - will be denied.
API Responses
All responses will be returned within JSON objects. The basic structure of these objects is:
{
"data": null,
"message": "",
"response": {
"error": "",
"code": 200
}
}
Any response which does not have these properties as listed above, MUST be treated as invalid.
The contents of the data attribute will depend on the API endpoint being interrogated.
The message attribute contains a human-readable message which, in most cases, describes the data set that has been returned. This is intended for debugging and may change unpredictably with future versions of ADAM.
The response code should mirror the HTTP response code which will be descriptive of the success or failure of the call. The error attribute will provide a human-readable description of the error. The error message is not intended to be interpreted by machines.
API Resources
These are the available documented resources. Other resources are available but are not listed here until their development is complete and considered stable.
APIRequests/test/get
A test method to the API.
Request
GET /apicore/apirequests/test/[Parameter1]/[Parameter2]
Parameters
[Parameter1]: An arbitrary parameter that is returned.
[Parameter2]: An arbitrary parameter that is returned.
Output
The output data will be a JSON object containing attributes parameter1 and parameter2, both of which will contain the values provided in the request.
Example:
https://demo.adam.co.za/apicore/apirequests/test/First%20Parameter/Second
{
"data": {
"parameter1": "First Parameter",
"parameter2": "Second"
},
"message": "Hello, you're speaking to Random High School's ADAM. We are currently on revision 6125 and the local time is 11:26:46.",
"response": {
"error": "OK",
"code": 200
}
}
AbsenteeKiosk/register/post
This registers a pupil as either present or late. It is intended to be used either by automated access control systems or ADAM’s own absentee kiosk.
Request
- POST to /apicore/absenteekiosk/register/<pupil>
- POST to /apicore/absenteekiosk/register with pupil=<pupil> as a form variable in the POST body
Parameters
- <pupil>: The ADAM identifier of the pupil.
Response
If the pupil identifier can be matched against an existing pupil, the endpoint will return two items in the data object. These are used internally by ADAM’s “Absentee Kiosk” feature.
- message: an HTML message which will contain the name of the pupil and confirmation that they have been registered.
- colour: an HTML hex code either red, green or amber depending on the status.
The times and windows are configured in ADAM’s site settings. Depending on these settings, ADAM will report one of the following HTTP status codes:
- The pupil identifier is not recognised: 404 (Not found)
- The pupil is too early and registers before the earliest time allowed: 406 (Not acceptable)
- The pupil registers during the first window and is recorded as present: 200 (OK)
- The pupil registers during the second window and is recorded as late: 202 (Accepted)
- The pupil is too late and registers after the second window closes: 406 (Not accetable)
In each case, the status code will be corroborated by a more descriptive message.
Example:
{
"data": {
"message": "<strong>Joe Smith</strong> has been recorded as <strong>present</strong>",
"colour": "green"
},
"message": "",
"response": {
"error": "Present",
"code": 200
}
}
Absentees/summarycount/get
Returns a list of pupils and the number of days they’ve been absent. This counts all the absentees with a reason that is set to count as “absent”. Only pupils with absentee records are returned. All pupils, including those that might have left the school, are returned.
Request
GET to /apicore/absentees/summarycount/<from>[/<to>]
Parameters
- <from>: An ISO formatted date to begin the summary. This date is included in the summary.
- <to>: OPTIONAL. An ISO formatted date to mark the end of the range of the summary. This date is included in the summary. If omitted, the current date is used instead.
Response
The data attribute will be an array of zero or more JSON objects.
Example:
GET /apicore/absentees/summarycount/2018-01-01/2018-01-31
{
"data": [
{
"pupil_id": 875,
"pupil_admin": "19634",
"absent_count": 1
},
{
"pupil_id": 879,
"pupil_admin": "52351",
"absent_count": 2
}
],
"message": "Absentee counts for all pupils from 2018-01-01 to 2018-01-31.",
"response": {
"error": "OK",
"code": 200
}
}
- pupil_id is ADAM’s internal database identifier and will always refer to a unique pupil.
- pupil_admin is the user-defined administration number. While this should not change, it may do so at the school’s discretion.
- absentee_count is the number of days that the pupil has been recorded as absent. This does not include absentee records which do not prejudice a pupil (such as “away on sports tour”). These reasons are customisable by the school.
Absentees/list/get
Gets a list of pupils absent with the reasons.
Request
GET to /apicore/absentees/list/[<date>[/<to>]]
Parameters
- <date>: OPTIONAL. An ISO formatted date to query the absentees. If omitted, today’s date is used.
- <to>: OPTIONAL. An ISO formatted date. If provided, all absentees on or between the <date> and <to> dates will be provided. If omitted, <to> effectively takes the same value as <date> and only absentees for that date are provided.
Response
GET /apicore/absentees/list/2018-01-30
{
"data": [
{
"pupil_id": 1720,
"pupil_admin": "47547",
"absent_date": "2018-01-31",
"absent_reason_id": 1,
"absent_reason_description": "Absent",
"absent_notes": "App Test"
}
],
"message": "Absentee list for all pupils from 2018-01-31 to 2018-01-31.",
"response": {
"error": "OK",
"code": 200
}
}
The data attribute contains an array of zero or more absentee records. Each record is a JSON object with the following properties:
- pupil_id is ADAM’s internal identifier for the pupil.
- pupil_admin is a school-provided identifier for the pupil. While this should not change, it may do so at the school’s discretion.
- absent_date gives the date that the absentee occurred on in case a range of dates was requested. A pupil can only have a single absentee record per day.
- absent_reason is an internal identifier corresponding to the absentee reason that was chosen.
- absent_reason_description is the descriptor for the absentee reason. It will be consistent per absent_reason in any one API call, but may change between calls at the school’s discretion.
- absent_notes contain the end-user provided notes related to the pupil’s absence. This may contain personal information.
Applications/applicationformfields/get
The ability to see which fields are accepted as part of the application form. These fields can be customised by updating the core and custom fields to change their availability in the application form.
Request
GET request to /apicore/applications/applicationformfields
Parameters
None
Response
The output data will be a JSON object containing the fields that are accepted. Within the data property are two sub arrays for families and pupils. These list the field names that are accepted.
Example:
https://demo.adam.co.za/apicore/applications/applicationformfields
{
"data": {
"families": [
"family_primary_idnum",
"family_primary_lastname",
"family_primary_firstname",
"family_primary_fullfirst",
"family_primary_title",
"family_primary_initials",
"family_primary_gender",
"family_primary_birth",
"family_primary_occupation",
"family_primary_employer",
"family_primary_workphone",
"family_primary_cell",
"family_primary_cell_sms",
"family_primary_email",
"family_secondary_idnum",
"family_secondary_lastname",
"family_secondary_firstname",
"family_secondary_fullfirst",
"family_secondary_title",
"family_secondary_initials",
"family_secondary_gender",
"family_secondary_birth",
"family_secondary_occupation",
"family_secondary_employer",
"family_secondary_workphone",
"family_secondary_cell",
"family_secondary_cell_sms",
"family_secondary_email",
"family_address_residential_1",
"family_address_residential_2",
"family_address_residential_suburb",
"family_address_residential_city",
"family_address_residential_province",
"family_address_residential_code",
"family_address_residential_country",
"family_address_postal_1",
"family_address_postal_2",
"family_address_postal_suburb",
"family_address_postal_city",
"family_address_postal_province",
"family_address_postal_code",
"family_address_postal_country",
"family_home_phone",
"family_home_fax",
"family_ice",
"family_ice_number",
"family_report_required"
],
"pupils": [
"pupil_lastname",
"pupil_fullfirst",
"pupil_firstname",
"pupil_gender",
"pupil_idnumber",
"pupil_birth",
"pupil_religion",
"pupil_population_id",
"pupil_language_id",
"pupil_language_other",
"pupil_teaching_language_id",
"pupil_entry",
"pupil_final",
"pupil_email",
"pupil_cell",
"pupil_cell_sms",
"pupil_allergies",
"pupil_medaid_name",
"pupil_medaid_number",
"pupil_medaid_principal",
"pupil_medaid_principal_id",
"pupil_doctor",
"pupil_doctor_phone",
"pupil_orphan_status",
"pupil_relationships",
"pupil_atschool",
"pupil_prevschool_firstprovince",
"pupil_prepschool",
"pupil_prevschool_country",
"pupil_prevschool_formalgrr",
"pupil_nationality",
"pupil_studypermit_required",
"pupil_studypermit",
"custom_38",
"custom_36"
]
},
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
Applications/apply/post
Submit an application to the site. Note that if the ID number submitted matches an existing parent, the child will automatically be linked to that parent. Note that when applications are submitted through ADAM’s web interface, ADAM automatically authenticates the parent to ensure that strange children are not linked to their profiles. Implicit in the web application is the validation of the email address which is done as part of the application process. Applications received via the API are not given such scrutiny and special care should be taken on the sending system to mitigate against spam and fraudulent attempts at applications.
Request
POST request to /apicore/applications/apply
Parameters
None - data sent by message body.
Body
The body of the request should contain the data required for the application. The data should either be in JSON format, or as an encoded query string (as is typical with normal form-submitted data).
Fields: The three fields, idnumber, email, and phone, may be duplicated within the parent information. It is not necessary that they complete them twice, but it is necessary for the information to be submitted both with the family and as separate information.
Note carefully the point about existing parents: if their ID or passport numbers already exist in ADAM’s database, the family information provided in the application will be silently discarded in favour of information already in the database. Thus having updated contact information provided in the email and phone fields is important. The silent discarding is to ensure that malicious actors cannot update family information without their authorisation.
- idnumber: The South African ID or passport number of the parent making the submission. This is used to match against other families. If omitted, no linking will take place. If this value matched the value of an existing family, the pupil will automatically be linked to that family and the information provided in the family info will be lost.
- email: The email address to get in tough with the parents regarding the application.
- phone: The phone number to get in touch with the parents regarding the application.
- application: The body of the application. This contains:
- pupil: An array of child information to be included on the application. Each element of the array is a sub-array of information for each child. A valid application must have at least one child and no more than 5 children.
- family: An array of family fields to be included.
- email: further details of the parents’ email addresses.
A minimum set of required fields is:
- Families:
- Primary last name (family_primary_lastname)
- Primary first name (family_primary_firstname)
- Primary full first names, can duplicate family_primary_firstname if required (family_primary_fullfirst)
- Pupils:
- Last name (pupil_lastname)
- Preferred name (pupil_firstname)
- Full first name, can duplicate pupil_firstname if required (pupil_fullfirst)
- Gender (pupil_gender)
- Year of entry (entry_year) - note non-standard field naming
- Month of entry (entry_month) - note non-standard field naming
- Grade of entry (entry_grade) - note non-standard field naming. Note, integers accepted only. For grades prior to Grade 1, zero or negative grades: Grade R = 0, Grade RR = -1, Grade RRR = -2, etc.
Example (JSON):
{
"idnumber": "1234567890123",
"email": "morticia@adam.co.za",
"phone": "0834699569",
"application": {
"pupil": [
{
"pupil_lastname": "Addams",
"pupil_firstname": "Wednesday",
"pupil_fullfirst": "Wednesday Jane",
"pupil_gender": "Female",
"entry_year": "2019",
"entry_month": "1",
"entry_grade": "10",
"relationship": [{
"primary": "biological",
"secondary": "step parent"
}]
},
{
"pupil_lastname": "Addams",
"pupil_firstname": "Pugsley",
"pupil_fullfirst": "Pugsley George",
"pupil_gender": "Male",
"entry_year": "2019",
"entry_month": "1",
"entry_grade": "8",
"relationship": [{
"primary": "step parent",
"secondary": "biological"
}]
}
],
"family": {
"family_primary_idnum": "1234567890123",
"family_primary_lastname": "Addams",
"family_primary_firstname": "Gomez",
"family_primary_fullfirst": "Gomez",
"family_secondary_idnum": "1234567890123",
"family_secondary_lastname": "Addams",
"family_secondary_firstname": "Morticia",
"family_secondary_fullfirst": "Morticia May"
},
"email": [
{
"member": "primary",
"address": "gomes@adam.co.za",
"bulk": "Yes",
"reports": "Yes"
},
{
"member": "secondary",
"address": "morticia@adam.co.za",
"bulk": "Yes",
"reports": "Yes"
}
]
}
}
Example (URL Encoded):
idnumber=1234567890123&email=morticia%40adam.co.za&phone=0834699569&application%5Bpupil%5D%5B0%5D%5Bpupil_lastname%5D=Addams&application%5Bpupil%5D%5B0%5D%5Bpupil_firstname%5D=Wednesday&application%5Bpupil%5D%5B0%5D%5Bpupil_fullfirst%5D=Wednesday+Jane&application%5Bpupil%5D%5B0%5D%5Bpupil_gender%5D=Female&application%5Bpupil%5D%5B0%5D%5Bentry_year%5D=2019&application%5Bpupil%5D%5B0%5D%5Bentry_month%5D=1&application%5Bpupil%5D%5B0%5D%5Bentry_grade%5D=10&application%5Bpupil%5D%5B0%5D%5Brelationship%5D%5B0%5D%5Bprimary%5D=biological&application%5Bpupil%5D%5B0%5D%5Brelationship%5D%5B0%5D%5Bsecondary%5D=step+parent&application%5Bpupil%5D%5B1%5D%5Bpupil_lastname%5D=Addams&application%5Bpupil%5D%5B1%5D%5Bpupil_firstname%5D=Pugsley&application%5Bpupil%5D%5B1%5D%5Bpupil_fullfirst%5D=Pugsley+George&application%5Bpupil%5D%5B1%5D%5Bpupil_gender%5D=Male&application%5Bpupil%5D%5B1%5D%5Bentry_year%5D=2019&application%5Bpupil%5D%5B1%5D%5Bentry_month%5D=1&application%5Bpupil%5D%5B1%5D%5Bentry_grade%5D=8&application%5Bpupil%5D%5B1%5D%5Brelationship%5D%5B0%5D%5Bprimary%5D=step+parent&application%5Bpupil%5D%5B1%5D%5Brelationship%5D%5B0%5D%5Bsecondary%5D=biological&application%5Bfamily%5D%5Bfamily_primary_idnum%5D=1234567890123&application%5Bfamily%5D%5Bfamily_primary_lastname%5D=Addams&application%5Bfamily%5D%5Bfamily_primary_firstname%5D=Gomez&application%5Bfamily%5D%5Bfamily_primary_fullfirst%5D=Gomez&application%5Bfamily%5D%5Bfamily_secondary_idnum%5D=1234567890123&application%5Bfamily%5D%5Bfamily_secondary_lastname%5D=Addams&application%5Bfamily%5D%5Bfamily_secondary_firstname%5D=Morticia&application%5Bfamily%5D%5Bfamily_secondary_fullfirst%5D=Morticia+May&application%5Bemail%5D%5B0%5D%5Bmember%5D=primary&application%5Bemail%5D%5B0%5D%5Baddress%5D=gomes%40adam.co.za&application%5Bemail%5D%5B0%5D%5Bbulk%5D=Yes&application%5Bemail%5D%5B0%5D%5Breports%5D=Yes&application%5Bemail%5D%5B1%5D%5Bmember%5D=secondary&application%5Bemail%5D%5B1%5D%5Baddress%5D=morticia%40adam.co.za&application%5Bemail%5D%5B1%5D%5Bbulk%5D=Yes&application%5Bemail%5D%5B1%5D%5Breports%5D=Yes
Response
A simple object providing notification of success or failure. The application field gives a code unique to this application which will, in a future development, allow for editing of the application.
{
"data": {
"application": "6ESKmQYMWH3KbJUdAESsZUi3BysaLx"
},
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
ClassReg/grade/get
Returns a list of classes that a grade of pupils is registered for.
Request
GET /apicore/classreg/grade/<grade>
Parameters
- <grade> is an integer representing the grade of pupils to retrieve from the database. Note that 0 represents Grade R and negative grades represent the pre-school grades.
Response
The response is an array of pupil registration objects, each following the structure below:
{
"data": [
{
"registration_id": 162555,
"pupil_id": 1234,
"pupil_lastname": "Last-Name",
"pupil_firstname": "First",
"class_id": 4231,
"class_description": "Z",
"class_gradeyear": "11",
"subject_id": 101,
"subject_name": "English Home Language",
"subject_short": "Eng",
"category_id": 1,
"category_description": "Academic",
"registration_datestart": "2024-01-20",
"registration_dateend": null,
"staff_id": 123,
"staff_firstname": "Educator",
"staff_lastname": "Mary"
}
],
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
DataQuery/get/get
Provides automated access to a whole-school scratch list. The contents of the scratch list fields can be customised as per the settings in ADAM found at Administration → Security → Manage Data Query API Fields.
Please treat this feature with the utmost care. By its very definition, it gives wide access to a range of personal data.
Create a Data Query Secret
In order to use this API endpoint, an additional data query token must be defined.
Note that field access definitions can only be linked to a single API Token. If multiple API tokens require access to the same fields, this process must be duplicated for each API token and a unique list created for each.
- Select the API Token that you want to associate with this query. Note that only API Tokens who have access to the DataQuery/get/get resource may be selected here.
- Make a note of the Secret and do not share this with unauthorized personnel.
- Choose a data source for the query. Once set here, this cannot be changed later.
- Add a comment to provide insight into the function and reason for this query.
Click on Add record when done.
A second screen will show, allowing you to select the fields required for this query:
Check the fields that you require and save your selections using the button at the bottom.
Request
GET /apicore/dataquery/get/<secret>
OR, for a modified data structure to provide more consistency for automated systems, (see example response below), add the version parameter “2” to the end of the request.
GET /apicore/dataquery/get/<secret>/2
Parameters
- <secret> is a series of random characters as created above.
Response
GET /apicore/dataquery/get/GXiE4V5qYB
{
"data": {
"49": {
"admin_number_1": "3316",
"lurits_number_177": "",
"age_6": "15 years, 200 days",
"gender_10": "Male"
},
"4688": {
"admin_number_1": "6333",
"lurits_number_177": "",
"age_6": "17 years, 10 days",
"gender_10": "Male"
}
},
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
The data attribute is a JSON object with zero or more attributes, each being the ID of the relevant data object as specified in the Data Query setup. Each of these objects will have a number of attributes depending on the fields chosen. Note that the names of these attributes can be overridden by the school. However, there is a unique numeric identifier that is appended to each which will remain constant. Logic should be based around that identifier. Note that custom fields will contain the word “custom” before the unique identifier and so that should also be checked for.
GET /apicore/dataquery/get/GXiE4V5qYB/2
Where the optional parameter “2” is included at the end, the structure of the returned data will change. An additional “fields” property is included with clearer textual descriptions of the fields. The fields in each of the data objects is identified by the immutable identifier. This allows automated systems to ignore parts of the field name that might change.
{
"fields": [
{
"id": 1,
"name": "Admin Number"
},
{
"id": 177,
"name": "LURITS Number"
},
{
"id": 6,
"name": "Age"
},
{
"id": 10,
"name": "Gender"
}
],
"data": {
"49": {
"1": "3316",
"177": "",
"6": "15 years, 200 days",
"10": "Male"
},
"4688": {
"1": "6333",
"177": "",
"6": "17 years, 10 days",
"10": "Male"
}
},
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
DataQuery/getsince/get
See above.
Request
GET /apicore/dataquery/getsince/<secret>/<timestamp>[/<version>]
Parameters
- <secret> is the secret defined for a list. This also determines what type of data and which fields are returned.
- <timestamp> is a Unix integer timestamp.
- <version> , if supplied, is the version of the data structure to be returned. See above.
Response
See above.
DataQuery/getone/get
See above.
Request
GET /apicore/dataquery/getone/<secret>/<identifier>[/<version>]
Parameters
- <secret> is the secret defined for a list. This also determines what type of data and which fields are returned.
- <identifier> is the identifier of the dataobject to be returned.
- <version> , if supplied, is the version of the data structure to be returned. See above.
Response
See above.
Export/families/get
Allows family information to be extracted easily.
Request
GET /apicore/export/families
GET /apicore/export/families/all
GET /apicore/export/families/current
GET /apicore/export/families?updated_since=2024-10-01+08:15:30
GET /apicore/export/families/all?updated_since=2024-10-01+08:15:30
GET /apicore/export/families/current?updated_since=2024-10-01+08:15:30
Parameters
The last parameter (all or current) may be omitted - the default setting is to return current families only. The structure of the data is unchanged.
An optional parameter, updated_since, will only return changes that have been made on or after the time specified. Any valid timestamp, that is URL encoded, can be used.
Note well that changes to email addresses are not reflected in the modified time.
Response
Valid responses will contain an array of family objects in the data property. The family_primary_email and family_secondary_email will be arrays of email addresses:
{
"data": [
{
"family_id": 531,
"family_admin": "0",
"family_primary_lastname": "Adamson",
"family_primary_firstname": "Adam",
"family_primary_title": "Mr",
"family_primary_idnum": "1234567890123",
"family_primary_occupation": "Businessman",
"family_primary_employer": "ADAM EduTech",
"family_primary_workphone": "0615096077",
"family_primary_cell": "0615096077",
"family_secondary_lastname": "",
"family_secondary_firstname": "",
"family_secondary_idnum": "",
"family_secondary_occupation": "",
"family_secondary_employer": "",
"family_secondary_workphone": "",
"family_secondary_cell": "",
"family_address_postal_1": "18 Lello Road",
"family_address_postal_2": "",
"family_address_postal_suburb": "Assagay",
"family_address_postal_city": "Outer West Durban",
"family_address_postal_province": "KwaZulu-Natal",
"family_address_postal_code": "3600",
"family_address_postal_country": "South Africa",
"family_address_residential_1": "18 Lello Road",
"family_address_residential_2": "",
"family_address_residential_suburb": "Assagay",
"family_address_residential_city": "Outer West Durban",
"family_address_residential_province": "KwaZulu-Natal",
"family_address_residential_code": "3610",
"family_address_residential_country": "South Africa",
"family_notes": "",
"family_modify": "2024-09-18 08:37:41",
"family_primary_email": [
"testing+primary@testing.adam.co.za",
"testing+primary2@testing.adam.co.za"
],
"family_secondary_email": [
]
}
],
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
ExternalAuth/auth/post
Allows ADAM to be used as an external authentication source.
Note well: This API endpoint will divulge user information for a valid login name. As with any API key, it is imperative that it is kept secret and changed if a breach is suspected.
Request
POST /apicore/externalauth/auth/
Parameters
These parameters are sent via form-data parameters.
- username: The username of the staff member or pupil, or the Identification number or passport number of a family member.
- password: The password associated with the username
Response
Integrating systems should check the HTTP response code rather than the presence of user information in the data object.
If a valid username and password are supplied, the HTTP response code will be 200. The data object contains the user information and the contained response code will be 200:
{
"data": {
"username": "admin",
"firstname": "Patrick",
"lastname": "Cloete",
"email": "testing+staff_1@adam.co.za",
"type": "staff",
"id": "1"
},
"message": "",
"response": {
"error": "Login successful",
"code": 200
}
}
If a valid username is supplied, but the password is incorrect, the HTTP response code will be 401. The data object will contain user information and the contained response code will be 401:
{
"data": {
"username": "admin",
"firstname": "Patrick",
"lastname": "Cloete",
"email": "testing+staff_1@adam.co.za",
"type": "staff",
"id": "1"
},
"message": "",
"response": {
"error": "Username or password not recognised",
"code": 401
}
}
If an invalid username is supplied, the HTTP response code will be 401. The data object will be empty and the contained response code will be 401.
{
"data": [],
"message": "",
"response": {
"error": "Username or password not recognised",
"code": 401
}
}
Pupils/image/get
Returns an image of a pupil.
Request
GET /apicore/pupils/image/<pupil_id>
Parameters
- <pupil_id>: The internal identifier of the pupil.
Response
GET /apicore/reporting/pupils/image/123
Unlike other API calls, this will return an image file and not a JSON object. The image type will be specified by the response’s Content-Type header, but will almost certainly be a JPG image. A response code of 404 suggests that the image does not exist.
Families/currentchildren/get
Request
GET /apicore/families/currentchildren/<family_id>
Parameters
- <family_id>: The internal identifier of the family.
Response
This query returns an array of pupils. If no pupils are attached to the family, or if the family identifier does not exist, then the response will be returned with a “404” HTTP status code.
{
"data": [
"49",
"4688"
],
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
Families/searchbyid/get
Request
GET /apicore/families/searchbyid/<RSA_ID_Number>
Parameters
- <RSA_ID_Number>: A South African ID number or international passport number for parents without an ID number. The parameter should be trimmed of spaces. This performs a simple text match with the database field and thus relies on reasonable data hygiene.
Response
This response returns an array of generally one family identifier, but if an ID number is associated with many parents, all will be returned in the array. This is discouraged in the interface, but schools may still do this.
Where the ID number cannot be found, a response will be returned with an HTTP 404 status code.
{
"data": [
"1234"
],
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
FamilyRelationships/family/get
Gets a list of current pupils linked to a family with their relationships descriptors for primary and secondary parents.
Request
GET /apicore/familyrelationships/family[/<family_id>]
Parameters
- <family_id>: The internal identifier for the family. If omitted, all families are returned.
Response
GET /apicore/familyrelationships/family/123
{
"data": [
"111": {
"primary": "biological",
"secondary": "step parent"
},
"321": {
"primary": "step parent",
"secondary": "biological"
}
],
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
The data attribute contains an array of 0 or more objects.
- The index of each array object is the identifier of the pupil.
- A primary and secondary key contain the relationship between the primary or secondary family member and the pupil. Note that a relationship will be returned even in instances where there may not be a secondary family member. Other logic must determine whether to discard this value or not.
Possible values include:
- biological
- adoptive parent
- step parent
- foster parent
- guardian
- sponsor
- relative
- Other
GET /apicore/familyrelationships/family
{
"data": [
"123": {
"111": {
"primary": "biological",
"secondary": "step parent"
},
"321": {
"primary": "step parent",
"secondary": "biological"
}
},
…
],
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
FamilyRelationships/pupil/get
Gets a list of families linked to a pupil with their relationships descriptors for primary and secondary parents.
Request
GET /apicore/familyrelationships/pupil[/<pupil_id>]
Parameters
- <pupil_id>: The internal identifier for the pupil. If omitted, all pupils are returned
Response
GET /apicore/familyrelationships/pupil/123
{
"data": [
"111": {
"primary": "biological",
"secondary": "step parent"
},
"321": {
"primary": "step parent",
"secondary": "biological"
}
],
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
The data attribute contains an array of 0 or more objects.
- The index of each array object is the identifier of the family.
- A primary and secondary key contain the relationship between the primary or secondary family member and the pupil. Note that a relationship will be returned even in instances where there may not be a secondary family member. Other logic must determine whether to discard this value or not.
Possible values include:
- biological
- adoptive parent
- step parent
- foster parent
- guardian
- sponsor
- relative
- Other
GET /apicore/familyrelationships/pupil
{
"data": [
"123": {
"111": {
"primary": "biological",
"secondary": "step parent"
},
"321": {
"primary": "step parent",
"secondary": "biological"
}
},
…
],
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
Leaves/approved/get
Gets a list of approved leaves with an end date that is either today or in the future.
Request
GET /apicore/leaves/approved[/<pupil>]
Parameters
- <pupil>: Optional: The identifier of the pupil in question.
Response
GET /apicore/leaves/approved/6050
{
"data": [
{
"leave_request_id": 4765,
"leave_request_out": "2024-10-18 14:15:00",
"leave_request_in": "2024-10-20 18:30:00",
"leave_request_destination": "Home",
"leave_request_host": "Parents",
"leave_request_host_contact": "083",
"leave_request_notes": "thanks",
"leave_request_approval_notes": "\n",
"leave_request_status": "Approved",
"leave_request_user_id": 1,
"leave_request_user_type": "staff",
"leave_request_submitted_datetime": "2024-10-15 10:37:01",
"leave_request_reminder_datetime": null,
"leave_request_choices": "Will he need Saturday Lunch:No\nWill he need Sunday Supper:No\nDo you need a gate code:No\n",
"leave_type_id": 1,
"leave_type_description": "Full Weekend Leave",
"leave_type_overnight": "Yes",
"leave_type_off_campus": "Yes"
}
],
"message": "Leaves for Joseph Tshabalala",
"response": {
"error": "OK",
"code": 200
}
}
The data attribute contains an array of 0 or more leave records.
Reporting/periods/get
Gets a list of reporting periods for a year.
Request
GET /apicore/reporting/periods[/<year>]
Parameters
- <year>: OPTIONAL. The calendar year in question. If omitted, the current calendar year is used.
Response
GET /apicore/reporting/periods/2018
{
"data": [
{
"period_id": "31",
"period_name": "Term 1",
"period_start": "2018-01-01",
"period_end": "2018-12-02",
"period_publish": "2018-12-02 12:00:00"
}
],
"message": "Reporting periods from year 2018",
"response": {
"error": "OK",
"code": 200
}
}
The data attribute contains an array of 0 or more reporting period objects.
- period_id is the internal identifier for the reporting period in question.
- period_name is the user-provided descriptor for that reporting period.
- period_start is the starting date of the reporting period. This is often set as the start of term, but some schools, who run concurrent or additional reporting periods may not align reporting periods with terms.
- period_end is the date on which the reporting period is deemed to have finished.
- period_publish is the date and time when the reports are made available on the parent portal. This date may change at the user discretion and so this value should always be double checked on or after this time if important actions are to occur.
Reporting/results/get
Gets all academic results for a reporting period.
Request
GET to /apicore/reporting/results/<reportingperiod>
Parameters
- <reportingperiod> is the value of the reporting period identifier. See the period_id property returned in the Reporting/periods/get request above.
Response
GET /apicore/reporting/results/31
{
"data": [
{
"pupil_id": 1754,
"pupil_admin": "55012",
"pupil_grade": 9,
"results": [
{
"subject_id": 1,
"subject_name": "English",
"dbe_subject_code": "",
"result_term": 50,
"result_ytd": 50
},
{
"subject_id": 40,
"subject_name": "Technology",
"dbe_subject_code": "15351142",
"result_term": null,
"result_ytd": null
}
],
"report_aggregate": 72.5,
"report_aggregate_ytd": 72.5,
"report_modified": "2016-02-11 11:36:17"
}
],
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
The data attribute contains an array of 0 or more pupil objects. The pupil object has the following attributes:
- pupil_id the internal identifier of the pupil.
- pupil_admin is a user-provided identifier. These should be consistent but can change at the school’s discretion.
- pupil_grade gives the grade that the pupil was in for this reporting period. Values are integers between -3 (Grade 0000) and 13 (Post Matric).
- results is an array of 0 or more subject result objects. These objects have the following properties:
- subject_id is the internal identifier for the subject in ADAM. Note that these may not be consistent as some schools have multiple versions of the same subject (e.g. English for Junior School vs English for High School). These values are not consistent across schools.
- subject_name is the user-provided name for that subject.
- dbe_subject_code is the Department of Basic Education’s subject code. Typically, this is specific to the grade and subject. Because some schools offer their curriculum in different configurations, some codes may be duplicated, and yet other subjects may not have a code assigned (which is represented by an empty string).
- result_term is the pupil’s result for this reporting period (normally akin to a term). A null value represents an absent result. This result is otherwise returned as a float and decimal places should be anticipated
- result_ytd is the pupil’s year-to-date result, a result that is often an aggregated result across a number of reporting periods. A null value represents an absent result.This result is otherwise returned as a float and decimal places should be anticipated.
- report_aggregate is a summary result (often, but not always, an average of all the subject results) for the pupil for that term.
- report_aggregate_ytd is a summary year-to-date result. Again, this is often, but not always an average of the subject results.
- report_modified is a timestamp of the last modification time of that report.
Staff/image/get
Returns an image of a staff member.
Request
GET /apicore/staff/image/<staff_id>
Parameters
- <staff_id>: The internal identifier of the pupil.
Response
GET /apicore/reporting/staff/image/123
Unlike other API calls, this will return an image file and not a JSON object. The image type will be specified by the response’s Content-Type header, but will almost certainly be a JPG image. A response code of 404 suggests that the image does not exist.
XDevMan/alumni/get
Returns a list of Alumni and their last-modified dates
Request
GET /apicore/xdevman/alumni/<year>
Parameters
- <year>: The year of matriculation of the pupils
Response
GET /apicore/xdevman/alumni/2019
{
"data": [
{
"pupil_id": "2240",
"pupil_admin": "7623",
"pupil_modify": "2019-11-11 16:25:54",
"alumni_modify": "2020-01-02 16:04:04"
},
{
"pupil_id": "1319",
"pupil_admin": "7289",
"pupil_modify": "2019-11-11 16:25:54",
"alumni_modify": "2020-01-02 16:04:04"
}
],
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
In each record, the internal ID and school-provided administration number are returned. There are two modification times because data for alumni is stored in two separate places (one from the historical pupil information, and another from the alumni-specific information).
XDevMan/currentpupils/get
Returns a list of current pupils and their list of modification dates.
Request
GET /apicore/xdevman/currentpupils
Parameters
- none
Response
GET /apicore/xdevman/currentpupils
{
"data": [
{
"pupil_id": "2240",
"pupil_admin": "7623",
"pupil_modify": "2019-11-11 16:25:54"
},
{
"pupil_id": "1319",
"pupil_admin": "7289",
"pupil_modify": "2019-11-11 16:25:54"
}
],
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
In each record, the internal ID and school-provided administration number are returned. There are two modification times because data for alumni is stored in two separate places (one from the historical pupil information, and another from the alumni-specific information).
XDevMan/leavers/get
Returns a list of Leavers and their last-modified dates. A leaver for a year is a person who was deregistered during the course of that year. It includes people from all grades.
Request
GET /apicore/xdevman/leavers/<year>
Parameters
- <year>: The year of matriculation of the pupils
Response
GET /apicore/xdevman/leavers/2019
{
"data": [
{
"pupil_id": "2240",
"pupil_admin": "7623",
"pupil_modify": "2019-11-11 16:25:54",
"alumni_modify": "2020-01-02 16:04:04"
},
{
"pupil_id": "1319",
"pupil_admin": "7289",
"pupil_modify": "2019-11-11 16:25:54",
"alumni_modify": "2020-01-02 16:04:04"
}
],
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
In each record, the internal ID and school-provided administration number are returned. There are two modification times because data for alumni is stored in two separate places (one from the historical pupil information, and another from the alumni-specific information).
XDevMan/alumnus/get
Returns the details of a single alumnus.
Request
GET /apicore/xdevman/alumnus/<pupil_id>
Parameters
- <year>: The year of matriculation of the pupils
Response
GET /apicore/xdevman/alumnus/999
{
"data": {
"pupil_id": "999",
"pupil_admin": "6727",
"pupil_lastname": "xxx",
"pupil_firstname": "xxx",
"pupil_fullfirst": "xxx",
"pupil_birth": "1994-08-xx",
"pupil_final": "2012",
"pupil_gender": "Female",
"pupil_entry": "2010-07-12",
"pupil_exit": "2012-12-31",
"pupil_idnumber": "9408xxx",
"pupil_population_id": "4",
"pupil_language_id": "2",
"pupil_language_other": "",
"pupil_email": "",
"pupil_email_personal": "",
"pupil_prepschool": "xxx",
"pupil_nationality": "South Africa",
"pupil_boarder": "3",
"alumni_title": "Miss",
"alumni_marital_status": "",
"alumni_maiden_name": "",
"alumni_spouse_title": "",
"alumni_spouse_firstname": "",
"alumni_spouse_gender": null,
"alumni_spouse_occupation": "",
"alumni_date_married": "0000-00-00",
"alumni_region": "",
"alumni_district": "",
"alumni_branch_id": null,
"alumni_type_id": "1",
"alumni_deceased": "No",
"alumni_deceased_date": null,
"alumni_workphone": "",
"alumni_homephone": "(031) xxx 6xxx",
"alumni_other": "",
"alumni_reason_left": "",
"alumni_exit_grade": "12",
"alumni_previous_school": null,
"alumni_qualification": "Senior Certificate",
"family": {
"families": [
{
"family_id": "873",
"family_admin": "0",
"family_primary_lastname": "xxx",
"family_primary_firstname": "xxx",
"family_primary_fullfirst": "xxx",
"family_primary_initials": "X",
"family_primary_title": "Mr",
"family_primary_idnum": "651xxxx",
"family_primary_gender": "Male",
"family_primary_occupation": "xxx",
"family_primary_employer": "xxx",
"family_primary_workphone": "0315xxx",
"family_primary_cell": "083xxx",
"family_primary_birth": "1965-10-xx",
"family_secondary_lastname": "xxx",
"family_secondary_firstname": "xxx",
"family_secondary_fullfirst": "xxx",
"family_secondary_initials": "X",
"family_secondary_title": "Mrs",
"family_secondary_idnum": "6607xxx",
"family_secondary_gender": "Female",
"family_secondary_occupation": "xxx",
"family_secondary_employer": "xxx",
"family_secondary_workphone": "0315xxx",
"family_secondary_cell": "083xxx",
"family_secondary_birth": "1966-07-xx",
"family_address_postal_1": "xxx",
"family_address_postal_2": "xxx",
"family_address_postal_suburb": "xxx",
"family_address_postal_city": "",
"family_address_postal_province": "",
"family_address_postal_code": "xxx",
"family_address_postal_country": "South Africa",
"family_address_residential_1": "xxx",
"family_address_residential_2": "xxx",
"family_address_residential_suburb": "xxx",
"family_address_residential_city": "",
"family_address_residential_province": "",
"family_address_residential_code": "",
"family_address_residential_country": "xxx",
"family_home_phone": "03150xxx",
"family_home_fax": "03150xxx"
}
],
"email": [
{
"email_family_id": "873",
"email_member": "primary",
"email_address": "xxx@example.com"
},
{
"email_family_id": "873",
"email_member": "secondary",
"email_address": "yyy@example.com"
}
]
}
},
"message": "",
"response": {
"error": "OK",
"code": 200
}
}
A record of a single alumnus is returned.
Specific Integration Requirements
Please see the Third Party Integration section in this documentation.