accountsplus documentation
Choose a version
Version 1.x
API reference #
The AccountsPlus sync server provides a REST API for authentication, business management, data synchronization, and attachment handling.
Base URL #
https://sync.accountsplus.app
Or for local development:
http://localhost:8080
Authentication #
Register #
POST /api/auth/register
Content-Type: application/json
{
"email": "[email protected]",
"name": "John Doe",
"password": "SecurePass123!"
}
Response:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "uuid",
"email": "[email protected]",
"name": "John Doe"
}
}
Login #
POST /api/auth/login
Content-Type: application/json
{
"email": "[email protected]",
"password": "SecurePass123!"
}
Response:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": "uuid",
"email": "[email protected]",
"name": "John Doe"
}
}
Generate token for business #
POST /api/auth/token
Authorization: Bearer <access_token>
Content-Type: application/json
{
"business_id": "uuid"
}
Response:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": "2026-08-18T12:00:00Z"
}
Refresh token #
POST /api/auth/refresh
Content-Type: application/json
{
"refresh_token": "previous_refresh_token"
}
Response:
{
"token": "new_access_token",
"refresh_token": "new_refresh_token",
"expires_at": "2026-08-18T12:00:00Z"
}
Business management #
List businesses #
GET /api/business
Authorization: Bearer <token>
Create business #
POST /api/business
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "My Business",
"abn": "12345678901"
}
Get business #
GET /api/business/{id}
Authorization: Bearer <token>
Data synchronization #
Push changes #
POST /api/sync/push
Authorization: Bearer <token>
X-Business-ID: <business_uuid>
Content-Type: application/json
{
"business_id": "uuid",
"instance_id": "device-uuid",
"branch": "main",
"changes": [
{
"table_name": "customers",
"record_uuid": "uuid",
"version": 5,
"operation": "update",
"data": { "name": "Updated Name" }
}
],
"tombstones": [
{
"table_name": "expenses",
"record_uuid": "uuid",
"version": 3
}
]
}
Response:
{
"accepted": 1,
"rejected": 0,
"server_version": 42,
"conflicts": []
}
Pull changes #
POST /api/sync/pull
Authorization: Bearer <token>
X-Business-ID: <business_uuid>
Content-Type: application/json
{
"since_version": 40
}
Response:
{
"changes": [
{
"id": 123,
"business_id": "uuid",
"version": 41,
"table_name": "invoices",
"record_uuid": "uuid",
"operation": "create",
"data": { "number": "INV-001", "total": 1500.00 },
"instance_id": "other-device-uuid",
"created_at": "2026-08-18T10:00:00Z"
}
],
"version": 42,
"has_more": false
}
Get sync status #
GET /api/sync/status
Authorization: Bearer <token>
X-Business-ID: <business_uuid>
Response:
{
"business_id": "uuid",
"server_version": 42,
"pending_conflicts": 0
}
List conflicts #
GET /api/sync/conflicts
Authorization: Bearer <token>
X-Business-ID: <business_uuid>
Response:
[
{
"id": 1,
"business_id": "uuid",
"branch_name": "device-a",
"table_name": "customers",
"record_uuid": "uuid",
"local_data": "{\"name\": \"Version A\"}",
"remote_data": "{\"name\": \"Version B\"}",
"resolution": "pending",
"created_at": "2026-08-18T10:00:00Z"
}
]
Resolve conflict #
POST /api/sync/conflicts/{id}/resolve
Authorization: Bearer <token>
Content-Type: application/json
{
"resolution": "local"
}
Options: local, remote, skip
Attachments #
Upload attachment #
POST /api/sync/attachment
Authorization: Bearer <token>
X-Business-ID: <business_uuid>
Content-Type: multipart/form-data
file: <binary>
record_type: invoice
record_uuid: <uuid>
Response:
{
"uuid": "attachment-uuid",
"filename": "receipt.pdf",
"stored_name": "abc123.pdf",
"size": 1024
}
Download attachment #
GET /api/sync/attachment/{uuid}
Authorization: Bearer <token>
Returns file content as application/octet-stream.
List attachments #
GET /api/sync/attachments?record_type=invoice&record_uuid=<uuid>
Authorization: Bearer <token>
X-Business-ID: <business_uuid>
Response:
[
{
"uuid": "attachment-uuid",
"filename": "receipt.pdf",
"stored_name": "abc123.pdf",
"size": 1024
}
]
WebSocket #
Connect #
GET /ws/sync?business_id=<uuid>&user_id=<uuid>
Upgrade: websocket
Connection: Upgrade
Events #
After connection, the server sends JSON messages:
{
"type": "change",
"table_name": "invoices",
"record_uuid": "uuid",
"version": 43,
"instance_id": "other-device-uuid"
}
Error responses #
400 Bad Request #
{
"error": "Invalid request",
"details": "Email format is invalid"
}
401 Unauthorized #
{
"error": "Authentication required"
}
403 Forbidden #
{
"error": "Not a member of this business"
}
404 Not Found #
{
"error": "Business not found"
}
409 Conflict #
{
"error": "Sync conflict detected",
"details": "Record modified on multiple devices"
}
429 Too Many Requests #
{
"error": "Rate limit exceeded",
"retry_after": 60
}
Rate limits #
- Authenticated requests: 100 requests per minute per user
- Unauthenticated requests: 20 requests per minute per IP
- Lockout: 5 failed attempts → 15 minute lockout
Security headers #
All responses include:
Strict-Transport-Security: max-age=31536000X-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockContent-Security-Policy: default-src 'self'