Skip to main content
The sync endpoints enable offline-first functionality, allowing mobile devices to synchronize inspection data, photos, and form updates with the server.

POST /api/v1/sync

Synchronize offline changes with the server. Supports batch operations for form fills, inspections, photo deletions, and signature deletions.

Authentication

Requires user authentication via Devise. Include authentication token in request headers.

Request body

sync_items
array
required
Array of sync items to process. Each item represents a change made offline.
sync_items[].type
string
required
Type of sync operation:
  • form_fill - Create or update form fill data
  • form_fill_update - Update form fill data (alias)
  • inspection - Update inspection status/notes
  • photo_delete - Delete photo from form fill
  • signature_delete - Delete signature from form fill
sync_items[].local_id
string
required
Client-side identifier for tracking sync results
sync_items[].data
object
required
Payload data specific to the sync type

Form fill sync payload

data.id
integer
Form fill ID (required for updates)
data.updated_at
datetime
Client’s last updated timestamp for conflict detection
data.data
object
Complete form data hash (for full sync)
data.changes
object
Partial changes to merge (for patch sync)
data.resolve_strategy
string
Conflict resolution strategy: use_local to force local version

Response

success
boolean
Overall sync success status
results
object
Sync results categorized by outcome
synced_at
datetime
Server timestamp of sync completion
message
string
Summary message with counts

Example request

{
  "sync_items": [
    {
      "type": "form_fill",
      "local_id": "offline-123",
      "data": {
        "id": 456,
        "updated_at": "2024-03-15T10:30:00Z",
        "data": {
          "Building_Name": "Main Building",
          "Location_row_1": "First Floor"
        }
      }
    },
    {
      "type": "photo_delete",
      "local_id": "delete-789",
      "data": {
        "form_fill_id": 456,
        "field_name": "Inspection_Photo_1"
      }
    }
  ]
}

Example response

{
  "success": true,
  "results": {
    "success": [
      {
        "local_id": "offline-123",
        "server_id": 456,
        "type": "form_fill",
        "message": "FormFill updated successfully"
      }
    ],
    "errors": [],
    "conflicts": []
  },
  "synced_at": "2024-03-15T10:35:22Z",
  "message": "Sync completed: 1 successful, 0 errors, 0 conflicts"
}

Conflict handling

When the server detects a version conflict (server’s updated_at is newer than client’s), it returns conflict data:
{
  "success": false,
  "conflict": true,
  "server_id": 456,
  "conflict_data": {
    "server_version": "2024-03-15T10:32:00Z",
    "local_version": "2024-03-15T10:30:00Z",
    "server_data": { "Building_Name": "Updated Building" },
    "local_data": { "Building_Name": "Main Building" }
  },
  "message": "Version conflict detected"
}
To resolve conflicts, send the request again with resolve_strategy: "use_local" to force the local version.

POST /api/v1/sync/upload_photo

Upload a photo from offline storage to a specific form fill field.

Request parameters

form_fill_id
integer
required
ID of the form fill to attach the photo to
field_name
string
required
Name of the form field for the photo
photo
file
required
Image file (JPEG, PNG). Maximum 10MB.

Response

success
boolean
Upload success status
photo_attachment_id
string
Unique attachment ID for the uploaded photo
message
string
Success or error message

Example (multipart form data)

curl -X POST https://example.com/api/v1/sync/upload_photo \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "form_fill_id=456" \
  -F "field_name=Inspection_Photo_1" \
  -F "photo=@/path/to/photo.jpg"

Error responses

error
string
Error message for validation failures:
  • "Required parameters: form_fill_id, field_name, photo"
  • "Not authorized to update this form"
  • "FormFill not found"
  • "File must be an image"
  • "File too large (max 10MB)"

GET /api/v1/sync/status

Check synchronization status and user statistics.

Response

success
boolean
Request success status
data
object
Sync status data

Example response

{
  "success": true,
  "data": {
    "user_id": 42,
    "total_inspections": 15,
    "pending_inspections": 3,
    "completed_inspections": 12,
    "total_form_fills": 60,
    "last_sync": "2024-03-15T09:15:00Z",
    "server_time": "2024-03-15T10:35:22Z"
  },
  "message": "Sync status retrieved successfully"
}

Implementation details

Conflict resolution

The sync endpoint implements optimistic locking using updated_at timestamps:
  1. Client sends last known updated_at with changes
  2. Server compares with current updated_at
  3. If server version is newer, return conflict data
  4. Client can resolve by:
    • Showing user a merge UI
    • Sending resolve_strategy: "use_local" to force local version
    • Implementing automatic merge logic

Data merging strategy

From sync_controller.rb:182-211:
# Merge local data with server data to preserve server-only keys
merged = (form_fill.data || {}).merge(form_fill_data[:data] || {})
form_fill.update!(data: merged)
This ensures:
  • Local changes override server values
  • Server-only keys (e.g., photo attachment IDs) are preserved
  • No data loss during sync

Authorization

All endpoints use Pundit policies to verify user access:
  • Users can only sync their own inspections
  • Technicians can access assigned inspections
  • Admins have full access
The sync endpoints are designed for offline-first mobile applications. They handle network interruptions gracefully and support batch operations for efficiency.

Best practices

Batch sync

Group multiple changes into a single sync request to minimize network requests

Conflict handling

Always implement conflict resolution UI for better user experience

Photo uploads

Upload photos separately after syncing form data to avoid timeout issues

Status checks

Poll sync status periodically to track pending changes

Error handling

Always check the results.errors and results.conflicts arrays even when success: true. Partial failures are common in batch operations.
Common error scenarios:
ErrorCauseResolution
Version conflictServer data changedImplement conflict resolution
FormFill not foundID mismatch or deletedRe-download inspection data
UnauthorizedMissing/invalid authRe-authenticate user
File too largePhoto > 10MBCompress before upload
Invalid sync typeUnknown typeCheck API version

Build docs developers (and LLMs) love