# Loopuman Enterprise API Documentation

> **The Human Layer for AI** - Get human intelligence on demand via API

## Overview

Loopuman provides a REST API for AI companies to access human workers at scale. Upload thousands of tasks, get quality human responses, pay only for approved work.

**Base URL:** `https://api.loopuman.com/api/v1`

---

## Quick Start
```bash
# 1. Get your API key from the admin dashboard
# 2. Test the connection
curl -X GET https://api.loopuman.com/health

# 3. Upload your first batch
curl -X POST https://api.loopuman.com/api/v1/tasks/bulk \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tasks": [{
      "title": "Rate this AI response",
      "description": "Is this response helpful, accurate, and safe?",
      "category": "ai_training",
      "budget": 50
    }],
    "webhook_url": "https://your-server.com/loopuman-webhook"
  }'
```

---

## Authentication

All API requests require an API key in the header:
```
X-API-Key: your_api_key_here
```

**Get your API key:** Contact enterprise@loopuman.com or generate via admin dashboard.

---

## Endpoints

### 1. Create Tasks (Bulk Upload)

**POST** `/api/v1/tasks/bulk`

Upload multiple tasks in a single request. Ideal for AI training, data labeling, and content moderation.

#### Request Body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `tasks` | array | Yes | Array of task objects (max 1000 per request) |
| `webhook_url` | string | No | URL to receive completion notifications |
| `priority` | string | No | `normal` (default), `high`, `urgent` |

#### Task Object

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `title` | string | Yes | Task title (max 100 chars) |
| `description` | string | Yes | Public instructions for workers |
| `private_description` | string | No | Hidden until worker accepts |
| `category` | string | Yes | Task category (see below) |
| `budget` | integer | Yes | Payment in VAE cents (100 = $1) |
| `max_workers` | integer | No | Workers needed (default: 1, max: 100) |
| `duration_hours` | integer | No | Time limit (default: 48) |
| `requires_attachment` | boolean | No | Require file upload |
| `external_id` | string | No | Your internal reference ID |

#### Categories

| Category | Use Case | AI Agents |
|----------|----------|-----------|
| `ai_training` | RLHF, preference ranking | ❌ Human only |
| `labeling` | Image/data annotation | ❌ Human only |
| `writing` | Content creation | ✅ AI backup |
| `micro` | Simple text tasks | ✅ AI backup |
| `research` | Information gathering | ❌ Human only |
| `transcription` | Audio to text | ❌ Human only |
| `translation` | Language translation | ❌ Human only |
| `verification` | Fact checking | ❌ Human only |
| `moderation` | Content review | ❌ Human only |
| `survey` | Human opinions | ❌ Human only |
| `local` | Location-based tasks | ❌ Human only |
| `mystery` | Mystery shopping | ❌ Human only |

#### Example Request
```json
{
  "tasks": [
    {
      "title": "Compare two AI responses",
      "description": "Read both responses and select which is more helpful, accurate, and harmless.",
      "private_description": "Response A: [text]\nResponse B: [text]",
      "category": "ai_training",
      "budget": 50,
      "max_workers": 3,
      "external_id": "comparison_batch_001_item_1"
    },
    {
      "title": "Label image content",
      "description": "Identify all objects in this image",
      "category": "labeling",
      "budget": 25,
      "max_workers": 1
    }
  ],
  "webhook_url": "https://api.yourcompany.com/loopuman/webhook",
  "priority": "normal"
}
```

#### Response
```json
{
  "success": true,
  "batch_id": "batch_abc123def456",
  "tasks_created": 2,
  "total_cost": 150,
  "estimated_completion": "2026-02-04T12:00:00Z",
  "tasks": [
    {
      "id": "task_xyz789",
      "external_id": "comparison_batch_001_item_1",
      "status": "open"
    },
    {
      "id": "task_xyz790",
      "external_id": null,
      "status": "open"
    }
  ]
}
```

---

### 2. Get Batch Status

**GET** `/api/v1/batches/:batch_id`

Check the progress of a task batch.

#### Response
```json
{
  "batch_id": "batch_abc123def456",
  "status": "in_progress",
  "created_at": "2026-02-03T10:00:00Z",
  "tasks": {
    "total": 100,
    "open": 45,
    "in_progress": 30,
    "completed": 25
  },
  "completion_percentage": 25,
  "estimated_completion": "2026-02-04T12:00:00Z"
}
```

---

### 3. Get Batch Results

**GET** `/api/v1/batches/:batch_id/results`

Download all completed submissions for a batch.

#### Query Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `status` | string | Filter: `approved`, `all` |
| `limit` | integer | Results per page (default: 100, max: 1000) |
| `offset` | integer | Pagination offset |

#### Response
```json
{
  "batch_id": "batch_abc123def456",
  "total_results": 25,
  "results": [
    {
      "task_id": "task_xyz789",
      "external_id": "comparison_batch_001_item_1",
      "worker_id": "worker_anon_123",
      "content": "Response A is better because...",
      "attachments": [],
      "submitted_at": "2026-02-03T11:30:00Z",
      "approved_at": "2026-02-03T11:35:00Z",
      "quality_score": 4.8
    }
  ]
}
```

---

### 4. Get Single Task

**GET** `/api/v1/tasks/:task_id`

Get details and submissions for a specific task.

#### Response
```json
{
  "id": "task_xyz789",
  "title": "Compare two AI responses",
  "status": "completed",
  "budget": 50,
  "max_workers": 3,
  "current_workers": 3,
  "submissions": [
    {
      "id": "sub_001",
      "content": "Response A is better...",
      "status": "approved",
      "worker_rating": 4.9
    }
  ]
}
```

---

### 5. Health Check

**GET** `/health`

Check API availability. No authentication required.
```json
{
  "status": "healthy",
  "version": "3.1",
  "timestamp": "2026-02-03T12:00:00Z"
}
```

---

## Webhooks

Receive real-time notifications when tasks are completed.

### Event Types

| Event | Description |
|-------|-------------|
| `task.accepted` | A worker accepted your task |
| `task.submitted` | Worker submitted their work |
| `task.approved` | Submission auto-approved (48h) or manually approved |
| `task.rejected` | Submission rejected |
| `batch.completed` | All tasks in batch are done |

### Webhook Payload
```json
{
  "event": "task.approved",
  "timestamp": "2026-02-03T12:00:00Z",
  "data": {
    "task_id": "task_xyz789",
    "external_id": "your_reference_id",
    "batch_id": "batch_abc123def456",
    "submission": {
      "id": "sub_001",
      "content": "The worker's response text...",
      "attachments": ["https://storage.loopuman.com/files/abc123.jpg"],
      "worker_rating": 4.8,
      "completed_in_minutes": 15
    }
  }
}
```

### Webhook Security

Verify webhooks using the signature header:
```
X-Loopuman-Signature: sha256=abc123...
```
```javascript
const crypto = require('crypto');
const signature = crypto
  .createHmac('sha256', YOUR_WEBHOOK_SECRET)
  .update(JSON.stringify(payload))
  .digest('hex');
const isValid = signature === receivedSignature;
```

---

## Rate Limits

| Endpoint | Limit | Window |
|----------|-------|--------|
| Bulk upload | 10 requests | per minute |
| Status/Results | 60 requests | per minute |
| Enterprise tier | 1000 requests | per minute |

Rate limit headers are included in every response:
- `X-RateLimit-Limit`: Max requests allowed
- `X-RateLimit-Remaining`: Requests left
- `X-RateLimit-Reset`: Unix timestamp when limit resets

---

## Error Codes

| Code | Description | Solution |
|------|-------------|----------|
| 400 | Bad request | Check request body format |
| 401 | Invalid API key | Verify your API key |
| 402 | Insufficient balance | Top up your account |
| 404 | Resource not found | Check ID is correct |
| 429 | Rate limit exceeded | Wait and retry |
| 500 | Server error | Contact support |

### Error Response Format
```json
{
  "error": "INSUFFICIENT_BALANCE",
  "message": "Your account balance is too low for this batch",
  "required": 15000,
  "available": 5000
}
```

---

## Pricing

| Component | Cost |
|-----------|------|
| Platform fee | 20% of budget |
| Worker payment | 80% of budget |

**Example:** 100 tasks × $0.50 budget = $50 base + $10 fee = **$60 total**

### Payment Methods
- Crypto: USDC, USDT, cUSD (Celo network)
- Bank transfer (Enterprise accounts)
- Monthly invoicing (Enterprise accounts)

---

## Code Examples

### Python
```python
import requests

API_KEY = "your_api_key"
BASE_URL = "https://api.loopuman.com/api/v1"

def upload_tasks(tasks, webhook_url=None):
    response = requests.post(
        f"{BASE_URL}/tasks/bulk",
        headers={"X-API-Key": API_KEY},
        json={"tasks": tasks, "webhook_url": webhook_url}
    )
    return response.json()

def get_results(batch_id):
    response = requests.get(
        f"{BASE_URL}/batches/{batch_id}/results",
        headers={"X-API-Key": API_KEY}
    )
    return response.json()

# Upload RLHF comparison tasks
tasks = [
    {
        "title": f"Compare AI responses #{i}",
        "description": "Which response is more helpful?",
        "private_description": f"A: {response_a}\nB: {response_b}",
        "category": "ai_training",
        "budget": 50,
        "max_workers": 3,
        "external_id": f"rlhf_batch_1_{i}"
    }
    for i, (response_a, response_b) in enumerate(your_comparison_pairs)
]

result = upload_tasks(tasks, webhook_url="https://api.yourco.com/webhook")
print(f"Batch ID: {result['batch_id']}")
```

### Node.js
```javascript
const axios = require('axios');

const API_KEY = 'your_api_key';
const BASE_URL = 'https://api.loopuman.com/api/v1';

async function uploadTasks(tasks, webhookUrl) {
  const response = await axios.post(
    `${BASE_URL}/tasks/bulk`,
    { tasks, webhook_url: webhookUrl },
    { headers: { 'X-API-Key': API_KEY } }
  );
  return response.data;
}

async function getResults(batchId) {
  const response = await axios.get(
    `${BASE_URL}/batches/${batchId}/results`,
    { headers: { 'X-API-Key': API_KEY } }
  );
  return response.data;
}

// Example: Image labeling tasks
const tasks = images.map((imageUrl, i) => ({
  title: `Label objects in image`,
  description: `Identify all objects visible in this image: ${imageUrl}`,
  category: 'labeling',
  budget: 25,
  external_id: `img_label_${i}`
}));

const result = await uploadTasks(tasks);
console.log(`Created batch: ${result.batch_id}`);
```

---

## Support

- **Email:** enterprise@loopuman.com
- **Response time:** < 24 hours
- **Enterprise SLA:** 99.9% uptime guarantee

---

## Changelog

| Version | Date | Changes |
|---------|------|---------|
| 3.1 | Feb 2026 | Added external_id support, webhook signatures |
| 3.0 | Jan 2026 | Rate limiting, batch endpoints |
| 2.0 | Dec 2025 | Initial enterprise API |
