Skip to content

GuidesWorking with the API

Uploads

Files go to KeepAnything in three steps: start the upload, put the bytes straight to storage, then complete it. Large files go in parts and can be resumed.

Uploads need a live key with files:write. (Documents for an envelope are different: they’re a single multipart upload.)

1. Start

POST /v1/uploads with the folder (company or a folder id), the file’s name, size and sha256 (64 hex characters). To add a new version of an existing file, send its file_id instead of a folder. The answer is one of three:

  • status: "done" — the account already stores exactly these bytes, so nothing needs to move: you get the file.
  • status: "upload" with one url — a small file (under 8 MB): PUT the whole file there.
  • status: "upload" with parts — PUT each part (part_size bytes, the last one shorter) to its URL. Only the first 100 part URLs come back; ask for more with POST /v1/uploads/{token}/parts and { "numbers": [101, 102, …] }.
curl https://api.skanything.io/v1/uploads \
  -H "Authorization: Bearer $SA_LIVE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "folder_id": "company",
    "name": "Lease agreement.pdf",
    "size": 184320,
    "sha256": "9f2c4e1b7a0d3c5e8f6a2b1d4c7e9f0a3b5d8c1e2f4a6b9d0c3e5f7a1b2c4d6e",
    "content_type": "application/pdf"
  }'

2. Put the bytes

The URLs are pre-signed storage URLs: send the bytes with a plain PUT and no Authorization header. They don’t count toward your rate limit.

curl -X PUT --data-binary @"Lease agreement.pdf" "<url from step 1>"

If you’re interrupted, GET /v1/uploads/{token} tells you which parts have arrived (received) so you only send the rest. The token is good for 7 days.

3. Complete

POST /v1/uploads/{token}/complete checks the size and SHA-256 and adds the file (or its new version). It answers with the file and its version_id. The file opens once the virus check passes — file.version_added tells you when.

  • 409 upload_incomplete — parts are missing (received lists the ones that arrived).
  • 422 size_mismatch — the bytes aren’t the size you announced.
  • 410 invalid_upload — the token expired. Start again.
  • 507 storage_full — the upload would go over your storage.

A new version based on an older one than the current, or on a file someone has locked, is kept as a conflicted copy: conflict says why.