Teacher Registration Verification
This guide shows you how to verify teacher registration across all Australian states and territories using the Oho API.
Overview
The /api/scan endpoint provides a unified interface for verifying teacher registration across seven Australian state and territory registration authorities. Validations are processed asynchronously - the endpoint queues the request and returns immediately with a correlation_id for tracking. Results are delivered later via webhook.
Teacher registration is managed by state-based authorities, and each state has specific requirements for educators working in schools. Oho provides verification across all states using a single endpoint with consistent payload structure.
Base URL: POST /api/scan
Authentication: Required (Bearer token)
Content-Type: application/json
Key Benefits:
- ✅ Single endpoint for all Australian state teacher registrations
- ✅ Consistent payload structure across all states
- ✅ Asynchronous processing for high performance
- ✅ Built-in retry logic and webhook delivery
State Coverage
Oho supports teacher registration verification across seven Australian states and territories:
| State/Territory | Authority | Type Code | Verification Speed | Notes |
|---|---|---|---|---|
| New South Wales | NESA (NSW Education Standards Authority) | nesa | <5 seconds | See detailed guide |
| Victoria | VIT (Victorian Institute of Teaching) | vit | <5 seconds | - |
| Queensland | QCT (Queensland College of Teachers) | qct | <5 seconds | - |
| South Australia | SATRB (Teachers Registration Board SA) | satrb | <10 seconds | - |
| Western Australia | WATRB (Teacher Registration Board WA) | watrb | <10 seconds | - |
| Tasmania | TASTRB (Teachers Registration Board TAS) | tastrb | <10 seconds | - |
| Northern Territory | NTTRB (Teacher Registration Board NT) | nttrb | <10 seconds | - |
Note: Australian Capital Territory (ACT) does not have a teacher registration scan type available.
For detailed NSW (NESA) verification information including status interpretation and best practices, see the NESA Integration Guide.
Getting Started
Prerequisites
Before submitting teacher registration verifications, ensure:
- Active organization account - Your organization must be active and in good standing
- Verified user status - Your API user must have completed email verification
- Constituents created (optional) - If linking to existing constituents, they must exist in your organization first
Universal Payload Structure
All teacher registration checks use the same payload structure. Simply change the type field to target different states:
{
"type": "nesa|vit|qct|satrb|watrb|tastrb|nttrb",
"identifier": "REGISTRATION_NUMBER",
"first_name": "First",
"middle_name": "Middle (optional)",
"surname": "Last",
"birth_date": "YYYY-MM-DD (optional)",
"constituent": {
"id": 123
}
}
Field Descriptions
| Field | Type | Description | Required |
|---|---|---|---|
type | string | Check type code (see table above) | Yes |
identifier | string | Teacher registration number | Yes |
first_name | string | First/given name | Yes |
middle_name | string | Middle name(s) | No |
surname | string | Surname/family name | Yes |
birth_date | string (YYYY-MM-DD) | Date of birth | No |
constituent.id | integer | Link to existing constituent | No |
Quick Start Examples
New South Wales (NESA)
curl -X POST https://app.weareoho.com/api/scan \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"type": "nesa",
"identifier": "123456",
"first_name": "Sarah",
"middle_name": "Jane",
"surname": "Smith",
"birth_date": "1990-05-15",
"constituent": {
"id": 12345
}
}'
Success Response:
{
"correlation_id": "550e8400-e29b-41d4-a716-446655440000"
}
For comprehensive NESA verification details, see the NESA Integration Guide.
Victoria (VIT)
{
"type": "vit",
"identifier": "VIT_NUMBER",
"first_name": "Michael",
"middle_name": "James",
"surname": "Chen",
"birth_date": "1988-03-22",
"constituent": {
"id": 12346
}
}
Queensland (QCT)
{
"type": "qct",
"identifier": "QCT_NUMBER",
"first_name": "Emma",
"middle_name": "Rose",
"surname": "Wilson",
"birth_date": "1992-11-08",
"constituent": {
"id": 12347
}
}
South Australia (SATRB)
{
"type": "satrb",
"identifier": "SATRB_NUMBER",
"first_name": "David",
"middle_name": "Paul",
"surname": "Thompson",
"birth_date": "1985-07-14",
"constituent": {
"id": 12348
}
}
Western Australia (WATRB)
{
"type": "watrb",
"identifier": "WATRB_NUMBER",
"first_name": "Jennifer",
"middle_name": "Anne",
"surname": "Brown",
"birth_date": "1991-09-30",
"constituent": {
"id": 12349
}
}
Tasmania (TASTRB)
{
"type": "tastrb",
"identifier": "TASTRB_NUMBER",
"first_name": "Robert",
"middle_name": "John",
"surname": "Davis",
"birth_date": "1987-02-18",
"constituent": {
"id": 12350
}
}
Northern Territory (NTTRB)
{
"type": "nttrb",
"identifier": "NTTRB_NUMBER",
"first_name": "Lisa",
"middle_name": "Marie",
"surname": "Anderson",
"birth_date": "1993-04-25",
"constituent": {
"id": 12351
}
}
Tracking Requests
Correlation ID
When you submit a scan request, the API returns a correlation_id:
{
"correlation_id": "550e8400-e29b-41d4-a716-446655440000"
}
Important: The correlation_id does not persist from the initial request to the webhook response. You cannot rely on it to match webhook results back to your original requests.
Matching Webhook Results
To match webhook results back to your original requests, use the registration number (identifier) and constituent information:
Your tracking approach should be:
- Store the
identifier(registration number) andconstituent.idwith your internal records when submitting a scan - When you receive a webhook, match it using
content.current.identifierandcontent.constituent.id - Use
content.current.id(the accreditation ID) for long-term references
Example tracking pattern:
// When submitting scan
await database.savePendingScan({
registration_number: '123456',
state: 'NESA',
constituent_id: 12345,
status: 'pending',
submitted_at: new Date()
});
// When receiving webhook
async function handleWebhook(webhookData) {
const identifier = webhookData.content.current.identifier;
const constituentId = webhookData.content.constituent?.id;
// Find your record by registration number and constituent
const scan = await database.findByIdentifierAndConstituent(
identifier,
constituentId
);
// Update with results
await database.updateScan(scan.id, {
accreditation_id: webhookData.content.current.id,
status: webhookData.content.current.status,
status_color: webhookData.content.current.status_color,
registry_response: webhookData.content.current.registry_response
});
}
Webhook Configuration
Since the /api/scan endpoint processes requests asynchronously, results are delivered to your webhook endpoint when validation completes.
For detailed instructions on setting up webhooks, configuring endpoints, and security best practices, see the Webhook Integration Guide.
Webhook Payload Structure
When a teacher registration scan completes, we POST the following JSON to your webhook:
{
"event": "accreditation_validation",
"correlation_id": "a8f5d2e1-3c4b-4d5e-8f9a-1b2c3d4e5f6a",
"message_id": 12345,
"content": {
"notification_type": "accreditation-result",
"org_id": 123,
"previous": null,
"current": {
"id": 8765,
"identifier": "123456",
"type": "nesa",
"status": "active",
"status_color": "green",
"status_flags": [],
"registry_response": {
"response": ["Active"]
},
"meta": {
"status": {
"found": true,
"current": true,
"messages": []
}
}
},
"constituent": {
"id": 12345,
"first_name": "Sarah",
"surname": "Smith",
"email": "sarah.smith@example.com",
"organization": {
"id": 123,
"name": "Organization Name",
"abn": "12345678901",
"is_active": true
}
},
"entity_type_id": 3,
"event_type_id": 2,
"changed_fields": null
}
}
Key webhook fields:
correlation_id- May differ from the initial request correlation_idcontent.current- Contains the complete accreditation detailscontent.current.status_color- Primary indicator:green,yellow, orredcontent.current.status_flags- Array of specific conditionscontent.previous- Previous state (null for new checks, populated for updates)content.constituent- Details about the teacher checked
Understanding Registry Responses
Important: Response structures vary by state. Different state authorities provide different information in their verification responses.
Common Response Pattern
Most states provide validation through:
status_color- Primary indicator (green,yellow,red)status_flags- Array of specific conditionsmeta.status- Validation metadataregistry_response- State-specific response data
Status Colors
| Status Color | Meaning | Action |
|---|---|---|
green | Valid and current registration | ✅ Approved to teach |
yellow | Needs attention or review | ⚠️ Review required |
red | Invalid or not found | ❌ Cannot teach |
Best practice: Always check status_color and status_flags as the primary validation indicators, not just registry_response.
NESA-Specific Behavior
NESA (NSW) has a unique characteristic: the registry only shows "Active" status if a registration number exists. The actual validation comes from matching teacher details, reflected in status_color and status_flags.
For detailed NESA status interpretation, see the NESA Integration Guide.
Best Practices
1. Use Registration Number for Tracking
Track scans using the registration number (identifier), not correlation_id:
// Store identifier when submitting
await database.createScan({
identifier: '123456',
type: 'nesa',
constituent_id: 12345,
status: 'pending'
});
// Match using identifier in webhook
const identifier = webhookData.content.current.identifier;
const scan = await database.findByIdentifier(identifier);
2. Check Status Color and Flags
Use status_color and status_flags as primary validation:
const statusColor = webhookData.content.current.status_color;
const statusFlags = webhookData.content.current.status_flags || [];
if (statusColor === 'green' && statusFlags.length === 0) {
// Valid registration - approved to teach
console.log('✅ Registration is valid');
} else if (statusColor === 'yellow') {
// Needs review
console.warn('⚠️ Registration needs review');
} else if (statusColor === 'red') {
// Invalid - cannot teach
console.error('❌ Registration is not valid');
}
3. Handle State-Specific Variations
Different states may have different response structures and validation details. Refer to state-specific documentation for detailed requirements.
4. Prevent Duplicate Scans
Check for existing scans before submitting:
const existing = await database.findByIdentifierAndConstituent(
'123456',
12345
);
if (existing && existing.scanned_recently) {
return existing; // Use cached result
}
// Submit new scan
await submitTeacherVerification({ /* ... */ });
5. Handle Webhooks Reliably
- Return
200 OKquickly (within 10 seconds) - Process webhook data asynchronously
- Match webhooks using
identifierandconstituent.id - Store the
accreditation.idfor long-term references
Common Use Cases
Education Onboarding
Verify teacher registration during onboarding:
async function onboardTeacher(teacherData) {
// 1. Create constituent
const constituent = await createConstituent({
first_name: teacherData.firstName,
surname: teacherData.surname,
email: teacherData.email
});
// 2. Submit registration verification
const check = await submitTeacherVerification({
type: teacherData.state, // e.g., 'nesa', 'vit', 'qct'
identifier: teacherData.registrationNumber,
first_name: teacherData.firstName,
surname: teacherData.surname,
birth_date: teacherData.dob,
constituent: { id: constituent.id }
});
return {
constituent_id: constituent.id,
correlation_id: check.correlation_id
};
}
Multi-State Organization
Verify teachers across multiple states:
async function verifyMultiStateTeachers(teachers) {
for (const teacher of teachers) {
const stateCode = getStateCode(teacher.state); // nesa, vit, qct, etc.
await submitTeacherVerification({
type: stateCode,
identifier: teacher.registrationNumber,
first_name: teacher.firstName,
surname: teacher.surname,
constituent: { id: teacher.constituentId }
});
}
}
function getStateCode(state) {
const stateMap = {
'NSW': 'nesa',
'VIC': 'vit',
'QLD': 'qct',
'SA': 'satrb',
'WA': 'watrb',
'TAS': 'tastrb',
'NT': 'nttrb'
};
return stateMap[state];
}
FAQ
Q: How do I choose the right state code?
A: Use the type code that matches where the teacher is registered:
- NSW teachers:
nesa - Victorian teachers:
vit - Queensland teachers:
qct - And so on (see State Coverage table above)
Teachers must be registered in the state where they teach.
Q: Can a teacher be registered in multiple states?
A: Yes, teachers can hold registrations in multiple states. You can submit separate verification requests for each state registration using the appropriate type code for each.
Q: What if I submit the same check twice?
A: Each request creates a new scan. To prevent duplicates, check your database for existing scans before submitting. Track scans using the combination of identifier (registration number) and constituent.id.
Q: How often should I verify teacher registrations?
A:
- Initial verification: Always verify before employment commences
- Ongoing monitoring: Quarterly or bi-annual checks recommended
- Annual verification: Before each school year begins
- Ad-hoc verification: After any reported concerns
Q: What's the difference between states?
A: Each state has its own teacher registration authority with different:
- Registration requirements
- Renewal processes
- Response structures
- Verification speeds
The Oho API provides a consistent interface, but underlying capabilities vary by state.
Troubleshooting
Common Issues and Solutions
1. 401 Unauthorized Error
Problem: {"status": 401, "message": "You are not authorized to view this resource"}
Solutions:
- ✅ Verify your API token is correct and hasn't expired
- ✅ Check the
Authorizationheader format:Bearer YOUR_TOKEN - ✅ Ensure your user account is verified (check email)
- ✅ Confirm your organization is active and in good standing
2. 400 Constituent Not Found
Problem: {"errors": {"constituent": {"id": ["Constituent doesn't exist in your organization"]}}}
Solutions:
- ✅ Create the constituent first:
POST /api/constituents - ✅ Verify you're using the correct constituent ID
- ✅ Or omit
constituentfield entirely for standalone accreditation
3. Webhook Not Receiving Results
Problem: Scan submitted successfully but no webhook callback received
Solutions:
- ✅ Verify webhook URL is configured in Settings → Webhooks
- ✅ Ensure webhook endpoint is publicly accessible via HTTPS
- ✅ Check webhook endpoint returns
200 OKwithin 10 seconds - ✅ Verify no firewall blocking incoming requests
- ✅ Look for failed webhooks in your dashboard
Quick Reference
Endpoint Summary
Endpoint: POST /api/scan
Content-Type: application/json
Authentication: Authorization: Bearer YOUR_TOKEN
State Type Codes
| State/Territory | Type Code | Authority |
|---|---|---|
| New South Wales | nesa | NESA |
| Victoria | vit | VIT |
| Queensland | qct | QCT |
| South Australia | satrb | SATRB |
| Western Australia | watrb | WATRB |
| Tasmania | tastrb | TASTRB |
| Northern Territory | nttrb | NTTRB |
Required Fields
Always required:
type- State type codeidentifier- Teacher registration numberfirst_name- First/given namesurname- Surname/family name
Optional for all states:
middle_name- Middle name(s)birth_date- Date of birth (YYYY-MM-DD)constituent.id- Link to existing constituent
Common HTTP Status Codes
| Code | Meaning | Common Cause |
|---|---|---|
| 200 | Success | Request queued successfully |
| 400 | Bad Request | Validation error (missing/invalid field) |
| 401 | Unauthorized | Invalid/missing API token |
| 429 | Too Many Requests | Rate limit exceeded |
| 503 | Service Unavailable | Registry temporarily unavailable |
Related Documentation
- NESA Integration Guide - Detailed NSW teacher registration verification
- Webhook Integration Guide - Setting up webhooks for all check types
- Constituent Management Guide - Managing people in your organization
- Glossary - Comprehensive terminology reference
Document Version
Version: 1.0
Last Updated: 2026-02-06
Endpoint: POST /api/scan (Universal with state-specific type field)
Status: Production Ready