Skip to content
Storage

Private files with signed transfers

Store private documents and serve them through authenticated flows, with short-lived signed tickets for direct upload and download.

  • Signed upload and download tickets
  • Range requests supported
  • Keys never leave your server
  • Direct transfer, signed

    Request a ticket, then PUT the file straight to the returned URL. Bytes do not detour through your application server.

  • Upload tickets are single-use

    An upload ticket is spent once. Download tickets stay valid until they expire, which makes them safe to hand to a media player.

  • Your API key stays server-side

    The download URL carries its own signature. Nothing about your API key is appended to a link you give a browser.

  • Range requests work

    Download URLs honour Range, so video scrubbing and resumable downloads behave the way clients expect.

  • Scopes, not blanket access

    blob.read permits reads, blob.write permits uploads and deletion. The key must belong to the project that owns the file.

  • Confirm closes the loop

    After the upload completes, a confirm call records the object. An abandoned upload does not silently become a phantom row.

Expiry is part of the contract

Every ticket endpoint returns an expiry. Use it. A client that caches a download URL for a day and retries forever will produce support tickets, not resilience.

typescriptUpload with a signed ticket
const ticket = await fetch(`${base}/api/storage/upload-ticket`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.VELTIC_API_KEY}` },
  body: JSON.stringify({ projectId, key: 'invoices/2026-09.pdf' })
}).then(response => response.json());

await fetch(ticket.uploadUrl, {
  method: 'PUT',
  headers: { 'x-veltic-upload-token': ticket.token },
  body: file
});

Build the whole backend in one project

One plan, one dashboard, one command line interface. PostgreSQL, applications, functions, storage and realtime.