# Towerops Mobile API Documentation

Version: 1.0
Base URL: `https://your-towerops-instance.com/api/v1/mobile`

## Table of Contents

- [Authentication](#authentication)
  - [QR Code Login Flow](#qr-code-login-flow)
  - [Session Management](#session-management)
- [Data Endpoints](#data-endpoints)
  - [Organizations](#organizations)
  - [Sites](#sites)
  - [Equipment](#equipment)
  - [Alerts](#alerts)
- [Error Handling](#error-handling)
- [Rate Limiting](#rate-limiting)

## Authentication

The Towerops Mobile API uses bearer token authentication. All authenticated requests must include an `Authorization` header:

```
Authorization: Bearer <session_token>
```

### QR Code Login Flow

The recommended authentication method is QR code login, which allows users to authenticate without entering credentials in the mobile app.

#### Step 1: User Generates QR Code

User logs into the web interface at `/mobile/qr-login` and generates a QR code containing a temporary token.

#### Step 2: Verify QR Token

**Endpoint:** `POST /api/v1/mobile/auth/qr/verify`

Verifies that a scanned QR token is valid before proceeding with login.

**Request Body:**
```json
{
  "token": "base64-encoded-token-from-qr-code"
}
```

**Response (200 OK):**
```json
{
  "valid": true,
  "user_email": "user@example.com"
}
```

**Response (401 Unauthorized):**
```json
{
  "valid": false,
  "error": "Invalid or expired token"
}
```

#### Step 3: Complete QR Login

**Endpoint:** `POST /api/v1/mobile/auth/qr/complete`

Completes the QR login by creating a long-lived mobile session (90 days).

**Request Body:**
```json
{
  "token": "base64-encoded-token-from-qr-code",
  "device_name": "iPhone 15 Pro",
  "device_os": "iOS 17.2",
  "app_version": "1.0.0"
}
```

**Response (200 OK):**
```json
{
  "session_token": "long-lived-bearer-token",
  "expires_at": "2026-04-15T19:44:25Z",
  "user": {
    "id": "user-uuid",
    "email": "user@example.com"
  }
}
```

**Response (401 Unauthorized):**
```json
{
  "error": "Invalid or expired token"
}
```

**Response (422 Unprocessable Entity):**
```json
{
  "error": "Failed to create session",
  "details": {
    "device_name": ["can't be blank"]
  }
}
```

**Notes:**
- QR tokens expire after 5 minutes
- QR tokens can only be used once
- Session tokens expire after 90 days
- Store the `session_token` securely in the device keychain/keystore

### Session Management

#### Get Current Session

**Endpoint:** `GET /api/v1/mobile/auth/session`

**Headers:**
```
Authorization: Bearer <session_token>
```

**Response (200 OK):**
```json
{
  "id": "session-uuid",
  "device_name": "iPhone 15 Pro",
  "device_os": "iOS 17.2",
  "app_version": "1.0.0",
  "last_used_at": "2026-01-15T19:44:25Z",
  "expires_at": "2026-04-15T19:44:25Z"
}
```

#### Revoke Session (Logout)

**Endpoint:** `DELETE /api/v1/mobile/auth/session`

**Headers:**
```
Authorization: Bearer <session_token>
```

**Response (200 OK):**
```json
{
  "success": true
}
```

## Data Endpoints

All data endpoints require authentication via the `Authorization: Bearer <token>` header.

### Organizations

#### List Organizations

**Endpoint:** `GET /api/v1/mobile/organizations`

Returns all organizations the authenticated user has access to.

**Response (200 OK):**
```json
{
  "organizations": [
    {
      "id": "org-uuid",
      "name": "Acme Corp",
      "sites_count": 3,
      "equipment_count": 15,
      "active_alerts_count": 2
    }
  ]
}
```

### Sites

#### List Sites

**Endpoint:** `GET /api/v1/mobile/organizations/:organization_id/sites`

Returns all sites for an organization.

**Response (200 OK):**
```json
{
  "sites": [
    {
      "id": "site-uuid",
      "name": "Main Office",
      "location": "New York, NY",
      "equipment_count": 5,
      "equipment_down_count": 1
    }
  ]
}
```

**Response (403 Forbidden):**
```json
{
  "error": "Access denied to this organization"
}
```

### Equipment

#### List Equipment

**Endpoint:** `GET /api/v1/mobile/organizations/:organization_id/equipment`

Returns all equipment for an organization with current status.

**Query Parameters:**
- `site_id` (optional): Filter by specific site UUID
- `status` (optional): Filter by status (`up`, `down`, `unknown`)

**Example:**
```
GET /api/v1/mobile/organizations/org-123/equipment?status=down&site_id=site-456
```

**Response (200 OK):**
```json
{
  "equipment": [
    {
      "id": "equipment-uuid",
      "name": "Core Router",
      "ip_address": "192.168.1.1",
      "site_name": "Main Office",
      "status": "up",
      "uptime": "15d 4h",
      "last_seen_at": "2026-01-15T19:44:25Z"
    }
  ]
}
```

#### Get Equipment Details

**Endpoint:** `GET /api/v1/mobile/equipment/:id`

Returns detailed equipment information including interfaces and sensors.

**Response (200 OK):**
```json
{
  "id": "equipment-uuid",
  "name": "Core Router",
  "ip_address": "192.168.1.1",
  "status": "up",
  "uptime": "15d 4h",
  "site": {
    "id": "site-uuid",
    "name": "Main Office"
  },
  "interfaces": [
    {
      "id": "interface-uuid",
      "name": "GigabitEthernet0/0",
      "alias": "WAN",
      "status": "up",
      "admin_status": "up",
      "speed": 1000000000,
      "mac_address": "00:1A:2B:3C:4D:5E"
    }
  ],
  "sensors": [
    {
      "id": "sensor-uuid",
      "name": "CPU Usage",
      "type": "cpu",
      "unit": "%",
      "current_value": 45.5,
      "status": "ok"
    }
  ]
}
```

**Response (404 Not Found):**
```json
{
  "error": "Equipment not found"
}
```

**Response (403 Forbidden):**
```json
{
  "error": "Access denied to this equipment"
}
```

### Alerts

#### List Alerts

**Endpoint:** `GET /api/v1/mobile/organizations/:organization_id/alerts`

Returns alerts for an organization.

**Query Parameters:**
- `severity` (optional): Filter by severity (`critical`, `warning`, `info`)
- `status` (optional): Filter by status (`active`, `acknowledged`, `resolved`)
- `limit` (optional): Number of alerts to return (default 50, max 200)

**Example:**
```
GET /api/v1/mobile/organizations/org-123/alerts?severity=critical&status=active&limit=100
```

**Response (200 OK):**
```json
{
  "alerts": [
    {
      "id": "alert-uuid",
      "severity": "critical",
      "status": "active",
      "message": "Equipment Down: Core Router",
      "equipment_name": "Core Router",
      "equipment_id": "equipment-uuid",
      "occurred_at": "2026-01-15T19:44:25Z"
    }
  ]
}
```

**Alert Severity Levels:**
- `critical`: Requires immediate attention (equipment down, critical sensor thresholds)
- `warning`: Requires attention (sensor warnings, interface changes)
- `info`: Informational (equipment up, sensor recovery)

**Alert Status Values:**
- `active`: Alert is active and unacknowledged
- `acknowledged`: Alert has been acknowledged but not resolved
- `resolved`: Alert has been resolved

## Error Handling

All API endpoints follow consistent error response formats.

### HTTP Status Codes

- `200 OK`: Request succeeded
- `400 Bad Request`: Invalid request parameters
- `401 Unauthorized`: Missing or invalid authentication token
- `403 Forbidden`: Authenticated but not authorized for this resource
- `404 Not Found`: Resource not found
- `422 Unprocessable Entity`: Validation errors
- `500 Internal Server Error`: Server error

### Error Response Format

```json
{
  "error": "Human-readable error message"
}
```

For validation errors (422):
```json
{
  "error": "Failed to create session",
  "details": {
    "field_name": ["error message"]
  }
}
```

## Rate Limiting

The API does not currently implement rate limiting, but it may be added in future versions. Implement exponential backoff in your client for failed requests.

## Best Practices

### Token Storage

- Store session tokens securely in the device keychain (iOS) or keystore (Android)
- Never log or expose tokens in plaintext
- Clear tokens on logout

### Session Management

- Check session expiration before making requests
- Implement automatic token refresh or re-authentication when sessions expire
- Handle 401 responses by prompting for re-authentication

### Network Requests

- Implement timeout handling (30 seconds recommended)
- Use exponential backoff for retries
- Cache responses appropriately (organizations list, equipment details)
- Update last_used_at is automatic - no need to ping

### Error Handling

- Display user-friendly error messages
- Log errors for debugging
- Handle network connectivity issues gracefully
- Implement offline mode where appropriate

## Example Client Implementation

### Swift (iOS)

```swift
import Foundation

class ToweropsAPI {
    private let baseURL = "https://your-instance.com/api/v1/mobile"
    private var sessionToken: String?

    // Authenticate with QR token
    func completeQRLogin(token: String, deviceInfo: DeviceInfo) async throws -> SessionToken {
        let url = URL(string: "\(baseURL)/auth/qr/complete")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")

        let body = [
            "token": token,
            "device_name": deviceInfo.name,
            "device_os": deviceInfo.os,
            "app_version": deviceInfo.appVersion
        ]
        request.httpBody = try JSONEncoder().encode(body)

        let (data, response) = try await URLSession.shared.data(for: request)

        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw APIError.authenticationFailed
        }

        let result = try JSONDecoder().decode(SessionToken.self, from: data)
        self.sessionToken = result.session_token
        return result
    }

    // Fetch organizations
    func fetchOrganizations() async throws -> [Organization] {
        guard let token = sessionToken else {
            throw APIError.notAuthenticated
        }

        let url = URL(string: "\(baseURL)/organizations")!
        var request = URLRequest(url: url)
        request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")

        let (data, response) = try await URLSession.shared.data(for: request)

        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw APIError.requestFailed
        }

        let result = try JSONDecoder().decode(OrganizationsResponse.self, from: data)
        return result.organizations
    }
}
```

### Kotlin (Android)

```kotlin
import okhttp3.*
import kotlinx.serialization.json.Json

class ToweropsAPI(private val baseURL: String = "https://your-instance.com/api/v1/mobile") {
    private val client = OkHttpClient()
    private var sessionToken: String? = null

    // Authenticate with QR token
    suspend fun completeQRLogin(token: String, deviceInfo: DeviceInfo): SessionToken {
        val json = """
            {
                "token": "$token",
                "device_name": "${deviceInfo.name}",
                "device_os": "${deviceInfo.os}",
                "app_version": "${deviceInfo.appVersion}"
            }
        """.trimIndent()

        val request = Request.Builder()
            .url("$baseURL/auth/qr/complete")
            .post(RequestBody.create(MediaType.parse("application/json"), json))
            .build()

        val response = client.newCall(request).execute()
        if (!response.isSuccessful) {
            throw APIException("Authentication failed")
        }

        val result = Json.decodeFromString<SessionToken>(response.body()!!.string())
        sessionToken = result.session_token
        return result
    }

    // Fetch organizations
    suspend fun fetchOrganizations(): List<Organization> {
        val token = sessionToken ?: throw APIException("Not authenticated")

        val request = Request.Builder()
            .url("$baseURL/organizations")
            .header("Authorization", "Bearer $token")
            .build()

        val response = client.newCall(request).execute()
        if (!response.isSuccessful) {
            throw APIException("Request failed")
        }

        val result = Json.decodeFromString<OrganizationsResponse>(response.body()!!.string())
        return result.organizations
    }
}
```

## Support

For API support, bug reports, or feature requests:
- GitHub Issues: https://github.com/yourusername/towerops
- Documentation: https://docs.towerops.net

## Changelog

### Version 1.0 (2026-01-15)
- Initial release
- QR code authentication
- Basic data endpoints (organizations, sites, equipment, alerts)
- Session management
