We're excited that you're looking to start using Apply Connect! The documentation provides sample requests and responses for all the methods that are currently supported. Before you get started, here are some quick tips:
GET https://example.smapply.org/api/programs/?per_page=20
Our API uses OAuth2 to authenticate your application. If you’re new to OAuth2, you can read about it in the official spec.
We’ve simplified the process so that the initial authentication is done through Apply from the administrator portal, making our authentication flow as follows:
Log in to your site, go to the Integrations Hub and click on the Configure button for Apply Connect. If you do not already have credentials, you can click the Generate Credentials button to generate new ones. Once generated you will have an access token, a refresh token, a client ID, and a client secret. The client secret is only shown to you once (when the credentials are first generated), so copy it somewhere safe for future use. If you lose or forget your client secret you'll need to generate new credentials in order to use the API after your access token expires, so try not to lose it.
These credentials are tied uniquely to your user account and to your site, anyone who has them can authenticate themselves as you through the API – so make sure you keep them secret. If at any time you feel that your API credentials may have been compromised, use the Revoke Credentials button to revoke your current credentials, which will invalidate and immediately erase the existing ones. You can then generate new ones if needed. Alternatively, if you just need to invalidate your access or refresh tokens, you can click on the Refresh Credentials button to get a new set of tokens without changing your client id or secret.
For your request to be authenticated, the access token you received from the previous step needs to be added to the header of every request that you send us. For example:
curl "https://example.smapply.org/api/programs/" -H "Authorization: Bearer smapply_access_token"
If you receive a 403 error after sending a request, it's possible that your access token is invalid. You can check the API settings where you generated the token to make sure that it hasn't expired.
Access tokens expire 30 days after they are generated. When you make a call with an expired token you will get a 403 error. You need to refresh your access token to continue to make API calls.
curl "https://example.smapply.org/api/o/token/" -d "grant_type=refresh_token\
&client_id=<CLIENT_ID>\
&client_secret=<CLIENT_SECRET>\
&refresh_token=<REFRESH_TOKEN>"
Please note that the grant_type, client_id, client_secret andrefresh_token arguments are not being sent in the url of the request. Those arguments should be sent as urlencoded data in the body of the POST request. If the call was successful, you will get a response of
{
"access_token": "<YOUR_NEW_ACCESS_TOKEN>",
"expires_in": 7200,
"refresh_token": "<YOUR_NEW_REFRESH_TOKEN>",
"scope": "admin",
"token_type": "Bearer"
}
You can start using your new access token to authenticate requests until it expires. The new values can also be seen from your Account Settings page. Note that the client ID and client secret remain the same when you refresh your tokens.
Since access tokens are long lived (30 days), you may want to revoke it if you believe it has been unintentionally exposed or as a security measure if you think it won't be used again before it expires. Revoking an access token immediately stops the use of that token, but a new access token can be created programmatically using the refresh token at theRefresh Token endpoint if you need a new one. You can also revoke refresh tokens if you would like to stop new access tokens from being created programmatically, but keep in mind that revoking a refresh token also revokes the associated access token. You can use theRevoke Token endpoint to revoke these tokens.
Revoke Access Token
curl "https://example.smapply.org/api/o/revoke_token/" -d "client_id=<CLIENT_ID>\
&client_secret=<CLIENT_SECRET>\
&token=<ACCESS_TOKEN>"
Revoke Refresh Token
curl "https://example.smapply.org/api/o/revoke_token/" -d "client_id=<CLIENT_ID>\
&client_secret=<CLIENT_SECRET>\
&token=<REFRESH_TOKEN>"
If you believe that your Client ID and Client Secret have been compromised, or you have forgotten or lost your Client Secret, you can use the Revoke Credentials button on theApply Connectintegrations page to revoke those credentials completely.
These samples are simple libraries for accessing the Connect API. While not production ready code (hello error checking), it should give you a starting point to see how to work with the API.
This YAML file is used by both examples. It holds the configuration for the code libraries. The initial values should be copied from your integration settings.
server: "https://slug.smapply.io"
clientId: yourClientId
clientSecret: yourSecret
refreshToken: yourRefreshToken
accessToken: yourAccessToken
connect.js: This simple library handles access token expiry. The configuration for the library is stored in a YAML file.
const yaml = require('yaml');
const fs = require('fs');
class SMApplyConnect {
constructor(configFile) {
const file = fs.readFileSync(configFile, 'utf8');
const config = yaml.parse(file)
this.configFile = configFile
this.accessToken = config.accessToken
this.refreshToken = config.refreshToken
this.clientSecret = config.clientSecret
this.clientId = config.clientId
this.server = config.server
}
saveConfig() {
const data = {
"server": this.server,
"clientId": this.clientId,
"clientSecret": this.clientSecret,
"refreshToken": this.refreshToken,
"accessToken": this.accessToken,
};
fs.writeFileSync(this.configFile, yaml.stringify(data));
}
async refreshTheToken() {
const url = `${this.server}/api/o/token/`;
const data = {
"grant_type": 'refresh_token',
"client_id": this.clientId,
"client_secret": this.clientSecret,
"refresh_token": this.refreshToken,
}
const response = await fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams(data)
});
if (response.status === 200) {
const newToken = await response.json();
this.accessToken = newToken['access_token']
this.refreshToken = newToken['refresh_token']
this.saveConfig()
}
}
async call({method, endpoint, params, data, headers}) {
const url = new URL(`${this.server}/api/${endpoint}/`);
headers.Authorization = `Bearer ${this.accessToken}`;
let response;
if (method === 'POST' || method === 'PATCH') {
for (const key of params)
url.searchParams.set(key, params[key]);
response = await fetch(url.toString(), {
method: method,
headers: headers,
body: JSON.stringify(data)
})
} else {
for (const key of Object.keys(params))
url.searchParams.set(key, params[key]);
const fullUrl = url.toString();
try {
response = await fetch(fullUrl, {
method: method,
headers: headers,
});
} catch(err) {
console.log(err);
}
}
return response;
}
async makeCall({method, endpoint, params, data, headers}) {
const props = {
method: method,
endpoint: endpoint,
params: params || {},
data: data,
headers: headers || {}
};
let response = await this.call(props);
if (response.status === 403) {
await this.refreshTheToken();
response = await this.call(props);
}
return response;
}
}
module.exports = SMApplyConnect;
applications.js: A sample program to retrieve applications.
const SMApplyConnect = require("./connect");
(async () => {
const connect = new SMApplyConnect('connect.yaml');
const response = await connect.makeCall({method: 'GET', endpoint: 'applications'});
const responseJSON = await response.json();
console.log(responseJSON);
})()
.then(() => process.exit(0))
.catch(err => {
console.log(err.stack);
process.exit(0);
});
All User Information System API call require API authentication; seeAuthentication
The User Information System can be configured through the user interface or through the API.
The User Information System integration page, available in Settings -> Integrations -> User Information System -> Configure will be updated to support the new requirements. An example of the new view is below. If you do not have access to the User Information System card on the Integrations page, please contact support for more information on how to get access to this feature.
To configure UIS through the API, make a POST call to the /api/uis/endpoint with the desired mapping. Here’s an example of valid payload to configure UIS:
{
"mapping": {
"user.first_name": "First name column",
"user.last_name": "Last name column",
"user.email": "Email column",
"user.timezone": "tz column",
"custom_fields": {
"Test Applicant CF": 7
}
}
}
“First name column”, “Last name column”, “Email column”, “tz column” and “Test Applicant CF” are all column headers that would be found in the CSV that will be uploaded. “7” is the internal identifier of the custom field that the data from “Test Applicant CF” will go to for the given user. You can retrieve a list of custom field identifiers using the/api/customfields endpoint.
An imported CSV for this integration may look something like this:
First name column,Last name column,Email column,Test Applicant CF,tz
Jane,Smith,jane.smith@mailinator.com,Pacific/Norfolk
Column names can be anything that you want, as long as they are mapped to the appropriate field. In the configuration we use the format user.* to identify which Apply field will be populated with the data that we find under the given column for each user. If, for example, you used “GivenName” as the column header that contained user first names, the mapping would be as below. user.first_name,user.last_name,user.email,user.uid, anduser.timezone are all static names that relate to specific fields in Apply.
"mapping": {
"user.first_name": "GivenName",
...
}
When importing users, you POST to the /api/uisimports/endpoint with a valid CSV file. We will validate the file against the UIS configuration for your site and return a response that will include information about the job that was created. For a full list of properties to expect, see the documentation for/api/uisimports/.
When an import is processed, by default we attempt to identify existing users using the email address that is included in the import. In some cases, an alternative way to identify the users may be preferred, such as by the users student ID, employee number, or some other value. These would be unique identifiers. Unique identifiers should be universally unique across your organization and your Apply site, meaning that no two users should have the same ID, and no ID should be re-used.
Unique identifiers are stored as custom field values on Apply, and when the User Information System is configured to use a unique identifier, it will validate that the CSV includes the column that has been identified as containing this identifier for users. If it is not included in the CSV, an error will occur.
When an import is requested and a unique identifier has been configured, we will first try to find a user based on matching custom field values. If one is found, we'll update their user profile with the information passed over in the import. If none are found, we'll create a new user using the details provided. You can populate the unique identifiers ahead of time for existing users, or when you import you can include the custom field that the identifier should go in and populate the data using the User Information System.
Warnings are issues identified during the import that will not stop the import from processing. An example of this would be if you identified that you were going to include user timezones in your CSV but did not include the column that you identified would contain this data. If a warning occurs, details of the warning will be included in the API response but the import will continue unless an error also occurs.
{
"warnings": [
{
"unmapped_columns": [
"unmapped column 'Test Applicant Field 2' found"
]
},
{
"missing_optional_columns": [
"column 'tz' is missing from import csv for attribute 'user.timezone'. Site timezone will be used instead."
]
}
]
}
Here we see
{
"errors": [
{
"missing_required_columns": [
"missing required mapped column 'UserFirstNameAttribute' for attribute 'user.first_name'"
]
}
]
}
Here we see
This file must contain the user’s first name, last name and email address with their respective column headers mapped in the UIS configuration. If a unique identifier is configured, it must also be sent. The column for the unique identifier must match the name of the custom field that it will be populated in. For example, if you are using the users student id as their unique identifier, and you’re sending “Student_ID” as the column name in the CSV with this value, you must also have a custom field called “Student_ID”. For optional fields, you can also include a column for user timezones and custom fields.
First name column,Last name column,Email column
Jane,Smith,jane.smith@mailinator.com
First name column,Last name column,Email column,STUDENT_ID
Jane,Smith,jane.smith@mailinator.com,123456
First name column,Last name column,Email column,STUDENT_ID,GPA,timezone
Jane,Smith,jane.smith@mailinator.com,123456,4.0,America/Toronto
When a call is made to the api/uisimports/ endpoint with a valid CSV file, a job will be created to process the import. A response like the one seen below will provide details on the job.
{
"id": 7,
"created_at": "2022-12-31T17:27:07.390",
"started_by": {
"id": 1,
"first_name": "Alexi",
"last_name": "Belsky",
"email": "alexibelsky@nbn.fake"
},
"status": 0,
"job_id": "",
"total_rows": 0,
"processed": 0,
"warnings": [],
"errors": []
}
You can check the status of the import job by querying/api/uisimports/:id/.