Captioning API · v1

Picute Captioning API

Gửi một URL video, nhận về MP4 có phụ đề, tệp SRT và JSON bản chép lời qua liên kết tải xuống có chữ ký.

Quick start

Pick your language. The snippets below submit a caption job using your API key — replace the placeholder before running.

curl -X POST https://picute.net/api/v1/captions \
  -H "Authorization: Bearer pk_live_<your_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "video_url": "https://cdn.example.com/clip.mp4",
    "language": "ko",
    "target_language": "en",
    "preset": "default",
    "style": { "font_size": 28, "position": "bottom_center", "outline_width": 2 },
    "callback_url": "https://api.your-app.com/webhooks/picute"
  }'

Tham chiếu API

Tất cả endpoint, schema yêu cầu và mã lỗi của v1. Nhấp vào bất kỳ thao tác nào để mở rộng schema yêu cầu và phản hồi.

Webhooks

Picute POSTs a signed webhook to your callback URL when a caption job reaches a terminal state. Verify the X-Picute-Signature header before trusting the payload.

Events

  • caption.completed — Caption job succeeded. data is the full Caption resource with output URLs populated.
  • caption.failed — Caption job failed. data.error has the error code and message.

Verify the signature

Compute HMAC-SHA256 over the raw 'timestamp.body' string with your webhook secret, then constant-time compare against the hex digest in the v1= field of the header.

import hmac, hashlib, time
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = "<your_webhook_secret_from_dashboard>"
TOLERANCE_SECONDS = 5 * 60

def verify(req):
    header = req.headers.get("X-Picute-Signature", "")
    parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
    timestamp = int(parts.get("t", "0"))
    received = parts.get("v1", "")
    if abs(time.time() - timestamp) > TOLERANCE_SECONDS:
        return False
    signed = f"{timestamp}.{req.get_data(as_text=True)}".encode("utf-8")
    expected = hmac.new(SECRET.encode("utf-8"), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, received)

@app.post("/webhooks/picute")
def hook():
    if not verify(request):
        abort(401)
    payload = request.get_json()
    print(payload["event"], payload["data"]["id"])
    return "", 204