# Quickstart

> From nothing to an uploaded, responsive image in about ten minutes.

import { Code } from '@astrojs/starlight/components';
import uploadImage from '../../../../examples/01-upload-an-image.ts?raw';
import imageUrls from '../../../../examples/02-image-urls.ts?raw';
import { TRANSFORM_WIDTHS } from '@nitida/asset-client';

You need [your credentials](/start/credentials/) first — an endpoint, a tenant
id and a runtime key. Everything below assumes you have them.

## Try it first, with no credentials at all

[**media-harness.vercel.app**](https://media-harness.vercel.app) is a public
bench running this exact SDK. Open it on your phone, drop in a photo
or a video, and watch the five steps happen one at a time — format in and out,
bytes in and out, dimensions, aspect ratio, and milliseconds per step. No
account, no key, no install.

It is the fastest way to see what the platform does to a file *before* you write
any code, and it is the same page we use to catch format regressions: the silent
PNG fallback that shipped for months only reproduces in a real browser on a real
phone, and a table like this one would have shown it on the first photo.

:::caution[What it is not]
It is a **bench**, not a product and not a storage service.

- Everything you upload lands in a **throwaway tenant** called `demo`, kept
  separate from every real one.
- **That tenant gets wiped.** Treat anything you put there as deleted already —
  do not upload anything you would miss, anything private, or anything you have
  no right to share.
- It runs the published `@nitida/sdk`, so what you see is what your own code
  will get — but the numbers come from *your* device, not from a guarantee.

Ready to build? Keep reading; the same upload takes about ten lines.
:::

## Install

```bash
bun add @nitida/sdk @nitida/asset-client
```

**Which one do you actually need?**

- Only *rendering* media that already exists → `@nitida/asset-client` alone.
  No network, no key, nothing to configure but a tenant id.
- Also *uploading* → add `@nitida/sdk`. It re-exports every URL builder, so you
  never import both for the same job.

## Upload

<Code code={uploadImage} lang="ts" title="upload-an-image.ts" />

One call does the whole loop: compress → hash → presign → PUT straight to
storage → register → poll until ready. You get back an id, a SHA and a URL.

:::danger[The runtime key is server-only]
`amk_rt_*` authorises writes to a paid platform. In a client bundle — including
anything named `NEXT_PUBLIC_*` — it is a write key strangers can read.

The shape that is always correct: **the browser talks to your route, your route
talks to nitida.**
:::

:::caution[Ask for the presets you need — the default is `["original"]` only]
No `presets` means **the original and nothing else**: no `thumb`, no ladder, no
responsive anything.

Getting it wrong is **recoverable** — cleanup keeps `raw/` while the asset row
exists (verified on a 2-month-old thumb-only asset), so
`regenerate({ presets })` can always fill the gap. What it costs meanwhile is
confusing: `getAssetUrl(asset,"md")` returns **404** while
`/t/…width=1280/<sha>.webp` happily serves the same image.

See [Variants and presets](/concepts/variants-and-presets/).
:::

## Render

<Code code={imageUrls} lang="ts" title="image-urls.ts" />

Widths must be on the allowed ladder — {TRANSFORM_WIDTHS.length} of them:
{TRANSFORM_WIDTHS.join(" · ")}. Anything else answers **400** at the
edge — deliberately, because an open resize endpoint is a denial-of-service
amplifier. Importing the `TransformWidth` type makes an off-ladder width a
*compile* error instead of a production surprise.

## Where to go next

| you want to… | read |
|---|---|
| understand what runs where | [How it works](/concepts/how-it-works/) |
| know which presets to ask for | [Variants and presets](/concepts/variants-and-presets/) |
| upload video | [Video and HLS](/guides/video-and-hls/) |
| keep a PNG's transparency | [Formats and transparency](/concepts/formats-and-transparency/) |
| use the palette or an AI proxy | [Beyond images](/concepts/beyond-images/) |
| work out what an error means | [What each symptom means](/guides/troubleshooting/) |
| hand this to a coding agent | [For agents](/start/for-agents/) |