Developers

Put large-file delivery in the workflow.

Use REST when you want the protocol or MCP when an agent or production tool should drive it. The same workflow is implemented in the pre-release CLI. In every case, file bytes go directly to object storage and never through the API or MCP endpoint.

Authentication

  • Create keys under Dashboard → API keys. Keys look like ysk_ + 40 characters and act as your account — transfers get your plan’s tier and limits.
  • Send the key on every request as Authorization: Bearer ysk_…. Bearer keys are the only auth on /api/v1 and /api/mcp — no cookies, no sessions.
  • The full key is shown once at creation; we store only a keyed hash. Lost it? Revoke and mint a new one — revocation applies immediately.

Every request

curl -s https://yasync.com/api/v1/transfers \
  -H "Authorization: Bearer ysk_a1b2c3d4…"

Remote MCP: the transfer workflow as tools

Connect a Streamable HTTP MCP client to https://yasync.com/api/mcp and send the same Bearer key. The endpoint is stateless and returns JSON. It never accepts file bodies: upload planning tools return presigned URLs, and the client PUTs each part directly to object storage.

Available tools

  • yasync_create_transfer, yasync_list_transfers, yasync_get_transfer
  • yasync_register_file, yasync_plan_upload, yasync_get_upload_status
  • yasync_sign_upload_parts, yasync_complete_upload, yasync_abort_upload
  • yasync_finalize_transfer, yasync_delete_transfer

The tools call the same transfer and upload core as REST, so plan limits, ownership checks, resume state, and API-key rate limits stay consistent.

List the MCP tools

curl -s -X POST https://yasync.com/api/mcp \
  -H "Authorization: Bearer $YASYNC_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Quickstart: send a file in six curls

The whole send pipeline, by hand. Export YASYNC_KEY first, then carry transferId, fileId, and uploadId forward from each response.

  1. 1 — Create a transfer

    Persist one UUID and reuse it on retries. Everything after this hangs off the returned transferId.

    curl -s -X POST https://yasync.com/api/v1/transfers \
      -H "Authorization: Bearer $YASYNC_KEY" \
      -H "Content-Type: application/json" \
      -d "{"creationRequestId":"$TRANSFER_REQUEST_UUID"}"
    # → {"transferId":"cme3k…","slug":"k4q0z7f2m9d1ab","url":"https://yasync.com/t/k4q0z7f2m9d1ab"}
  2. 2 — Register the file

    Name + exact byte size. Persist a key-safe fileId and reuse it on retries. Folder structure travels in the name ("renders/shot_010.mov").

    curl -s -X POST https://yasync.com/api/v1/transfers/$TRANSFER_ID/files \
      -H "Authorization: Bearer $YASYNC_KEY" \
      -H "Content-Type: application/json" \
      -d "{"fileId":"$CLIENT_FILE_ID","name":"big.mov","size":83886080}"
    # → {"fileId":"x7f3…","name":"big.mov","size":"83886080","contentType":"video/quicktime"}
  3. 3 — Plan the upload

    Compute the whole-file CRC32 locally first. Under 100 MiB it is required in the plan because the returned single-PUT URL is checksum-pinned; bigger files switch to per-part CRC32 — see below.

    curl -s -X POST https://yasync.com/api/v1/transfers/$TRANSFER_ID/uploads \
      -H "Authorization: Bearer $YASYNC_KEY" \
      -H "Content-Type: application/json" \
      -d "{"fileId":"x7f3…","fileSize":83886080,"crc32":"$CRC32_HEX"}"
    # → {"uploadId":"u01b…","mode":"s3_single_put","uploadUrl":"https://…","checksumCrc32":"…","requiredHeaders":{"x-amz-checksum-crc32":"…"}, …}
  4. 4 — PUT the bytes

    Straight to object storage — yasync never proxies your file bytes. Send the plan’s Content-Type and every returned requiredHeaders entry.

    curl -s -X PUT "$UPLOAD_URL" \
      -H "Content-Type: video/quicktime" \
      -H "x-amz-checksum-crc32: $CHECKSUM_CRC32_BASE64" \
      --data-binary @big.mov
  5. 5 — Complete the upload

    CRC32 is required again at completion. The server verifies stored size; for multipart it also folds the ordered, R2-validated part checksums and matches the whole-file CRC32.

    curl -s -X POST https://yasync.com/api/v1/transfers/$TRANSFER_ID/uploads/$UPLOAD_ID/complete \
      -H "Authorization: Bearer $YASYNC_KEY" \
      -H "Content-Type: application/json" \
      -d "{"crc32":"$CRC32_HEX"}"
  6. 6 — Finalize → link

    Optional title, message, and bcrypt-hashed password. Transactional email is disabled in development, so distribute the returned link through your own workflow.

    curl -s -X POST https://yasync.com/api/v1/transfers/$TRANSFER_ID/finalize \
      -H "Authorization: Bearer $YASYNC_KEY" \
      -H "Content-Type: application/json" \
      -d '{"title":"Rough cut v4","password":"review-secret"}'
    # → {"url":"https://yasync.com/t/k4q0z7f2m9d1ab"}

Big files: multipart

Files of 100 MiB and up plan as mode: "s3_multipart" with a fixed partSize and partCount. Request presigned part URLs in batches, PUT the parts in parallel with each URL’s requiredHeaders (keep each response’s ETag header), then send them all to /complete. Interrupted? Ask /status which parts already landed and resume from there.

# CRC32 each part locally; base64-encode its four big-endian bytes.
# Sign URLs for parts 1-2 (batch up to 100 per call).
curl -s -X POST https://yasync.com/api/v1/transfers/$TRANSFER_ID/uploads/$UPLOAD_ID/parts \
  -H "Authorization: Bearer $YASYNC_KEY" \
  -H "Content-Type: application/json" \
  -d '{"parts":[{"partNumber":1,"checksumCrc32":"y/Q5Jg=="},{"partNumber":2,"checksumCrc32":"QixqFQ=="}]}'

# PUT each part with its returned x-amz-checksum-crc32 header. Then complete
# with every ETag, the same part checksums, and the required whole-file CRC32.
curl -s -X POST https://yasync.com/api/v1/transfers/$TRANSFER_ID/uploads/$UPLOAD_ID/complete \
  -H "Authorization: Bearer $YASYNC_KEY" \
  -H "Content-Type: application/json" \
  -d '{"parts":[{"partNumber":1,"etag":"\"9b2cf5…\"","checksumCrc32":"y/Q5Jg=="},{"partNumber":2,"etag":"\"41ac00…\"","checksumCrc32":"QixqFQ=="}],"crc32":"3f8d2a1c"}'

Endpoint reference

All byte counts are JSON strings (they can exceed 2⁵³). Timestamps are ISO 8601. Base URL: https://yasync.com.

MethodPathWhat it does
POST/api/v1/transfersCreate a draft; creationRequestId makes retries idempotent
GET/api/v1/transfersList your transfers — cursor pagination via ?limit & ?cursor
GET/api/v1/transfers/{id}Transfer details + completed files
DELETE/api/v1/transfers/{id}Delete a transfer and its files
POST/api/v1/transfers/{id}/filesRegister a file; client fileId makes retries idempotent
POST/api/v1/transfers/{id}/uploadsPlan an upload: checksum-pinned single PUT (< 100 MiB) or multipart
POST/api/v1/transfers/{id}/uploads/{uploadId}/partsBatch-sign checksum-pinned multipart part URLs (up to 100 per call)
POST/api/v1/transfers/{id}/uploads/{uploadId}/completeComplete an upload — validates checksums and stored size
GET/api/v1/transfers/{id}/uploads/{uploadId}/statusUpload state + which parts already landed (resume)
DELETE/api/v1/transfers/{id}/uploads/{uploadId}Abort an upload session
POST/api/v1/transfers/{id}/finalizePublish: draft → live share link (title, message, optional password; email recipient fields are disabled)

Errors

Every non-2xx response has one stable shape. code is machine-matchable and never changes meaning within v1; message is for humans; some errors add a details object (e.g. the exact byte caps).

{
  "error": {
    "code": "FILE_TOO_LARGE",
    "message": "Files on this tier are limited to 50 GB",
    "details": { "maxFileBytes": "53687091200" }
  }
}
CodeMeaning
INVALID_API_KEYMissing, malformed, or revoked key — check the Authorization header
RATE_LIMITEDA rate limit tripped — back off and retry with jitter
NOT_FOUNDThe transfer/upload does not exist or is not yours
BAD_REQUESTMalformed body or parameters (message says what)
TRANSFER_NOT_DRAFTFiles/uploads only work before finalize
FILE_TOO_LARGESingle file exceeds your tier cap
TRANSFER_TOO_LARGEWhole transfer exceeds your tier cap
FILE_SIZE_MISMATCHfileSize differs from the registered file
UPLOAD_ALREADY_ACTIVEA session for this file is live — abort it before re-planning
UPLOAD_NOT_ACTIVESession already completed/cancelled/failed
UPLOAD_SESSION_EXPIREDSession outlived its 48 h window — plan again
UPLOADS_IN_PROGRESSFinish or abort open uploads before finalizing
NO_COMPLETED_FILESFinalize needs at least one completed file
STORAGE_LIMITStorage cap reached with auto-scale off — enable auto-scale or free space

Rate limits

  • 120 requests / minute per key across all v1 endpoints.
  • 60 transfer creations / 10 minutes per key — batch files into one transfer instead of one transfer per file.
  • 120 part-signing calls / minute per upload, each signing up to 100 part URLs — that comfortably feeds thousands of parallel parts.
  • Hitting a limit returns 429 with code: "RATE_LIMITED" — back off and retry. Presigned PUTs to storage are not rate-limited by us.

CLI protocol preview

The tested yasync engine supports parallel multipart, persisted resume state, local CRC32, and machine-readable --json output. The command surface below is implemented, but no public binary or install script is published yet.

yasync login                          # link this machine to your account (browser approval)
yasync send big.mov --json            # create → upload → finalize; prints the link
yasync send renders/ --title "Night renders" --password s3cret
yasync receive https://yasync.com/t/k4q0z7f2m9d1ab -o ~/Downloads
yasync ls                             # your transfers
yasync rm cme3k…                      # delete one
Desktop & CLI release status

Signed Apple Silicon macOS preview verified locally; public packages are pending.

Versioning & stability

  • /api/v1 is stable: fields are only ever added. Anything breaking ships as /api/v2 — v1 keeps working.
  • /api/mcp is a thin adapter over the same v1 transfer core. Tool schemas may gain optional fields, but existing tool names and required inputs remain compatible.
  • Write clients that tolerate unknown JSON fields and match errors by error.code, not message text.
  • An OpenAPI spec is on the roadmap for a later release; this page is the v1 reference for now.