---
name: okfile
description: Upload files and publish static sites through an OkFile-compatible server.
---

# OkFile

## 30-Second Start (CLI)

    pip install --upgrade okfile==2.1.1
    okfile config --server https://sh.pckee.com
    okfile upload ./file.png
    okfile publish ./my-site/

Use this skill when a user asks to publish a local file, return a shareable link, or publish a static site folder.

Set OKFILE_SERVER to the selected server, for example:

    export OKFILE_SERVER="http://your-internal-server:8080"
    export OKFILE_API_KEY="okf_..."

The API key is optional. The same client can target the hosted service by setting OKFILE_SERVER to https://www.okfile.com.

## File upload

1. Call GET $OKFILE_SERVER/api/upload/config.
2. For a file at or below quickUploadMaxSize, send multipart form field file to POST /api/upload/quick.
3. For a larger file, send filename, size, contentType, and optional preferredPartSize to POST /api/upload/prepare.
4. When mode is single, PUT the exact bytes to uploadUrl and call POST /api/upload/complete with id and the optional response ETag.
5. When mode is multipart, upload every entry in parts using its uploadUrl. Send the returned partNumber/ETag pairs to POST /api/upload/complete. If complete returns missingParts, upload only those parts and retry complete.
6. Return url for inline viewing and downloadUrl when download behavior is requested.

Send X-API-Key when OKFILE_API_KEY is set. Optional upload fields are expiresAt (RFC 3339), maxDownloads (positive integer), burnAfterRead (boolean), and storage (one of upload/config storage.profiles[].id). Omit storage to use the server default. For quick upload, send storage as a multipart form field; for prepare, send it as a JSON field.

Quick example:

    curl -X POST "$OKFILE_SERVER/api/upload/quick" \
      -H "X-API-Key: $OKFILE_API_KEY" \
      -F "file=@report.pdf"

## Static Site Hosting

1. Enumerate every file below the folder with its relative path, byte size, and content type.
2. Call POST /api/site/prepare with siteName, optional entryPath, and files.
3. Upload every file through the normal file API.
4. Call POST /api/site/complete with siteId, siteToken, and relativePath/fileId entries.
5. Return the resulting siteUrl.

Do not synthesize an index file. If the folder has no root index.html, omit entryPath; the server returns a directory listing.

## Limits and behavior

- Always discover limits from /api/upload/config.
- Uploads larger than multipartThreshold use resumable multipart uploads when multipart.supported is true.
- Anonymous files expire after the server-configured period and have a download cap.
- API-key uploads persist unless expiresAt is supplied.
- /d/{id} counts against maxDownloads; /i/{id} is the inline/raw URL.
- Hosted video transcoding is advertised as unsupported by the Go server. Upload video as a normal file.

## Errors

API errors are JSON objects with `success=false`. Read `code` for stable program logic, display `message` (or legacy `error`) to the user, and retain `requestId` for server log lookup.

Only retry when `retryable=true`, a network request fails, or an intermediary returns HTTP 429/502/503/504. Honor `retryAfterSeconds` or the `Retry-After` header. Do not retry request, authentication, permission, missing-resource, or payload-limit failures without changing the request.

An error can also include `action`, `hint`, `docs`, and operation-specific recovery fields. For `MULTIPART_INCOMPLETE`, upload only `missingParts`, retain successful part ETags, and retry complete. The server echoes a valid inbound `X-Request-ID` or generates one in both the response header and body.

Example:

    {
      "success": false,
      "error": "Storage is temporarily unavailable",
      "message": "Storage is temporarily unavailable",
      "code": "STORAGE_UNAVAILABLE",
      "retryable": true,
      "retryAfterSeconds": 5,
      "action": "retry",
      "hint": "Retry later and provide requestId to the administrator if it persists.",
      "requestId": "req_8x2k19",
      "timestamp": "2026-10-12T06:00:00Z",
      "docs": "/SKILL.md#errors"
    }
