---
title: "Caricare un'immagine del prodotto"
description: Upload multipart di una singola immagine al prodotto. PNG/JPG/WebP, ≤5 MB, max 20 immagini. Niente Idempotency-Key (multipart non hashabile in sicurezza).
canonical: "https://www.tracepass.eu/it/docs/upload-product-image"
locale: it
source: "https://www.tracepass.eu/it/docs/upload-product-image"
---

# Caricare un'immagine del prodotto

> Upload multipart di una singola immagine al prodotto. PNG/JPG/WebP, ≤5 MB, max 20 immagini. Niente Idempotency-Key (multipart non hashabile in sicurezza).

```http
POST /api/v1/products/{id}/images
```

Carica un singolo file immagine (multipart/form-data, nome del campo `file`) e lo aggiunge in coda all'array `imageUrls` del prodotto. Usatelo quando non avete pronto un URL del CDN — l'immagine finisce nel nostro bucket R2 e l'URL pubblico viene restituito nella risposta.

Solo PNG / JPG / WebP, max 5 MB per file, max 20 immagini per prodotto. **Nessun supporto per Idempotency-Key** — i corpi multipart non sono calcolabili in hash in modo sicuro. Il client dovrebbe verificare l'esistenza + saltare in caso di nuovo tentativo. Il corrispondente `DELETE /api/v1/products/{id}/images/{index}` rimuove una singola immagine tramite indice su base zero.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Authorization` | header | string | yes | `Bearer <token>` — una chiave API `tp_` (Developer → API Keys; più semplice, per server-to-server) oppure un access token OAuth 2.0 (Developer → OAuth Apps; per app autorizzate dall'utente, scoped e revocabili). La pagina Authentication contiene il flusso OAuth completo e l'elenco degli scopes. |
| `Content-Type` | header | string | yes | `multipart/form-data` con il parametro boundary impostato dal vostro client HTTP. |
| `id` | path | ObjectId | yes | ID del prodotto. |
| `file` | body | binary (PNG / JPG / WebP, ≤ 5 MB) | yes | Byte dell'immagine inviati come campo del modulo `file`. |

## Examples

```bash
curl -sS -X POST \
  https://app.tracepass.eu/api/v1/products/6650a1b2c3d4e5f6a7b8c9d0/images \
  -H "Authorization: Bearer tp_REDACTED_xxxxxxxxxxxx" \
  -F "file=@./battery-hero.jpg"
```

```typescript
import { readFile } from "node:fs/promises";

const file = await readFile("./battery-hero.jpg");
const form = new FormData();
form.set("file", new Blob([file], { type: "image/jpeg" }), "battery-hero.jpg");

const res = await fetch(
  `https://app.tracepass.eu/api/v1/products/${id}/images`,
  {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.TRACEPASS_API_KEY}` },
    body: form,
  },
);
if (!res.ok) throw new Error(`Upload failed: ${res.status}`);
const { imageUrl, imageUrls } = await res.json();
```

```python
import os, requests

with open("battery-hero.jpg", "rb") as fh:
    res = requests.post(
        f"https://app.tracepass.eu/api/v1/products/{product_id}/images",
        headers={"Authorization": f"Bearer {os.environ['TRACEPASS_API_KEY']}"},
        files={"file": ("battery-hero.jpg", fh, "image/jpeg")},
    )
res.raise_for_status()
data = res.json()
print(data["imageUrl"])
```

## Responses

### 201 — Caricato

```json
{
  "imageUrl": "https://r2.tracepass.eu/<companyId>/products/<id>/images/<imageId>.jpg",
  "imageUrls": [
    "https://existing-image-1.jpg",
    "https://r2.tracepass.eu/<companyId>/products/<id>/images/<imageId>.jpg"
  ]
}
```

### 400 — Nessun file

```json
{ "error": "No file provided. Send as multipart/form-data with field name 'file'." }
```

### 413 — Troppo grande

```json
{ "error": "Image must be under 5MB" }
```

### 415 — Non supportato

```json
{ "error": "Unsupported MIME type — accepts image/png, image/jpeg, image/webp" }
```

### 422 — Limite raggiunto

```json
{ "error": "Product already has 20 images (max). Delete one first." }
```

## Related

- [Aggiornare un prodotto (sostituire imageUrls)](https://www.tracepass.eu/it/docs/update-product.md)
- [Creare un prodotto](https://www.tracepass.eu/it/docs/create-product.md)
