Avela API and Webhooks provides an open set of tools that allows you to integrate data from Avela to any 3rd party system including your SIS. This step to step guide walks you through the decisions you have to make.
Decision Points
- When do you start the integration - Upon status change or scheduled?
- What data will you be passing over?
- How will you deduplicate with records in you your SIS or 3rd party system?
- How will you be handling transformations?
- What data are you passing back to Avela to help manage deduplication?
When do you start the integration?
Do you start the integration upon an event, such as a status change? Or do you schedule the process? This is purely based on your desire and we’ve seen customers do it both ways. We believe that webhooks gives you the most up to date integration. However, we’ve also seen customers schedule a batch on a nightly basis using our API. We’re happy to provide additional examples.
How to access Avela’s API
Please connect with your client services manager to initiate the integration process:
Step 1: Avela will share domain-specific API credentials.
This includes a client name, client ID, and client secret you will use to authenticate the API connection.
Step 2: Bookmark Avela’a API documentation.
Save the documentation URLs to the Avela API. Note: there is a URL for each data environment.
- Production: https://prod.api-docs.avela.dev/v2/index.html
- UAT: https://uat.api-docs.avela.dev/v2/indexhtml
Step 3: Authenticate your API access.
- Create a Bearer Token as the authentication token with the following code. Note: this token is only valid for 24 hours, while the client ID/secret never changes.
curl --location 'https://uat.auth.avela.org/oauth/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'client_id=CLIENTID' \
--data-urlencode 'client_secret=CLIENTSECRET' \
--data-urlencode 'audience=https://uat.api.apply.avela.org/v1/graphql'
2. Use the Bearer Token in your API calls. See our API documentation for more details: https://uat.api-docs.avela.dev/v2/index.html
Using Webhooks
Using Avela’s webhooks allows for more real time integration. Webhooks lets you know when an event has happened in Avela so that the integration can run on just that single record. In this example, you receive a webhook event for a form where the parent has accepted an offer. From the form, you want to get additional details about the form and the answers regarding the applicant and the contacts. You need to prepare and transform the data for your SIS. Finally, you upload to the SIS.
The webhook event payload you can expect to receive is below.
{
"created_at": "2024-01-18T16:42:49.675517+00:00",
"deleted_at": null,
"entity_id": "c67c7e17-30d9-4c27-8906-a19eade84f19",
"entity_type": "OFFER",
"id": "b1a363ac-0671-454a-ab3a-525ab182ef60",
"modified_by_user_id": "auth0|651ad4bff1d7142df679037f",
"name": "OFFER_ACCEPTED",
"new_values": {
"enrollment_period_id": "90d172a7-c3c7-4bbc-87b9-0382d5c2338f",
"form_id": "c67c7e17-30d9-4c27-8906-a19eade84f19",
"form_template_id": "f96800af-3a69-47e1-8842-655cd7e6eb65",
"form_template_key": "application-form",
"id": "0debd283-e62e-4ce0-a094-3d713e12d00d",
"organization_id": "695d45d3-3e6d-44d5-bc96-d2b85e90349e",
"status": "Accepted"
},
"old_values": {
"enrollment_period_id": "90d172a7-c3c7-4bbc-87b9-0382d5c2338f",
"form_id": "c67c7e17-30d9-4c27-8906-a19eade84f19",
"form_template_id": "f96800af-3a69-47e1-8842-655cd7e6eb65",
"form_template_key": "f96800af-3a69-47e1-8842-655cd7e6eb65",
"id": "0debd283-e62e-4ce0-a094-3d713e12d00d",
"organization_id": "695d45d3-3e6d-44d5-bc96-d2b85e90349e",
"status": "Offered"
},
"organization_id": "695d45d3-3e6d-44d5-bc96-d2b85e90349e",
"transaction_type": "UPDATE",
"updated_at": "2024-01-18T16:42:49.675517+00:00"
}
Step 1 - Webhook Endpoint
The webhook endpoint receives an event and calls our API to get detail form information and the answers.
import express from 'express';
import axios from 'axios';
const app = express();
app.use(express.json());
app.post('/webhook', async (req, res) => {
try {
const { name, entity_id, entity_type } = req.body;
if (name !== 'OFFER_ACCEPTED') {
// if it's not an accepted offer event then ignore the event
return;
}
await processEvent(entity_id);
res.status(200).json({ message: 'Processing started' });
} catch (error) {
console.error('Error processing event:', error);
res.status(500).json({ error: 'Internal Server Error' });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Webhook listening on port ${PORT}`));
Step 2 - Fetch data from Avela
Using the Avela forms API endpoints, fetch detail information for the form and the answers
const fetchFormData = async (formId: string) => {
const avelaAPI = "https://prod.execute-api.apply.avela.org/api";
const [formResponse, questionsResponse] = await Promise.all([
axios.get(`${avelaAPI}/forms/${formId}`),
axios.get(`${avelaAPI}/forms/${formId}/questions`),
]);
return {
form: formResponse.form,
questions: questionsResponse.questions,
};
};
Using the Avela applicants API endpoint, fetch detail student information
const fetchStudentData = async (applicantId: string) => {
const avelaAPI = "https://prod.execute-api.apply.avela.org/api";
const applicantResponse = await axios.get(
`${avelaAPI}/applicants/${applicantId}`
);
return {
applicant: applicantResponse.applicant,
};
};
Step 3- Prepare and transform the data for your SIS
In this step you transform data from Avela to your SIS. We’ve provided a few examples including:
- Simple one to one mapping like firstName
- Concatenation like address
- For single or multi select answers, you can grab the key from Avela for that answer instead of the answer value. In some cases you won’t need to complete a transformation - you can simply use the value you have mapped in they key.
const upsertData = async (form, questions, applicant) => {
const sisAPI = "https://your-sis.com/api";
const studentPayload = {
id: applicant.reference_id,
avelaId: applicant.id,
firstName: applicant.first_name,
lastName: applicant.last_name,
email: applicant.email_address,
birthDate: applicant.birth_date,
address: [
{
type: "home",
street: `${applicant.street_address} ${applicant.street_address_line_2}`,
city: applicant.city,
state: applicant.state,
postalCode: applicant.zip_code,
},
],
gender: questions.find((q) => q.key === "gender").answer.free_text.value,
race: questions.find((q) => q.key === "race").answer.free_text.value,
};
let sisResponse;
// if existing student, then update
if (applicant.reference_id) {
sisResponse = await axios.post(`${sisAPI}/student/${applicant.reference_id}`, studentPayload);
}
else {
// search for student by first/last name and birthdate
const params = {
firstName: applicant.first_name,
lastName: applicant.last_name,
birthDate: applicant.birth_date,
};
const searchResponse = await axios.get(`${sisAPI}/student`, {params: params});
const foundStudents = searchResponse.data?.students;
if (foundStudents && foundStudents.length === 1) {
sisResponse = await axios.post(`${sisAPI}/student/${foundStudents[0].id}`, studentPayload);
}
else {
sisResponse = await axios.put(`${sisAPI}/student`, studentPayload);
}
}
// return student ID from SIS response
return {
studentId: sisResponse.data.student_id,
};
};
Step 4 - Upsert student reference ID to Avela
Keep the reference IDs up to date between your systems by updating the Avela student reference ID for new students created in your SIS.
const upsertReferenceId = async (applicantId, referenceId) => {
const avelaAPI = "https://prod.execute-api.apply.avela.org/api";
await axios.post(`${avelaAPI}/applicants/${applicantId}`, {
id: applicantId,
reference_id: referenceId,
});
};
Step 5 - Process the Event
const processEvent = async (formId) => {
const { form, questions } = await fetchFormData(formId);
const applicant = await fetchStudentData(form.applicant.id);
const referenceID = await upsertData(form, questions, applicant);
// upsert student reference ID if it changed
if (applicant_reference_id !== refrenceId) {
upsertReferenceId(applicant.id, referenceId);
}
console.log(
`Successfully processed form submission for applicant ${applicantId}`
);
};
How will you deduplicate with records in your SIS
A piece of logic you may consider is detecting potential duplicates before updating your system. Since this is part of your middleware, you can determine the best logic to find a duplicate. Avela has seen success in the following logic:
- Determine if the student has an existing ID that maps to your SIS record. This maybe a value that you had imported or was added earlier. If there is already a mapping based on ID, you can use that to update the record.
// if existing student, then update
if (applicant.reference_id) {
sisResponse = await axios.post(`${sisAPI}/student/${applicant.reference_id}`, studentPayload);
}
- If there is no ID, then the logic to determine if there is a likely duplicate based on your logic (like first and last name and date of birth all exactly match). If this all matches, stop the integration and output an error that you can then use to correct. If there is no error, then you can continue to create a new student.
// search for student by first/last name and birthdate
const params = {
firstName: applicant.first_name,
lastName: applicant.last_name,
birthDate: applicant.birth_date,
};
const searchResponse = await axios.get(`${sisAPI}/student`, {params: params});
const foundStudents = searchResponse.data?.students;
if (foundStudents && foundStudents.length === 1) {
sisResponse = await axios.post(`${sisAPI}/student/${foundStudents[0].id}`, studentPayload);
}
else {
sisResponse = await axios.put(`${sisAPI}/student`, studentPayload);
}
const referenceID = await upsertData(form, questions, applicant);
// upsert student reference ID if it changed
if (applicant_reference_id !== refrenceId) {
upsertReferenceId(applicant.id, referenceId);
}
Comments
0 comments
Article is closed for comments.