# Signed Uploads [View original](https://ittybit.com/docs/uploads/signed) ## How it works A signed upload url includes a valid signature which will grant access to your project for a limited time. Once you have the signed url, you make a `PUT` request with the file in the request body. When the upload completes, ittybit will return a [File object](/docs/files) in the response. *** ## Signed urls A signed url looks like this: `https://you.ittybit.net/image.png?expiry=1765432100&signature=1234abcd5678efgh9012` The `domain` is your project's delivery domain (see [Domains](/docs/domains) for info on setting up custom domains) e.g. `https://you.ittybit.net`. The `path` is the full path to the file you want to access e.g. `private/image.png`. The `expiry` is a unix timestamp for some time in the near future. The URL will stop working after this time e.g. `1765432100`. The `signature` is a base64 encoded string. The string that is encoded is the `domain`, `path`, and `expiry` values, signed with your project's API key. *** ## 1. Get a signed url To get a valid signed url, you will need to make a request to your server, get the url, and return it to your client. If you're using a REST API for data fetching (could be powered by Express/Node, Laravel, Rails, or similar) then this would probably be a request to an API route. Next.js, SvelteKit, and similar frontend frameworks can use server actions to generate or fetch a signed url directly. ```js // Example fetch request to your own API server const response = await fetch('https://yourapi.com/upload-url', { method: 'POST', headers: { 'Content-Type': 'application/json', // Add your authentication headers }, body: JSON.stringify({ filename: 'image.png', }), }); const { signedUrl } = await response.json(); ``` *** ### Server-side code From your server-side code, you can safely use your project's API key. This can authenticate a request to the ittybit API, or be used to generate a signature directly. *** #### A. Ittybit API You can use the `/signatures` endpoint to get a signed url from the ittybit API. Send a request from your backend to the [signatures endpoint](/api/signatures). ```js const response = await fetch('https://api.ittybit.com/signatures', { method: 'POST', headers: { 'Authorization': 'Bearer ITTYBIT_API_KEY' }, body: JSON.stringify({ filename: 'image.png', method: 'put' }), }); const { data, error } = await response.json(); if (error) { console.error(error); // maybe handle the error } const signedUrl = data?.url; return res.json({ signedUrl }); ``` *** #### B. Generate signatures You can skip the additional request by generating a signature yourself. It's not much extra code, and it's pretty much always faster because there's no network round trip. ```js import crypto from 'crypto'; const DOMAIN = 'you.ittybit.net'; const ITTYBIT_API_KEY = process.env.ITTYBIT_API_KEY; function generateSignature({ string }) { const hmac = crypto.createHmac('sha256', ITTYBIT_API_KEY); hmac.update(string); const base64 = hmac.digest('base64url'); return base64; } async function createSignedUrl({ path }) { try { const expiry = Math.floor(Date.now() / 1000) + 60 * 60; // 1 hour from now const string = `${path}?expiry=${expiry}&method=put`; const signature = generateSignature({ string }); const signedUrl = `https://${DOMAIN}/${string}&signature=${signature}`; return { signedUrl }; } catch (error) { // handle the error } } // Example usage const { signedUrl } = await createSignedUrl({ path: 'image.png' }); // Outputs: https://you.ittybit.net/image.png?expiry=1735689600&method=put&signature=a1b2c3d4e5f6... // Upload to a specific folder const { signedUrl: signedUrl2 } = await createSignedUrl({ path: 'nested/folders/image.png' }); // Outputs: https://you.ittybit.net/nested/folders/image.png?expiry=1735689600&method=put&signature=a1b2c3d4e5f6... ``` A JS/node backend example is given above. More language examples are coming soon but please [contact us](https://ittybit.com/support) and we'd be happy to help you write something that fits into your stack. *** ## 2. Upload the file Once you have the signed url, you can upload the file to ittybit. *** ### Simple upload For most files, you can use a single `PUT` request. ```js const response = await fetch(signedUrl, { method: 'PUT', body: file, }); ``` *** ### Resumable upload For large files (100MB+), you can use the `signedUrl` for multiple `PUT` requests containing different chunks of the file. ```js const response = await fetch(signedUrl, { method: 'PUT', headers: { 'Content-Range': `bytes=0-16777216/100000000`, }, body: chunk, }); ``` See [Resumable Uploads](/docs/uploads#resumable-uploads) for more details. *** ## 3. Handle the response When your upload completes, ittybit will return a [File object](/docs/files) in the response. ```js const { meta, data, error, links } = await response.json(); ``` * `meta`: contains information about the request. * `data`: contains the file object (or `null` if the upload failed). * `error`: will contain an error message if the upload failed (or `null`). * `links`: contains links to the file object resource. *** ## File object ```json { "id": "file_abcdefgh1234", "kind": "image", "type": "image/png", "width": 3000, "height": 2000, "filesize": 12345678, "url": "https://you.ittybit.net/image.png", "created": "2025-01-01T01:23:45Z", "updated": "2025-01-01T01:23:45Z" } ``` You can persist this info to your database, and use it to serve the file to your users. *** [^1]: You get a free `*.ittybit.net` domain when you create a project. You can also add [Custom Domains](/docs/domains) in your project settings.