Skip to main content

Peako Studio API Reference

Complete reference for Peako Studio API. Base URL: https://peako.shin0x.space

All requests require authentication via X-API-Key header or peako_session cookie.


Auth

Sign In with Google

GET /auth/google

Initiates OAuth 2.0 flow with PKCE protection.

Response: 302 redirect to Google consent screen

Example:

curl https://peako.shin0x.space/auth/google
# Redirects to: https://accounts.google.com/o/oauth2/v2/auth?...

Google Callback

GET /auth/google/callback?code=CODE&state=STATE

Completes OAuth flow after user consent.

Response: 302 redirect to dashboard + sets peako_session cookie

Example:

curl https://peako.shin0x.space/auth/google/callback?code=...&state=...
# Sets httpOnly cookie, redirects to /dashboard

Logout

POST /auth/logout

Clears session.

Response: 302 redirect to /login

Example:

curl -X POST -b "peako_session=TOKEN" https://peako.shin0x.space/auth/logout

Templates (Project/Design State)

Get Template

GET /api/templates/:id

Fetch a template's design state.

Response:

{
"id": "template-123",
"name": "My Video",
"compositionState": {
"tracks": [...],
"duration": 10,
"fps": 30,
"width": 1920,
"height": 1080
},
"createdAt": 1708852843000,
"updatedAt": 1708852890000
}

Create Template

POST /api/templates

Create a new template.

Request:

{
"name": "My Video",
"compositionState": {
"tracks": [],
"duration": 10,
"fps": 30,
"width": 1920,
"height": 1080
}
}

Response:

{
"id": "template-123",
"name": "My Video",
"createdAt": 1708852843000
}

Update Template

PUT /api/templates/:id

Save template design state.

Request:

{
"name": "Updated Name",
"compositionState": { ... }
}

Response:

{
"id": "template-123",
"updatedAt": 1708852890000
}

List Templates

GET /api/templates

Get all templates for current user.

Response:

{
"templates": [
{
"id": "template-1",
"name": "Video 1",
"updatedAt": 1708852890000
},
{
"id": "template-2",
"name": "Video 2",
"updatedAt": 1708852800000
}
]
}

Render Jobs

Submit Render Job

POST /api/templates/:id/render

Queue a template for rendering. Returns immediately with job ID.

Request:

{
"quality": "1080p",
"format": "mp4",
"delivery": "async"
}

Params:

  • quality"hd" (720p), "1080p" (default), "4k"
  • format"mp4" (default), "webm", "mov"
  • delivery"response" (binary), "storage" (CDN), "async" (poll)

Response (202 - Queued):

{
"jobId": "job-550e8400-e29b-41d4-a716",
"status": "queued"
}

Get Job Status

GET /api/job/:jobId

Poll rendering job status.

Response:

{
"jobId": "job-550e8400-e29b-41d4-a716",
"status": "queued|active|done|failed",
"progress": 45,
"outputUrl": "https://cdn.example.com/output.mp4",
"error": null,
"createdAt": 1708852843000,
"updatedAt": 1708852890000
}

Statuses:

  • queued — waiting for worker
  • active — rendering now
  • done — complete, outputUrl ready
  • failed — error occurred

Cancel Job

DELETE /api/job/:jobId

Cancel a queued job (cannot cancel active/done/failed).

Response:

{
"jobId": "job-550e8400-e29b-41d4-a716",
"status": "cancelled"
}

Assets

Upload Asset

POST /api/assets/upload

Upload video, audio, or image file.

Request (Multipart):

curl -X POST \
-F "file=@video.mp4" \
-H "X-API-Key: YOUR_KEY" \
https://peako.shin0x.space/api/assets/upload

Response:

{
"url": "https://cdn.example.com/assets/video.mp4",
"filename": "video.mp4",
"mimeType": "video/mp4",
"size": 5242880,
"duration": 120
}

Accepted types: video/mp4, video/webm, video/quicktime, audio/mpeg, audio/wav, image/jpeg, image/png, image/gif


Video Operations

All operations support two input models:

Multipart (local file):

curl -X POST \
-F "file=@video.mp4" \
-F "start=5" \
-F "duration=10" \
https://peako.shin0x.space/api/OPERATION

JSON (URL):

{
"src_url": "https://cdn.example.com/video.mp4",
"start": 5,
"duration": 10
}

Trim

POST /api/trim

Trim video to time range.

Params:

  • start — start time in seconds
  • duration — clip length in seconds
  • output_format"mp4" (default), "webm", "mov"
  • delivery"response", "storage", "async" (default: "response")

Example Request (JSON):

{
"src_url": "https://example.com/video.mp4",
"start": 10,
"duration": 30,
"delivery": "async"
}

Example Response:

{
"jobId": "job-123",
"status": "queued"
}

Merge

POST /api/merge

Concatenate multiple video clips.

Request:

{
"clips": [
{ "src_url": "https://example.com/clip1.mp4" },
{ "src_url": "https://example.com/clip2.mp4" }
],
"delivery": "async"
}

Response:

{
"jobId": "job-123",
"status": "queued"
}

Mute

POST /api/mute

Silence audio or a region of video.

Request:

{
"src_url": "https://example.com/video.mp4",
"muteStart": 5,
"muteDuration": 3,
"delivery": "async"
}

Add Audio

POST /api/add-audio

Mix audio track or background music into video.

Request (JSON):

{
"video_src_url": "https://example.com/video.mp4",
"audio_src_url": "https://example.com/audio.mp3",
"volume": 0.8,
"delivery": "async"
}

Params:

  • volume — 0.0 (mute) to 1.0 (full), default: 1.0

Subtitle

POST /api/subtitle

Burn SRT subtitles into video.

Request:

{
"src_url": "https://example.com/video.mp4",
"srt_url": "https://example.com/captions.srt",
"font_size": 24,
"color": "#FFFFFF",
"position": "bottom",
"delivery": "async"
}

Params:

  • font_size — text size (default: 24)
  • color — hex color (default: "#FFFFFF")
  • position"top", "center", "bottom" (default: "bottom")

Blur

POST /api/blur

Blur regions (faces, text, etc.) in video.

Request:

{
"src_url": "https://example.com/video.mp4",
"regions": [
{
"x": 100,
"y": 100,
"width": 200,
"height": 200,
"blurRadius": 20,
"startTime": 0,
"endTime": 5
}
],
"delivery": "async"
}

Region params:

  • x, y, width, height — pixel coordinates
  • blurRadius — 1–50 (default: 15)
  • startTime, endTime — seconds

Speed Ramp

POST /api/speed-ramp

Apply speed keyframes (slow-mo to speed-up).

Request:

{
"src_url": "https://example.com/video.mp4",
"startSpeed": 0.5,
"endSpeed": 2.0,
"delivery": "async"
}

Params:

  • startSpeed — 0.1–4.0 (default: 1.0)
  • endSpeed — 0.1–4.0 (default: 1.0)
note

Speed-ramp is linear only — cannot do piecewise keypoints. Uses FFmpeg, not Remotion.


Effects & Transitions

Get Effects Catalog

GET /api/effects/catalog

List all available effects.

Response:

{
"effects": [
{
"id": "fade-in",
"name": "Fade In",
"category": "appear",
"description": "Fades clip up from black",
"preview": "https://cdn.example.com/fade-in.png",
"params": [
{
"name": "duration",
"type": "number",
"default": 0.5,
"min": 0.1,
"max": 5.0
}
]
}
]
}

Get Transitions Catalog

GET /api/transitions/catalog

List all available transitions.

Response:

{
"transitions": [
{
"id": "fade",
"name": "Fade",
"description": "Fade from one clip to next",
"preview": "https://cdn.example.com/fade.png",
"duration": [0.2, 2.0]
}
]
}

System

Health Check

GET /health

Check API status.

Response:

{
"status": "ok",
"qsv": true,
"workers": 4,
"queued": 2,
"redis": true
}

Fields:

  • status"ok" or "degraded"
  • qsv — Hardware video encoding available
  • workers — Active job workers
  • queued — Jobs waiting
  • redis — Redis connection OK

User Account

Get Current User

GET /auth/me

Get authenticated user info.

Response:

{
"id": "user-123",
"email": "user@example.com",
"createdAt": 1708852843000
}

Rotate API Key

POST /auth/key/rotate

Invalidate current key, generate new one.

Response:

{
"apiKey": "pk_live_NEW_KEY_HERE",
"rotatedAt": 1708852890000
}

Error Codes

CodeStatusMeaning
AUTH_REQUIRED401Missing or invalid API key/session
INVALID_REQUEST400Bad params or malformed body
FILE_TOO_LARGE413File exceeds size limit (2GB)
NOT_FOUND404Resource not found
CONFLICT409Cannot perform in current state
RENDER_FAILED500Video operation error
INTERNAL_ERROR500Unexpected error

Rate Limiting

  • Global: 100 requests/minute
  • Per User: 20 concurrent jobs
  • Per IP: 1000 requests/hour

Check response headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1708852890

Deprecation

Old v1 routes still work but are deprecated (sunset 2026-07-01):

POST /api/v1/trim       → use POST /api/trim
POST /api/v1/merge → use POST /api/merge
POST /api/v1/mute → use POST /api/mute
etc.

All v1 responses include deprecation headers.