F fyuhls
API Reference

fyuhls Public API

The current /api/v1/ API is designed for account-bound integrations. It supports personal API tokens, direct-to-storage multipart uploads, a managed upload shortcut for desktop tools, owner-scoped file metadata (including public share fields), and application-controlled download links that keep delivery policy inside fyuhls.

Version: v1
Auth: Bearer token or X-API-Token
Uploads: Multipart direct-to-storage
Downloads: Signed app links

Overview

The API follows the same quota, package, visibility, storage, and delivery rules used by the main application. A token belongs to a specific user, so uploads created with that token appear in that user's account and count against that user's quota.

  • Uploads are account-bound.
  • Quota is reserved before upload completion.
  • Large files should stay on the multipart direct-to-storage path.
  • Download links stay application-controlled so fyuhls can decide CDN, signed-origin, or tracked delivery.
  • Public file metadata includes the same share fields shown on the download page.

Authentication

Headers

Authorization: Bearer fyu_your_token_here
X-API-Token: fyu_your_token_here

Notes

  • Token requests do not require CSRF.
  • Browser-session calls still work for the web UI.
  • Revoking a token blocks future API use immediately.

Tokens and Scopes

Users create personal API tokens from the account settings page. Tokens are displayed once, stored hashed, and can be revoked without changing the user's password.

Scope Purpose
files.upload Create upload sessions, sign parts, report parts, complete, and abort multipart uploads.
files.read Read file metadata (including public share fields) and request application-controlled download links.

Idempotency

Upload creation and completion support Idempotency-Key or X-Idempotency-Key so clients can safely retry after a timeout or dropped response.

Idempotency-Key: desktop-client-42e0f2f4-1
  • Completed responses are replayed when the same key and payload are reused.
  • In-flight duplicates return 409.
  • Reusing the same key with a different payload is rejected.

Endpoint Map

Method Endpoint Purpose
POST/api/v1/uploads/sessionsCreate a multipart upload session.
POST/api/v1/uploads/managedCreate a session and return signed part URLs in one response.
GET/api/v1/uploads/sessions/{id}Inspect an upload session for resume or retry.
POST/api/v1/uploads/sessions/{id}/parts/signRequest signed URLs for specific part numbers.
POST/api/v1/uploads/sessions/{id}/parts/reportReport a successful part upload and its ETag.
POST/api/v1/uploads/sessions/{id}/completeFinalize the upload and create the file record.
POST/api/v1/uploads/sessions/{id}/abortAbort a multipart session and release reservation state.
GET/api/v1/files/{id}Return owner-scoped file metadata, including public share fields.
GET/api/v1/downloads/{id}/linkReturn an application-signed download link.

Code Samples

These examples start with the managed-upload shortcut because it is the easiest public integration path. Replace the domain, token, file IDs, and folder IDs with your own values.

curl: create a managed upload

curl -X POST "https://your-site.example/api/v1/uploads/managed" \
  -H "Authorization: Bearer fyu_your_token_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: desktop-upload-001" \
  -d '{
    "filename": "archive.iso",
    "size": 10737418240,
    "mime_type": "application/octet-stream",
    "folder_id": 123,
    "part_numbers": [1, 2, 3],
    "expires_in": 3600
  }'

curl: get file metadata

curl "https://your-site.example/api/v1/files/123" \
  -H "Authorization: Bearer fyu_your_token_here"

curl: get a download link

curl "https://your-site.example/api/v1/downloads/123/link" \
  -H "Authorization: Bearer fyu_your_token_here"

PHP: create a managed upload

<?php
$payload = [
    'filename' => 'archive.iso',
    'size' => 10737418240,
    'mime_type' => 'application/octet-stream',
    'folder_id' => 123,
    'part_numbers' => [1, 2, 3],
    'expires_in' => 3600,
];

$ch = curl_init('https://your-site.example/api/v1/uploads/managed');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer fyu_your_token_here',
        'Content-Type: application/json',
        'Idempotency-Key: desktop-upload-001',
    ],
    CURLOPT_POSTFIELDS => json_encode($payload),
]);

$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

var_dump($status, json_decode($response, true));

Node.js: create a managed upload

const response = await fetch('https://your-site.example/api/v1/uploads/managed', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer fyu_your_token_here',
    'Content-Type': 'application/json',
    'Idempotency-Key': 'desktop-upload-001'
  },
  body: JSON.stringify({
    filename: 'archive.iso',
    size: 10737418240,
    mime_type: 'application/octet-stream',
    folder_id: 123,
    part_numbers: [1, 2, 3],
    expires_in: 3600
  })
});

const data = await response.json();
console.log(response.status, data);

End-to-end multipart example

1. Create the upload session

curl -X POST "https://your-site.example/api/v1/uploads/sessions" \
  -H "Authorization: Bearer fyu_your_token_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: upload-session-001" \
  -d '{
    "filename": "archive.iso",
    "size": 10737418240,
    "mime_type": "application/octet-stream",
    "folder_id": 123
  }'

2. Request a signed URL for part 1

curl -X POST "https://your-site.example/api/v1/uploads/sessions/ups_ab12cd34ef56/parts/sign" \
  -H "Authorization: Bearer fyu_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "part_numbers": [1],
    "expires_in": 3600
  }'

3. Upload that part directly to object storage

curl -X PUT "https://signed-storage-url-from-step-2" \
  -H "Content-Type: application/octet-stream" \
  --data-binary "@archive.part1"

4. Report the completed part back to fyuhls

curl -X POST "https://your-site.example/api/v1/uploads/sessions/ups_ab12cd34ef56/parts/report" \
  -H "Authorization: Bearer fyu_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "part_number": 1,
    "etag": "\"etag-returned-by-storage\"",
    "part_size": 67108864
  }'

5. Complete the upload after all parts are reported

curl -X POST "https://your-site.example/api/v1/uploads/sessions/ups_ab12cd34ef56/complete" \
  -H "Authorization: Bearer fyu_your_token_here" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: upload-complete-001" \
  -d '{
    "checksum_sha256": "optional-final-sha256"
  }'

Managed Upload

POST /api/v1/uploads/managed is the easiest entry point for desktop tools. It creates the upload session and returns signed part URLs in one call.

Example request

{
  "filename": "archive.iso",
  "size": 10737418240,
  "mime_type": "application/octet-stream",
  "folder_id": 123,
  "part_numbers": [1, 2, 3],
  "expires_in": 3600
}

Example response

{
  "status": "ok",
  "session": {
    "public_id": "ups_ab12cd34ef56",
    "status": "pending"
  },
  "part_size_bytes": 67108864,
  "parts": [
    {
      "part_number": 1,
      "method": "PUT",
      "url": "https://..."
    }
  ],
  "complete_url": "/api/v1/uploads/sessions/ups_ab12cd34ef56/complete",
  "report_part_url": "/api/v1/uploads/sessions/ups_ab12cd34ef56/parts/report"
}

Multipart Upload Flow

  1. Create a session or use the managed-upload shortcut.
  2. Request signed part URLs for the next batch of parts.
  3. Upload the parts directly to your configured object storage bucket.
  4. Report each uploaded part with its part number, ETag, and size.
  5. Complete the session once all parts are uploaded and reported.

Report-part example

{
  "part_number": 1,
  "etag": "\"8b1a9953c4611296a827abf8c47804d7\"",
  "part_size": 67108864
}

Complete example

{
  "checksum_sha256": "optional-client-calculated-sha256"
}

For large-file integrations, keep the client on the direct multipart path. That is the scalable design for multi-terabyte daily upload volume.

Resume Interrupted Uploads

The expected resume flow is: keep the upload session ID locally, fetch the current session state after interruption, identify the parts that still need to be uploaded, and then request new signed URLs for only those missing parts.

  1. Persist the upload session public_id when the upload starts.
  2. After a restart or dropped network, call GET /api/v1/uploads/sessions/{id}.
  3. Use the returned state to determine what is already complete and what is still missing.
  4. Request fresh signed URLs for the missing parts only.
  5. Upload those parts, report them, and then complete the session.

Resume check

curl "https://your-site.example/api/v1/uploads/sessions/ups_ab12cd34ef56" \
  -H "Authorization: Bearer fyu_your_token_here"

Typical resumed session response

{
  "status": "ok",
  "session": {
    "public_id": "ups_ab12cd34ef56",
    "status": "uploading",
    "expected_size": 10737418240,
    "uploaded_bytes": 201326592,
    "completed_parts": 3
  }
}

Request fresh URLs only for the missing parts

curl -X POST "https://your-site.example/api/v1/uploads/sessions/ups_ab12cd34ef56/parts/sign" \
  -H "Authorization: Bearer fyu_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "part_numbers": [4, 5, 6],
    "expires_in": 3600
  }'

Do not reuse stale signed storage URLs after a long pause. Request fresh part URLs before resuming when the original URLs may have expired.

Files and Downloads

File metadata

GET /api/v1/files/{id} returns owner-scoped metadata including filename, file size, mime type, short ID, visibility, folder ID, status, download count, and public share fields (when the file is public).

Download links

GET /api/v1/downloads/{id}/link returns an application-signed link with a time-limited expires value. fyuhls still decides whether delivery uses CDN, signed origin, or an app-controlled path.

{
  "status": "ok",
  "file": {
    "id": 123,
    "short_id": "1a2dd2a8",
    "filename": "example.zip",
    "file_size": 1048576,
    "mime_type": "application/zip",
    "is_public": 1,
    "downloads": 42,
    "share_fields": [
      {"label": "Page Link", "value": "https://your-site.example/file/1a2dd2a8"},
      {"label": "HTML Code", "value": "<a href="...">example.zip</a>"}
    ]
  }
}
{
  "status": "ok",
  "url": "https://your-site.example/download/123?token=...",
  "expires_in": 3600,
  "delivery": "cdn",
  "delivery_reason": "public_object_storage_cdn"
}

Errors and Limits

  • 401: authentication failed or the token is invalid.
  • 403: scope is missing or CSRF failed for a browser-session write.
  • 404: the file or upload session is not accessible to the caller.
  • 409: the same idempotency key is already being processed.
  • 422: request validation failed or upload/provider state is inconsistent.

API calls are rate-limited per token, per user, and per IP. Exact thresholds are site-configurable.

Production Checklist

  • Use personal API tokens instead of browser cookies.
  • Send idempotency keys on create and complete.
  • Expose ETag in bucket CORS for multipart uploads.
  • Do not hardcode bucket URLs in clients. Use the download-link endpoint.
  • Persist upload session IDs locally so tools can resume.
  • Validate the full path against your real B2, R2, Wasabi, or S3-compatible bucket before going live.