Skip to main content

Overview

The Education verification API lets you confirm that an applicant to your education program is associated with a recognised educational institution. The same flow supports both student verification (typically higher education – universities, colleges, community colleges) and teacher verification (typically K–12 – primary and secondary schools, with some higher education).

If you would prefer Goodstack to host the UI for your application flow, please get in touch with your account manager about a hosted setup for Education verification.

Below we outline a typical API implementation. Code examples are shown for both audiences – toggle the Student / Teacher tabs in each example to see the relevant payload.

1. Get your API keys

info

Get in touch with our product & engineering team to get access to the dashboard: engineering-support@goodstack.io

Before you start your implementation, you will need access to the dashboard to retrieve your Goodstack API keys.

Go to the Keys page in your Partner dashboard to access your Publishable Key and your Secret Key.

Authenticate your API requests by using these keys in the Authorization request header. The secret key should be used for server-to-server requests; the publishable key is for requests made from your front end to public API endpoints.

Do not publish your secret key in source control or use it in any front-end code.

2. Find the applicant's institution

2a. Collect the country

We strongly recommend collecting the applicant's country as the first step of your flow. Including a countryCode when you search for institutions makes results dramatically more accurate.

You can retrieve the full list of supported countries (with their ISO three-letter codes) from the Countries API:

GET /v1/countries

200 OK
{
"data": [
{
"code": "GBR",
"name": "United Kingdom"
}
],
"object": "Country"
}

Country codes are three-letter ISO codes (USA, GBR, FRA, etc.).

2b. Search for the institution

Call the Search Organisations endpoint with the applicant's countryCode, type[]=education, and a search query. The type[]=education filter restricts results to schools, colleges, universities, and other recognised educational institutions in Goodstack's database. This endpoint can be authenticated with your publishable key.

GET /v1/organisations?type[]=education&countryCode=USA&query=greenfield

200 OK
{
"data": [
{
"id": "organisation_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"name": "Greenfield University",
"countryCode": "USA"
}
]
}

Path A – Institution found

If the applicant selects their institution from the search results, store the returned organisationId for the Validation Submission request in step 4.

Path B – Institution not found

If no matching institution appears, switch your form to manual entry. Collect the following fields from the applicant:

  • organisationName – the name of the school (required)
  • countryCode – three-letter ISO code (required)
  • website – the school's official website (optional but highly recommended; a website helps our verification team locate the institution faster)
  • addressLine1, addressLine2, city, state, postal – the school's address (optional but recommended; they help our verification team locate the institution faster)

The submission also requires a language field – this is collected with the applicant's details in §3.

Registry fields not required for Education

You may notice that the general Create Validation Submission reference lists registryName and registryId as required fields. These are not required for Education verification and you should not ask the applicant for them as commonly students and teachers do not know their institution's registry identifier or it may not apply.

Goodstack relaxes the registry requirement whenever your configuration's allowed organisation types include education (whether on its own or alongside nonprofit / social_impact). Omit these fields from your manual-entry payload and the submission will be accepted.

3. Collect applicant details

Collect the applicant's name, email, and preferred language. These are sent with the Validation Submission and are used by the Agent Verification check to confirm the applicant is associated with the institution (for example, by checking that their email domain matches the school).

{
"firstName": "Alex",
"lastName": "Doe",
"email": "alex.doe@greenfield.edu",
"language": "en-US"
}

The language field accepts any RFC 5646 language code (en-US, en-GB, fr-FR, etc.).

4. Submit the validation

info

All verification checks are asynchronous. Most submissions resolve within a few seconds, but some can take up to 72 hours.

Once you have the institution and applicant details, submit them to the Create Validation Submission endpoint.

Validation configuration

Each submission must reference a configurationId that tells Goodstack which checks to run and which institution types qualify. Configurations are set up by our onboarding team and your account manager will provide your configurationId before you go live. You can list the configurations active on your account via the Retrieve Partner Configurations API.

What runs behind the scenes

Each Education verification submission runs three checks automatically – you do not need to call separate endpoints to create them:

Submission payload

Variant A – with organisationId. When the applicant found their institution in search results (Path A from §2):

POST https://api.goodstack.io/v1/validation-submissions
Authorization: Secret Key
Content-Type: application/json

{
"configurationId": "configuration_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"organisationId": "organisation_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"firstName": "Alex",
"lastName": "Doe",
"email": "alex.doe@greenfield.edu",
"language": "en-US"
}

Variant B – without organisationId. When the applicant entered their institution details manually (Path B from §2). registryName and registryId are omitted – see the callout in §2:

POST https://api.goodstack.io/v1/validation-submissions
Authorization: Secret Key
Content-Type: application/json

{
"configurationId": "configuration_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"organisationName": "Greenfield University",
"website": "https://www.greenfield.edu",
"addressLine1": "500 College Avenue",
"city": "San Jose",
"state": "California",
"postal": "95112",
"countryCode": "USA",
"firstName": "Alex",
"lastName": "Doe",
"email": "alex.doe@greenfield.edu",
"language": "en-US"
}

Submissions created via Variant B have a null organisationId on creation. If the institution details are confirmed valid, Goodstack will create the organisation record and the ID will be available via webhook or the Retrieve Validation Submission API.

Adding metadata

The submission also accepts an optional metadata object – an arbitrary key-value record you can use to link the submission back to your own systems (account IDs, application form fields, etc.). Metadata is returned with the submission on every read and in every webhook.

5. Interpret the response

When you submit a validation, Goodstack responds with a 200 OK and the current state of the submission. The top-level status tells you whether the application is complete, in review, or needs further input from the applicant.

The status field will be one of:

StatusMeaning
succeededAll checks passed. The applicant qualifies.
failedOne or more checks failed. The applicant does not qualify.
pendingChecks are still running, or the applicant needs to provide more information.

When status === 'pending', look at the nested agentVerification.status to decide whether you need to ask the applicant for documentation:

agentVerification.statusMeaningUI action
approvedAgent check passed; other checks still runningShow "in review" screen
pending_user_verificationGoodstack is reaching out to the applicant directlyShow "in review" screen
pending_reviewSubmission queued for manual review by GoodstackShow "in review" screen
pendingGoodstack needs additional documentation to verify the applicantPrompt the applicant to upload a document (see §6)
rejectedAgent verification failedShow failure messaging (see §7 for failure reasons)

The decision tree your front end should implement:

Case 1 – Application fully approved

{
"status": "succeeded",
"agentVerification": { "status": "approved" }
}

Show the success screen. No further applicant action is required.

Case 2 – In review, no applicant action needed

{
"status": "pending",
"agentVerification": { "status": "approved" }
}

Show an "Application is being reviewed" screen. The applicant does not need to do anything; you will receive a webhook when the submission resolves.

Case 3 – Documentation required

{
"status": "pending",
"agentVerification": { "status": "pending" }
}

OR

{
"status": "pending",
"agentVerification": { "status": "pending_review" }
}

Prompt the applicant to upload supporting documentation. See §6.

6. Upload supporting documents

When the applicant needs to upload documentation, send it to the Create Validation Submission Document endpoint as a multipart/form-data request:

POST https://api.goodstack.io/v1/validation-submission-documents
Authorization: Secret Key
Content-Type: multipart/form-data

file=<binary>
validationSubmissionId=validationsubmission_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Accepted formats: jpg, png, pdf. Maximum file size: 5 MB.

Once uploaded, the document is reviewed by Goodstack's verification team. You will receive a validation_submission.succeeded or validation_submission.failed webhook (see §7) once the review is complete.

Coaching the applicant

The document the applicant uploads has a large impact on how quickly the verification completes. Give the applicant a short, specific list of acceptable proofs rather than a generic "upload a document" prompt. Examples include a student ID, an enrolment letter, an official school correspondence, or – for teachers – a payslip showing the school name or a letter on school letterhead.

7. Receive and process results (webhooks)

info

To receive results, you will need the Webhook Subscriptions API.

After creating validation submissions, subscribe to webhooks so your system is notified when each submission resolves. The three events for Education verification are:

  • validation_submission.created – fired as soon as you successfully POST to the create endpoint
  • validation_submission.succeeded – all checks in the configuration passed
  • validation_submission.failed – one or more checks failed

Example payloads

validation_submission.created – sent when Goodstack creates a Validation Submission.

{
"object": "event",
"data": {
"id": "event_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"eventType": "validation_submission.created",
"createdAt": "2026-04-15T11:00:00.000Z",
"eventData": {
"id": "validationsubmission_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"status": "pending",
"createdAt": "2026-04-15T10:30:00.000Z",
"organisationId": "organisation_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"agentVerificationId": "agentverification_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"agentVerification": {
"id": "agentverification_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"firstName": "Alex",
"lastName": "Doe",
"email": "alex.doe@greenfield.edu",
"status": "pending",
"rejectionReasonCode": null
},
"eligibilitySubscriptionId": "eligibilitysubscription_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"validationRequestId": "validationrequest_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"validationRequest": {
"id": "validationrequest_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"name": "Greenfield University",
"acceptedAt": null,
"rejectedAt": null,
"organisationTypes": []
},
"monitoringSubscriptionId": null,
"validationInviteId": null,
"partnerFields": {},
"metadata": {}
}
}
}

validation_submission.failed – sent when a submission moves to the failed state. The failureReasons array tells you which checks failed and why.

{
"object": "event",
"data": {
"id": "event_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"eventType": "validation_submission.failed",
"createdAt": "2026-04-15T11:10:00.000Z",
"eventData": {
"id": "validationsubmission_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"status": "failed",
"agentVerification": {
"status": "rejected",
"rejectionReasonCode": "fake_email_used"
},
"failureReasons": [
{
"check": "agent_verification",
"reason": { "rejectionReasonCode": "fake_email_used" }
},
{
"check": "eligibility",
"reason": {
"status": "live",
"results": {
"eligibilityStatus": "fail"
}
}
}
]
}
}
}

The validation_submission.succeeded payload has the same shape with status: "succeeded" and no failureReasons array.

Handling common failure reasons

When a submission fails, the failureReasons array breaks down which checks failed and gives a machine-readable reason. The most common reasons your front end should handle:

Rejection Reason CodeWhat it meansWhat to surface to the applicant
fake_email_usedThe applicant's email could not be verified as belonging to the institution"We could not verify your school email. Please reapply using a valid school-issued address."
validation_request_failedWe could not confirm the institution is a legitimate educational organisation"We could not verify your school. Please double-check the school details or contact support."
user_verification_expiredThe applicant did not respond to a verification step in time"Your application has expired. Please reapply."
otherA reason that does not map to a specific code (review the webhook payload)Generic failure messaging; consider logging for review

Always check the full failureReasons array – a submission can fail for more than one reason.

Subscribing and verifying webhooks

Before subscribing, create server-side event handlers to trigger any required actions (account upgrades, follow-up emails, etc.). When your handlers are ready, register them via the Create Webhook Subscription endpoint.

info

Process webhooks as quickly as possible and return a 200 response as soon as you have verified and stored the event. If your handler is slow, Goodstack will time out and retry. Use a queue if your processing is non-trivial.

You can verify that an incoming webhook was sent by Goodstack using the standard signature-verification pattern. See the Verifying webhooks section of the Webhooks concept page for a complete code example and details on the Goodstack-Signature header.

Flow diagram

End-to-end flow of an Education verification submission from the applicant's perspective: