# Quickstart

> Create a project and a bucket, upload a zip of your code, and read a file back, in about five minutes.

## Before you start

You need a **Clatter Labs account**. If you don't have one, create it at [accounts.clatterlabs.com](https://accounts.clatterlabs.com). Code Buckets is included free: 1,000,000 tokens a month and 1 backup.

<Steps>
<Step title="Create an API key and copy your Account ID">
Sign in to the [Console](https://console.clatterlabs.com). Your **Account ID** (like `CL-7K2M9QXA`) is on the Home page and in **Settings**.

Go to **API keys › Create key**, and tick **buckets:read**, **buckets:write** and **backups:write**. The key starts with `clsk_` and is shown only once.

```bash
export CLATTER_API_KEY="clsk_..."          # Console › API keys
export CLATTER_ACCOUNT_ID="CL-XXXXXXXX"    # Console › Settings
export CB="https://api.codebuckets.clatterlabs.xyz"
```
</Step>
<Step title="Create a project">
The response carries your **Project ID** (`cbp_…`).

<CodeGroup>
```bash cURL
curl $CB/v1/projects \
  -H "Authorization: Bearer $CLATTER_API_KEY" \
  -H "X-Clatter-Account: $CLATTER_ACCOUNT_ID" \
  -H "Content-Type: application/json" \
  -d '{"name": "storefront"}'
```

```python Python
import os, requests

CB = "https://api.codebuckets.clatterlabs.xyz"
headers = {
    "Authorization": f"Bearer {os.environ['CLATTER_API_KEY']}",
    "X-Clatter-Account": os.environ["CLATTER_ACCOUNT_ID"],
}
project = requests.post(f"{CB}/v1/projects", headers=headers, json={"name": "storefront"}).json()
print(project["id"])  # cbp_...
```

```javascript JavaScript
const CB = "https://api.codebuckets.clatterlabs.xyz";
const headers = {
  Authorization: `Bearer ${process.env.CLATTER_API_KEY}`,
  "X-Clatter-Account": process.env.CLATTER_ACCOUNT_ID,
  "Content-Type": "application/json",
};
const project = await fetch(`${CB}/v1/projects`, { method: "POST", headers, body: JSON.stringify({ name: "storefront" }) }).then((r) => r.json());
console.log(project.id); // cbp_...
```
</CodeGroup>
</Step>
<Step title="Create a bucket">
```bash
curl $CB/v1/projects/PROJECT_ID/buckets \
  -H "Authorization: Bearer $CLATTER_API_KEY" \
  -H "X-Clatter-Account: $CLATTER_ACCOUNT_ID" \
  -H "Content-Type: application/json" \
  -d '{"name": "web"}'
```

You get back the bucket id (`cbk_…`).
</Step>
<Step title="Upload a zip">
Uploads are chunked so big files work and can resume. Start the upload with the file's name and size, send each 16 MiB chunk, then complete it.

<CodeGroup>
```bash cURL
FILE=storefront.zip
SIZE=$(wc -c < $FILE | tr -d ' ')
UP=$(curl -s $CB/v1/buckets/BUCKET_ID/uploads \
  -H "Authorization: Bearer $CLATTER_API_KEY" -H "X-Clatter-Account: $CLATTER_ACCOUNT_ID" \
  -H "Content-Type: application/json" -d "{\"name\": \"$FILE\", \"size\": $SIZE}" | jq -r .id)

split -b 16777216 -d -a 5 $FILE part-
i=0
for p in part-*; do
  curl -s -X PUT $CB/v1/uploads/$UP/chunks/$i \
    -H "Authorization: Bearer $CLATTER_API_KEY" -H "X-Clatter-Account: $CLATTER_ACCOUNT_ID" \
    -H "Content-Type: application/octet-stream" --data-binary @$p > /dev/null
  i=$((i+1))
done

curl -s -X POST $CB/v1/uploads/$UP/complete \
  -H "Authorization: Bearer $CLATTER_API_KEY" -H "X-Clatter-Account: $CLATTER_ACCOUNT_ID"
```

```python Python
import os, requests

path = "storefront.zip"
size = os.path.getsize(path)
up = requests.post(f"{CB}/v1/buckets/{bucket_id}/uploads", headers=headers, json={"name": path, "size": size}).json()

with open(path, "rb") as f:
    for i in range(up["chunk_count"]):
        chunk = f.read(up["chunk_size"])
        requests.put(f"{CB}/v1/uploads/{up['id']}/chunks/{i}", headers=headers, data=chunk).raise_for_status()

done = requests.post(f"{CB}/v1/uploads/{up['id']}/complete", headers=headers).json()
print(done["status"])  # processing, then ready
```

```javascript JavaScript
import { open, stat } from "node:fs/promises";

const path = "storefront.zip";
const { size } = await stat(path);
const up = await fetch(`${CB}/v1/buckets/${bucketId}/uploads`, { method: "POST", headers, body: JSON.stringify({ name: path, size }) }).then((r) => r.json());

const fh = await open(path);
for (let i = 0; i < up.chunk_count; i++) {
  const buf = Buffer.alloc(Math.min(up.chunk_size, size - i * up.chunk_size));
  await fh.read(buf, 0, buf.length, i * up.chunk_size);
  const r = await fetch(`${CB}/v1/uploads/${up.id}/chunks/${i}`, { method: "PUT", headers: { ...headers, "Content-Type": "application/octet-stream" }, body: buf });
  if (!r.ok) throw new Error((await r.json()).error.message);
}
await fh.close();
const done = await fetch(`${CB}/v1/uploads/${up.id}/complete`, { method: "POST", headers }).then((r) => r.json());
console.log(done.status); // processing, then ready
```
</CodeGroup>
</Step>
<Step title="Browse the code">
A few seconds later the zip is unpacked. List the tree and read a file:

```bash
curl "$CB/v1/buckets/BUCKET_ID/tree" \
  -H "Authorization: Bearer $CLATTER_API_KEY" -H "X-Clatter-Account: $CLATTER_ACCOUNT_ID"

curl "$CB/v1/buckets/BUCKET_ID/files/README.md" \
  -H "Authorization: Bearer $CLATTER_API_KEY" -H "X-Clatter-Account: $CLATTER_ACCOUNT_ID"
```

Or open the bucket in the [Console](https://console.clatterlabs.com/code-buckets) and click through the files.
</Step>
<Step title="Back it up">
```bash
curl $CB/v1/buckets/BUCKET_ID/backups \
  -H "Authorization: Bearer $CLATTER_API_KEY" -H "X-Clatter-Account: $CLATTER_ACCOUNT_ID" \
  -H "Content-Type: application/json" -d '{"name": "First backup"}'
```
</Step>
</Steps>

## Next steps

<CardGroup cols={2}>
<Card title="Concepts" icon="boxes" href="/code-buckets/concepts">Projects, buckets, objects and refs.</Card>
<Card title="Uploads" icon="upload" href="/code-buckets/uploads">Big files, resuming and checksums.</Card>
<Card title="Backups" icon="history" href="/code-buckets/backups">Snapshot and restore.</Card>
<Card title="Events" icon="list" href="/code-buckets/events">Every change, in order.</Card>
</CardGroup>
