# Yüklemeler

Bir `file` alanı yükleme token'larıyla yanıtlanır. Önce ziyaretçinin seçtiği her dosyayı yükleyin, ardından token'ları başvurunun `files` nesnesinde gönderin (bkz. [Başvurular](./submissions)). Başvurular gibi yüklemeler de bir `forms:write` anahtarı gerektirir; bu nedenle bunları tarayıcıdan değil, her zaman sunucunuzdan gönderin (bkz. [forms:write anahtarlarını sunucunuzda tutun](./index#formswrite-anahtarlarn-sunucunuzda-tutun)).

## POST /v1/api/forms/{key}/uploads

Formun yayınlanmış sürümündeki bir `file` alanı için tek bir dosya saklar ve bunun için bir token döndürür. `forms:write` scope'unu gerektirir. Form etkin ve yayınlanmış olmalıdır.

Dosya gizli olarak saklanır: herkese açık bir URL'si yoktur ve bir başvuru onu talep ettiğinde satıcı dosyayı dashboard'daki gelen kutusundan açar.

### Yol parametreleri

| Ad | Tür | Zorunlu | Açıklama |
| --- | --- | --- | --- |
| `key` | string | Evet | Form anahtarı, örneğin `quote-request`. |

### Header'lar

| Ad | Tür | Zorunlu | Açıklama |
| --- | --- | --- | --- |
| `Authorization` | string | Evet | `Bearer {forms_write_key}`. |
| `Content-Type` | string | Evet | `multipart/form-data` (form verisi gönderdiğinizde HTTP istemciniz bunu ayarlar). |
| `X-Borkol-Visitor-Ip` | string | Hayır | Ziyaretçinin IP adresi (IPv4 veya IPv6). Yükleme sınırı ziyaretçi adresi başına sayılır. Yoksa veya geçerli bir IP adresi değilse bunun yerine bağlantının adresi (sunucunuz) kullanılır. Bkz. [Ziyaretçi IP'si ve saatlik sınır](./index#ziyareti-ipsi-ve-saatlik-snr). |

### Gövde parametreleri

Gövdeyi `multipart/form-data` olarak gönderin.

| Ad | Tür | Zorunlu | Açıklama |
| --- | --- | --- | --- |
| `field` | string | Evet | Formun yayınlanmış sürümündeki bir `file` alanının anahtarı, en fazla 64 karakter. |
| `file` | file | Evet | Tek bir dosya. Türü adına veya tarayıcının gönderdiği `Content-Type` değerine göre değil, içeriğine göre belirlenir ve alanın `accept` değerine uymalıdır: `image` JPEG, PNG, WebP ve HEIC/HEIF kabul eder; `pdf` PDF kabul eder. SVG ve diğer tüm türler reddedilir. En fazla alanın `max_mb` değeri kadar (1 ile 20 MB). |

```bash title="Request"
curl -X POST "https://api.borkol.com/v1/api/forms/quote-request/uploads" \
  -H "Authorization: Bearer {forms_write_key}" \
  -H "Accept: application/json" \
  -H "X-Borkol-Visitor-Ip: 203.0.113.24" \
  -F "field=brief" \
  -F "file=@brief.pdf"
```

```javascript title="Request (JS)"
// A Next.js route handler: the browser posts multipart (field, file) here, never to Borkol.
export async function POST(request) {
  // Only trust an IP header your own proxy or host sets.
  const visitorIp = request.headers.get("x-real-ip") ?? "";
  const res = await fetch("https://api.borkol.com/v1/api/forms/quote-request/uploads", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.BORKOL_FORMS_KEY}`,
      Accept: "application/json",
      "X-Borkol-Visitor-Ip": visitorIp,
    },
    body: await request.formData(),
  });
  return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
}
```

Durum: `201 Created`. Sonuç `data` içinde döner.

| Ad | Tür | Zorunlu | Açıklama |
| --- | --- | --- | --- |
| `token` | string (UUID) | her zaman | Başvurunun `files` nesnesinde aynı alan anahtarı altında gönderin. |
| `name` | string | her zaman | Saklanan dosya adı: ziyaretçinin dosya adı, yol veya kontrol karakterleri olmadan, uzantıdan önce en fazla 150 karakter, içeriğin kanıtladığı uzantıyla (`jpg`, `png`, `webp`, `heic` veya `pdf`). Addan hiçbir şey kalmazsa `file`. |
| `size` | integer | her zaman | Bayt cinsinden dosya boyutu. |

```json title="Response"
{
  "data": {
    "token": "9b2f0c3e-5a1d-4f7e-8c2b-1d3e4f5a6b7c",
    "name": "brief.pdf",
    "size": 482113
  }
}
```

### Token'lar

- Bir token, yüklendiği forma ve alana aittir. Başka bir form veya başka bir alan için gönderilirse başvuru `files.{key}` üzerinde `422` döndürür.
- Bir token **tek** bir başvuru için çalışır. Reddedilen bir başvuru (herhangi bir `422`) hiçbir şey talep etmez; bu nedenle aynı token'lar düzeltilmiş başvuruyla yeniden gönderilebilir.
- Bir token'ın süresi yüklemeden **24 saat** sonra dolar. Süresi dolmuş bir token `files.{key}` üzerinde `422` döndürür; dosyayı yeniden yükleyin. Hiçbir başvurunun talep etmediği dosyalar, token'larının süresi dolduğunda silinir.
- Her istekte tek bir dosya yükleyin. `max_files` değeri 1'den büyük bir alan için her dosyayı ayrı yükleyin ve tüm token'ları tek bir listede, en fazla `max_files` kadar gönderin.
- Yükleme formun kurallarına bakmaz: alanın gösterilip gösterilmediğine başvuru geldiğinde karar verilir. O anda gizli olan bir alanın token'ları yok sayılır ve kullanılmadan süreleri dolar.

### Yükleme sınırı

Her form **ziyaretçi başına saatte 30 yükleme** kabul eder; bu sayı `X-Borkol-Visitor-Ip` değerinden gelen ziyaretçi adresi başına, başvuru sınırından ayrı olarak sayılır. `422` ile başarısız olan yüklemeler dahil her deneme sayılır. Sınırın üzerinde:

```json title="Response"
{
  "message": "Too many uploads. Try again later."
}
```

Yanıtın durumu `429`'dur ve `Retry-After` header'ı taşımaz. Dakikada 120 isteklik mağaza geneli sınır da geçerlidir (bkz. [Hatalar ve Rate Limit'ler](/tr/api/getting-started/errors-and-rate-limits)).

### Hatalar

**422 doğrulama.** Standart zarf (bkz. [Hatalar ve Rate Limit'ler](/tr/api/getting-started/errors-and-rate-limits)):

| Anahtar | Mesaj | Ne zaman |
| --- | --- | --- |
| `field` | Laravel'in standart mesajları | Eksik, string değil veya 64 karakterden uzun. |
| `field` | `Not a file field of this form.` | Yayınlanmış sürümde bu anahtara sahip bir `file` alanı yok. |
| `file` | Laravel'in standart mesajları | Eksik, yüklenmiş bir dosya değil veya 20 MB'tan büyük. |
| `file` | `The file may not be larger than {max} MB.` | Alanın `max_mb` değerinden büyük. |
| `file` | `This file type is not accepted here.` | İçerik, alanın `accept` değerinin izin verdiği bir tür değil. |

```json title="Response"
{
  "message": "This file type is not accepted here.",
  "errors": {
    "file": ["This file type is not accepted here."]
  }
}
```

**Diğer hatalar.**

- Mağazanın bu anahtara sahip bir formu yoksa veya form taslak, arşivlenmiş, hiç yayınlanmamış ya da silinmişse `404`.
- Mağaza için forms modülü kapalıysa `403` `{"error":"Module not enabled: forms"}`, anahtarda `forms:write` yoksa `403` `{"message":"Invalid ability provided."}`.
- Her `/v1/api` route'unda olduğu gibi `401`.
