# 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.