Raven Zero - API Specification
π‘ API Overview
Content Type: application/json (except file uploads)
Authentication: None (anonymous service)
Rate Limiting: Yes (per IP)
API Documentation: /scalar (interactive), /docs (Swagger), /redoc (ReDoc)
π Endpoints Summary
| Method | Endpoint | Purpose | Rate Limit |
|---|---|---|---|
POST | /upload/ | Upload file | 30/hour |
GET | /preview/{key} | Preview upload (one-time) | β |
GET | /download/{key} | Download file | β |
GET | /status/{key} | Check file status | β |
GET | /health/ | Health check | 1/second |
π€ POST /upload/
Upload a file with auto-destruction parameters.
Request
Content-Type: multipart/form-data
| Field | Type | Required | Default | Validation | Description |
|---|---|---|---|---|---|
file | File | β Yes | β | β€ 10MB | File to upload |
expiry | Integer | No | 10 | 1-60 | Minutes until expiration |
uses | Integer | No | 1 | 1-5 | Number of allowed downloads |
Response
Status Code: 201 Created
{
"key": "correct-horse-battery",
"preview_url": "https://domain.com/preview/correct-horse-battery",
"download_url": "https://domain.com/download/correct-horse-battery",
"expiry": "2024-12-10T16:00:00Z",
"uses": 3,
"filename": "document.pdf",
"size": 51200,
"created_at": "2024-12-10T15:50:00Z",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
Examples
cURL:
curl -X POST https://domain.com/upload/ \
-F "[email protected]" \
-F "expiry=10" \
-F "uses=3"
Python (httpx):
import httpx
files = {"file": ("document.pdf", open("document.pdf", "rb"), "application/pdf")}
data = {"expiry": 10, "uses": 3}
response = httpx.post("https://domain.com/upload/", files=files, data=data)
print(response.json())
Error Responses
| Status | Condition | Response |
|---|---|---|
400 | File too large | {"detail": "File too large: X bytes (Max Size: 10,485,760 bytes)"} |
400 | Invalid MIME type | {"detail": "File type not suported: application/x-executable"} |
429 | Rate limit exceeded | {"detail": "Rate limit exceeded"} |
500 | Server error | {"detail": "Internal error while processing upload: ..."} |
ποΈ GET /preview/{key}
Preview upload details. Can only be accessed once for security.
Request
| Parameter | Type | Required | Description |
|---|---|---|---|
key | string | β Yes | Diceware key (e.g., correct-horse-battery) |
Response
Status Code: 200 OK
{
"key": "correct-horse-battery",
"filename": "document.pdf",
"size": 51200,
"mime_type": "application/pdf",
"expiry": "2024-12-10T16:00:00Z",
"uses": 3,
"minutes_left": 8,
"download_url": "https://domain.com/download/correct-horse-battery",
"curl_example": "curl -O https://domain.com/download/correct-horse-battery",
"created_at": "2024-12-10T15:50:00Z",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
Error Responses
| Status | Condition | Response |
|---|---|---|
404 | Upload not found | {"detail": "Upload not found or link expired"} |
404 | Already previewed | {"detail": "This file preview has already been accessed..."} |
429 | IP blocked | {"detail": "Demasiados intentos fallidos. IntΓ©ntalo mΓ‘s tarde."} |
π₯ GET /download/{key}
Download the file. Decrements remaining uses and verifies integrity.
Request
| Parameter | Type | Required | Description |
|---|---|---|---|
key | string | β Yes | Diceware key |
Response
Status Code: 200 OK
Headers:
Content-Disposition: attachment; filename="document.pdf"
Content-Type: application/pdf
X-SHA256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Body: Decrypted file binary content
Examples
cURL (save to file):
curl -O https://domain.com/download/correct-horse-battery
wget:
wget https://domain.com/download/correct-horse-battery
Behavior
- First download: Uses decremented 3 β 2
- Second download: Uses decremented 2 β 1
- Third download: Uses decremented 1 β 0 β File auto-deletes
- Fourth attempt: Returns 404
Error Responses
| Status | Condition | Response |
|---|---|---|
404 | Not found/expired | {"detail": "File not found"} |
410 | No uses remaining | {"detail": "Download limit has been reached"} |
429 | IP blocked | {"detail": "Demasiados intentos fallidos. Bloqueado temporalmente."} |
500 | Integrity failed | {"error_code": "INTEGRITY_CHECK_FAILED", ...} |
π GET /status/{key}
Check the status of an upload without consuming downloads.
Request
| Parameter | Type | Required | Description |
|---|---|---|---|
key | string | β Yes | Diceware key |
Response
Status Code: 200 OK
{
"key": "correct-horse-battery",
"status": "active",
"remaining_uses": 2,
"expires_at": "2024-12-10T16:00:00Z",
"is_accessible": true
}
Possible Status Values:
activeβ File is available for downloadexpiredβ Time limit reachedburnedβ Download limit reachedexpired_or_burnedβ File no longer exists
π₯ GET /health/
Health check endpoint for monitoring.
Response
Status Code: 200 OK
{
"status": "healthy",
"version": "0.1.0",
"timestamp": "2024-12-10T15:50:00Z",
"services": {
"redis": "online",
"storage": "online",
"scheduler": "online",
"diceware": "online"
},
"uptime_seconds": 3600,
"started_at": "2024-12-10T14:50:00Z"
}
| Field | Description |
|---|---|
status | healthy or degraded |
services.redis | Redis connectivity |
services.storage | Filesystem writability |
services.scheduler | Background job status |
services.diceware | Wordlist loaded correctly |
uptime_seconds | Time since application start |
π Security Headers
All responses include:
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: no-referrer
X-Request-ID: {uuid}
π¦ Rate Limiting
| Endpoint | Limit |
|---|---|
POST /upload/ | 30/hour |
GET /health/ | 1/second |
Anti Brute-Force:
- 10 failed attempts β IP blocked for 30 minutes
- Applies to:
/download,/preview,/status
π CORS Policy
Allowed Origins: * (configurable)
Allowed Methods: GET, POST
Allowed Headers: Content-Type, Accept
π Error Response Format
Standard error:
{
"detail": "Human-readable error message"
}
Validation error (Pydantic):
{
"detail": [
{
"type": "error_type",
"loc": ["body", "field_name"],
"msg": "Error message"
}
]
}
π’ HTTP Status Codes
| Code | Meaning | When Used |
|---|---|---|
200 | OK | Successful GET |
201 | Created | Successful upload |
400 | Bad Request | Validation error |
404 | Not Found | Upload doesnβt exist/expired |
410 | Gone | Download limit exhausted |
429 | Too Many Requests | Rate limit/IP blocked |
500 | Internal Server Error | Server error |
π’ Data Types
Diceware Key Format
Pattern: word1-word2-word3
Example: correct-horse-battery
Rules:
- Exactly 3 words
- Lowercase letters only
- Separated by hyphens
- 7,776 word dictionary
- Combinations: 470,184,984,576 (~38.9 bits entropy)
Timestamp Format
Standard: ISO 8601 (UTC)
Format: YYYY-MM-DDTHH:MM:SSZ
Example: 2024-12-10T15:50:00Z
File Size
Format: Integer (bytes)
Maximum: 10,485,760 (10 MB)
π§ͺ Testing the API
Quick Test Flow
# 1. Upload a file
KEY=$(curl -s -X POST http://localhost:8000/upload/ \
-F "[email protected]" \
-F "expiry=10" \
-F "uses=2" | jq -r '.key')
echo "Key: $KEY"
# 2. Preview (first time works)
curl http://localhost:8000/preview/$KEY | jq
# 3. Preview again (should fail - one-time only)
curl http://localhost:8000/preview/$KEY | jq
# 4. Check status
curl http://localhost:8000/status/$KEY | jq
# 5. Download (first time)
curl -O http://localhost:8000/download/$KEY
# 6. Download (second time, last use, file auto-deletes)
curl -O http://localhost:8000/download/$KEY
# 7. Download (should fail - no uses remaining)
curl http://localhost:8000/download/$KEY | jq
π Interactive Documentation
| URL | Description |
|---|---|
/scalar | Scalar API documentation (modern UI) |
/docs | Swagger UI |
/redoc | ReDoc |
/openapi.json | OpenAPI schema |
API Version: 0.1.0