Release: Wombat

Wombat is the current DocsHub architecture.

Read the Roadmap

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 #

Security headers #

All responses include: