# Quickstart

Upload, inspect, and retrieve a private file with the Safeput development API.

The development API creates a private upload target, processes the uploaded
bytes asynchronously, and makes a policy-approved file temporarily retrievable.

## 1. Create a project key

Log in to the [development dashboard](https://dev.safeput.dev/dashboard/),
create a test project, and create an API key. Save the complete `sp_test_...`
value when it is shown; Safeput stores only its digest and cannot reveal it
again. Keep the key on your backend.

## 2. Request an upload target

```sh
curl https://dev.safeput.dev/v1/uploads \
  --request POST \
  --header "Authorization: Bearer $SAFEPUT_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: invoice-2026-08-15" \
  --data '{"filename":"invoice.pdf","content_type":"application/pdf","size":12480}'
```

The response contains an `upload` record and an `upload_target`. The maximum
declared file size is 52,428,800 bytes (50 MiB).

## 3. Upload the bytes

Use the returned URL within 10 minutes and preserve every returned header:

```sh
curl "$UPLOAD_URL" \
  --request PUT \
  --header "Authorization: $UPLOAD_AUTHORIZATION" \
  --header "Content-Type: application/pdf" \
  --upload-file invoice.pdf
```

Set `UPLOAD_AUTHORIZATION` to the returned `upload_target.headers.Authorization`
value, not your project key. The target is single-use, including after rejection
or deletion. Send a known-length file/Blob, not an unbounded chunked stream; the
byte length must exactly match the declared size. A completed transfer returns
`204` and does not yet mean the file is clean. The R2 bucket remains private.

## 4. Wait for inspection

```sh
curl "https://dev.safeput.dev/v1/uploads/$UPLOAD_ID" \
  --header "Authorization: Bearer $SAFEPUT_API_KEY"
```

Poll until `status` reaches `clean`, `rejected`, `failed`, or `expired`. Only a
`clean` record with `download_available: true` can be retrieved.

## 5. Retrieve a clean file

```sh
curl "https://dev.safeput.dev/v1/uploads/$UPLOAD_ID/download-url" \
  --request POST \
  --header "Authorization: Bearer $SAFEPUT_API_KEY"
```

Use the returned `download.url` within five minutes. Safeput streams the file
with a sanitized attachment filename, its detected content type, and `no-store`
headers. Read [Retrieving files](/docs/retrieving-files/) for retention and
security details.
