FE Colleges | April 2026
Overview
Use it to look up an HTTP status code or error message, understand the cause, and find the fix.
All endpoints require OAuth 2.0 bearer-token authentication. Errors are returned as JSON regardless of the source — authentication, field validation, security checks, rate limiting, business logic, or unhandled exceptions.
Response shapes at a glance
Different error conditions return different JSON structures. The table below shows each shape.
Status code | Response body shape |
400 — Field validation |
|
400 — Security check |
|
400 — Path/body mismatch |
|
400 — Wrong endpoint |
|
401 |
|
403 |
|
404 |
|
429 |
|
500 (production) |
|
500 (non-production) |
|
Import order and entity dependencies
Several resources must be imported before others that reference them. Importing a child entity before its parent produces a 403 Forbidden response.
Order | Resource | Depends on |
1 | Faculties | (none) |
2 | Sectors | (none) |
3 | Campuses | (none) |
4 | Programme Areas | Faculties, Sectors |
5 | Staff | (none) |
6 | Courses | Programme Areas |
7 | Programmes | Programme Areas |
8 | Students | Staff (tutor), Campuses |
9 | Applicants | (none) |
10 | Businesses | (none) |
11 | Business Contacts | Businesses |
12 | Business Departments | Businesses |
13 | Student Courses | Students, Courses |
14 | Student Primary Contacts | Students |
15 | Student Target Hours | Students, Student Courses |
16 | Student Target Skills | Students |
Rate limiting
A per-client request limit is enforced during business hours. The client identifier is the ClientID claim in the bearer token — not the IP address.
Setting | Value |
Request limit | 60,000 requests |
Window | 60 minutes (rolling) |
Enforced hours | 08:00–18:00 UTC |
Enforced days | Monday–Friday (weekends exempt) |
Client identifier | ClientID claim in the bearer token |
Global errors — all endpoints
The following errors can occur on any request, regardless of endpoint, before the action executes. They are not repeated in each resource section below.
Message | Meaning and fix | Code | Category | Field |
| The OAuth 2.0 bearer token is missing, expired, or malformed. Tokens are valid for 7 days (168 hours). | 401 | Authentication | Authorization |
| One or more request body fields failed validation. All validation errors are returned together in | 400 | Field validation |
|
| One or more string values contain characters that could pose a security risk. The request is rejected before processing. | 400 | Security check |
|
| The per-client limit of 60,000 requests per 60-minute rolling window has been exceeded. Enforced Monday–Friday 08:00–18:00 UTC only. Weekends are exempt. | 429 | Rate limiting |
|
| An unhandled exception occurred. All exceptions are logged automatically. In production only the generic message is returned; non-production responses also include | 500 | Server error |
|
Faculties — /api/v1/faculties
Message | Meaning and fix | Code | Category | Field |
The Code field is required. | The | 400 | Field validation | Code |
The Name field is required. | The | 400 | Field validation | Name |
Maximum number of characters allowed for Name is 200. | The | 400 | Field validation | Name |
The faculty code: {value} does not match the path: {code} | The | 400 | Path/body mismatch | Code |
Faculty does not exist or has been deleted | No faculty exists with the code in the URL path, or it has been soft-deleted. | 404 | Not found |
|
Sectors — /api/v1/sectors
Message | Meaning and fix | Code | Category | Field |
The Code field is required. | The | 400 | Field validation | Code |
The Name field is required. | The | 400 | Field validation | Name |
Maximum number of characters allowed for Name is 200. | The | 400 | Field validation | Name |
Sector does not exist or has been deleted | No sector exists with the code in the URL path, or it has been soft-deleted. | 404 | Not found |
|
Campuses — /api/v1/campuses
Message | Meaning and fix | Code | Category | Field |
The ExternalCampusID field is required. | The | 400 | Field validation | ExternalCampusID |
The Name field is required. | The | 400 | Field validation | Name |
Maximum number of characters allowed for Name is 200. | The | 400 | Field validation | Name |
Campus does not exist or has been deleted | No campus exists with the | 404 | Not found |
|
Programme Areas — /api/v1/programmeareas
Message | Meaning and fix | Code | Category | Field |
The Code field is required. | The | 400 | Field validation | Code |
The Name field is required. | The | 400 | Field validation | Name |
Maximum number of characters allowed for Name is 200. | The | 400 | Field validation | Name |
Maximum number of characters allowed for Location is 200. | The | 400 | Field validation | Location |
The FacultyCode field is required. | The | 400 | Field validation | FacultyCode |
Unable to save Programme Area: The Faculty {facultyCode} does not exist. | The referenced faculty has not been imported yet. Faculties must exist before programme areas. | 403 | Business logic | FacultyCode |
Unable to save Programme Area: The sector {sectorCode} does not exist. | The referenced sector has not been imported yet. | 403 | Business logic | SectorCode |
Unable to perform bulk delete for programme areas: The {dateValue} is not a valid date | The | 403 | Business logic | earliestImportedUtcDateTime |
Programme area does not exist or has been deleted | No programme area exists with the code in the URL path. | 404 | Not found |
|
Staff — /api/v1/staff
Message | Meaning and fix | Code | Category | Field |
The ExternalID field is required. | The | 400 | Field validation | ExternalID |
Maximum number of characters allowed for the External ID is 100. | The | 400 | Field validation | ExternalID |
The FirstName field is required. | The | 400 | Field validation | FirstName |
The LastName field is required. | The | 400 | Field validation | LastName |
The Email field is required. | The | 400 | Field validation | |
The Email field is not a valid e-mail address. | The | 400 | Field validation | |
The value of field Gender must be one of the specified values. | The | 400 | Field validation | Gender |
Maximum number of characters allowed for the Title is 100. | The | 400 | Field validation | Title |
Maximum number of characters allowed for the Job Title is 200. | The | 400 | Field validation | JobTitle |
The Telephone field is not a valid phone number. | The | 400 | Field validation | Telephone |
Unable to save staff member: The External login {externalLoginValue} is already linked to another user in the system | The Microsoft or Google login identity is already linked to a different Grofar user account. | 403 | Business logic | ExternalLogin |
Unable to save staff member: Could not find role: {roleName} | A role specified in the request does not exist in the Grofar system configuration. This is a configuration issue, not a data issue. | 403 | Business logic | Role |
Unable to perform bulk delete for staff: The {dateValue} is not a valid date | The | 403 | Business logic | earliestImportedUtcDateTime |
Staff Member does not exist or has been deleted | No staff member exists with the | 404 | Not found |
|
Students — /api/v1/students
Message | Meaning and fix | Code | Category | Field |
The ExternalID field is required. | The | 400 | Field validation | ExternalID |
Maximum number of characters allowed for the External ID is 100. | The | 400 | Field validation | ExternalID |
The FirstName field is required. | The | 400 | Field validation | FirstName |
The LastName field is required. | The | 400 | Field validation | LastName |
The Email field is required. | The | 400 | Field validation | |
The Email field is not a valid e-mail address. | The | 400 | Field validation | |
The value of field Gender must be one of the specified values. | The | 400 | Field validation | Gender |
The value of field AgeBand must be one of the specified values. | The | 400 | Field validation | AgeBand |
The Telephone field is not a valid phone number. | The | 400 | Field validation | Telephone |
The Mobile field is not a valid phone number. | The | 400 | Field validation | Mobile |
The Student ExternalID: {value} does not match the path: {path} | The | 400 | Path/body mismatch | ExternalID |
Unable to save student: Date of birth is incorrect date format | The | 403 | Business logic | DateOfBirth |
Unable to save student: No tutor with External ID {tutorExternalID} exists | The referenced tutor (staff member) has not been imported, or the | 403 | Business logic | TutorExternalID |
Unable to save student: No campus with External ID {campusExternalID} exists | The referenced campus has not been imported, or the | 403 | Business logic | CampusExternalID |
Unable to save student: {value} is not a valid Learning Difficulty. | One of the ILR learning difficulty codes in the | 403 | Business logic | LearningDifficulties |
Unable to save student: Failed to parse the Learning Difficulties CSV. The values must be one of 1 2 3 ... 99 separated by , | The | 403 | Business logic | LearningDifficulties |
Unable to save student: The External login {externalLoginValue} is already linked to another user in the system | The Microsoft or Google login identity is already linked to a different user account. | 403 | Business logic | ExternalLogin |
Unable to perform bulk delete for students: The {dateValue} is not a valid date | The | 403 | Business logic | earliestImportedUtcDateTime |
Student does not exist or has been deleted | No student exists with the | 404 | Not found |
|
Applicants — /api/v1/applicants
Message | Meaning and fix | Code | Category | Field |
The ExternalID field is required. | The | 400 | Field validation | ExternalID |
The FirstName field is required. | The | 400 | Field validation | FirstName |
The LastName field is required. | The | 400 | Field validation | LastName |
The Email field is required. | The | 400 | Field validation | |
The Email field is not a valid e-mail address. | The | 400 | Field validation | |
The value of field Gender must be one of the specified values. | The | 400 | Field validation | Gender |
Please enter a valid national insurance number. | The | 400 | Field validation | NationalInsuranceNumber |
Value must be a valid ethnicity code. | The | 400 | Field validation | Ethnicity |
Value must be a valid nationality. | The | 400 | Field validation | Nationality |
The Telephone field is not a valid phone number. | The | 400 | Field validation | Telephone |
The Mobile field is not a valid phone number. | The | 400 | Field validation | Mobile |
The Applicants ExternalID: {value} does not match the path: {path} | The | 400 | Path/body mismatch | ExternalID |
Unable to save applicant: Cannot create the applicant with internal id {grofarInternalID} and external id {externalID} because the applicant already exists with this external id | An applicant with this | 403 | Business logic | ExternalID |
Unable to save applicant: The date of birth is not recognised as a date format | The | 403 | Business logic | DateOfBirth |
Unable to perform bulk delete for applicants: The {dateValue} is not a valid date | The | 403 | Business logic | earliestImportedUtcDateTime |
Applicant does not exist or has been deleted | No applicant exists with the | 404 | Not found |
|
Businesses — /api/v1/businesses
Message | Meaning and fix | Code | Category | Field |
The ExternalBusinessID field is required. | The | 400 | Field validation | ExternalBusinessID |
The Name field is required. | The business | 400 | Field validation | Name |
Maximum number of characters allowed for the Business Name is 200. | The business | 400 | Field validation | Name |
The Website field is not a valid fully-qualified http, https, or ftp URL. | The | 400 | Field validation | Website |
The value of field IndustrySector must be one of the specified values (A, B, C ... U). | The | 400 | Field validation | IndustrySector |
The value of field JobCategory must be one of the specified values (1 - 25). | The | 400 | Field validation | JobCategory |
The value of field CompanySize must be one of: 1, 2-10, 11-50, 51-200, 201-500, 501-1000, 1001-5000, 5001-10000, 10000+. | The | 400 | Field validation | CompanySize |
The external business id: {value} does not match the path: {path} | The | 400 | Path/body mismatch | ExternalBusinessID |
Unable to save business: The business owner {ownerExternalID} does not exist or has been deleted | The staff member referenced as the business owner has not been imported, or the | 403 | Business logic | BusinessOwnerExternalID |
Unable to save business: Cannot update the business with internal id {grofarInternalID} because another business already exists with externalBusinessID {externalBusinessID} | Another business record already exists with this | 403 | Business logic | ExternalBusinessID |
Unable to save business: The PublicLiabilityExpiresDate is not recognised as a date format | The | 403 | Business logic | PublicLiabilityExpiresDate |
Unable to save business: The EmployerLiabilityInsuranceExpiresDate is not recognised as a date format | Use | 403 | Business logic | EmployerLiabilityInsuranceExpiresDate |
Unable to save business: {url} is not a well formed URL. | One of the URLs in the | 403 | Business logic | OtherSites |
Unable to perform bulk delete for businesses: The {dateValue} is not a valid date | The | 403 | Business logic | earliestImportedUtcDateTime |
Business does not exist or has been deleted | No business exists with the | 404 | Not found |
|
Business Contacts — /api/v1/businesses/{businessid}/contacts
Message | Meaning and fix | Code | Category | Field |
The Email field is not a valid e-mail address. | The contact | 400 | Field validation | |
The Telephone field is not a valid phone number. | The contact | 400 | Field validation | Telephone |
The value of field Gender must be one of the specified values. | The | 400 | Field validation | Gender |
Maximum number of characters allowed for the Title is 50. | The | 400 | Field validation | Title |
The external business id: {value} does not match the path: {path} | The | 400 | Path/body mismatch | ExternalBusinessID |
The external contact id: {value} does not match the path: {path} | The | 400 | Path/body mismatch | ExternalContactID |
Unable to save business contact: The business {externalBusinessID} does not exist. | The parent business has not been imported, or the | 403 | Business logic | ExternalBusinessID |
Unable to save business contact: Cannot create the business contact with id: {contactID} (business id: {businessID}) as a FirstName or a LastName is required | When creating a new contact, either | 403 | Business logic | FirstName / LastName |
Unable to save business contact: Cannot update the business contact with internal id {grofarInternalID} because the business {businessID} already has a contact with external id {contactID} | Another contact for this business already uses this | 403 | Business logic | ExternalContactID |
Unable to save business contact: {url} is not a well formed URL. | A URL in | 403 | Business logic | OtherSites |
Unable to save business contact: The External login {externalLoginValue} is already linked to another user in the system | The Microsoft or Google login identity is already linked to a different user account. | 403 | Business logic | ExternalLogin |
Unable to perform bulk delete for business contacts: The {dateValue} is not a valid date | The | 403 | Business logic | earliestImportedUtcDateTime |
Business Contact does not exist or has been deleted | No contact exists with the | 404 | Not found |
|
Business Departments — /api/v1/businesses/{businessid}/departments
Message | Meaning and fix | Code | Category | Field |
The external business id: {value} does not match the path: {path} | The | 400 | Path/body mismatch | ExternalBusinessID |
The external department id: {value} does not match the path: {path} | The | 400 | Path/body mismatch | ExternalDepartmentID |
Unable to save business department: The business {externalBusinessID} does not exist or has been deleted | The parent business has not been imported, or has been deleted. | 403 | Business logic | ExternalBusinessID |
Unable to save business department: Cannot update the department with internal id {grofarInternalID} because the business {businessID} already has a department with external id {departmentID} | Another department for this business already uses this | 403 | Business logic | ExternalDepartmentID |
Unable to save business department: The HealthAndSafetyCheckedDate is not recognised as a date format | Use ISO 8601: | 403 | Business logic | HealthAndSafetyCheckedDate |
Unable to save business department: The HealthAndSafetyExpiresDate is not recognised as a date format | Use ISO 8601: | 403 | Business logic | HealthAndSafetyExpiresDate |
Unable to save business department: The HigherRiskAssessmentDocumentsReviewedDate is not recognised as a date format | Use ISO 8601: | 403 | Business logic | HigherRiskAssessmentDocumentsReviewedDate |
Unable to save business department: HasEmployerLiabilityInsurance and EmployerLiabilityInsuranceNotRequired cannot both be true. | These two boolean flags are mutually exclusive. A department cannot both have ELI and be marked as not requiring ELI. | 403 | Business logic | HasEmployerLiabilityInsurance / EmployerLiabilityInsuranceNotRequired |
Cannot delete the primary department {departmentID} for business {businessID} | The primary department of a business cannot be deleted. First make another department primary, then delete this one. | 403 | Business logic |
|
Unable to perform bulk delete for business departments: The {dateValue} is not a valid date | The | 403 | Business logic | earliestImportedUtcDateTime |
Business department does not exist or has been deleted | No department exists with the | 404 | Not found |
|
Courses — /api/v1/courses
Message | Meaning and fix | Code | Category | Field |
The Code field is required. | The | 400 | Field validation | Code |
The Title field is required. | The | 400 | Field validation | Title |
Maximum number of characters allowed for Title is 200. | The | 400 | Field validation | Title |
The ProgrammeAreaCode field is required. | The | 400 | Field validation | ProgrammeAreaCode |
Course Level must be a number between 0 and 6. | The | 400 | Field validation | Level |
The value of field CourseYear must be one of: 1, 2, 3, 4, 5. | The | 400 | Field validation | CourseYear |
The field TargetPlacementType must be between 1 and 14. |
| 400 | Field validation | TargetPlacementType |
The Course code: {value} does not match the path: {code}. | The | 400 | Path/body mismatch | Code |
The Course instance: {value} does not match the path: {instance}. | The | 400 | Path/body mismatch | Instance |
Cannot save a Course Instance using the course endpoint. Use the course/instance endpoint. | A request body containing an | 400 | Wrong endpoint |
|
Unable to save course: The Programme Area {programmeAreaCode} does not exist. | The referenced programme area has not been imported, or the code does not match. | 403 | Business logic | ProgrammeAreaCode |
Unable to save course: The StartDate is not recognised as a date format | The | 403 | Business logic | StartDate |
Unable to save course: The EndDate is not recognised as a date format | The | 403 | Business logic | EndDate |
Unable to perform bulk delete for courses: The {dateValue} is not a valid date | The | 403 | Business logic | earliestImportedUtcDateTime |
Course does not exist or has been deleted | No course or course instance exists at the URL path given. | 404 | Not found |
|
Programmes — /api/v1/programmes
Message | Meaning and fix | Code | Category | Field |
The Code field is required. | The | 400 | Field validation | Code |
The Instance field is required. | The | 400 | Field validation | Instance |
The Title field is required. | The | 400 | Field validation | Title |
The ProgrammeAreaCode field is required. | The | 400 | Field validation | ProgrammeAreaCode |
The Programme code: {value} does not match the path: {code} | The | 400 | Path/body mismatch | Code |
The Programme instance: {value} does not match the path: {instance} | The | 400 | Path/body mismatch | Instance |
Cannot save a Programme Instance using the programme endpoint. Use the programme/instance endpoint. | A programme instance was submitted to the programme endpoint. Use | 400 | Wrong endpoint |
|
Unable to save programme: The Programme Area {programmeAreaCode} does not exist. | The referenced programme area has not been imported. Programme Areas must exist before Programmes. | 403 | Business logic | ProgrammeAreaCode |
Unable to perform bulk delete for programmes: The {dateValue} is not a valid date | The | 403 | Business logic | earliestImportedUtcDateTime |
Programme does not exist or has been deleted | No programme or programme instance exists at the URL path given. | 404 | Not found |
|
Student Courses — /api/v1/studentcourses
Message | Meaning and fix | Code | Category | Field |
The ExternalStudentID field is required. | The | 400 | Field validation | ExternalStudentID |
The CourseCode field is required. | The | 400 | Field validation | CourseCode |
The value of field Status must be one of: Completed, Transferred, Continuing, Withdrawn, TemporarilyWithdrawn. | The | 400 | Field validation | Status |
The External Student ID: {value} does not match the path: {path}. | The | 400 | Path/body mismatch | ExternalStudentID |
The Course code: {value} does not match the path: {code}. | The | 400 | Path/body mismatch | CourseCode |
The courseInstance property is missing from the body - it must match the parameter value: {instance}. | The URL path includes a course instance segment but the | 400 | Path/body mismatch | CourseInstance |
Cannot save a Student Course instance. Use the course/instance endpoint to save a Student Course Instance. | A student course instance was submitted to the non-instance endpoint. | 400 | Wrong endpoint |
|
Unable to save course instance: The student {externalStudentID} does not exist or has been deleted | The referenced student has not been imported, or the | 403 | Business logic | ExternalStudentID |
Unable to save course instance: The course {courseCode} {courseInstance} does not exist or has been deleted | The referenced course or course instance has not been imported, or the code/instance does not match. | 403 | Business logic | CourseCode / CourseInstance |
Unable to save course instance: The StartDate is not recognised as a date format | Use ISO 8601: | 403 | Business logic | StartDate |
Unable to save course instance: The EndDate is not recognised as a date format | Use ISO 8601: | 403 | Business logic | EndDate |
Unable to perform bulk delete for student courses: The {dateValue} is not a valid date | The | 403 | Business logic | earliestImportedUtcDateTime |
Student Course does not exist or has been deleted | No student course enrolment exists at the URL path given. | 404 | Not found |
|
Student Primary Contacts — /api/v1/studentprimarycontacts
Message | Meaning and fix | Code | Category | Field |
The ExternalStudentID field is required. | The | 400 | Field validation | ExternalStudentID |
The FirstName field is required. | The | 400 | Field validation | FirstName |
The LastName field is required. | The | 400 | Field validation | LastName |
The Email field is not a valid e-mail address. | The | 400 | Field validation | |
The HomePhoneNumber field is not a valid phone number. | The | 400 | Field validation | HomePhoneNumber |
The MobilePhoneNumber field is not a valid phone number. | The | 400 | Field validation | MobilePhoneNumber |
Unable to save student primary contact: The student {externalStudentID} does not exist or has been deleted | The referenced student has not been imported, or the | 403 | Business logic | ExternalStudentID |
Unable to perform bulk delete for student primary contacts: The {dateValue} is not a valid date | The | 403 | Business logic | earliestImportedUtcDateTime |
Student primary contact does not exist or has been deleted | No student primary contact exists at the URL path given. | 404 | Not found |
|
Student Target Hours — /api/v1/studenttargethours
Message | Meaning and fix | Code | Category | Field |
The ExternalStudentID field is required. | The | 400 | Field validation | ExternalStudentID |
Unable to save student target hours: The student {externalStudentID} does not exist or has been deleted | The referenced student has not been imported. | 403 | Business logic | ExternalStudentID |
Unable to save student target hours: Cannot import Target Hours for Student {externalStudentID} for course {courseCode}/{courseInstance} as this Student Course enrolment has not been imported for the student. | The student course enrolment must exist before target hours can be set for it. | 403 | Business logic | CourseCode / CourseInstance |
Unable to save student target hours: Cannot Import Target Hours for Student {externalStudentID} the ToAcademicYear: {toYear} is not greater than the FromAcademicYear: {fromYear} | The | 403 | Business logic | ToAcademicYear / FromAcademicYear |
The Student does not exist or has been deleted | No student exists with the | 404 | Not found |
|
Student Target Skills — /api/v1/studentTargetSkills
Message | Meaning and fix | Code | Category | Field |
The ExternalStudentID field is required. | The | 400 | Field validation | ExternalStudentID |
The External Student ID: {value} does not match the path: {externalStudentID}. | The | 400 | Path/body mismatch | ExternalStudentID |
Unable to save student target skill: The student {externalStudentID} does not exist or has been deleted | The referenced student has not been imported. | 403 | Business logic | ExternalStudentID |
Unable to save student target skill: No skill with id {skillID} exists | The skill ID referenced in the request does not exist in the Grofar system. | 403 | Business logic | SkillID |
Student does not exist or has been deleted | No student exists with the | 404 | Not found |
|
Quick-reference: common causes
Most API errors fall into one of these categories:
401 on every request — the bearer token is missing, expired, or malformed. Tokens are valid for 7 days.
403 with "does not exist" — a referenced entity (student, course, campus, faculty, etc.) has not been imported yet, or the ID does not match exactly. Check import order and ID casing.
400 path/body mismatch — the ID in the request body does not match the ID in the URL. Always keep them in sync, especially when reusing request templates.
400 wrong endpoint — a resource instance (e.g. course instance) was submitted to the base endpoint rather than the
/instance/endpoint.400 invalid phone number — the value contains non-dialable text such as
x123,Ext 4567, or labels likeMum's number. Send only the dialable number.400 security check — a field contains angle brackets, ampersands, or other characters flagged as a potential security risk. Strip or encode them.
403 date format — a date field is not in ISO 8601 format (
YYYY-MM-DD). This is a 403 business logic error, not a 400 validation error, for several resources.429 rate limit — 60,000 requests per 60-minute window during business hours. Schedule large imports outside 08:00–18:00 UTC on weekdays.
500 server error — often caused by a null or unexpected value in a field that passes validation but causes a server-side exception. Contact Grofar support with the request body and timestamp.
