Formlar

Başvurular

Bir ziyaretçinin etkin bir forma verdiği yanıtları ve yükleme token'larını ziyaretçi IP header'ı, honeypot, saatlik sınır, Turnstile, doğrulama hataları ve teşekkür yanıtıyla sunucunuzdan gönderin.

Markdown olarak görüntüle

Başvuru, bir ziyaretçinin bir forma verdiği yanıtlardır. Başvuruyu tarayıcıdan değil, her zaman sunucunuzdan gönderin: gereken forms:write anahtarı satıcı adına e-posta gönderebilir (bkz. forms:write anahtarlarını sunucunuzda tutun).

POST /v1/api/forms/{key}/submissions

Yanıtları formun yayınlanmış sürümüne göre kontrol eder, yüklenen dosyaları talep eder, başvuruyu saklar, numaralandırır ve teşekkür ayarlarıyla yanıt verir. forms:write scope'unu gerektirir. Form etkin ve yayınlanmış olmalıdır.

Yol parametreleri

AdTürZorunluAçıklama
keystringEvetForm anahtarı, örneğin quote-request.

Header'lar

AdTürZorunluAçıklama
AuthorizationstringEvetBearer {forms_write_key}.
Content-TypestringEvetapplication/json.
X-Borkol-Visitor-IpstringHayırZiyaretçinin IP adresi (IPv4 veya IPv6). Saatlik sınır ziyaretçi adresi başına sayılır ve Turnstile kontrolü bu adresle gönderilir. 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.
User-AgentstringHayırBaşvuruyla birlikte saklanır (ilk 500 karakter) ve gelen kutusunda tarayıcı olarak gösterilir. Orada görmek istiyorsanız ziyaretçinin kendi User-Agent değerini iletin.

Gövde parametreleri

AdTürZorunluAçıklama
answersobjectEvetAlan anahtarına göre yanıtlar; {} olabilir. Türe göre değer tipleri Alan türleri altında listelenmiştir. Bilinmeyen anahtarlar, heading ve paragraph alanları ve kuralları tarafından gizlenen alanlara verilen yanıtlar hata vermeden atılır.
localestringHayırZiyaretçinin dili, en fazla 16 karakter, örneğin nl. Mağaza için etkin bir dil, thank_you.message değerinin ve ziyaretçiye giden onay e-postasının dilini belirler; diğer durumlarda mağazanın varsayılan dili kullanılır.
filesobjectHayırDosya yanıtları: alan anahtarından POST /v1/api/forms/{key}/uploads ile alınan yükleme token'ları listesine, örneğin {"photos": ["9b2f0c3e-..."]}. Alan başına en fazla max_files token. Zorunlu dosya alanları burada yanıtlanır; answers içinde bir dosya alanı için gönderilen değer yok sayılır.
turnstile_tokenstringKoşulluCloudflare Turnstile widget'ının token'ı. Form settings.turnstile_site_key döndürüyorsa zorunludur; aksi halde yok sayılır. Tek kullanımlıktır: her 422 sonrasında yeni bir token alın. Bkz. Turnstile.
metaobjectHayırSatıcının gelen kutusunda gösterilen sayfa bilgileri. Aşağıda listelenmeyen her şey yok sayılır.
meta.page_urlstringHayırFormun bulunduğu sayfa. Yalnızca en fazla 2000 karakterlik mutlak bir http:// veya https:// URL'si ise saklanır; aksi halde atılır.
meta.referrerstringHayırZiyaretçinin geldiği sayfa. page_url ile aynı kural geçerlidir.
meta.utmobjectHayırKampanya değerleri, örneğin {"source": "google", "campaign": "spring"}. Yalnızca string değerler; en fazla 200 karakterlik ilk 10 anahtar tutulur ve değerler 200 karaktere kısaltılır.
honeypotstringHayırİnsanların hiç görmediği bir giriş alanının değeri. Boş bir string veya null dışında herhangi bir değerse istek bot olarak değerlendirilir (aşağıya bakın).
Request
curl -X POST "https://api.borkol.com/v1/api/forms/quote-request/submissions" \
  -H "Authorization: Bearer {forms_write_key}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "X-Borkol-Visitor-Ip: 203.0.113.24" \
  -d '{
    "locale": "en",
    "answers": {
      "services": ["website"],
      "pages": 8,
      "campaign": "spring",
      "name": "Ada Lovelace",
      "email": "ada@example.com",
      "consent": true
    },
    "files": {
      "brief": ["9b2f0c3e-5a1d-4f7e-8c2b-1d3e4f5a6b7c"]
    },
    "turnstile_token": "0.AbCdEf...",
    "meta": {
      "page_url": "https://example.com/quote",
      "referrer": "https://www.google.com/",
      "utm": { "source": "google" }
    },
    "honeypot": ""
  }'
Request (JS)
// A Next.js route handler: the browser posts { locale, answers, files, turnstile_token } here, never to Borkol.
export async function POST(request) {
  const body = await request.json();
  // 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/submissions", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.BORKOL_FORMS_KEY}`,
      "Content-Type": "application/json",
      Accept: "application/json",
      "X-Borkol-Visitor-Ip": visitorIp,
      "User-Agent": request.headers.get("user-agent") ?? "",
    },
    body: JSON.stringify({ ...body, meta: { page_url: request.headers.get("referer") ?? undefined } }),
  });
  return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
}

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

AdTürZorunluAçıklama
numberstring veya nullher zamanBaşvuru numarası, örneğin INQ-0042; mağazanın talep numara serisinden gelir (varsayılan olarak INQ- ve dört basamaklı sıralı bir numara). Yalnızca honeypot durumunda null.
thank_you.messagestring veya nullher zamanBaşvurunun dilindeki teşekkür mesajı veya satıcı yazmadıysa null.
thank_you.redirect_urlstring veya nullher zamanZiyaretçinin gönderileceği bir https:// adresi veya mesajı göstermek için null.
Response
{
  "data": {
    "number": "INQ-0042",
    "thank_you": {
      "message": "Thanks! We will get back to you soon.",
      "redirect_url": null
    }
  }
}

Bir başvuru nasıl kontrol edilir

Şu sırayla:

  1. Form aranır: etkin değilse veya hiç yayınlanmamışsa 404.
  2. Honeypot. honeypot boş bir string veya null dışında herhangi bir değer taşıyorsa API hemen {"number": null, "thank_you": {...}} ile 201 döndürür. Hiçbir şey saklanmaz, e-posta veya webhook gönderilmez, Turnstile kontrol edilmez ve deneme saatlik sınıra sayılmaz. Ziyaretçiye farklı bir şey söylemeyin: yanıt bilerek başarılı görünür.
  3. Saatlik sınır. Deneme bu form ve ziyaretçi adresi (X-Borkol-Visitor-Ip değerinden) için sayılır. Formun sınırının üzerinde (satıcı saatte 1 ile 100 arasında belirler, varsayılan 5) API 429 döndürür. Sonrasında 422 ile başarısız olanlar dahil sayılan her deneme bütçeden düşer.
  4. Turnstile, yalnızca form settings.turnstile_site_key döndürüyorsa. turnstile_token Cloudflare'de doğrulanır; başarısız bir kontrol turnstile üzerinde 422 döndürür. Bu noktadan sonra, ne olursa olsun token tükenmiştir.
  5. Yanıtlar. Önce gövdenin yapısı, ardından yanıtlar yayınlanmış sürüme göre kontrol edilir: kurallar hangi adımların ve alanların gösterileceğine karar verir, kurallarıyla gizlenen alanlara verilen yanıtlar atılır ve zorunlu değildir, zorunlu görünür alanlar yanıtlanmalıdır ve her değer kendi türüne uymalıdır. files içindeki yükleme token'ları burada liste olarak kontrol edilir (files.{key} hataları).
  6. Dosyalar. Yalnızca yanıtlar geçerliyse: görünür bir dosya alanının her token'ı talep edilir. Her token var olmalı, bu forma ve bu alana ait olmalı, kullanılmamış ve süresi dolmamış olmalı ve bir kez geçmelidir; aksi halde API files.{key} üzerinde 422 döndürür. Talep etme, saklama ile aynı transaction içinde gerçekleşir; bu nedenle reddedilen bir başvuru hiçbir şey talep etmez: ziyaretçi yanıtları düzeltip aynı token'ları yeniden gönderebilir (yeni bir Turnstile token'ı ile).
  7. Saklama. Görünür yanıtlar (hidden alan değerleri dahil) sürüm, dil ve meta ile birlikte saklanır; talep edilen dosyalar başvuruya eklenir. Ham ziyaretçi IP'si asla saklanmaz, yalnızca HMAC-SHA256 hash'i tutulur. E-posta adresi mağazanın tam olarak bir kişisiyle (büyük/küçük harf duyarsız) eşleşirse başvuru o kişiye bağlanır; hiçbir zaman kişi oluşturulmaz.
  8. Duyuru, yanıt saklandıktan sonra yapılır: satıcıya e-posta, ziyaretçiye onay e-postası (form bunu açık tutuyorsa ve ziyaretçi bir e-posta verdiyse), dashboard bildirimi ve form_submission.created webhook'u. Bunlardan birindeki hata satıcı için kaydedilir ve 201 yanıtını asla değiştirmez.

Adres alanları ve hizmet bölgesi

Aramalı (config.lookup: true) her görünür address alanı için sunucu, hizmet bölgesini kontrol etmeden önce posta kodunu ve bina numarasını yeniden arar:

  • Bulundu: siz hiç koordinat göndermediyseniz veya gönderdikleriniz eşleşmeden 1 km'den fazla uzaktaysa eşleşmenin koordinatları lat ve lon değerlerinin yerine geçer.
  • Bulunamadı: gönderdiğiniz koordinatlar doğrulanamaz; bu yüzden lat ve lon atılır (null).
  • Arama kullanılamıyor: gönderdiğiniz koordinatlar olduğu gibi kalır.

config.service_area olan bir alan için sunucu daha sonra başlangıç noktasından adres koordinatlarına kuş uçuşu mesafeyi ölçer ve alan için {distance_km, in_area} değerini başvurunun meta.address alanında alan anahtarıyla saklar (satıcı bunu gelen kutusunda görür). Adresin koordinatı yoksa distance_km ve in_area null olur. Hizmet bölgesi olmayan bir alan meta.address içine bir şey eklemez.

  • mode: "notice": karar ne olursa olsun başvuru kabul edilir; ziyaretçi mesajı formunuzda zaten görmüştür.
  • mode: "block": adresi kesin olarak bölge dışında olan (in_area false) bir başvuru, answers.{key} üzerinde alanın message metniyle (başvurunun dilinde) 422 verir. Koordinatı olmayan bir adres (bulunamadı veya arama kullanılamıyor ve sizden koordinat yok) kabul edilir. block için lookup: true gerekir, çünkü doğrulanmış koordinatlar yalnızca aranmış bir adreste vardır.

Hatalar

422 doğrulama. Yola göre anahtarlanmış hatalarla standart zarf (bkz. Hatalar ve Rate Limit'ler):

AnahtarNe zaman
answersanswers eksik veya bir nesne değil.
localeString değil veya 16 karakterden uzun.
files, metaVar ama nesne değil.
turnstileFormda Turnstile açık ve token eksik, geçersiz, süresi dolmuş veya daha önce kullanılmış ya da Cloudflare onu doğrulayamadı.
answers.{key}Görünür bir alanın yanıtı eksik veya türüne uymuyor.
files.{key}Zorunlu ve görünür bir file alanının token'ı yok, değer bir token listesi değil veya max_files değerinden fazla token içeriyor ya da bir token talep edilemiyor.
Response
{
  "message": "This field is required. (and 1 more error)",
  "errors": {
    "answers.name": ["This field is required."],
    "answers.email": ["Must be an email address."]
  }
}

answers.{key} ve files.{key} üzerindeki mesajlar:

MesajTür
This field is required.consent dışında boş bırakılan her zorunlu alan.
You must accept this.consent: zorunlu ve boş bırakılmış (false boş sayılır) veya ne true ne de boş olan bir yanıt.
Must be text of at most 255 characters.text.
Must be text of at most 10000 characters.textarea.
Must be a number.number.
Must be an email address.email.
Must be a phone number.phone.
Must be a date in YYYY-MM-DD format.date.
Must be one of the configured options.select.
Must be a link., A link needs a url., A link must be absolute, root-relative, mailto: or tel:., A link label may be at most 200 characters.link.
Choose one of the options.choice: seçenek olmayan bir değer (veya multiple olduğunda liste olmayan bir değer).
Choose at least {min}., Choose at most {max}.multiple içeren choice, min veya max dışında.
Must be text of at most 500 characters.hidden.
Must be a valid address.address: nesne olmaması, geçersiz posta kodu veya aralık dışı koordinatlar. Eksik posta kodu, bina numarası, sokak veya şehir This field is required. hatasını verir.
Must be a list of files., At most {max} files are allowed.file.
This upload is invalid or has expired. Upload the file again.file: var olmayan, başka bir forma veya alana ait, daha önce kullanılmış, süresi dolmuş (yüklemeden 24 saat sonra) ya da iki kez geçen bir token.

turnstile üzerindeki mesaj The spam check failed. Reload the page and try again. şeklindedir. Her 422 sonrasında Turnstile widget'ını sıfırlayın: token kullanılmıştır.

@borkol/forms, check içinden tam olarak bu mesajları döndürür (yalnızca sunucunun verebildiği yükleme ve Turnstile mesajları hariç); böylece sunucunun hatalarını ve kendi hatalarınızı aynı şekilde gösterebilirsiniz.

429 saatlik sınır.

Response
{
  "message": "Too many submissions. Try again later."
}

Bu yanıtta Retry-After header'ı yoktur. Dakikada 120 isteklik mağaza geneli sınır da Too Many Attempts. mesajı ve bir Retry-After header'ı ile 429 döndürebilir.

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.