AI Agent API

xPandorax Developer Documentation — v2.0

Connect your AI agent to xPandorax and programmatically manage your producer channel. Create, read, update, and delete videos, cuts, pictures, models, and your producer profile — all via a simple REST API. This documentation covers authentication, all endpoints, request/response schemas, error handling, and best practices.

Overview

The xPandorax AI Agent API enables automated content management for producer accounts. Whether you're building a Python scraper, an n8n workflow, a LangChain agent, or a custom integration, this API gives you full CRUD access to your producer's content.

18
Endpoints
5
Content Types
120
Req / Min

Content ownership: Before using this API, ensure you have the legal right to upload and distribute all content. xPandorax enforces DMCA compliance and will suspend accounts found violating third-party copyrights.

Authentication

All API requests require a Bearer token in the Authorization header. Tokens are generated from the AI Department in your Creator Studio.

Token Format

http
Authorization: Bearer xpx_yo...token

Getting a Token

  1. Navigate to Creator Studio → AI Department
  2. Click New Token
  3. Give your token a descriptive name (e.g. "My Scraper Bot")
  4. Accept the warranty terms
  5. Copy the raw token immediately — it is shown only once

Security note: Store your token as an environment variable (e.g. XPANDORAX_API_TOKEN). Never hardcode tokens in source code or expose them in client-side JavaScript. Revoke compromised tokens immediately from the AI Department dashboard.

Base URL

base-url
https://xpandorax.com

All endpoints are relative to this base URL. Example: https://xpandorax.com/api/agent/v2/content/videos

Endpoints

All content management endpoints are under /api/agent/v2/content/*. These endpoints manage content under your active producer profile. The legacy upload endpoints are under /api/agent/*.

Producer Profile

GET/api/agent/v2/content/producer
PATCH/api/agent/v2/content/producer

Videos

GET/api/agent/v2/content/videos
POST/api/agent/v2/content/videos
PATCH/api/agent/v2/content/videos/:id
DELETE/api/agent/v2/content/videos/:id

Cuts (Short Clips)

GET/api/agent/v2/content/cuts
POST/api/agent/v2/content/cuts
PATCH/api/agent/v2/content/cuts/:id
DELETE/api/agent/v2/content/cuts/:id

Pictures

GET/api/agent/v2/content/pictures
POST/api/agent/v2/content/pictures
PATCH/api/agent/v2/content/pictures/:id
DELETE/api/agent/v2/content/pictures/:id

Models

GET/api/agent/v2/content/models
POST/api/agent/v2/content/models
PATCH/api/agent/v2/content/models/:id
DELETE/api/agent/v2/content/models/:id

Producer Profile

Get or update your active producer profile. Each API token is tied to the producer associated with your account.

GET /api/agent/v2/content/producer

json
// Response (200)
{
  "id": "uuid",
  "name": "My Studio",
  "slug": "my-studio",
  "description": "Premium content producer",
  "logo_url": "https://media.xpandorax.com/logos/...",
  "cover_url": "https://media.xpandorax.com/covers/...",
  "website": "https://example.com",
  "social_links": {
    "twitter": "@mystudio",
    "instagram": "@mystudio"
  },
  "is_verified": true,
  "content_count": {
    "videos": 42,
    "cuts": 15,
    "pictures": 8,
    "models": 5
  },
  "created_at": "2026-01-15T00:00:00Z",
  "updated_at": "2026-05-17T00:00:00Z"
}

PATCH /api/agent/v2/content/producer

Partial update — send only the fields you want to change.

FieldTypeRequiredDescription
namestringNoStudio name
descriptionstringNoBio / description
logo_urlstringNoLogo image URL
cover_urlstringNoCover/banner image URL
websitestringNoProducer website URL
social_linksobjectNoSocial media URLs object

Videos

GET /api/agent/v2/content/videos

Query parameters: ?page=1 ?limit=50 ?sort=created_at ?order=desc

POST /api/agent/v2/content/videos

FieldTypeRequiredDescription
titlestringYesVideo title (max 255 chars)
descriptionstringNoVideo description
video_urlstringYesDirect CDN URL to the video file
thumbnail_urlstringNoURL to the video thumbnail image
tagsstring[]NoArray of tag strings
categoriesstring[]NoCategory names (must exist on platform)
modelsstring[]NoModel IDs to associate
duration_hoursnumberNoDuration hours component (0-23)
duration_minutesnumberNoDuration minutes component (0-59)
duration_secondsnumberNoDuration seconds component (0-59)
is_hdbooleanNoMark as HD content
is_exclusivebooleanNoMark as exclusive content
is_privatebooleanNoHide from public listings
json
// Request body
{
  "title": "Filipina Amateur Couple - Sarapbabe Exclusive",
  "description": "Amateur couple filmed in Manila. High quality exclusive content.",
  "video_url": "https://cdn.example.com/videos/filipina-couple.mp4",
  "thumbnail_url": "https://cdn.example.com/thumbs/filipina-couple.jpg",
  "tags": ["filipina", "amateur", "couple", "exclusive"],
  "categories": ["Filipina", "Amateur", "Couples"],
  "models": ["model-uuid-1"],
  "duration_hours": 0,
  "duration_minutes": 12,
  "duration_seconds": 34,
  "is_hd": true,
  "is_exclusive": true
}

// Response (201)
{
  "id": "new-video-uuid",
  "title": "Filipina Amateur Couple - Sarapbabe Exclusive",
  "status": "published",
  "slug": "filipina-amateur-couple-sarapbabe-exclusive",
  "created_at": "2026-05-18T08:30:00Z"
}

PATCH /api/agent/v2/content/videos/:id

Partial update — supports same fields as POST except video_url (immutable after creation).

DELETE /api/agent/v2/content/videos/:id

Soft-deletes the video. Returns 204 No Content on success.

Cuts (Short Clips)

Cuts are short video clips (typically under 60 seconds). Similar schema to videos but optimised for short-form content.

FieldTypeRequiredDescription
titlestringYesCut title
descriptionstringNoShort description
cut_urlstringYesCDN URL to the clip file
thumbnail_urlstringNoThumbnail image URL
tagsstring[]NoArray of tags
categoriesstring[]NoCategory names
modelsstring[]NoAssociated model IDs
source_video_idstringNoID of the parent video if this is a clip
is_privatebooleanNoHide from public

Pictures (Galleries)

Picture entries represent galleries or individual images. Each entry can have multiple image URLs.

FieldTypeRequiredDescription
titlestringYesGallery title
descriptionstringNoGallery description
image_urlsstring[]YesArray of image CDN URLs
thumbnail_urlstringNoCover thumbnail URL
tagsstring[]NoArray of tags
categoriesstring[]NoCategory names
modelsstring[]NoAssociated model IDs
is_privatebooleanNoHide from public

Models

Models are the performers / talent associated with your content. Each model entry stores biographical data and appearance attributes.

FieldTypeRequiredDescription
namestringYesModel's display name
biostringNoShort biography / description
image_urlstringNoProfile image URL
agenumberNoAge (must be 18+)
ethnicitystringNoEthnicity (e.g. Filipina, Latina)
hair_colorstringNoHair color
eye_colorstringNoEye color
height_cmnumberNoHeight in centimeters
weight_kgnumberNoWeight in kilograms
measurementsstringNoBody measurements (e.g. 34C-24-36)
tagsstring[]NoArray of descriptive tags
social_linksobjectNoObject with social URLs e.g. {"instagram":"..."}
json
// Request body
{
  "name": "Mia Santos",
  "bio": "Filipina model from Manila. 5ft 5in, athletic build.",
  "image_url": "https://cdn.example.com/models/mia-santos.jpg",
  "age": 24,
  "ethnicity": "Filipina",
  "hair_color": "Black",
  "eye_color": "Brown",
  "height_cm": 165,
  "weight_kg": 55,
  "measurements": "34C-24-36",
  "tags": ["filipina", "athletic", "brunette"],
  "social_links": {
    "instagram": "https://instagram.com/mia_santos",
    "twitter": "https://twitter.com/mia_santos"
  }
}

// Response (201)
{
  "id": "new-model-uuid",
  "name": "Mia Santos",
  "slug": "mia-santos",
  "created_at": "2026-05-18T08:30:00Z"
}

Legacy Upload Flow (Video Uploads)

The legacy upload flow is a three-step process for direct video file uploads. Use the v2 Content Management API (above) for videos already hosted elsewhere.

1

Request Upload URL

POST/api/agent/upload-url

Send file metadata to receive a presigned Backblaze B2 URL and upload log ID.

json
// Request
{
  "filename":     "my-video.mp4",
  "filesize":     104857600,          // bytes (max 5 GB)
  "content_type": "video/mp4"         // video/mp4 | video/webm | video/quicktime
}

// Response
{
  "upload_log_id": "uuid",
  "presigned_url": "https://s3.us-east-005.backblazeb2.com/...",
  "b2_key":        "agent-uploads/2026/05/my-video.mp4",
  "cdn_url":       "https://media.xpandorax.com/agent-uploads/...",
  "expires_at":    "2026-05-18T08:45:00Z",
  "quota_used":    1,
  "quota_limit":   10
}
2

Upload Binary to B2

PUTPresigned URL (external)

PUT the video binary file directly to the presigned URL. No auth header required for this step.

json
curl -X PUT \
  -H "Content-Type: video/mp4" \
  --data-binary @/path/to/video.mp4 \
  "https://s3.us-east-005.backblazeb2.com/..."
3

Confirm with Metadata

POST/api/agent/confirm-upload

Provide title, tags, categories and source URL. Gemini validates the content; if approved, the video enters the admin review queue.

json
// Request
{
  "upload_log_id": "uuid-from-step-1",
  "metadata": {
    "title":               "Filipina Amateur Couple",
    "description":         "Amateur couple video",
    "tags":                ["filipina", "amateur"],
    "categories":          ["Filipina", "Amateur"],
    "original_source_url": "https://sarapbabe.com/video/123",
    "thumbnail_url":       "https://sarapbabe.com/thumb/123.jpg",
    "duration_hours":      0,
    "duration_minutes":    12,
    "duration_seconds":    34
  }
}

// Approved response
{
  "success": true,
  "video_id": "uuid",
  "status": "pending_review",
  "gemini_confidence": 0.95,
  "cleaned_title": "Filipina Amateur Couple"
}

// Rejected response
{
  "success": false,
  "rejected": true,
  "reason": "Content flags detected",
  "issues": ["Title appears clickbait", "Source URL domain not recognized"],
  "gemini_confidence": 0.30
}

Code Examples

curl

bash
# List videos
curl -H "Authorization: Bearer xpx_your_token_here" \
  https://xpandorax.com/api/agent/v2/content/videos

# Create a video
curl -X POST \
  -H "Authorization: Bearer xpx_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "My Agent Uploaded Video",
    "video_url": "https://cdn.example.com/video.mp4",
    "tags": ["exclusive", "hd"]
  }' \
  https://xpandorax.com/api/agent/v2/content/videos

# Update a video
curl -X PATCH \
  -H "Authorization: Bearer xpx_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{"title": "Updated Title", "is_hd": true}' \
  https://xpandorax.com/api/agent/v2/content/videos/video-uuid

# Delete a video
curl -X DELETE \
  -H "Authorization: Bearer xpx_your_token_here" \
  https://xpandorax.com/api/agent/v2/content/videos/video-uuid

Python (requests)

python
import requests
import os

API_TOKEN = os.environ["XPANDORAX_API_TOKEN"]
BASE = "https://xpandorax.com/api/agent/v2"
HEADERS = {"Authorization": f"Bearer {API_TOKEN}"}

# ── List videos ──
resp = requests.get(f"{BASE}/content/videos", headers=HEADERS)
videos = resp.json()
print(f"Found {len(videos)} videos")

# ── Create a video ──
video_data = {
    "title": "My Agent Uploaded Video",
    "description": "Created via AI Agent API",
    "video_url": "https://cdn.example.com/video.mp4",
    "thumbnail_url": "https://cdn.example.com/thumb.jpg",
    "tags": ["exclusive", "hd"],
    "duration_minutes": 15,
    "is_hd": True,
}
resp = requests.post(f"{BASE}/content/videos", headers=HEADERS, json=video_data)
print(resp.json())

# ── Add a model ──
model_data = {
    "name": "Mia Santos",
    "ethnicity": "Filipina",
    "height_cm": 165,
    "tags": ["brunette", "athletic"],
}
resp = requests.post(f"{BASE}/content/models", headers=HEADERS, json=model_data)
print(resp.json())

# ── Full upload flow (legacy) ──
video_path = "/path/to/video.mp4"
file_size = os.path.getsize(video_path)

# Step 1: request presigned URL
r1 = requests.post(f"{BASE.rsplit('/v2',1)[0]}/upload-url",
    headers=HEADERS,
    json={"filename": "video.mp4", "filesize": file_size, "content_type": "video/mp4"})
r1.raise_for_status()
data = r1.json()

# Step 2: upload to B2
with open(video_path, "rb") as f:
    r2 = requests.put(data["presigned_url"],
        data=f, headers={"Content-Type": "video/mp4"})
r2.raise_for_status()

# Step 3: confirm with metadata
r3 = requests.post(f"{BASE.rsplit('/v2',1)[0]}/confirm-upload",
    headers=HEADERS,
    json={
        "upload_log_id": data["upload_log_id"],
        "metadata": {
            "title": "Filipina Amateur",
            "tags": ["filipina", "amateur"],
            "original_source_url": "https://example.com/video/1",
        }
    })
print(r3.json())

JavaScript (fetch)

javascript
const API_TOKEN = process.env.XPANDORAX_API_TOKEN;
const BASE = "https://xpandorax.com/api/agent/v2";
const headers = {
  Authorization: `Bearer ${API_TOKEN}`,
  "Content-Type": "application/json",
};

// ── List videos ──
const resp = await fetch(`${BASE}/content/videos`, { headers });
const videos = await resp.json();
console.log(`Found ${videos.length} videos`);

// ── Create a video ──
const createResp = await fetch(`${BASE}/content/videos`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    title: "My Agent Uploaded Video",
    video_url: "https://cdn.example.com/video.mp4",
    tags: ["exclusive", "hd"],
    is_hd: true,
  }),
});
const newVideo = await createResp.json();
console.log("Created:", newVideo.id);

// ── Update producer profile ──
const updateResp = await fetch(`${BASE}/content/producer`, {
  method: "PATCH",
  headers,
  body: JSON.stringify({
    description: "Updated bio from my AI agent",
  }),
});
console.log(await updateResp.json());

// ── Delete a video ──
await fetch(`${BASE}/content/videos/video-uuid`, {
  method: "DELETE",
  headers,
});

Response Formats

Success Response

json
// Single resource
GET /api/agent/v2/content/videos/video-id
{
  "id": "uuid",
  "title": "Video Title",
  "description": "...",
  "slug": "video-title",
  "video_url": "https://...",
  "thumbnail_url": "https://...",
  "tags": ["tag1", "tag2"],
  "categories": ["Category1"],
  "models": [{ "id": "uuid", "name": "Model Name" }],
  "duration": { "hours": 0, "minutes": 12, "seconds": 34 },
  "is_hd": true,
  "views": 1520,
  "status": "published",
  "created_at": "2026-05-18T08:30:00Z",
  "updated_at": "2026-05-18T09:00:00Z"
}

// List response
GET /api/agent/v2/content/videos
{
  "data": [ { ... }, { ... } ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 142,
    "total_pages": 3
  }
}

Error Response

json
{
  "error": {
    "code": "validation_error",
    "message": "Title is required",
    "details": {
      "field": "title",
      "reason": "missing_required_field"
    }
  }
}

Error Codes

400
Bad RequestInvalid request body, missing required fields, or malformed JSON
401
UnauthorizedMissing or invalid API token. Check the Authorization header format.
403
ForbiddenToken does not have the required scope for this action
404
Not FoundThe requested resource (video, model, etc.) does not exist
409
ConflictResource already exists or operation conflicts with current state
422
UnprocessableValidation failed: content violates platform guidelines
429
Rate LimitedToo many requests. Wait for the rate limit window to reset.
500
Server ErrorInternal server error. Contact support if persistent.

Rate Limits

120 requests per minute

Per API token. The rate limit resets every 60 seconds. If you exceed this limit, subsequent requests will return 429 Too Many Requests. Implement exponential backoff with jitter in your agent to handle rate limits gracefully. Contact support if you need a higher limit.

Best Practices

Retry with Backoff

Implement exponential backoff (1s → 2s → 4s → 8s) with jitter for 429 and 5xx responses. This prevents hammering the API during transient failures.

Idempotent Operations

PATCH and DELETE operations are idempotent. Calling PATCH with the same data multiple times produces the same result. Use this to safely retry on network errors.

Content Compliance

Ensure all uploaded content complies with xPandorax's Terms of Service. Gemini validates content metadata in the upload flow — titles should be descriptive, not clickbait. All models must be 18+ verified.

Rate Limit Headers

Monitor response headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. These tell you exactly when the window resets.

Tags & Categories

Use existing categories where possible. Tags are free-form but should be lowercase, single words. Consistent tagging improves content discoverability.

Batch Updates

For bulk operations, batch your requests with reasonable concurrency (5-10 concurrent requests). Each request is independently rate-limited against your token's 120 req/min.

Framework Integrations

DMCA / Content Disputes

To report a content ownership dispute or request a takedown, use the DMCA API endpoint. All reports are reviewed by our admin team within 72 hours.

POST/api/agent/dmca
json
{
  "reporter_name":     "Your Full Legal Name",
  "reporter_email":    "you@example.com",
  "claim_description": "I own this content and did not authorise its upload. [min 50 chars]",
  "video_id":          "optional-video-uuid",
  "original_url":      "https://original-source.com/video"
}

Note: False DMCA claims may result in legal liability. Only submit claims for content you own or represent. xPandorax follows the DMCA safe harbor provisions.

Changelog

2.02026-05-18
  • Complete v2 Content Management API released
  • New endpoints for Producer, Videos, Cuts, Pictures, Models
  • Structured request/response schemas with validation
  • Token scope system for fine-grained access control
  • This documentation site launched
1.12026-03-10
  • Added Gemini content validation on upload confirmation
  • Rate limit increased to 120 req/min
  • Added test-validation endpoint for pre-flight checks
1.02026-01-15
  • Initial AI Agent API release
  • Three-step upload flow (upload-url → B2 → confirm)
  • Daily quota tracking per token

Ready to Connect Your AI Agent?

Head to the AI Department in your Creator Studio to generate your API token and start managing your content programmatically.