# InstaRecipe API Documentation

This document describes the InstaRecipe API endpoints for the async queue-based recipe extraction system.

## Base URL

All API endpoints are relative to your InstaRecipe instance:
```
https://your-instarecipe-instance.com/api
```

## Authentication

Currently, no authentication is required for API access. This may change in future versions.

## Content Type

All requests should use `Content-Type: application/json` unless otherwise specified.

## Error Handling

All endpoints return standardized error responses:

```json
{
  "error": "Error type",
  "message": "Human-readable error message",
  "details": { /* Additional error context */ }
}
```

HTTP status codes follow REST conventions:
- `200` - Success
- `201` - Created
- `400` - Bad Request (invalid input)
- `404` - Not Found
- `409` - Conflict (e.g., cannot retry pending item)
- `410` - Gone (deprecated endpoint)
- `500` - Internal Server Error

## Queue Management Endpoints

### POST /api/queue

Enqueue an Instagram URL for async processing.

**Request:**
```json
{
  "url": "https://instagram.com/p/abc123"
}
```

**Supported URL Formats:**
- Posts: `https://instagram.com/p/{post-id}`
- Posts (www): `https://www.instagram.com/p/{post-id}`
- Reels: `https://instagram.com/reel/{reel-id}`
- IGTV: `https://instagram.com/tv/{video-id}`
- With query parameters: `https://instagram.com/reel/{reel-id}?utm_source=share`

**URL Requirements:**
- Must use HTTPS protocol
- Hostname must be `instagram.com` or `www.instagram.com`
- Any Instagram path is accepted (posts, reels, IGTV, etc.)
- Query parameters and hash fragments are allowed

**Examples:**
```json
// Post URL
{ "url": "https://instagram.com/p/ABC123" }

// Reel URL with tracking
{ "url": "https://www.instagram.com/reel/DSevV5CDcNm/?utm_source=ig_web_copy_link" }

// IGTV URL
{ "url": "https://instagram.com/tv/XYZ789" }
```

**Response (201 Created):**
```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "url": "https://instagram.com/reel/DSevV5CDcNm/?utm_source=ig_web_copy_link",
  "status": "pending",
  "phases": [
    {
      "name": "extraction",
      "status": "pending",
      "progress": 0
    },
    {
      "name": "parsing", 
      "status": "pending",
      "progress": 0
    },
    {
      "name": "uploading",
      "status": "pending", 
      "progress": 0
    }
  ],
  "createdAt": "2024-12-21T10:30:00Z",
  "updatedAt": "2024-12-21T10:30:00Z"
}
```

**Errors:**
- `400` - Invalid URL format (not a valid URL)
- `400` - URL must use HTTPS protocol
- `400` - URL must be from instagram.com domain
- `400` - Missing or invalid URL parameter

### GET /api/queue

List queue items with optional filtering, pagination, and sorting.

**Query Parameters:**
- `status` (optional): Filter by status (`pending`, `in_progress`, `success`, `error`, `unhealthy`)
- `limit` (optional): Number of items to return (default: 50, max: 100)
- `offset` (optional): Number of items to skip (default: 0)
- `sort` (optional): Sort field (`createdAt`, `updatedAt`, `status`) (default: `createdAt`)
- `order` (optional): Sort order (`asc`, `desc`) (default: `desc`)

**Examples:**
```bash
GET /api/queue                                    # All items
GET /api/queue?status=error                       # Failed items only
GET /api/queue?limit=10&offset=20                # Pagination
GET /api/queue?sort=status&order=asc             # Sort by status
```

**Response (200 OK):**
```json
{
  "items": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "url": "https://instagram.com/p/abc123",
      "status": "success",
      "phases": [
        {
          "name": "extraction",
          "status": "completed",
          "startedAt": "2024-12-21T10:30:01Z",
          "completedAt": "2024-12-21T10:30:15Z",
          "progress": 100
        },
        {
          "name": "parsing",
          "status": "completed", 
          "startedAt": "2024-12-21T10:30:15Z",
          "completedAt": "2024-12-21T10:30:25Z",
          "progress": 100
        },
        {
          "name": "uploading",
          "status": "completed",
          "startedAt": "2024-12-21T10:30:25Z", 
          "completedAt": "2024-12-21T10:30:30Z",
          "progress": 100
        }
      ],
      "results": {
        "recipe": {
          "name": "Chocolate Chip Cookies",
          "description": "Delicious homemade cookies",
          "servings": 24,
          "ingredients": [
            {
              "food": "flour",
              "amount": 2.25,
              "unit": "cups"
            }
          ],
          "steps": [
            {
              "instruction": "Preheat oven to 375°F",
              "time": 5
            }
          ],
          "keywords": ["cookies", "dessert", "chocolate"],
          "image": "https://instagram.com/image.jpg"
        },
        "tandoorUrl": "https://tandoor.example.com/recipe/123",
        "extractedText": "Raw extracted text...",
        "thumbnail": "https://instagram.com/thumbnail.jpg"
      },
      "createdAt": "2024-12-21T10:30:00Z",
      "updatedAt": "2024-12-21T10:30:30Z"
    }
  ],
  "total": 42,
  "hasMore": true
}
```

### GET /api/queue/{id}

Get details for a specific queue item.

**Path Parameters:**
- `id`: Queue item UUID

**Response (200 OK):**
Returns the same queue item structure as in the list response.

**Errors:**
- `400` - Invalid UUID format
- `404` - Queue item not found

### POST /api/queue/{id}/retry

Retry a failed queue item. Only items with `error` or `unhealthy` status can be retried.

**Path Parameters:**
- `id`: Queue item UUID

**Response (200 OK):**
```json
{
  "success": true,
  "message": "Item queued for retry",
  "item": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "pending",
    "updatedAt": "2024-12-21T11:00:00Z"
  }
}
```

**Errors:**
- `400` - Invalid UUID format
- `404` - Queue item not found
- `409` - Cannot retry item with current status (only `error` and `unhealthy` can be retried)

## Real-time Updates

### GET /api/queue/stream

Server-Sent Events (SSE) endpoint for real-time queue updates.

**Query Parameters:**
- `itemId` (optional): Filter updates for specific item
- `status` (optional): Filter updates by status

**Headers:**
```
Accept: text/event-stream
Cache-Control: no-cache
```

**Response:**
SSE stream with the following event types:

#### connection
Sent when connection is established:
```
event: connection
data: {"message": "Connected to queue stream", "timestamp": "2024-12-21T10:30:00Z"}
```

#### queue-update  
Sent when queue item status changes:
```
event: queue-update
data: {
  "itemId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "in_progress", 
  "timestamp": "2024-12-21T10:30:01Z",
  "progress": [
    {
      "name": "extraction",
      "status": "in_progress",
      "startedAt": "2024-12-21T10:30:01Z",
      "progress": 45
    }
  ]
}
```

#### ping
Keep-alive ping sent every 30 seconds:
```
event: ping
data: {"timestamp": "2024-12-21T10:30:30Z"}
```

**Usage Examples:**

**JavaScript:**
```javascript
const eventSource = new EventSource('/api/queue/stream');

eventSource.addEventListener('connection', (event) => {
  console.log('Connected:', JSON.parse(event.data));
});

eventSource.addEventListener('queue-update', (event) => {
  const update = JSON.parse(event.data);
  console.log('Queue update:', update);
  updateUI(update);
});

eventSource.addEventListener('ping', (event) => {
  console.log('Keep-alive ping');
});

eventSource.onerror = (error) => {
  console.error('SSE error:', error);
  // Reconnect logic here
};
```

**curl:**
```bash
curl -N -H "Accept: text/event-stream" \
  "https://your-instance.com/api/queue/stream?itemId=550e8400-e29b-41d4-a716-446655440000"
```

## Push Notifications

### GET /api/notifications/vapid-key

Get the VAPID public key required for push notification subscriptions.

**Response (200 OK):**
```json
{
  "publicKey": "BDummyPublicKeyForDevelopment...",
  "applicationServerKey": "BDummyPublicKeyForDevelopment..."
}
```

### POST /api/notifications/subscribe

Subscribe to push notifications for queue processing updates.

**Request:**
```json
{
  "subscription": {
    "endpoint": "https://fcm.googleapis.com/fcm/send/...",
    "keys": {
      "p256dh": "BIBn3E_YUVpW5f6_Eq_GH...",
      "auth": "tBiH_Y1nPSuVh7TRMhcf..."
    }
  },
  "clientId": "unique-client-identifier"
}
```

**Response (200 OK):**
```json
{
  "success": true,
  "message": "Successfully subscribed to push notifications",
  "subscriptionCount": 5
}
```

**Errors:**
- `400` - Invalid subscription object or missing clientId

### DELETE /api/notifications/subscribe

Unsubscribe from push notifications.

**Request:**
```json
{
  "clientId": "unique-client-identifier"
}
```

**Response (200 OK):**
```json
{
  "success": true,
  "message": "Successfully unsubscribed from push notifications", 
  "subscriptionCount": 4
}
```

## Legacy Endpoints (Deprecated)

### POST /api/extract ⚠️ DEPRECATED

**Status:** `410 Gone`

This synchronous extraction endpoint is deprecated. Use `POST /api/queue` instead.

**Migration:**
```javascript
// ❌ Old synchronous approach
const response = await fetch('/api/extract', {
  method: 'POST',
  body: JSON.stringify({ url })
});
const result = await response.json(); // Wait 30-60 seconds

// ✅ New async queue approach  
const response = await fetch('/api/queue', {
  method: 'POST', 
  body: JSON.stringify({ url })
});
const queueItem = await response.json(); // Immediate response
```

### POST /api/extract-stream ⚠️ DEPRECATED

**Status:** `410 Gone`

This SSE endpoint is deprecated. Use `POST /api/queue` + `GET /api/queue/stream` instead.

**Migration:**
```javascript
// ❌ Old approach
const response = await fetch('/api/extract-stream', {
  method: 'POST',
  body: JSON.stringify({ url })
});

// ✅ New approach
const queueResponse = await fetch('/api/queue', {
  method: 'POST',
  body: JSON.stringify({ url })
});
const item = await queueResponse.json();

const eventSource = new EventSource(`/api/queue/stream?itemId=${item.id}`);
```

## Data Models

### QueueItem

```typescript
interface QueueItem {
  id: string;                    // UUID v4
  url: string;                   // Instagram URL
  status: 'pending' | 'in_progress' | 'success' | 'error' | 'unhealthy';
  
  phases: Array<{
    name: 'extraction' | 'parsing' | 'uploading';
    status: 'pending' | 'in_progress' | 'completed' | 'error';
    startedAt?: string;          // ISO 8601 timestamp
    completedAt?: string;        // ISO 8601 timestamp  
    progress?: number;           // 0-100
  }>;
  
  results?: {
    recipe?: Recipe;             // Structured recipe data
    tandoorUrl?: string;         // Tandoor recipe URL
    extractedText?: string;      // Raw extracted text
    thumbnail?: string;          // Image URL
  };
  
  error?: string;                // Error message
  createdAt: string;            // ISO 8601 timestamp
  updatedAt: string;            // ISO 8601 timestamp
}
```

### Recipe

```typescript
interface Recipe {
  name: string;
  description?: string;
  servings?: number;
  prepTime?: number;            // Minutes
  cookTime?: number;            // Minutes
  totalTime?: number;           // Minutes
  
  ingredients: Array<{
    food: string;
    amount?: number;
    unit?: string;
  }>;
  
  steps: Array<{
    instruction: string;
    time?: number;              // Minutes
  }>;
  
  keywords?: string[];          // Recipe tags
  image?: string;               // Image URL
  nutrition?: {                 // Nutritional information
    calories?: number;
    protein?: number;
    carbs?: number;
    fat?: number;
  };
}
```

## Rate Limiting

Currently, no rate limiting is enforced, but this may change in future versions. Consider implementing client-side rate limiting to avoid overwhelming the service.

## WebSocket Alternative

For applications that cannot use Server-Sent Events, consider polling the `GET /api/queue/{id}` endpoint every 5-10 seconds for status updates.

## Error Recovery

When implementing clients, consider these error recovery strategies:

1. **Network Errors**: Retry with exponential backoff
2. **SSE Connection Drops**: Automatically reconnect after 5-10 seconds
3. **Queue Item Failures**: Present retry option to users
4. **Push Notification Failures**: Gracefully degrade to polling

## Examples

### Complete Processing Workflow

```javascript
async function processInstagramUrl(url) {
  try {
    // 1. Enqueue URL
    const queueResponse = await fetch('/api/queue', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ url })
    });
    
    const queueItem = await queueResponse.json();
    console.log('Enqueued:', queueItem.id);
    
    // 2. Listen for real-time updates
    const eventSource = new EventSource(`/api/queue/stream?itemId=${queueItem.id}`);
    
    return new Promise((resolve, reject) => {
      eventSource.addEventListener('queue-update', (event) => {
        const update = JSON.parse(event.data);
        
        if (update.status === 'success') {
          eventSource.close();
          resolve(update.results);
        } else if (update.status === 'error') {
          eventSource.close();
          reject(new Error(update.error));
        }
        
        // Handle progress updates
        console.log('Progress:', update.progress);
      });
      
      eventSource.onerror = (error) => {
        eventSource.close();
        reject(error);
      };
    });
    
  } catch (error) {
    console.error('Processing failed:', error);
    throw error;
  }
}

// Usage
processInstagramUrl('https://instagram.com/p/abc123')
  .then(results => {
    console.log('Recipe extracted:', results.recipe);
    if (results.tandoorUrl) {
      console.log('Uploaded to Tandoor:', results.tandoorUrl);
    }
  })
  .catch(error => {
    console.error('Extraction failed:', error.message);
  });
```

For more examples and integration guides, see the [Migration Documentation](/docs/MIGRATION.md).