Appearance
Apply
Call scholarships.submitApplicationRequirementV2 once for each answer. When Owlflow returns viewer.allMandatoryRequirementsCompleted: true (or right away if the scholarship has no mandatory requirements), call scholarships.applyV2 once to create and submit the application.
Don't call scholarships.applyV2 before saving mandatory requirements. Owlflow checks that all mandatory requirements are saved first and returns an error if any are missing.
Include Authorization: Bearer <token> on each request. Before rendering the application form, load the requirement IDs, validation rules, and limits from the scholarship detail query.
Both V2 mutations return the updated scholarship and application progress. Don't reload scholarships.byId after every saved answer.
Save one answer
Set requirementId to the requirement definition's ID and scholarshipId to the scholarship ID. Include id only when replacing a saved response that already has an applicationRequirement.id.
graphql
mutation SaveRequirement($input: ApplicationRequirementInput!) {
scholarships {
submitApplicationRequirementV2(input: $input) {
applicationRequirement { id }
scholarship {
id
viewer {
allMandatoryRequirementsCompleted
requirementProgress { completed progress }
}
}
}
}
}Store the applicationRequirement.id that Owlflow returns. You'll need it to replace or delete the response later.
You don't need to save a response for an optional requirement (isOptional: true). Core writes a GOAL response once. Repeating the mutation doesn't update the stored payload.
Short answer (INPUT)
json
{
"input": {
"type": "INPUT",
"scholarshipId": "12345",
"requirementId": "901",
"text": "Lincoln High School"
}
}Display title and description as the prompt. Apply any validation rules from config.
Essay (TEXT)
json
{
"input": {
"type": "TEXT",
"scholarshipId": "12345",
"requirementId": "902",
"text": "I want to study computer science because..."
}
}Read the prompt from title and description on RequirementText, and check essayTopics when the scholarship offers multiple topic choices. Validate the response against minWords, maxWords, minCharacters, and maxCharacters.
When allowFile is true, the student may upload a document instead of entering text. Send the document in fileBase64 with its fileName, and validate it against allowedExtensions and maxFileSize in bytes. The optional source field can identify where the student found the scholarship, such as search or recommended.
AI drafts are optional. See Essays.
Survey (SURVEY)
The answers object maps each question ID to an array of selected option values. Radio questions contain one value, while checkbox questions may contain several. Send questions.options.value, not the display label.
json
{
"input": {
"type": "SURVEY",
"scholarshipId": "12345",
"requirementId": "903",
"answers": {
"q-1": ["yes"],
"q-2": ["leadership", "community"]
}
}
}Confirmation (SPECIAL_ELIGIBILITY)
Display the requirement's specialEligibilityValue as the confirmation statement. When the student checks the box, save the string true as the response.
json
{
"input": {
"type": "SPECIAL_ELIGIBILITY",
"scholarshipId": "12345",
"requirementId": "904",
"specialEligibilityValue": "true"
}
}File (FILE) and image (IMAGE)
json
{
"input": {
"type": "FILE",
"scholarshipId": "12345",
"requirementId": "905",
"fileName": "transcript.pdf",
"fileBase64": "<base64>"
}
}For an image, use type: "IMAGE" and validate the file against allowedFormats rather than allowedExtensions. When the requirement provides dimensions, also enforce minWidth, maxWidth, minHeight, and maxHeight. To attach a file the student has already uploaded, send accountFileId instead of fileBase64.
Goal (GOAL)
Direct the student to link. The variant is either TRACKING_MODE_FULL or TRACKING_MODE_LINK. After the student completes the goal, save a JSON object such as:
json
{
"input": {
"type": "GOAL",
"scholarshipId": "12345",
"requirementId": "906",
"goalValue": { "source": "partner-site", "completedAt": "2026-10-06T18:00:00.000Z" }
}
}Submit the application
Once viewer.allMandatoryRequirementsCompleted is true, call scholarships.applyV2 to create and submit the application:
graphql
mutation Apply($scholarshipId: ID!, $subSource: String) {
scholarships {
applyV2(scholarshipId: $scholarshipId, subSource: $subSource) {
application { id dateApplied }
scholarship {
id
title
viewer { hasApplication hasSubmitted allMandatoryRequirementsCompleted }
}
}
}
}json
{ "scholarshipId": "12345", "subSource": "partner-site" }Use subSource to record where the student applied, such as a page or chat surface on your site. A successful applyV2 call sets viewer.hasApplication and viewer.hasSubmitted to true.
Replace or remove an answer
To replace a response, call submitApplicationRequirementV2 with id set to the existing applicationRequirement.id.
Call deleteApplicationRequirementV2 to remove an answer:
graphql
mutation DeleteRequirement($id: ID!, $scholarshipId: ID!, $type: RequirementTypeEnum!) {
scholarships {
deleteApplicationRequirementV2(id: $id, scholarshipId: $scholarshipId, type: $type) {
scholarship {
id
viewer { allMandatoryRequirementsCompleted }
}
}
}
}json
{ "id": "555", "scholarshipId": "12345", "type": "TEXT" }Here, id refers to the saved response rather than the requirement definition.
