Meet the new Scripe, live on October 7.Register

OpenAPI reference · Uploads

Mint a presigned S3 PUT URL

POST/uploads

Returns a single-use presigned PUT URL the customer uploads bytes to directly. The returned id is an opaque handle (upl_...) the customer then references via POST /v1/sources (type: "file") or POST /v1/knowledge (type: "file") to kick off ingestion.

The URL is bound to the supplied contentType and the Content-Length cap — the customer cannot reuse the URL for a different content type or larger payload.

Required scope: sources:write or knowledge:write — either satisfies, because the same upload feeds both ingest endpoints. (Only the deprecated 2026-08-01 version accepts any valid token here.) Caps differ per content type (audio/video up to 500 MB, image up to 25 MB, application/pdf up to 100 MB, text/plain up to 25 MB).

Bytes are GC'd by an S3 lifecycle rule after 24 hours if never referenced.

Authorization

  • Authorizationstringrequired

    Bearer token in the Authorization header.

    Pass Authorization: Bearer scripe_sk_live_<...> (or scripe_sk_test_<...> for test keys) on every request. Keys are scoped to a single workspace and can be revoked from the Scripe dashboard.

    The same header also accepts an OAuth 2.1 access token (scripe_oat_*); both credentials share one scope vocabulary and every operation below documents the scope it requires. An API key can hold every scope named on this surface except webhooks:manage, which is grantable to OAuth tokens only today — the webhook-endpoint operations answer 403 scope_missing to every API key. Operations that name no scope accept any valid token of the workspace.

Header parameters

  • Scripe-Api-Versionstring

    Pin the API version. Format YYYY-MM-DD. Omit to receive the currently rolling default. Unknown versions return 400 version_unsupported.

  • Idempotency-Keystring

    Opaque string (1–64 chars, [A-Za-z0-9_-]) used to dedup retried writes. Within 24h of the first request, the same key

    • same body returns the original response (Idempotent-Replayed: true). Same key + different body returns 409 idempotency_key_conflict.

    Strongly recommended for every write — see /docs/api/v1/idempotency.

Request bodyapplication/json

  • contentTypestringrequired

    MIME type of the file. Accepted families: audio/*, video/*, image/*, application/pdf, application/msword (+ Office Open XML variants), text/plain. Other types return 422 unprocessable.

  • maxSizeBytesinteger

    Optional. Max bytes the customer expects to upload. Defaults to the per-content-type cap (500 MB for audio/video, 100 MB for PDF, etc.). Customer-supplied values over the cap return 422 unprocessable.

Responses

  • 200

    Presigned URL minted (or replayed via Idempotency-Key).

  • 400

    Malformed request (bad cursor, bad limit, etc.).

  • 401

    Missing, malformed, expired, or revoked API key.

  • 403

    Plan not eligible, scope missing, or workspace mismatch.

  • 409

    Idempotency-Key reused with a different body within the 24h dedup window. Pick a fresh key or replay the original body verbatim.

  • 422

    Body shape was JSON but failed validation (unprocessable).

  • 429

    Sliding-window rate limit exceeded.

  • 503

    Upload subsystem unavailable (AWS creds missing).

Example request

bash
curl --request POST \
  --url 'https://api.scripe.io/v1/uploads' \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "contentType": "audio/mpeg",
  "maxSizeBytes": 0
}'

Example response (200)

json
{
  "data": {
    "id": "upl_acme_workspace_random_id_xxx",
    "uploadUrl": "https://example.com",
    "method": "PUT",
    "contentType": "audio/mpeg",
    "maxSizeBytes": 0,
    "expiresAt": "2026-08-10T09:00:00Z"
  }
}