openapi: 3.1.0 info: title: EDUT Member App Channel API version: 1.0.0 description: | Deterministic member communication channel contract. App notifications are derived from wallet-authenticated membership and entitlement state. servers: - url: https://api.edut.ai security: - WalletSession: [] paths: /member/channel/device/register: post: summary: Register or refresh a member app device channel binding. operationId: registerMemberDevice requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DeviceRegisterRequest' responses: '200': description: Device channel binding active. content: application/json: schema: $ref: '#/components/schemas/DeviceRegisterResponse' '403': description: Membership is not active for this wallet. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /member/channel/device/unregister: post: summary: Remove a device channel binding. operationId: unregisterMemberDevice requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/DeviceUnregisterRequest' responses: '200': description: Device channel binding removed. content: application/json: schema: $ref: '#/components/schemas/DeviceUnregisterResponse' '404': description: Device channel binding not found. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /member/channel/events: get: summary: Poll deterministic member channel events. operationId: listMemberChannelEvents parameters: - in: query name: wallet required: true schema: type: string description: Wallet address tied to session. - in: query name: device_id required: true schema: type: string - in: query name: cursor required: false schema: type: string - in: query name: limit required: false schema: type: integer minimum: 1 maximum: 100 default: 25 responses: '200': description: Event page for in-app inbox rendering. content: application/json: schema: $ref: '#/components/schemas/EventListResponse' '403': description: Membership inactive or suspended. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /member/channel/events/{event_id}/ack: post: summary: Acknowledge event delivery/read state. operationId: acknowledgeMemberChannelEvent parameters: - in: path name: event_id required: true schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/EventAckRequest' responses: '200': description: Event acknowledged. content: application/json: schema: $ref: '#/components/schemas/EventAckResponse' '404': description: Event not found. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /member/channel/support/ticket: post: summary: Open support/admin ticket (org root owner only). operationId: createOwnerSupportTicket requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SupportTicketRequest' responses: '200': description: Support ticket accepted. content: application/json: schema: $ref: '#/components/schemas/SupportTicketResponse' '403': description: Principal is not org root owner. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' components: securitySchemes: WalletSession: type: http scheme: bearer bearerFormat: EDUT-WALLET-SESSION schemas: DeviceRegisterRequest: type: object required: - wallet - chain_id - device_id - platform - org_root_id - principal_id - principal_role - app_version properties: wallet: type: string description: Hex wallet address. chain_id: type: integer description: Active chain id in app session. device_id: type: string description: Stable app-install identifier. platform: type: string enum: [ios, android, desktop] org_root_id: type: string description: Organization boundary id for channel scoping. principal_id: type: string description: Human principal id for this session. principal_role: type: string enum: [workspace_member, org_root_owner] app_version: type: string push_provider: type: string enum: [apns, fcm, webpush, none] default: none push_token: type: string description: Provider token if push is enabled. DeviceRegisterResponse: type: object required: - channel_binding_id - status - poll_interval_seconds - server_time properties: channel_binding_id: type: string status: type: string enum: [active] poll_interval_seconds: type: integer minimum: 10 server_time: type: string format: date-time DeviceUnregisterRequest: type: object required: - wallet - device_id properties: wallet: type: string device_id: type: string DeviceUnregisterResponse: type: object required: - status - wallet - device_id properties: status: type: string enum: [removed] wallet: type: string device_id: type: string EventListResponse: type: object required: - wallet - device_id - org_root_id - principal_id - events - next_cursor - server_time properties: wallet: type: string device_id: type: string org_root_id: type: string principal_id: type: string events: type: array items: $ref: '#/components/schemas/MemberEvent' next_cursor: type: string nullable: true server_time: type: string format: date-time MemberEvent: type: object required: - event_id - class - created_at - title - body - dedupe_key - policy_hash properties: event_id: type: string class: type: string enum: - offer_available - entitlement_activated - platform_update - publisher_update - membership_policy - admin_health - admin_config - admin_update created_at: type: string format: date-time title: type: string body: type: string dedupe_key: type: string requires_ack: type: boolean default: true policy_hash: type: string payload: type: object additionalProperties: true visibility_scope: type: string enum: [member, owner_admin] default: member EventAckRequest: type: object required: - wallet - device_id - acknowledged_at properties: wallet: type: string device_id: type: string acknowledged_at: type: string format: date-time EventAckResponse: type: object required: - status - event_id - acknowledged_at properties: status: type: string enum: [acknowledged] event_id: type: string acknowledged_at: type: string format: date-time ErrorResponse: type: object required: - error - code properties: error: type: string code: type: string correlation_id: type: string SupportTicketRequest: type: object required: - wallet - org_root_id - principal_id - category - summary properties: wallet: type: string org_root_id: type: string principal_id: type: string category: type: string enum: [admin_support, health_diagnostic, boundary_issue, update_control] summary: type: string context: type: object additionalProperties: true SupportTicketResponse: type: object required: - status - ticket_id - created_at properties: status: type: string enum: [accepted] ticket_id: type: string created_at: type: string format: date-time