openapi: 3.0.3 info: title: Appio V1 API description: | Public API for external consumers. All endpoints require API key authentication with Bearer token. ## Authentication All endpoints require an API key in the Authorization header: ``` Authorization: Bearer ``` API keys use a `prod_` or `demo_` prefix. ## Service Access All endpoints require the `X-Service-Id` header. The API key must have access to the specified service. version: 1.0.0 contact: name: Appio Support url: https://appio.so servers: - url: http://localhost:8082 description: Local development server - url: https://api.appio.so description: Production server security: - BearerAuth: [] tags: - name: Services description: Service operations - name: Devices description: Device management operations - name: Notifications description: Notification management operations paths: /v1/services/{id}: parameters: - $ref: '#/components/parameters/ServiceIdPath' - $ref: '#/components/parameters/ServiceIdHeader' get: tags: - Services summary: Get service details description: Returns details of a specific service. X-Service-Id must match the {id} path parameter. operationId: v1GetService responses: '200': description: Successful response content: application/json: schema: $ref: '#/components/schemas/Service' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalError' /v1/devices: parameters: - $ref: '#/components/parameters/ServiceIdHeader' get: tags: - Devices summary: List devices for service description: Returns a paginated list of all devices associated with the service operationId: v1ListDevices parameters: - $ref: '#/components/parameters/LimitParam' - $ref: '#/components/parameters/AfterParam' responses: '200': description: Successful response content: application/json: schema: type: object properties: data: type: array items: $ref: '#/components/schemas/Device' pagination: $ref: '#/components/schemas/PaginationMeta' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalError' delete: tags: - Devices summary: Deactivate devices by user ID description: Deactivates all devices for a specific customer user ID operationId: v1DeactivateDevicesByUser parameters: - name: user_id in: query required: true description: Customer user ID to deactivate devices for schema: type: string example: "customer_user_123" responses: '200': description: Devices deactivated successfully content: application/json: schema: type: array items: $ref: '#/components/schemas/ResponseID' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalError' /v1/devices/{id}: parameters: - $ref: '#/components/parameters/DeviceIdPath' - $ref: '#/components/parameters/ServiceIdHeader' get: tags: - Devices summary: Get device details description: Returns details of a specific device operationId: v1GetDevice responses: '200': description: Successful response content: application/json: schema: $ref: '#/components/schemas/Device' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalError' delete: tags: - Devices summary: Deactivate device description: Deactivates a specific device operationId: v1DeactivateDevice responses: '200': description: Device deactivated successfully content: application/json: schema: $ref: '#/components/schemas/ResponseID' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalError' /v1/notifications: parameters: - $ref: '#/components/parameters/ServiceIdHeader' get: tags: - Notifications summary: List notifications description: | Returns a paginated list of notifications for the service. Supports filtering by device_id, user_id, and status. - If device_id is provided, returns notifications for that device - If user_id is provided, returns notifications for that customer user - Otherwise, returns all notifications for the service operationId: v1ListNotifications parameters: - $ref: '#/components/parameters/LimitParam' - $ref: '#/components/parameters/AfterParam' - name: device_id in: query required: false description: Filter by device ID schema: type: string pattern: '^dvc_[0-9a-z]{26}$' example: "dvc_00000000000000000000000000" - name: user_id in: query required: false description: Filter by customer user ID schema: type: string example: "customer_user_123" - name: status in: query required: false description: Filter by notification status schema: type: string enum: [created, queued, completed, failed, skipped] example: "completed" responses: '200': description: Successful response content: application/json: schema: type: object properties: data: type: array items: $ref: '#/components/schemas/NotificationResponse' pagination: $ref: '#/components/schemas/PaginationMeta' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalError' post: tags: - Notifications summary: Create notification description: Creates a new notification for the service operationId: v1CreateNotification parameters: - name: device_id in: query required: false description: Target a specific device ID schema: type: string pattern: '^dvc_[0-9a-z]{26}$' - name: user_id in: query required: false description: Target devices for a specific customer user ID schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/NotificationRequest' examples: immediate: summary: Immediate notification value: payload: title: "New Message" message: "You have a new message" scheduled: summary: Scheduled notification value: payload: title: "Reminder" message: "Don't forget your appointment" scheduled_at: "2025-10-23T10:00:00Z" with_link: summary: Notification with link value: payload: title: "Check this out" message: "Click to view more details" link: "https://example.com/details" complete: summary: Notification with all fields value: payload: title: "New Update" message: "A new update is available" link: "https://example.com/update" image_url: "https://example.com/images/update.png" responses: '201': description: Notification created successfully content: application/json: schema: $ref: '#/components/schemas/ResponseID' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalError' /v1/notifications/{id}: parameters: - $ref: '#/components/parameters/NotificationIdPath' - $ref: '#/components/parameters/ServiceIdHeader' get: tags: - Notifications summary: Get notification details description: Returns details of a specific notification with delivery statistics operationId: v1GetNotification responses: '200': description: Successful response content: application/json: schema: $ref: '#/components/schemas/NotificationResponseWithStats' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalError' components: securitySchemes: BearerAuth: type: http scheme: bearer description: | API key authentication. Provide your API key as a Bearer token: `Authorization: Bearer ` API keys use a `prod_` or `demo_` prefix. parameters: ServiceIdHeader: name: X-Service-Id in: header required: true description: Service ID that the API key has access to schema: type: string pattern: '^svc_[0-9a-z]{26}$' example: "svc_00000000000000000000000000" ServiceIdPath: name: id in: path required: true description: Service ID schema: type: string pattern: '^svc_[0-9a-z]{26}$' example: "svc_00000000000000000000000000" DeviceIdPath: name: id in: path required: true description: Device ID schema: type: string pattern: '^dvc_[0-9a-z]{26}$' example: "dvc_00000000000000000000000000" NotificationIdPath: name: id in: path required: true description: Notification ID schema: type: string pattern: '^ntf_[0-9a-z]{26}$' example: "ntf_00000000000000000000000000" LimitParam: name: limit in: query schema: type: integer default: 50 minimum: 1 maximum: 100 description: Maximum number of items to return AfterParam: name: after in: query schema: type: string description: Cursor ID for pagination - returns items after this ID schemas: ResponseID: type: object required: - id properties: id: type: string description: ID of the created/updated resource example: "svc_00000000000000000000000000" Service: type: object required: - id properties: id: type: string pattern: '^svc_[0-9a-z]{26}$' example: "svc_00000000000000000000000000" title: type: string maxLength: 255 example: "My Service" description: type: string maxLength: 1000 example: "A service for my application" logo_url: type: string maxLength: 512 example: "https://example.com/logo.png" banner_url: type: string maxLength: 512 example: "https://example.com/banner.png" url: type: string maxLength: 512 example: "https://example.com" text_color: type: string pattern: '^#[0-9A-Fa-f]{6}$' example: "#FFFFFF" background_color: type: string pattern: '^#[0-9A-Fa-f]{6}$' example: "#000000" accent_color: type: string pattern: '^#[0-9A-Fa-f]{6}$' example: "#FF5733" Device: type: object required: - id - user_id - name - platform properties: id: type: string pattern: '^dvc_[0-9a-z]{26}$' example: "dvc_00000000000000000000000000" user_id: type: string description: Customer user ID example: "customer_user_123" name: type: string maxLength: 255 example: "John's iPhone" platform: type: string enum: [ios, android] example: "ios" os_version: type: string maxLength: 50 example: "17.0" model: type: string maxLength: 100 example: "iPhone15,2" device_token: type: string maxLength: 255 example: "abc123def456" notifications_enabled: type: boolean example: true device_identifier: type: string maxLength: 50 example: "device_identifier_123" marketing_name: type: string maxLength: 100 example: "iPhone 14 Pro" NotificationRequest: type: object required: - payload properties: payload: $ref: '#/components/schemas/NotificationPayload' scheduled_at: type: string format: date-time description: Schedule notification for future delivery (max 30 days) example: "2025-10-23T10:00:00Z" NotificationPayload: type: object required: - title - message properties: title: type: string description: Notification title (max 50 characters) maxLength: 50 example: "New Message" message: type: string description: Notification message body (max 200 characters) maxLength: 200 example: "You have a new message" link: type: string description: Optional link URL to open when notification is tapped example: "https://example.com/chat" image_url: type: string description: Optional image URL to display in the notification example: "https://example.com/images/notification.png" NotificationResponse: type: object required: - id - service_id - status - payload properties: id: type: string pattern: '^ntf_[0-9a-z]{26}$' example: "ntf_00000000000000000000000000" service_id: type: string pattern: '^svc_[0-9a-z]{26}$' example: "svc_00000000000000000000000000" status: type: string enum: [created, queued, completed, failed, skipped] example: "completed" payload: $ref: '#/components/schemas/NotificationPayload' scheduled_at: type: string format: date-time nullable: true example: "2025-10-23T10:00:00Z" NotificationResponseWithStats: allOf: - $ref: '#/components/schemas/NotificationResponse' - type: object properties: delivery_stats: $ref: '#/components/schemas/NotificationDeliveryStats' NotificationDeliveryStats: type: object properties: total: type: integer created: type: integer queued: type: integer completed: type: integer failed: type: integer Error: type: object required: - error properties: error: type: object required: - message properties: message: type: string example: "Invalid input data" data: type: object description: Optional extra information for user-facing errors (e.g., doc_url, entity, validation_errors) PaginationMeta: type: object properties: next: type: string nullable: true description: URL for the next page of results, or null if no more results responses: BadRequest: description: Bad request - invalid input content: application/json: schema: $ref: '#/components/schemas/Error' example: error: message: "Invalid input data" data: doc_url: "https://docs.appio.so/#api-services" entity: "service" validation_errors: - field: "title" reason: "title is required" Unauthorized: description: Unauthorized - missing or invalid API key content: application/json: schema: $ref: '#/components/schemas/Error' example: error: message: "Authentication required" Forbidden: description: Forbidden - API key does not have access to this service content: application/json: schema: $ref: '#/components/schemas/Error' example: error: message: "Access denied" NotFound: description: Not found - resource does not exist content: application/json: schema: $ref: '#/components/schemas/Error' example: error: message: "Resource not found" InternalError: description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' example: error: message: "An internal error occurred"