Skip to main content

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/TerritoryAuthorityType CodeVerification SpeedNotes
New South WalesNESA (NSW Education Standards Authority)nesa<5 secondsSee detailed guide
VictoriaVIT (Victorian Institute of Teaching)vit<5 seconds-
QueenslandQCT (Queensland College of Teachers)qct<5 seconds-
South AustraliaSATRB (Teachers Registration Board SA)satrb<10 seconds-
Western AustraliaWATRB (Teacher Registration Board WA)watrb<10 seconds-
TasmaniaTASTRB (Teachers Registration Board TAS)tastrb<10 seconds-
Northern TerritoryNTTRB (Teacher Registration Board NT)nttrb<10 seconds-

Note: Australian Capital Territory (ACT) does not have a teacher registration scan type available.

State-Specific Details

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:

  1. Active organization account - Your organization must be active and in good standing
  2. Verified user status - Your API user must have completed email verification
  3. 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

FieldTypeDescriptionRequired
typestringCheck type code (see table above)Yes
identifierstringTeacher registration numberYes
first_namestringFirst/given nameYes
middle_namestringMiddle name(s)No
surnamestringSurname/family nameYes
birth_datestring (YYYY-MM-DD)Date of birthNo
constituent.idintegerLink to existing constituentNo

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"
}
Detailed NSW Guide

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:

  1. Store the identifier (registration number) and constituent.id with your internal records when submitting a scan
  2. When you receive a webhook, match it using content.current.identifier and content.constituent.id
  3. 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.

Webhook Setup Guide

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_id
  • content.current - Contains the complete accreditation details
  • content.current.status_color - Primary indicator: green, yellow, or red
  • content.current.status_flags - Array of specific conditions
  • content.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:

  1. status_color - Primary indicator (green, yellow, red)
  2. status_flags - Array of specific conditions
  3. meta.status - Validation metadata
  4. registry_response - State-specific response data

Status Colors

Status ColorMeaningAction
greenValid and current registration✅ Approved to teach
yellowNeeds attention or review⚠️ Review required
redInvalid 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 OK quickly (within 10 seconds)
  • Process webhook data asynchronously
  • Match webhooks using identifier and constituent.id
  • Store the accreditation.id for 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 Authorization header 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 constituent field 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 OK within 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/TerritoryType CodeAuthority
New South WalesnesaNESA
VictoriavitVIT
QueenslandqctQCT
South AustraliasatrbSATRB
Western AustraliawatrbWATRB
TasmaniatastrbTASTRB
Northern TerritorynttrbNTTRB

Required Fields

Always required:

  • type - State type code
  • identifier - Teacher registration number
  • first_name - First/given name
  • surname - 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

CodeMeaningCommon Cause
200SuccessRequest queued successfully
400Bad RequestValidation error (missing/invalid field)
401UnauthorizedInvalid/missing API token
429Too Many RequestsRate limit exceeded
503Service UnavailableRegistry temporarily unavailable


Document Version

Version: 1.0 Last Updated: 2026-02-06 Endpoint: POST /api/scan (Universal with state-specific type field) Status: Production Ready