Skip to main content

Video Operations

Standalone FFmpeg-powered video processing endpoints. Each operation is stateless and synchronous — it processes the input file and immediately returns the output (or a presigned URL/job ID depending on delivery mode).

All routes require authentication via x-api-key header.


Delivery Modes

Every video operation supports three delivery modes. Choose based on your use case:

response

Binary video stream returned directly in the HTTP response body.

  • Behavior: Synchronous. Request blocks until processing completes.
  • Response headers: Content-Type: video/mp4 (or video/webm, video/quicktime), Content-Disposition: attachment; filename="UUID.ext"
  • Status code: 200
  • When to use: Caller wants the processed video immediately. Best for small files (under 50 MB) and simple request-response flows.
  • Cleanup: Output is stored in tmpfs and cleaned by the stale-dir watcher after the stream is consumed.

Example response:

HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Disposition: attachment; filename="a7f3c1e0.mp4"
Content-Length: 15728640

[binary video data...]

storage

Output uploaded to MinIO peako-output bucket. A presigned URL (24h TTL) returned in JSON.

  • Behavior: Synchronous. Request blocks until processing completes AND the file is uploaded to MinIO.
  • Response body:
    {
    "url": "https://peako-minio.shin0x.space/peako-output/peako-op-output-uuid.mp4?X-Amz-Algorithm=...",
    "expires_at": "2026-03-20T15:30:00Z"
    }
  • Status code: 200
  • MinIO cleanup: Object is automatically deleted after the HTTP response is fully sent (fire-and-forget). Client must download within 24h — in practice, the URL becomes invalid once the response lifecycle ends.
  • When to use: Caller wants a URL to pass to another step (e.g., n8n workflow chaining). The presigned URL hostname is peako-minio.shin0x.space and can be passed directly as url / urls to another ops route without re-uploading.

Example response:

{
"url": "https://peako-minio.shin0x.space/peako-output/peako-op-output-uuid.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...",
"expires_at": "2026-03-20T15:30:00Z"
}

async

Job enqueued in BullMQ. Returns a job ID immediately. Caller polls GET /api/jobs/:id for status.

  • Behavior: Non-blocking. Returns immediately with a job ID. Processing happens in a background worker queue.
  • Response body:
    {
    "job_id": "550e8400-e29b-41d4-a716-446655440000",
    "status": "queued",
    "promoted_to_async": true // only present if auto-promoted from response/storage due to file size
    }
  • Status code: 202 (Accepted)
  • Auto-promotion: Files >50 MB are automatically promoted to async regardless of requested delivery mode. Response includes promoted_to_async: true.
  • Queue limit: Max 20 concurrent async jobs per user. Returns 429 if limit reached.
  • When to use: Large files (>50 MB), or when caller does not need immediate response. Poll GET /api/jobs/:id to check status and retrieve the output URL.

Example response:

{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "queued"
}

Chaining Operations

Ops routes can be chained: pass the url from a storage delivery response as url/urls to another route.

How it works: Peako detects URLs from peako-minio.shin0x.space and routes them through the MinIO SDK client directly, bypassing DNS. This works from within Docker where peako-minio.shin0x.space does not resolve over the network.

Example chain:

1. POST /api/trim (delivery: storage) → returns url
2. POST /api/blur (url: [url from step 1], delivery: storage) → returns new url
3. POST /api/merge (urls: [[url from step 2], other_url], delivery: response) → returns binary video

URL Parameter Security

All url and urls parameters are validated by both SSRF DNS check AND CDN hostname allowlist:

  • SSRF Check: Verifies the domain resolves to a public IP (no private IPs, localhost, or link-local addresses)
  • CDN Allowlist: Verifies the hostname is in the allowlisted CDN providers (*.b-cdn.net, *.bunnycdn.com, *.shin0x.space, *.r2.dev, peako-minio.shin0x.space, etc.)

Both checks must pass. URLs must be publicly resolvable AND come from trusted CDN providers.


POST /api/trim

Trim a video to a time range. Returns only the [start, start+duration] segment.

Authentication: Required (x-api-key header)

Request — Model A (Multipart)

curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@input.mp4" \
-F "start=5" \
-F "duration=10" \
-F "output_format=mp4" \
-F "delivery=response" \
https://peako.shin0x.space/api/trim
FieldTypeRequiredDefaultNotes
filebinaryVideo file. Max 2 GB.
startnumberStart time in seconds. Must be ≥ 0.
durationnumberDuration in seconds. Must be > 0. Clamped to remaining video length.
output_formatstringmp4mp4, mov, webm
deliverystringresponseresponse, storage, async

Request — Model B (JSON)

curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/video.mp4",
"start": 5,
"duration": 10,
"output_format": "mp4",
"delivery": "response"
}' \
https://peako.shin0x.space/api/trim
FieldTypeRequiredDefaultNotes
urlstringVideo URL. Must pass SSRF DNS check AND CDN allowlist validation. Accepts peako-minio.shin0x.space presigned URLs.
startnumberStart time in seconds. Must be ≥ 0.
durationnumberDuration in seconds. Must be > 0.
output_formatstringmp4mp4, mov, webm
deliverystringresponseresponse, storage, async

Response Examples

delivery: response (200 OK)

HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Disposition: attachment; filename="a7f3c1e0.mp4"
Content-Length: 5242880

[binary video data]

delivery: storage (200 OK)

{
"url": "https://peako-minio.shin0x.space/peako-output/peako-op-output-uuid.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&...",
"expires_at": "2026-03-20T15:30:00Z"
}

delivery: async (202 Accepted)

{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "queued"
}

POST /api/merge

Concatenate 2–10 video clips into a single file. Optional fade transition between clips.

Authentication: Required (x-api-key header)

Request — Model A (Multipart)

curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@intro.mp4" \
-F "file=@main.mp4" \
-F "file=@outro.mp4" \
-F "transition=fade" \
-F "transition_duration=0.5" \
-F "output_format=mp4" \
-F "delivery=response" \
https://peako.shin0x.space/api/merge
FieldTypeRequiredDefaultNotes
file[0..9]binary✅ (min 2)Video files. Any field name accepted. Min 2, max 10. Each max 2 GB. Iterated in upload order.
transitionstringnonenone, fade
transition_durationnumber0.5Transition duration in seconds. Only applies when transition != none.
output_formatstringmp4mp4, mov, webm
deliverystringresponseresponse, storage, async

Request — Model B (JSON)

curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"urls": [
"https://example.com/intro.mp4",
"https://example.com/main.mp4",
"https://example.com/outro.mp4"
],
"transition": "fade",
"transition_duration": 0.5,
"output_format": "mp4",
"delivery": "response"
}' \
https://peako.shin0x.space/api/merge
FieldTypeRequiredDefaultNotes
urlsarrayArray of video URLs. Min 2, max 10. Each URL must pass SSRF DNS check AND CDN allowlist validation. Accepts peako-minio.shin0x.space presigned URLs.
transitionstringnonenone, fade
transition_durationnumber0.5Transition duration in seconds.
output_formatstringmp4mp4, mov, webm
deliverystringresponseresponse, storage, async

Edge Case Example: MinIO URL Chaining

# Trim video → storage (get URL) → Merge that URL with another video
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"urls": [
"https://peako-minio.shin0x.space/peako-output/peako-op-output-uuid.mp4?X-Amz-Algorithm=...",
"https://example.com/outro.mp4"
],
"transition": "fade",
"delivery": "response"
}' \
https://peako.shin0x.space/api/merge

The first URL from a previous trim operation is passed directly as urls[0]. Peako detects the peako-minio.shin0x.space hostname and routes it through MinIO SDK.

Response Examples

delivery: response (200 OK)

HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Disposition: attachment; filename="merged-video.mp4"
Content-Length: 52428800

[binary video data]

delivery: storage (200 OK)

{
"url": "https://peako-minio.shin0x.space/peako-output/merged-uuid.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&...",
"expires_at": "2026-03-20T16:00:00Z"
}

POST /api/blur

Apply blur to a video — either full-frame or a specific rectangular region.

Authentication: Required (x-api-key header)

Request — Model A (Multipart)

# Full-frame blur
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@input.mp4" \
-F "intensity=20" \
-F "output_format=mp4" \
-F "delivery=response" \
https://peako.shin0x.space/api/blur
# Regional blur (face, license plate, etc.)
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@input.mp4" \
-F "intensity=20" \
-F "region={\"x\":100,\"y\":50,\"width\":200,\"height\":100}" \
-F "delivery=response" \
https://peako.shin0x.space/api/blur
FieldTypeRequiredDefaultNotes
filebinaryVideo file. Max 2 GB.
intensitynumber15Blur strength. Min 1, max 100. Maps to FFmpeg boxblur radius.
regionJSONBlur region (JSON string in multipart): {"x": 100, "y": 50, "width": 200, "height": 100}. All values in pixels. Omit for full-frame blur.
output_formatstringmp4mp4, mov, webm
deliverystringresponseresponse, storage, async

Request — Model B (JSON)

# Full-frame blur
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/video.mp4",
"intensity": 20,
"delivery": "response"
}' \
https://peako.shin0x.space/api/blur
# Regional blur
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/video.mp4",
"intensity": 20,
"region": {
"x": 100,
"y": 50,
"width": 200,
"height": 100
},
"delivery": "response"
}' \
https://peako.shin0x.space/api/blur
FieldTypeRequiredDefaultNotes
urlstringVideo URL. Must pass SSRF DNS check AND CDN allowlist validation. Accepts peako-minio.shin0x.space presigned URLs.
intensitynumber15Blur strength. Min 1, max 100.
regionobjectBlur region: { x: number, y: number, width: number, height: number }. All values in pixels, non-negative. Omit for full-frame.
output_formatstringmp4mp4, mov, webm
deliverystringresponseresponse, storage, async

POST /api/mute

Remove audio track from a video. Output is video-only, no audio.

Authentication: Required (x-api-key header)

Request — Model A (Multipart)

curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@input.mp4" \
-F "output_format=mp4" \
-F "delivery=response" \
https://peako.shin0x.space/api/mute
FieldTypeRequiredDefaultNotes
filebinaryVideo file. Max 2 GB.
output_formatstringmp4mp4, mov, webm
deliverystringresponseresponse, storage, async

Request — Model B (JSON)

curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/video.mp4",
"delivery": "response"
}' \
https://peako.shin0x.space/api/mute
FieldTypeRequiredDefaultNotes
urlstringVideo URL. Must pass SSRF DNS check AND CDN allowlist validation. Accepts peako-minio.shin0x.space presigned URLs.
output_formatstringmp4mp4, mov, webm
deliverystringresponseresponse, storage, async

Response Example

delivery: response (200 OK)

HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Disposition: attachment; filename="muted-video.mp4"
Content-Length: 15728640

[binary video data — audio track removed]

POST /api/add-audio

Mix or replace audio track on a video. Supports two modes: mix (blend original + new audio) or replace (strip original, use new audio only).

Authentication: Required (x-api-key header)

Request — Model A (Multipart)

# Mix mode: blend video audio + new audio
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@video.mp4" \
-F "audio=@music.mp3" \
-F "mode=mix" \
-F "audio_volume=0.8" \
-F "video_volume=0.5" \
-F "delivery=response" \
https://peako.shin0x.space/api/add-audio
# Replace mode: use new audio only
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@video.mp4" \
-F "audio=@voiceover.mp3" \
-F "mode=replace" \
-F "delivery=response" \
https://peako.shin0x.space/api/add-audio
# Audio from URL
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@video.mp4" \
-F "audio_url=https://example.com/music.mp3" \
-F "mode=mix" \
-F "delivery=response" \
https://peako.shin0x.space/api/add-audio
FieldTypeRequiredDefaultNotes
filebinaryVideo file. Max 2 GB.
audiobinary✅ (or audio_url)Audio file (mp3, aac, wav, ogg, flac). Field name must be exactly audio.
audio_urlstring✅ (or audio)URL of audio file as a form text field. Must pass SSRF DNS check AND CDN allowlist validation. Accepts peako-minio.shin0x.space presigned URLs.
modestringmixmix (blend audio), replace (strip original)
audio_volumenumber1.0Volume multiplier for new audio. Min 0, max 2. (1.0 = normal, 0.5 = half, 2.0 = double)
video_volumenumber1.0Volume multiplier for original video audio. Only applies in mix mode. Min 0, max 2.
output_formatstringmp4mp4, mov, webm
deliverystringresponseresponse, storage, async

Request — Model B (JSON)

curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/video.mp4",
"audio_url": "https://example.com/music.mp3",
"mode": "mix",
"audio_volume": 0.8,
"video_volume": 0.5,
"delivery": "response"
}' \
https://peako.shin0x.space/api/add-audio
FieldTypeRequiredDefaultNotes
urlstringVideo URL. Must pass SSRF DNS check AND CDN allowlist validation.
audio_urlstringAudio file URL. Must pass SSRF DNS check AND CDN allowlist validation. Accepts peako-minio.shin0x.space presigned URLs.
modestringmixmix, replace
audio_volumenumber1.0Volume multiplier for new audio. Min 0, max 2.
video_volumenumber1.0Volume multiplier for original video audio (mix mode only). Min 0, max 2.
output_formatstringmp4mp4, mov, webm
deliverystringresponseresponse, storage, async

POST /api/speed-ramp

Apply variable playback speed to a video using keypoints. Each keypoint defines the playback speed at a specific time.

Authentication: Required (x-api-key header)

note

Important: The speed-ramp route hardcodes output to mp4. No output_format parameter is accepted or needed. This is a known inconsistency with other ops routes.

Request — Model A (Multipart)

curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@input.mp4" \
-F "keypoints=[{\"timeSec\":0,\"speed\":1.0},{\"timeSec\":5,\"speed\":2.0},{\"timeSec\":10,\"speed\":1.0}]" \
-F "delivery=response" \
https://peako.shin0x.space/api/speed-ramp
FieldTypeRequiredDefaultNotes
filebinaryVideo file. Max 2 GB.
keypointsJSONJSON array: [{timeSec: number, speed: number}]. Sorted by timeSec ascending. Min 1 keypoint. Speed range: 0.25–4.0.
deliverystringresponseresponse, storage, async

Request — Model B (JSON)

curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/video.mp4",
"keypoints": [
{ "timeSec": 0, "speed": 0.5 },
{ "timeSec": 5, "speed": 1.0 },
{ "timeSec": 10, "speed": 2.0 }
],
"delivery": "response"
}' \
https://peako.shin0x.space/api/speed-ramp
FieldTypeRequiredDefaultNotes
urlstringVideo URL. Must pass SSRF DNS check AND CDN allowlist validation. Accepts peako-minio.shin0x.space presigned URLs.
keypointsarrayArray of { timeSec: number, speed: number }. Min 1 keypoint. Sorted by timeSec. Speed: 0.25–4.0.
deliverystringresponseresponse, storage, async

Keypoint Format

FieldTypeNotes
timeSecnumberPosition in the source video (seconds).
speednumberPlayback speed multiplier. 0.25 = quarter speed (slow-mo), 1.0 = normal, 2.0 = double speed (fast-forward). Range: 0.25–4.0.
warning

FFmpeg uses piecewise linear interpolation between keypoints. Audio pitch is adjusted to match speed changes.

Response Example

delivery: response (200 OK)

HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Disposition: attachment; filename="speed-ramped.mp4"
Content-Length: 8388608

[binary video data — variable speed applied]

POST /api/subtitle

Burn subtitle text permanently into video frames (hard-coded captions). Accepts SRT or ASS format.

Authentication: Required (x-api-key header)

danger

The subtitle_url parameter only accepts URLs from peako.shin0x.space/assets/*. This is intentional security design. External subtitle hosting is not supported. Use the /api/transcribe endpoint to generate subtitle SRT files stored at peako.shin0x.space/assets/*.

Request — Model A (Multipart)

# Subtitle file upload
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@video.mp4" \
-F "subtitle=@captions.srt" \
-F "output_format=mp4" \
-F "delivery=response" \
https://peako.shin0x.space/api/subtitle
# Subtitle file upload — ASS format
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@video.mp4" \
-F "subtitle=@captions.ass" \
-F "format=ass" \
-F "delivery=response" \
https://peako.shin0x.space/api/subtitle
FieldTypeRequiredDefaultNotes
filebinaryVideo file. Max 2 GB.
subtitlebinary✅ (or subtitle_url)Subtitle file (.srt or .ass). Field name must be exactly subtitle. Format auto-detected from extension.
subtitle_urlstring✅ (or subtitle)URL of subtitle file as a form text field. Must be peako.shin0x.space/assets/* URL only.
formatstringOverride auto-detected format (srt or ass). Rarely needed.
output_formatstringmp4mp4, mov, webm
deliverystringresponseresponse, storage, async

Request — Model B (JSON)

# Subtitle from peako.shin0x.space/assets/ (auto-transcribe output)
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/video.mp4",
"subtitle_url": "https://peako.shin0x.space/assets/srt-uuid.srt",
"delivery": "response"
}' \
https://peako.shin0x.space/api/subtitle
FieldTypeRequiredDefaultNotes
urlstringVideo URL. Must pass SSRF DNS check AND CDN allowlist validation.
subtitle_urlstringSubtitle URL. Must be peako.shin0x.space/assets/* only. This is where auto-transcribe SRT files are stored.
formatstringOverride auto-detected format (srt or ass).
output_formatstringmp4mp4, mov, webm
deliverystringresponseresponse, storage, async

Subtitle File Formats

SRT (SubRip)

1
00:00:00,000 --> 00:00:02,500
First subtitle text

2
00:00:02,500 --> 00:00:05,000
Second subtitle text

ASS (Advanced SubStation Alpha)

[Script Info]
Title: My Subtitles

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,0,2,0,0,0,1

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.00,0:00:02.50,Default,,0,0,0,,First subtitle text
Dialogue: 0,0:00:02.50,0:00:05.00,Default,,0,0,0,,Second subtitle text

Response Example

delivery: response (200 OK)

HTTP/1.1 200 OK
Content-Type: video/mp4
Content-Disposition: attachment; filename="subtitled-video.mp4"
Content-Length: 20971520

[binary video data — subtitles burned into frames]

Error Codes Reference

All video operations return standard HTTP error codes with JSON error bodies:

{
"error": "ERROR_CODE",
"message": "Human-readable error message"
}

Common Errors (All Routes)

StatusCodeMeaningOccurs When
400VALIDATION_ERRORRequest body validation failedMissing required field, wrong type, invalid value
400INPUT_MISSINGRequired input field missingurl/urls not provided
400FILE_VALIDATION_ERRORFile upload failedWrong MIME type, corrupted file header, not a valid video/audio file
400WRONG_FIELD_NAMEField name errorUsing singular url instead of urls for merge endpoint
413PAYLOAD_TOO_LARGEFile too largeExceeds 2 GB limit per file
429RATE_LIMITEDQueue limit reachedMax 20 concurrent async jobs per user (async delivery mode only)
502BAD_GATEWAYDownload failedFailed to fetch video from url or audio from audio_url

Route-Specific Errors

POST /api/trim | POST /api/blur | POST /api/mute | POST /api/add-audio | POST /api/speed-ramp | POST /api/subtitle

StatusCodeMeaningExample
500RENDER_FAILEDFFmpeg operation failed"Trim render failed: codec not supported"

POST /api/merge

StatusCodeNotes
400MINIMUM_FILES_REQUIREDAt least 2 files required
400MAXIMUM_FILES_EXCEEDEDMaximum 10 files for merge
400USE_URLS_ARRAYSingular url used instead of array urls

POST /api/add-audio

StatusCodeNotes
400AUDIO_FIELD_MISSINGNeither audio file nor audio_url provided
400AUDIO_VALIDATION_FAILEDAudio file validation failed (not mp3/aac/wav/ogg/flac)

POST /api/subtitle

StatusCodeNotes
400SUBTITLE_FIELD_MISSINGNeither subtitle file nor subtitle_url provided
400SUBTITLE_PARSE_FAILEDSRT/ASS file could not be parsed
400SUBTITLE_NO_ENTRIESSubtitle file contains no valid cue entries
400SUBTITLE_URL_INVALIDsubtitle_url is not a peako.shin0x.space/assets/* URL

Edge Case Examples

Example 1: MinIO URL Chaining (storage → storage)

Trim a video and store it, then use that output as input to merge:

# Step 1: Trim → storage
TRIM_RESPONSE=$(curl -s -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/long-video.mp4",
"start": 0,
"duration": 30,
"delivery": "storage"
}' \
https://peako.shin0x.space/api/trim)

TRIMMED_URL=$(echo $TRIM_RESPONSE | jq -r '.url')

# Step 2: Merge that URL with another video
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"urls\": [
\"$TRIMMED_URL\",
\"https://example.com/outro.mp4\"
],
\"delivery\": \"response\"
}" \
https://peako.shin0x.space/api/merge

Example 2: File Type Error (Wrong MIME Type)

# ❌ WRONG: Uploading a text file as video
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@notes.txt" \
https://peako.shin0x.space/api/trim

# Response: 400 Bad Request
# {
# "error": "FILE_VALIDATION_ERROR",
# "message": "File validation failed: expected video/mp4, got text/plain"
# }

Example 3: Missing Required Field

# ❌ WRONG: No start/duration for trim
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/video.mp4"
}' \
https://peako.shin0x.space/api/trim

# Response: 400 Bad Request
# {
# "error": "VALIDATION_ERROR",
# "message": "Validation failed: start is required (required)"
# }

Example 4: URL Security Validation

# ❌ WRONG: Private IP address (SSRF blocked)
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "http://192.168.1.100/video.mp4",
"delivery": "response"
}' \
https://peako.shin0x.space/api/trim

# Response: 400 Bad Request
# {
# "error": "VALIDATION_ERROR",
# "message": "URL fails SSRF check: private IP not allowed"
# }
# ❌ WRONG: Non-allowlisted CDN domain
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://random-cdn.com/video.mp4",
"delivery": "response"
}' \
https://peako.shin0x.space/api/trim

# Response: 400 Bad Request
# {
# "error": "VALIDATION_ERROR",
# "message": "URL fails CDN allowlist check: domain not in allowlist"
# }

Example 5: File Size Over Limit

# ❌ WRONG: 5 GB file (over 2 GB limit)
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@huge-video.mp4" \
https://peako.shin0x.space/api/trim

# Response: 413 Payload Too Large
# {
# "error": "PAYLOAD_TOO_LARGE",
# "message": "File too large. Maximum upload size is 2 GB."
# }

Example 6: Auto-Promotion to Async (File >50 MB)

# POST with delivery: response but file is 100 MB
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-F "file=@large-video.mp4" \
-F "start=0" \
-F "duration=10" \
-F "delivery=response" \
https://peako.shin0x.space/api/trim

# Response: 202 Accepted (auto-promoted to async)
# {
# "job_id": "550e8400-e29b-41d4-a716-446655440000",
# "status": "queued",
# "promoted_to_async": true
# }

Example 7: Merge Array Bounds

# ❌ WRONG: Only 1 file (min 2 required)
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"urls": ["https://example.com/video.mp4"],
"delivery": "response"
}' \
https://peako.shin0x.space/api/merge

# Response: 400 Bad Request
# {
# "error": "MINIMUM_FILES_REQUIRED",
# "message": "At least 2 files required for merge"
# }
# ❌ WRONG: 15 files (max 10)
curl -X POST \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"urls": [
"https://example.com/1.mp4",
"https://example.com/2.mp4",
... (13 more) ...
],
"delivery": "response"
}' \
https://peako.shin0x.space/api/merge

# Response: 400 Bad Request
# {
# "error": "MAXIMUM_FILES_EXCEEDED",
# "message": "Maximum 10 files for merge"
# }

Next: Effects & Transitions