Complete technical reference for the NDSS CRM RESTful API, including authentication, CRUD endpoints for all major entities, Python service endpoints, PHP integration connectors, real-time subscriptions, webhooks, error handling, and rate limiting.
The NDSS CRM platform exposes a comprehensive RESTful API that enables external systems, custom integrations, and third-party tools to interact programmatically with all major platform entities. The API is built on top of the Next.js API Routes layer (located in src/app/api/) with additional service endpoints provided by Python (for data processing, analytics, and report generation) and PHP (for legacy system connectors, data synchronisation, and bulk import/export operations).
All API requests are made against the following base URL:
Production: https://app.newdawnsupport.com.au/api
Staging: https://staging.newdawnsupport.com.au/api
Development: http://localhost:3000/api
/api/clients, /api/staff, /api/shifts). Actions that do not map directly to CRUD operations use verb suffixes (e.g., /api/shifts/:id/approve).application/json content type. Dates follow ISO 8601 format (YYYY-MM-DDTHH:mm:ss.sssZ).success, data, message, and meta fields.page, limit, cursor, and sort query parameters.?status=active®ion=NSW).v1. The version is included in the base path: /api/v1/. All endpoints documented in this chapter are V1 unless otherwise stated.
All API requests (except POST /api/auth/login, POST /api/auth/register, and POST /api/auth/forgot-password) require a valid Bearer token in the Authorization header. Tokens are obtained by calling the login endpoint and are valid for 60 minutes. Refresh tokens are valid for 30 days.
// Required header for authenticated requests
Authorization: Bearer <access_token>
Content-Type: application/json
Every API response follows this structure:
{
"success": true,
"data": { ... },
"message": "Operation completed successfully",
"meta": {
"page": 1,
"limit": 25,
"total": 247,
"totalPages": 10,
"hasNext": true,
"hasPrev": false
}
}
All API endpoints are rate-limited to protect platform stability. The default rate limit is 100 requests per minute per authenticated user. Certain high-cost endpoints (report generation, bulk operations) have lower limits. Rate limit headers are included in every response. See Section 19.13 for full details.
The Authentication API handles user login, registration, logout, token refresh, and password recovery. All authentication endpoints are prefixed with /api/auth/. These endpoints interact directly with Supabase / Oracle Auth and the NDSS CRM user profiles table in PostgreSQL.
POST /api/auth/login
Authenticates a user with email and password. Returns an access token, refresh token, and user profile information including the assigned role.
Request Body:
{
"email": "admin@newdawnsupport.com.au",
"password": "SecureP@ssw0rd!"
}
Success Response (200 OK):
{
"success": true,
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "v1.MjAyNC0wNi0xNVQx...",
"expiresIn": 3600,
"tokenType": "Bearer",
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "admin@newdawnsupport.com.au",
"firstName": "Sarah",
"lastName": "Mitchell",
"role": "master_admin",
"status": "active",
"lastLogin": "2025-01-15T09:23:41.000Z"
}
},
"message": "Login successful"
}
Error Response (401 Unauthorized):
{
"success": false,
"error": {
"code": "AUTH_INVALID_CREDENTIALS",
"message": "Invalid email or password",
"statusCode": 401
}
}
POST /api/auth/register
Creates a new user account. This endpoint is restricted to administrators who can register new staff members. Self-registration is not permitted. The new user receives an email verification link.
Request Body:
{
"email": "jane.smith@newdawnsupport.com.au",
"password": "TempP@ssw0rd123!",
"firstName": "Jane",
"lastName": "Smith",
"role": "support_worker",
"phone": "+61412345678",
"department": "Community Support",
"employmentType": "full_time",
"startDate": "2025-02-01"
}
Success Response (201 Created):
{
"success": true,
"data": {
"id": "660e8400-e29b-41d4-a716-446655440001",
"email": "jane.smith@newdawnsupport.com.au",
"role": "support_worker",
"status": "pending_verification",
"createdAt": "2025-02-01T10:00:00.000Z"
},
"message": "User registered successfully. Verification email sent."
}
POST /api/auth/logout
Invalidates the current session and revokes the refresh token. Requires a valid access token in the Authorization header.
Request Headers:
Authorization: Bearer <access_token>
Success Response (200 OK):
{
"success": true,
"message": "Logout successful. Session terminated."
}
POST /api/auth/refresh
Exchanges a valid refresh token for a new access token and refresh token pair. The old refresh token is invalidated immediately to prevent token reuse attacks.
Request Body:
{
"refreshToken": "v1.MjAyNC0wNi0xNVQx..."
}
Success Response (200 OK):
{
"success": true,
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "v1.MjAyNC0wNy0xNVQx...",
"expiresIn": 3600
},
"message": "Token refreshed successfully"
}
POST /api/auth/forgot-password
Initiates a password reset flow by sending a time-limited reset link to the registered email address. The reset token expires after 60 minutes.
Request Body:
{
"email": "jane.smith@newdawnsupport.com.au"
}
Success Response (200 OK):
{
"success": true,
"message": "If an account with that email exists, a password reset link has been sent."
}
The forgot-password endpoint always returns a success response regardless of whether the email exists in the system. This prevents email enumeration attacks. The actual reset email is only sent if the account exists and is in an active state.
The Client API provides full CRUD operations for managing NDIS participant records. All client endpoints require authentication and are subject to role-based access control. Only users with roles that include client management permissions (e.g., master_admin, administrator, service_coordinator, support_worker) can access these endpoints.
GET /api/clients
Retrieves a paginated list of clients. Supports filtering by status, region, service type, NDIS plan status, and assigned coordinator.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number for pagination |
limit | integer | 25 | Number of records per page (max 100) |
status | string | - | Filter by status: active, inactive, waitlist, discharged |
region | string | - | Filter by service region (e.g., NSW, VIC, QLD) |
coordinatorId | UUID | - | Filter by assigned service coordinator |
serviceType | string | - | Filter by NDIS service category |
search | string | - | Full-text search across name, NDIS number, email |
sort | string | lastName:asc | Sort field and direction (e.g., createdAt:desc) |
Success Response (200 OK):
{
"success": true,
"data": [
{
"id": "c001-uuid",
"ndisNumber": "431234567",
"firstName": "Michael",
"lastName": "Thompson",
"dateOfBirth": "1985-03-22",
"status": "active",
"region": "NSW",
"primaryDisability": "Intellectual Disability",
"planStartDate": "2024-07-01",
"planEndDate": "2025-06-30",
"totalBudget": 85000.00,
"budgetUsed": 42350.75,
"coordinatorId": "s001-uuid",
"coordinatorName": "Lisa Chen",
"createdAt": "2024-07-01T00:00:00.000Z",
"updatedAt": "2025-01-10T14:30:00.000Z"
}
],
"meta": {
"page": 1,
"limit": 25,
"total": 247,
"totalPages": 10
}
}
GET /api/clients/:id
Retrieves a single client record by UUID. Returns the full client profile including personal details, NDIS plan information, funding allocations, service agreements, goals, and document references.
Success Response (200 OK):
{
"success": true,
"data": {
"id": "c001-uuid",
"ndisNumber": "431234567",
"firstName": "Michael",
"lastName": "Thompson",
"dateOfBirth": "1985-03-22",
"gender": "male",
"email": "m.thompson@email.com",
"phone": "+61400111222",
"address": {
"street": "42 Wattle Drive",
"suburb": "Parramatta",
"state": "NSW",
"postcode": "2150"
},
"primaryDisability": "Intellectual Disability",
"secondaryDisabilities": ["Epilepsy"],
"status": "active",
"ndisPlan": {
"planNumber": "PLN-2024-00891",
"startDate": "2024-07-01",
"endDate": "2025-06-30",
"managementType": "plan_managed",
"totalBudget": 85000.00,
"categories": [
{ "code": "01", "name": "Assistance with Daily Life", "budget": 45000.00 },
{ "code": "04", "name": "Assistance with Social and Community Participation", "budget": 25000.00 },
{ "code": "15", "name": "Improved Daily Living Skills", "budget": 15000.00 }
]
},
"emergencyContact": {
"name": "Margaret Thompson",
"relationship": "Mother",
"phone": "+61400333444"
},
"riskLevel": "medium",
"coordinatorId": "s001-uuid",
"createdAt": "2024-07-01T00:00:00.000Z",
"updatedAt": "2025-01-10T14:30:00.000Z"
}
}
POST /api/clients
Creates a new client record. Requires at minimum: first name, last name, date of birth, NDIS number, and primary disability. All other fields are optional at creation and can be populated later.
Request Body (minimum required fields):
{
"firstName": "David",
"lastName": "Wilson",
"dateOfBirth": "1990-08-15",
"ndisNumber": "439876543",
"primaryDisability": "Autism Spectrum Disorder",
"region": "VIC",
"coordinatorId": "s002-uuid"
}
Success Response (201 Created):
{
"success": true,
"data": {
"id": "c002-uuid",
"ndisNumber": "439876543",
"firstName": "David",
"lastName": "Wilson",
"status": "active",
"createdAt": "2025-01-20T11:00:00.000Z"
},
"message": "Client created successfully"
}
PUT /api/clients/:id
Updates an existing client record. Supports partial updates (only fields included in the request body are modified). The id and ndisNumber fields cannot be changed after creation.
Request Body (partial update example):
{
"phone": "+61400555666",
"address": {
"street": "88 Eucalyptus Lane",
"suburb": "Richmond",
"state": "VIC",
"postcode": "3121"
},
"riskLevel": "low"
}
DELETE /api/clients/:id
Soft-deletes a client record by setting status to archived and deletedAt to the current timestamp. Client data is retained for audit and compliance purposes in accordance with NDIS data retention requirements (minimum 7 years). Only master_admin and administrator roles can perform this operation.
Success Response (200 OK):
{
"success": true,
"message": "Client archived successfully. Data retained for compliance."
}
The Staff API provides endpoints for managing employee records, including personal details, qualifications, certifications, availability, and employment information. Access is restricted based on role permissions.
GET /api/staff
Retrieves a paginated list of staff members. Supports filtering by role, status, department, qualification, and availability.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number |
limit | integer | 25 | Records per page (max 100) |
role | string | - | Filter by role identifier |
status | string | - | active, inactive, on_leave, terminated |
department | string | - | Filter by department name |
qualification | string | - | Filter by qualification type |
availableDate | string | - | ISO date to check availability |
search | string | - | Full-text search across name, email |
Success Response (200 OK):
{
"success": true,
"data": [
{
"id": "s001-uuid",
"firstName": "Lisa",
"lastName": "Chen",
"email": "l.chen@newdawnsupport.com.au",
"role": "service_coordinator",
"department": "Community Support",
"status": "active",
"employmentType": "full_time",
"ndisWorkerScreening": {
"status": "cleared",
"expiryDate": "2024-03-15"
},
"qualifications": [
"Certificate IV in Disability",
"First Aid Certificate"
],
"createdAt": "2024-01-15T00:00:00.000Z"
}
],
"meta": { "page": 1, "limit": 25, "total": 86, "totalPages": 4 }
}
GET /api/staff/:id
Returns the full staff profile including personal details, employment history, qualifications, certifications, training records, availability schedule, and assigned clients.
POST /api/staff
Creates a new staff record. This endpoint also triggers the user registration process (see Section 19.2.2). Required fields: first name, last name, email, role, and employment type.
Request Body:
{
"firstName": "Tom",
"lastName": "Nguyen",
"email": "t.nguyen@newdawnsupport.com.au",
"phone": "+61400777888",
"role": "support_worker",
"department": "SIL Services",
"employmentType": "part_time",
"startDate": "2025-03-01",
"qualifications": [
"Certificate III in Individual Support",
"NDIS Worker Orientation Module"
],
"availability": {
"monday": { "available": true, "start": "07:00", "end": "15:00" },
"tuesday": { "available": true, "start": "07:00", "end": "15:00" },
"wednesday": { "available": false },
"thursday": { "available": true, "start": "14:00", "end": "22:00" },
"friday": { "available": true, "start": "07:00", "end": "15:00" },
"saturday": { "available": false },
"sunday": { "available": false }
}
}
PUT /api/staff/:id
Updates an existing staff record. Supports partial updates. Role changes require master_admin or administrator privileges and trigger an audit log entry.
DELETE /api/staff/:id
Soft-deletes a staff record by setting status to terminated. The associated user account is deactivated but not removed. All historical records (shifts, timesheets, progress notes) are preserved for audit compliance.
The Shift API manages the complete lifecycle of rostered shifts, from creation and assignment through to completion and timesheet approval. Shifts are the core operational unit linking staff members to client service delivery.
GET /api/shifts
Retrieves shifts with filtering by date range, staff member, client, status, and location. Commonly used to populate calendar views and roster screens.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
startDate | ISO date | Start of date range (required) |
endDate | ISO date | End of date range (required) |
staffId | UUID | Filter by assigned staff member |
clientId | UUID | Filter by client |
status | string | scheduled, in_progress, completed, cancelled, no_show |
location | string | Filter by service location |
Success Response (200 OK):
{
"success": true,
"data": [
{
"id": "sh001-uuid",
"clientId": "c001-uuid",
"clientName": "Michael Thompson",
"staffId": "s003-uuid",
"staffName": "Tom Nguyen",
"date": "2025-01-20",
"startTime": "08:00",
"endTime": "16:00",
"duration": 8.0,
"status": "scheduled",
"serviceType": "Assistance with Daily Life",
"ndisLineItem": "01_011_0107_1_1",
"location": "Client Home",
"notes": "Morning routine support and meal preparation"
}
]
}
POST /api/shifts
Creates a new shift. The system validates staff availability, checks for scheduling conflicts, and verifies the client has sufficient NDIS budget remaining for the service type.
Request Body:
{
"clientId": "c001-uuid",
"staffId": "s003-uuid",
"date": "2025-01-22",
"startTime": "08:00",
"endTime": "16:00",
"serviceType": "Assistance with Daily Life",
"ndisLineItem": "01_011_0107_1_1",
"location": "Client Home",
"recurrence": {
"type": "weekly",
"endDate": "2025-06-30",
"daysOfWeek": [1, 3, 5]
}
}
PUT /api/shifts/:id
Updates a shift. Only shifts in scheduled status can be fully edited. Shifts in in_progress status can only have their end time and notes modified.
DELETE /api/shifts/:id
Cancels a shift by setting status to cancelled. A cancellation reason is required. If the shift is part of a recurring series, the user can choose to cancel just this occurrence or all future occurrences.
PATCH /api/shifts/:id/status
Updates the status of a shift. Valid transitions are enforced by the system:
| Current Status | Allowed Transitions | Required By |
|---|---|---|
scheduled | in_progress, cancelled | Staff, Roster Officer |
in_progress | completed, cancelled | Staff, Roster Officer |
completed | approved | Coordinator, Admin |
approved | invoiced | Finance |
POST /api/shifts/:id/clock-in
Records the start time when a support worker begins a shift. Captures GPS coordinates (if enabled) and device information for verification purposes.
{
"timestamp": "2025-01-22T08:02:15.000Z",
"latitude": -33.8148,
"longitude": 151.0012,
"deviceId": "mobile-ios-a1b2c3"
}
POST /api/shifts/:id/clock-out
Records the end time when a support worker completes a shift. Requires a progress note to be submitted with the clock-out. The shift status transitions to completed.
{
"timestamp": "2025-01-22T16:05:30.000Z",
"latitude": -33.8148,
"longitude": 151.0012,
"progressNote": "Assisted Michael with morning routine, meal prep, and community access outing to local shops. Michael was in good spirits and engaged well in all activities.",
"goalsAddressed": ["goal-001", "goal-003"]
}
The Invoice API handles the creation, management, submission, and approval of NDIS invoices. Invoices are generated from approved shifts and linked to specific NDIS line items and client plans.
GET /api/invoices
Retrieves a paginated list of invoices with filtering by status, client, date range, and payment status.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
status | string | draft, pending, submitted, paid, rejected, void |
clientId | UUID | Filter by client |
startDate | ISO date | Invoice date range start |
endDate | ISO date | Invoice date range end |
minAmount | number | Minimum invoice amount |
maxAmount | number | Maximum invoice amount |
Success Response (200 OK):
{
"success": true,
"data": [
{
"id": "inv001-uuid",
"invoiceNumber": "INV-2025-00142",
"clientId": "c001-uuid",
"clientName": "Michael Thompson",
"ndisNumber": "431234567",
"invoiceDate": "2025-01-25",
"dueDate": "2025-02-24",
"totalAmount": 2480.00,
"gst": 0.00,
"status": "submitted",
"lineItems": [
{
"description": "Assistance with Daily Life - Weekday Daytime",
"ndisLineItem": "01_011_0107_1_1",
"quantity": 40,
"unit": "hours",
"rate": 62.00,
"amount": 2480.00
}
],
"createdAt": "2025-01-25T10:00:00.000Z"
}
]
}
POST /api/invoices
Creates a new invoice. Invoices can be created manually or auto-generated from approved shifts within a specified date range.
Manual Creation Request Body:
{
"clientId": "c001-uuid",
"invoiceDate": "2025-01-25",
"dueDate": "2025-02-24",
"lineItems": [
{
"description": "Assistance with Daily Life - Weekday Daytime",
"ndisLineItem": "01_011_0107_1_1",
"quantity": 40,
"unit": "hours",
"rate": 62.00
}
]
}
Auto-Generation from Shifts:
// POST /api/invoices/generate
{
"clientId": "c001-uuid",
"startDate": "2025-01-01",
"endDate": "2025-01-31",
"autoSubmit": false
}
PATCH /api/invoices/:id/submit
Submits a draft invoice for approval. Validates all line items against the NDIS price guide and checks the client's remaining budget before submission. Transitions the invoice status from draft to pending.
PATCH /api/invoices/:id/approve
Approves a pending invoice. Only users with the finance, administrator, or master_admin role can approve invoices. Transitions the status from pending to submitted (ready for NDIS claim).
{
"approverNotes": "Verified against approved shifts. All line items match NDIS price guide rates."
}
PATCH /api/invoices/:id/void
Voids an invoice. A void reason is required. Voided invoices cannot be reactivated. The associated NDIS budget allocation is released back to the client's plan.
The Incident API manages the reporting, investigation, and resolution of incidents in accordance with NDIS Quality and Safeguards Commission requirements. All incident-related actions are logged in an immutable audit trail.
GET /api/incidents
Retrieves incidents with filtering by severity, status, client, staff member, category, and date range.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
severity | string | low, medium, high, critical |
status | string | reported, under_investigation, action_required, resolved, closed |
category | string | injury, medication_error, behaviour, property_damage, abuse_neglect, restrictive_practice, death, other |
clientId | UUID | Filter by involved client |
reportedBy | UUID | Filter by reporting staff member |
POST /api/incidents
Reports a new incident. Certain categories (abuse/neglect, restrictive practices, death) automatically trigger notifications to the Compliance Officer and may require reporting to the NDIS Quality and Safeguards Commission within 24 hours.
Request Body:
{
"clientId": "c001-uuid",
"category": "injury",
"severity": "medium",
"dateTime": "2025-01-20T14:30:00.000Z",
"location": "Client Home - Kitchen",
"description": "Client sustained a minor burn to left hand while assisting with meal preparation. First aid administered immediately. No medical attention required.",
"immediateActions": "Cold water applied for 20 minutes. Area dressed with sterile bandage. Client monitored for 1 hour.",
"witnessIds": ["s003-uuid"],
"reportedBy": "s003-uuid"
}
PATCH /api/incidents/:id/status
Updates the status of an incident. Status transitions follow a defined workflow:
| Current Status | Allowed Transitions | Required Role |
|---|---|---|
reported | under_investigation | Compliance Officer, Admin |
under_investigation | action_required, resolved | Compliance Officer, Admin |
action_required | resolved | Compliance Officer, Admin |
resolved | closed | Master Admin, Admin |
NDSS CRM leverages Supabase / Oracle Real-Time to deliver instant updates to connected clients without polling. This is used extensively for the dashboard activity feed, shift status updates, messaging, and notification delivery. The platform uses both PostgreSQL Change Data Capture (CDC) channels and Supabase / Oracle Broadcast channels.
CDC subscriptions listen for INSERT, UPDATE, and DELETE events on specific database tables. These are used for data synchronisation across connected browser sessions.
TypeScript Client Example:
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
// Subscribe to shift status changes
const shiftSubscription = supabase
.channel('shift-updates')
.on(
'postgres_changes',
{
event: 'UPDATE',
schema: 'public',
table: 'shifts',
filter: 'status=eq.in_progress'
},
(payload) => {
console.log('Shift updated:', payload.new);
// Update the roster calendar in real-time
updateRosterView(payload.new);
}
)
.subscribe();
// Subscribe to new incident reports
const incidentSubscription = supabase
.channel('incident-alerts')
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'incidents',
filter: 'severity=in.(high,critical)'
},
(payload) => {
showUrgentNotification(payload.new);
}
)
.subscribe();
// Subscribe to client record changes for a specific client
const clientSubscription = supabase
.channel('client-c001')
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'clients',
filter: 'id=eq.c001-uuid'
},
(payload) => {
refreshClientProfile(payload.new);
}
)
.subscribe();
Broadcast channels are used for ephemeral, user-to-user communication that does not need to be persisted in the database. This includes typing indicators, presence status, and live collaboration signals.
// Broadcast: typing indicator for messaging
const messagingChannel = supabase
.channel('messaging-room-001')
.on('broadcast', { event: 'typing' }, (payload) => {
showTypingIndicator(payload.payload.userId);
})
.subscribe();
// Send typing event
messagingChannel.send({
type: 'broadcast',
event: 'typing',
payload: { userId: currentUser.id, displayName: currentUser.name }
});
// Presence: track online users on the dashboard
const presenceChannel = supabase
.channel('online-users')
.on('presence', { event: 'sync' }, () => {
const state = presenceChannel.presenceState();
updateOnlineUserList(state);
})
.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
await presenceChannel.track({
userId: currentUser.id,
role: currentUser.role,
onlineAt: new Date().toISOString()
});
}
});
| Table | Events | Common Use Case |
|---|---|---|
shifts | INSERT, UPDATE, DELETE | Roster calendar real-time sync |
incidents | INSERT, UPDATE | Urgent incident notifications |
messages | INSERT | New message delivery |
notifications | INSERT | Push notification delivery |
clients | UPDATE | Client profile change alerts |
invoices | INSERT, UPDATE | Invoice status change notifications |
timesheets | INSERT, UPDATE | Timesheet submission alerts |
progress_notes | INSERT | New progress note notifications |
NDSS CRM uses a Python microservice layer for computationally intensive operations including data processing, report generation, analytics computations, and scheduled batch jobs. The Python services are built with Flask and communicate with the main Next.js application via internal HTTP calls. These endpoints are not directly exposed to external consumers but are invoked by the Next.js API layer.
POST /python/reports/generate
Generates complex reports in PDF, CSV, or XLSX format. Supports all report types defined in the platform (financial summaries, client outcome reports, compliance audits, staff utilisation reports, etc.).
Request Body:
{
"reportType": "financial_summary",
"parameters": {
"startDate": "2024-07-01",
"endDate": "2024-12-31",
"region": "NSW",
"groupBy": "month",
"includeGst": true
},
"outputFormat": "pdf",
"requestedBy": "s001-uuid"
}
Python Service Implementation (excerpt):
from flask import Flask, request, jsonify, send_file
from reportlab.lib.pagesizes import A4
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
import psycopg2
import pandas as pd
app = Flask(__name__)
@app.route('/python/reports/generate', methods=['POST'])
def generate_report():
data = request.get_json()
report_type = data['reportType']
params = data['parameters']
output_format = data.get('outputFormat', 'pdf')
# Connect to PostgreSQL via Supabase / Oracle connection string
conn = psycopg2.connect(os.environ['DATABASE_URL'])
if report_type == 'financial_summary':
df = pd.read_sql(
"""
SELECT
DATE_TRUNC(%s, i.invoice_date) AS period,
COUNT(i.id) AS invoice_count,
SUM(i.total_amount) AS total_revenue,
SUM(CASE WHEN i.status = 'paid' THEN i.total_amount ELSE 0 END) AS collected,
SUM(CASE WHEN i.status = 'submitted' THEN i.total_amount ELSE 0 END) AS outstanding
FROM invoices i
JOIN clients c ON i.client_id = c.id
WHERE i.invoice_date BETWEEN %s AND %s
AND (%s IS NULL OR c.region = %s)
GROUP BY period
ORDER BY period
""",
conn,
params=[
params['groupBy'],
params['startDate'],
params['endDate'],
params.get('region'),
params.get('region')
]
)
if output_format == 'pdf':
filepath = generate_pdf_report(df, report_type, params)
elif output_format == 'csv':
filepath = generate_csv_report(df, report_type)
elif output_format == 'xlsx':
filepath = generate_xlsx_report(df, report_type)
return send_file(filepath, as_attachment=True)
return jsonify({'error': 'Unknown report type'}), 400
POST /python/analytics/compute
Performs complex analytics calculations including staff utilisation rates, client outcome trends, budget burn-rate projections, and compliance score aggregations.
Request Body:
{
"metric": "staff_utilisation",
"parameters": {
"startDate": "2025-01-01",
"endDate": "2025-01-31",
"department": "Community Support",
"granularity": "daily"
}
}
Response:
{
"success": true,
"data": {
"metric": "staff_utilisation",
"averageUtilisation": 78.5,
"peakUtilisation": 95.2,
"lowUtilisation": 42.1,
"dataPoints": [
{ "date": "2025-01-01", "utilisation": 72.3, "scheduledHours": 480, "workedHours": 347 },
{ "date": "2025-01-02", "utilisation": 81.7, "scheduledHours": 520, "workedHours": 425 }
]
}
}
POST /python/data/process
Handles batch data processing tasks including NDIS price guide imports, bulk client data transformations, scheduled data cleanup, and data validation routines.
Available Processing Tasks:
| Task Type | Description | Schedule |
|---|---|---|
price_guide_import | Imports and parses the latest NDIS price guide CSV into the platform | On demand / quarterly |
budget_recalculation | Recalculates budget usage for all active clients based on approved invoices | Nightly at 02:00 AEST |
expiry_alerts | Scans staff certifications and generates alerts for upcoming expiries | Daily at 06:00 AEST |
plan_review_alerts | Identifies NDIS plans approaching end date (30, 60, 90 day warnings) | Weekly on Monday |
data_deduplication | Identifies and flags potential duplicate client or staff records | On demand |
compliance_scoring | Calculates organisation-wide compliance scores across all NDIS Practice Standards | Monthly on 1st |
GET /python/jobs/status
Returns the status of all scheduled Python jobs including last run time, next scheduled run, and success/failure counts.
{
"success": true,
"data": {
"jobs": [
{
"name": "budget_recalculation",
"status": "completed",
"lastRun": "2025-01-20T02:00:00.000Z",
"nextRun": "2025-01-21T02:00:00.000Z",
"duration": "4m 23s",
"recordsProcessed": 247,
"consecutiveSuccesses": 45
},
{
"name": "expiry_alerts",
"status": "completed",
"lastRun": "2025-01-20T06:00:00.000Z",
"nextRun": "2025-01-21T06:00:00.000Z",
"duration": "1m 12s",
"alertsGenerated": 7,
"consecutiveSuccesses": 30
}
]
}
}
NDSS CRM includes a PHP integration layer that provides connectivity with legacy systems, bulk data import/export functionality, and specialised data synchronisation connectors. The PHP services are built with Laravel and run as a separate microservice that communicates with the main platform via internal API calls and shared PostgreSQL database access.
The PHP integration layer provides connectors for common legacy systems used by NDIS providers that are transitioning to NDSS CRM.
POST /php/connectors/sync
Initiates a data synchronisation job between a legacy system and NDSS CRM.
Request Body:
{
"connector": "myob",
"direction": "pull",
"entities": ["invoices", "payments"],
"dateRange": {
"from": "2025-01-01",
"to": "2025-01-31"
},
"credentials": {
"apiKey": "stored-credential-ref-001"
}
}
Supported Legacy Connectors:
| Connector | Type | Entities Supported | Direction |
|---|---|---|---|
myob | Accounting | Invoices, Payments, Contacts | Pull / Push |
xero | Accounting | Invoices, Payments, Contacts | Pull / Push |
proda | NDIS Claims | Claims, Responses | Push / Pull |
carelink | Care Management | Clients, Plans, Notes | Pull only |
deputy | Rostering | Shifts, Timesheets, Staff | Pull / Push |
employment_hero | HR/Payroll | Staff, Leave, Payroll | Push only |
POST /php/import/bulk
Handles bulk data imports from CSV, XLSX, and JSON files. Supports client records, staff records, historical shifts, and invoice data. The import process includes validation, duplicate detection, and error reporting.
PHP Service Implementation (excerpt):
// Laravel Controller - BulkImportController.php
namespace App\Http\Controllers;
use App\Services\ImportService;
use App\Jobs\ProcessBulkImport;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class BulkImportController extends Controller
{
public function import(Request $request)
{
$validated = $request->validate([
'entity' => 'required|in:clients,staff,shifts,invoices',
'file' => 'required|file|mimes:csv,xlsx,json|max:10240',
'options.skipDuplicates' => 'boolean',
'options.updateExisting' => 'boolean',
'options.dryRun' => 'boolean',
]);
$file = $request->file('file');
$entity = $validated['entity'];
$options = $validated['options'] ?? [];
// Queue the import job for background processing
$importJob = ImportService::create([
'entity' => $entity,
'filename' => $file->getClientOriginalName(),
'status' => 'queued',
'requestedBy' => $request->user()->id,
]);
ProcessBulkImport::dispatch($importJob, $file->path(), $options);
return response()->json([
'success' => true,
'data' => [
'importId' => $importJob->id,
'status' => 'queued',
],
'message' => 'Import job queued. Check status at /php/import/{id}/status',
], 202);
}
}
POST /php/export/bulk
Generates bulk data exports in CSV, XLSX, JSON, or PDF format. Exports respect the requesting user's role-based data access permissions.
Request Body:
{
"entity": "clients",
"format": "xlsx",
"filters": {
"status": "active",
"region": "NSW"
},
"columns": [
"ndisNumber", "firstName", "lastName",
"dateOfBirth", "primaryDisability", "status",
"planStartDate", "planEndDate", "totalBudget"
],
"includeHeaders": true
}
GET /php/import/:id/status
Returns the current status and progress of a bulk import job.
{
"success": true,
"data": {
"importId": "imp-001-uuid",
"entity": "clients",
"status": "processing",
"progress": {
"totalRows": 500,
"processed": 342,
"successful": 335,
"failed": 7,
"skipped": 0,
"percentComplete": 68.4
},
"errors": [
{ "row": 45, "field": "ndisNumber", "message": "NDIS number already exists in system" },
{ "row": 112, "field": "dateOfBirth", "message": "Invalid date format. Expected YYYY-MM-DD" }
],
"startedAt": "2025-01-20T10:00:00.000Z",
"estimatedCompletion": "2025-01-20T10:08:00.000Z"
}
}
NDSS CRM supports outbound webhooks that notify external systems when specific events occur within the platform. Webhooks are configured through the Admin Settings panel or via the API. Each webhook endpoint receives a POST request with a JSON payload describing the event.
POST /api/webhooks
{
"url": "https://external-system.example.com/webhook",
"events": [
"client.created",
"client.updated",
"shift.completed",
"invoice.submitted",
"incident.reported"
],
"secret": "whsec_a1b2c3d4e5f6...",
"active": true,
"description": "Sync events to external accounting system"
}
| Event | Trigger | Payload Includes |
|---|---|---|
client.created | New client record created | Full client object |
client.updated | Client record modified | Updated fields, previous values |
client.archived | Client soft-deleted | Client ID, archive reason |
staff.created | New staff member registered | Staff profile (excluding sensitive data) |
staff.updated | Staff record modified | Updated fields |
shift.created | New shift scheduled | Full shift object |
shift.completed | Shift marked as completed | Shift with timesheet data |
shift.cancelled | Shift cancelled | Shift ID, cancellation reason |
invoice.created | New invoice generated | Invoice with line items |
invoice.submitted | Invoice submitted for claim | Invoice summary |
invoice.paid | Payment received | Invoice ID, payment details |
incident.reported | New incident filed | Incident details (redacted PII) |
incident.resolved | Incident resolution recorded | Incident ID, resolution summary |
compliance.alert | Compliance threshold breach | Alert type, affected entity |
All webhook deliveries follow this standard payload structure:
{
"id": "evt_001-uuid",
"type": "shift.completed",
"timestamp": "2025-01-20T16:05:30.000Z",
"version": "v1",
"data": {
// Event-specific payload
},
"metadata": {
"source": "ndss-crm",
"environment": "production",
"deliveryAttempt": 1
}
}
Each webhook delivery includes a X-NDSS-Signature header containing an HMAC-SHA256 signature computed from the request body and the webhook secret. Receiving systems should verify this signature to ensure the webhook was sent by NDSS CRM and has not been tampered with.
// Verification example (Node.js)
const crypto = require('crypto');
function verifyWebhookSignature(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from('sha256=' + expected)
);
}
If a webhook delivery fails (non-2xx response or timeout after 30 seconds), NDSS CRM retries the delivery using exponential backoff:
| Attempt | Delay | Cumulative Wait |
|---|---|---|
| 1 (initial) | Immediate | 0 seconds |
| 2 | 30 seconds | 30 seconds |
| 3 | 2 minutes | 2.5 minutes |
| 4 | 10 minutes | 12.5 minutes |
| 5 | 30 minutes | 42.5 minutes |
| 6 | 2 hours | 2 hours 42.5 minutes |
| 7 (final) | 6 hours | 8 hours 42.5 minutes |
After 7 failed attempts, the webhook is marked as failed and the administrator is notified. The webhook endpoint is automatically disabled after 50 consecutive failures.
The NDSS CRM API uses standard HTTP status codes combined with structured error response bodies to communicate failure conditions. Every error response includes a machine-readable error code, a human-readable message, and the HTTP status code.
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "One or more fields failed validation",
"statusCode": 422,
"details": [
{
"field": "email",
"message": "Must be a valid email address",
"code": "invalid_format"
},
{
"field": "ndisNumber",
"message": "NDIS number must be exactly 9 digits",
"code": "invalid_length"
}
]
}
}
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | BAD_REQUEST | Malformed request syntax or invalid request body |
| 401 | AUTH_INVALID_CREDENTIALS | Email or password is incorrect |
| 401 | AUTH_TOKEN_EXPIRED | Access token has expired. Use refresh endpoint. |
| 401 | AUTH_TOKEN_INVALID | Token is malformed or has been revoked |
| 403 | AUTH_INSUFFICIENT_PERMISSIONS | User role does not have permission for this action |
| 403 | AUTH_ACCOUNT_DISABLED | User account has been deactivated |
| 404 | RESOURCE_NOT_FOUND | Requested resource does not exist or has been archived |
| 409 | CONFLICT_DUPLICATE | A record with the same unique identifier already exists |
| 409 | CONFLICT_SCHEDULE | Shift conflicts with existing scheduled shift for this staff member |
| 422 | VALIDATION_ERROR | Request body failed schema validation |
| 422 | BUDGET_EXCEEDED | Requested service exceeds available NDIS plan budget |
| 422 | INVALID_STATUS_TRANSITION | Requested status change is not permitted from current status |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests. Retry after the specified time. |
| 500 | INTERNAL_ERROR | Unexpected server error. Contact support if persistent. |
| 502 | SERVICE_UNAVAILABLE | Downstream service (Python/PHP) is temporarily unavailable |
| 503 | MAINTENANCE_MODE | Platform is undergoing scheduled maintenance |
Rate limiting protects the NDSS CRM platform from abuse and ensures fair resource allocation across all consumers. Rate limits are applied per authenticated user (identified by access token) and vary by endpoint category.
| Endpoint Category | Limit | Window | Notes |
|---|---|---|---|
| Standard CRUD (GET, POST, PUT, DELETE) | 100 requests | 1 minute | Most API endpoints |
| Authentication (login, register, refresh) | 10 requests | 1 minute | Protects against brute-force attacks |
| Search / List with complex filters | 30 requests | 1 minute | Database-intensive queries |
| Report Generation (Python) | 5 requests | 5 minutes | Computationally expensive operations |
| Bulk Import/Export (PHP) | 3 requests | 10 minutes | Long-running batch operations |
| Webhook Management | 20 requests | 1 minute | Configuration endpoints only |
| File Upload | 10 requests | 1 minute | Maximum 10 MB per file |
Every API response includes the following rate limit headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1706185260
Retry-After: 45 // Only included when rate limit is exceeded (429 response)
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Please retry after 45 seconds.",
"statusCode": 429,
"retryAfter": 45
}
}
Organisations that require higher rate limits for approved integrations can request a limit increase through the Admin Settings panel or by contacting Newdawn Support Services. Custom rate limit tiers are available for enterprise integration partners.