diff --git a/docs/BROADCAST_SURVEY_API_REFERENCE.md b/docs/BROADCAST_SURVEY_API_REFERENCE.md new file mode 100644 index 00000000..b34c9186 --- /dev/null +++ b/docs/BROADCAST_SURVEY_API_REFERENCE.md @@ -0,0 +1,750 @@ +# Broadcast & Survey API Reference + +> **Base URL**: `https://api.bytelyst.io/v1` +> **Authentication**: Bearer token (JWT) required for all endpoints except public ones +> **Product ID**: All requests must include `X-Product-ID` header + +--- + +## Authentication + +```http +Authorization: Bearer +X-Product-ID: +``` + +--- + +## Broadcasts + +### Admin Endpoints + +#### List Broadcasts +```http +GET /admin/broadcasts +``` + +**Query Parameters:** +| Parameter | Type | Description | +|-----------|------|-------------| +| `status` | string | Filter by status: `draft`, `scheduled`, `sending`, `paused`, `completed` | +| `channel` | string | Filter by channel: `push`, `in_app`, `email`, `sms` | +| `limit` | integer | Max items (default: 20, max: 100) | +| `offset` | integer | Pagination offset | + +**Response:** +```json +{ + "broadcasts": [ + { + "id": "bc_123456789", + "productId": "lysnrai", + "title": "New Feature Announcement", + "body": "Check out our latest update!", + "channel": "push", + "priority": "high", + "status": "completed", + "target": { + "platforms": ["ios", "android"], + "userSegments": ["pro"], + "percentageRollout": 100 + }, + "metrics": { + "targetedCount": 1500, + "sentCount": 1500, + "deliveredCount": 1420, + "openedCount": 890, + "clickedCount": 340, + "dismissedCount": 45 + }, + "createdAt": "2024-01-15T10:30:00Z", + "updatedAt": "2024-01-15T10:35:00Z" + } + ], + "total": 47, + "limit": 20, + "offset": 0 +} +``` + +--- + +#### Get Broadcast +```http +GET /admin/broadcasts/:id +``` + +**Response:** Single broadcast object (same as list item) + +--- + +#### Create Broadcast +```http +POST /admin/broadcasts +``` + +**Request Body:** +```json +{ + "title": "New Feature Announcement", + "body": "Check out our latest update with voice commands!", + "channel": "push", + "priority": "high", + "style": "banner", + "ctaText": "Try it now", + "ctaUrl": "https://app.bytelyst.io/voice", + "imageUrl": "https://cdn.bytelyst.io/banner/voice.jpg", + "target": { + "platforms": ["ios", "android"], + "userSegments": ["pro"], + "appVersionMin": "2.0.0", + "appVersionMax": null, + "countryCodes": ["US", "CA"], + "percentageRollout": 100, + "specificUserIds": null + }, + "scheduledAt": "2024-01-20T09:00:00Z", + "expiresAt": "2024-02-20T09:00:00Z", + "deepLink": { + "screen": "voice_settings", + "params": { "tab": "commands" } + } +} +``` + +**Response:** Created broadcast object with `id` + +--- + +#### Update Broadcast +```http +PUT /admin/broadcasts/:id +``` + +**Request Body:** Partial broadcast object (same as create) + +**Response:** Updated broadcast object + +--- + +#### Delete Broadcast +```http +DELETE /admin/broadcasts/:id +``` + +**Response:** `204 No Content` + +--- + +#### Send Broadcast +```http +POST /admin/broadcasts/:id/send +``` + +Triggers immediate send (or schedules if `scheduledAt` is set). + +**Response:** +```json +{ + "success": true, + "message": "Broadcast queued for sending", + "estimatedReach": 1500 +} +``` + +--- + +#### Pause Broadcast +```http +POST /admin/broadcasts/:id/pause +``` + +Pauses a sending broadcast. + +**Response:** +```json +{ + "success": true, + "message": "Broadcast paused" +} +``` + +--- + +#### Get Broadcast Metrics +```http +GET /admin/broadcasts/:id/metrics +``` + +**Response:** +```json +{ + "targetedCount": 1500, + "sentCount": 1500, + "deliveredCount": 1420, + "openedCount": 890, + "clickedCount": 340, + "dismissedCount": 45, + "convertedCount": 120, + "deliveryRate": 94.7, + "openRate": 59.3, + "clickRate": 22.7, + "conversionRate": 8.0 +} +``` + +--- + +#### Clone Broadcast +```http +POST /admin/broadcasts/:id/clone +``` + +Creates a copy of the broadcast as a draft. + +**Response:** New broadcast object with `id` + +--- + +### Public Endpoints (Authenticated) + +#### List In-App Messages +```http +GET /public/messages/in-app +``` + +Returns active in-app messages for the authenticated user. + +**Response:** +```json +{ + "messages": [ + { + "id": "msg_987654321", + "broadcastId": "bc_123456789", + "title": "New Feature Announcement", + "body": "Check out our latest update!", + "style": "banner", + "priority": "high", + "ctaText": "Try it now", + "ctaUrl": "https://app.bytelyst.io/voice", + "imageUrl": "https://cdn.bytelyst.io/banner/voice.jpg", + "deepLink": { + "screen": "voice_settings", + "params": { "tab": "commands" } + }, + "status": "unread", + "createdAt": "2024-01-15T10:30:00Z" + } + ] +} +``` + +--- + +#### Mark Message Read +```http +POST /public/messages/:id/read +``` + +**Response:** +```json +{ + "success": true, + "message": "Marked as read" +} +``` + +--- + +#### Dismiss Message +```http +POST /public/messages/:id/dismiss +``` + +**Response:** +```json +{ + "success": true, + "message": "Message dismissed" +} +``` + +--- + +#### Track Message Click +```http +POST /public/messages/:id/click +``` + +**Response:** +```json +{ + "success": true, + "message": "Click tracked" +} +``` + +--- + +## Surveys + +### Admin Endpoints + +#### List Surveys +```http +GET /admin/surveys +``` + +**Query Parameters:** +| Parameter | Type | Description | +|-----------|------|-------------| +| `status` | string | Filter by status: `draft`, `active`, `paused`, `completed` | +| `limit` | integer | Max items (default: 20, max: 100) | +| `offset` | integer | Pagination offset | + +**Response:** +```json +{ + "surveys": [ + { + "id": "srv_123456789", + "productId": "lysnrai", + "title": "Product Feedback Survey", + "description": "Help us improve your experience", + "status": "active", + "questionCount": 5, + "responseCount": 892, + "incentive": { + "type": "pro_days", + "amount": 7 + }, + "createdAt": "2024-01-10T08:00:00Z", + "updatedAt": "2024-01-15T14:30:00Z" + } + ], + "total": 12, + "limit": 20, + "offset": 0 +} +``` + +--- + +#### Get Survey +```http +GET /admin/surveys/:id +``` + +**Response:** Full survey with questions +```json +{ + "id": "srv_123456789", + "productId": "lysnrai", + "title": "Product Feedback Survey", + "description": "Help us improve your experience", + "status": "active", + "questions": [ + { + "id": "q1", + "type": "nps", + "text": "How likely are you to recommend us?", + "required": true, + "minValue": 0, + "maxValue": 10 + }, + { + "id": "q2", + "type": "single_choice", + "text": "What feature do you use most?", + "required": true, + "options": [ + { "id": "voice", "text": "Voice dictation", "emoji": "🎙️" }, + { "id": "keyboard", "text": "Keyboard", "emoji": "⌨️" }, + { "id": "desktop", "text": "Desktop app", "emoji": "💻" } + ] + } + ], + "target": { + "platforms": ["ios", "android"], + "userSegments": ["active"] + }, + "displayTrigger": { + "type": "delay_seconds", + "seconds": 30 + }, + "incentive": { + "type": "pro_days", + "amount": 7 + }, + "metrics": { + "startedCount": 1200, + "completedCount": 892, + "completionRate": 74.3 + }, + "createdAt": "2024-01-10T08:00:00Z", + "updatedAt": "2024-01-15T14:30:00Z" +} +``` + +--- + +#### Create Survey +```http +POST /admin/surveys +``` + +**Request Body:** +```json +{ + "title": "Product Feedback Survey", + "description": "Help us improve your experience", + "questions": [ + { + "id": "q1", + "type": "nps", + "text": "How likely are you to recommend us?", + "required": true, + "minValue": 0, + "maxValue": 10 + }, + { + "id": "q2", + "type": "single_choice", + "text": "What feature do you use most?", + "description": "Select your primary use case", + "required": true, + "options": [ + { "id": "voice", "text": "Voice dictation", "emoji": "🎙️" }, + { "id": "keyboard", "text": "Keyboard", "emoji": "⌨️" } + ] + }, + { + "id": "q3", + "type": "text_long", + "text": "What could we improve?", + "required": false, + "maxLength": 500, + "showIf": { + "questionId": "q1", + "operator": "not_equals", + "value": ["9", "10"] + } + } + ], + "target": { + "platforms": ["ios", "android"], + "userSegments": ["active"], + "percentageRollout": 100 + }, + "displayTrigger": { + "type": "delay_seconds", + "seconds": 30 + }, + "incentive": { + "type": "pro_days", + "amount": 7 + } +} +``` + +**Response:** Created survey object with `id` + +--- + +#### Update Survey +```http +PUT /admin/surveys/:id +``` + +**Request Body:** Partial survey object + +**Response:** Updated survey object + +--- + +#### Delete Survey +```http +DELETE /admin/surveys/:id +``` + +**Response:** `204 No Content` + +--- + +#### Get Survey Metrics +```http +GET /admin/surveys/:id/metrics +``` + +**Response:** +```json +{ + "questionStats": [ + { + "questionId": "q1", + "questionType": "nps", + "responseCount": 892, + "distribution": { + "0": 12, + "1": 8, + "2": 15, + "7": 120, + "8": 200, + "9": 180, + "10": 357 + }, + "average": 8.2, + "npsScore": 68 + } + ], + "startedCount": 1200, + "completedCount": 892, + "completionRate": 74.3, + "averageTimeSeconds": 145 +} +``` + +--- + +#### Export Survey Responses +```http +GET /admin/surveys/:id/export +``` + +**Query Parameters:** +| Parameter | Type | Description | +|-----------|------|-------------| +| `format` | string | `csv` or `json` (default: `csv`) | + +**Response:** File download (CSV or JSON) + +CSV format: +```csv +response_id,user_id,started_at,completed_at,q1,q2,q3 +resp_abc123,user_xyz,2024-01-15T10:30:00Z,2024-01-15T10:32:45Z,8,voice,Make it faster +``` + +--- + +### Public Endpoints (Authenticated) + +#### Get Active Survey +```http +GET /public/surveys/active +``` + +Returns the active survey for the authenticated user, if any. + +**Response:** +```json +{ + "id": "srv_123456789", + "title": "Product Feedback Survey", + "description": "Help us improve your experience", + "questions": [ + { + "id": "q1", + "type": "nps", + "text": "How likely are you to recommend us?", + "required": true, + "minValue": 0, + "maxValue": 10 + } + ], + "currentQuestionIndex": 0, + "incentive": { + "type": "pro_days", + "amount": 7 + } +} +``` + +Or `204 No Content` if no active survey. + +--- + +#### Start Survey +```http +POST /public/surveys/:id/start +``` + +**Response:** +```json +{ + "success": true, + "surveyStateId": "ss_987654321", + "currentQuestionIndex": 0 +} +``` + +--- + +#### Submit Answer +```http +POST /public/surveys/:id/answers +``` + +**Request Body:** +```json +{ + "questionId": "q1", + "answer": { + "type": "rating", + "value": { "value": 8 } + } +} +``` + +**Response:** +```json +{ + "success": true, + "currentQuestionIndex": 1, + "nextQuestionId": "q2", + "isComplete": false +} +``` + +--- + +#### Complete Survey +```http +POST /public/surveys/:id/complete +``` + +**Response:** +```json +{ + "success": true, + "incentiveClaimed": true, + "incentiveType": "pro_days", + "incentiveAmount": 7 +} +``` + +--- + +#### Dismiss Survey +```http +POST /public/surveys/:id/dismiss +``` + +**Response:** +```json +{ + "success": true, + "message": "Survey dismissed" +} +``` + +--- + +## Types Reference + +### Broadcast Status +| Status | Description | +|--------|-------------| +| `draft` | Created but not yet sent | +| `scheduled` | Scheduled for future send | +| `sending` | Currently being sent | +| `paused` | Send paused by admin | +| `completed` | Fully sent and finished | + +### Message Priority +| Priority | Description | +|----------|-------------| +| `low` | Silent delivery, no badge | +| `normal` | Standard notification | +| `high` | Prominent with sound | +| `urgent` | Critical, persistent alert | + +### Message Style +| Style | Description | +|-------|-------------| +| `banner` | Top/bottom banner | +| `modal` | Center dialog | +| `fullscreen` | Full-screen takeover | +| `toast` | Brief ephemeral message | + +### Survey Question Types +| Type | Description | +|------|-------------| +| `single_choice` | Radio button selection | +| `multiple_choice` | Checkbox multi-select | +| `rating` | Star rating (1-5) | +| `scale` | Numeric scale | +| `nps` | Net Promoter Score (0-10) | +| `text_short` | Single line text | +| `text_long` | Multi-line textarea | +| `ranking` | Drag-to-order items | +| `dropdown` | Single select dropdown | + +--- + +## Error Responses + +All errors follow this format: + +```json +{ + "error": { + "code": "NOT_FOUND", + "message": "Broadcast not found", + "requestId": "req_abc123" + } +} +``` + +### Error Codes +| Code | HTTP Status | Description | +|------|-------------|-------------| +| `BAD_REQUEST` | 400 | Invalid request data | +| `UNAUTHORIZED` | 401 | Missing or invalid token | +| `FORBIDDEN` | 403 | Insufficient permissions | +| `NOT_FOUND` | 404 | Resource not found | +| `CONFLICT` | 409 | Resource already exists | +| `RATE_LIMITED` | 429 | Too many requests | +| `INTERNAL_ERROR` | 500 | Server error | + +--- + +## Rate Limits + +| Endpoint Group | Limit | +|----------------|-------| +| Admin endpoints | 100 req/min per user | +| Public endpoints | 60 req/min per user | +| Message tracking | 1000 req/min per user | + +--- + +## Webhooks + +Events sent to configured webhook URLs: + +### Broadcast Events +- `broadcast.created` +- `broadcast.sent` +- `broadcast.completed` +- `broadcast.read` +- `broadcast.clicked` + +### Survey Events +- `survey.created` +- `survey.started` +- `survey.completed` +- `survey.response_submitted` + +**Webhook Payload:** +```json +{ + "event": "broadcast.clicked", + "timestamp": "2024-01-15T10:32:15Z", + "data": { + "broadcastId": "bc_123456789", + "userId": "user_xyz", + "messageId": "msg_987654321" + } +} +```