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' components: securitySchemes: WalletSession: type: http scheme: bearer bearerFormat: EDUT-WALLET-SESSION schemas: DeviceRegisterRequest: type: object required: - wallet - chain_id - device_id - platform - 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] 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 - events - next_cursor - server_time properties: wallet: type: string device_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 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 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