작동 방식
제출이 거치는 모든 검사를 순서대로, 각 검사가 반환할 수 있는 응답과 함께 설명합니다.
/f/<formId>로 오는 모든 POST는 같은 파이프라인을 거칩니다. 비용이 적은 검사가 먼저 실행되므로, 대량 요청이나 알 수 없는 폼은 거의 비용이 들지 않습니다. 처음 실패한 검사가 응답을 결정합니다.
본문을 읽기 전
- 1플러드 가드모든 폼을 합쳐 IP당 분당 600건429 rate_limited
- 2폼 조회알 수 없거나, 삭제되었거나, 비활성화된 폼404 not_found
- 3폼 레이트 리밋폼의 프리셋, 방문자별 및 폼별429 rate_limited
- 4일시 중지대시보드에서 폼이 일시 중지됨403 form_paused
POST 읽기
- 5파싱urlencoded, multipart, JSON, UTF-8, 크기 제한400, 413, 415 또는 422
- 6API 키Authorization 헤더를 보낸 경우에만403 forbidden
- 7허용 도메인Origin 또는 Referer를 폼의 도메인과 비교스팸 또는 403
- 8사람 확인확인이 켜져 있을 때 Turnstile 토큰403 또는 확인 페이지
판정
- 9검증게시된 필드가 있으면 그 기준으로422 validation_failed
- 10점수허니팟, 타이밍, 링크, 키워드, 중복5점 이상이면 스팸
- 11멱등성같은 키로 다시 보내면 첫 응답을 그대로 반환첫 응답, 409 또는 422
- 12월간 사용량워크스페이스 한도를 크게 초과429 quota_exceeded
저장됨
제출 저장됨
코드에는 201, 브라우저에는 303
- 웹훅서명됨, 약 3일간 재시도
- 알림 이메일폼의 수신자에게
- 대시보드와 API실시간, 즉시
스팸도 저장되고 성공처럼 응답하지만, 아무것도 보내지 않습니다.
단계별 동작
무엇보다 먼저 레이트 리밋
레이트 리밋은 본문을 읽기 전에 실행됩니다. 플러드 가드(IP당 분당 600건)는 폼 조회보다도 먼저 실행되므로, 대량 요청이 데이터베이스까지 닿지 않습니다. 폼 자체의 규칙은 조회 직후에 실행됩니다. 거부된 요청은 허용량을 소모하지 않습니다. 정확한 수치는 레이트 리밋에 있습니다.
파싱
본문은 urlencoded, multipart, JSON으로 파싱합니다. 텍스트는 UTF-8로 디코딩하고 NFC로 정규화하며, CRLF는 LF로 바꾸고 제어 문자는 제거합니다. _로 시작하는 이름은 _redirect, _subject 같은 예약된 지시어이며 응답으로 저장되지 않습니다. HTML 폼을 참고하세요.
누가 보냈는지
Authorization: Bearer 헤더는 선택 사항입니다. submissions:write 스코프가 있는 시크릿 키(ik_sk_)는 허용 도메인 검사와 Ikiform 사람 확인을 건너뜁니다. 퍼블리셔블 키(ik_pub_)는 키에 등록된 도메인에서만 작동합니다. 키가 없으면 Origin으로 POST를 판단합니다.
스팸
스팸은 절대 오류가 아닙니다. 스팸 모드에서 허용 도메인 정책을 통과하지 못했거나 점수가 5점 이상인 POST는 spam 상태로 저장되고, 실제 제출과 같은 성공 응답을 받습니다. 그래서 봇은 아무것도 알 수 없습니다. 스팸은 웹훅이나 이메일을 보내지 않고 사용량에도 포함되지 않습니다. 스팸 방지를 참고하세요.
저장과 전달
제출, 웹훅 전송, 알림 이메일은 하나의 데이터베이스 배치로 기록된 뒤 큐로 넘어갑니다. 웹훅 전송과 이메일이 응답을 늦추는 일은 없으며, 전송 정보 없이 제출만 저장되는 일도 없습니다.
JSON 또는 리디렉션
같은 엔드포인트가 브라우저에는 페이지로, 코드에는 JSON으로 응답합니다. Ikiform은 요청마다 다음 기준으로 판단합니다.
- 1Accept에 application/json 포함JSON
- 2본문이 JSONJSON
- 3Sec-Fetch-Mode: navigate리디렉션 또는 페이지
- 4그 외 Sec-Fetch-Mode (예: cors)JSON
- 5Sec-Fetch-Mode 없음, Accept에 text/html 포함리디렉션 또는 페이지
- 6그 밖의 모든 경우 (예: curl)JSON
처음으로 일치하는 규칙이 적용됩니다.
JSON 모드에서 성공 응답은 201 { "ok": true, "id": "sub_..." }입니다. 페이지 모드에서 성공 응답은 303 리디렉션이며, 오류가 나면 필드 문제를 레이블별로 보여 주는 호스팅 오류 페이지를 렌더링합니다.
제출이 저장된 뒤
ID
| ID | 형식 | 참고 |
|---|---|---|
| 폼 | frm_xxxxxxxxxxxxxxxx | frm_ 뒤에 무작위 문자 16자. 공개 정보이며 폼의 action URL에 들어 있습니다. |
| 제출 | sub_01k7v2c9t3e8m5q4x6z0a1b2c3 | sub_ 뒤에 26자, 시간순으로 정렬됩니다. |
| 웹훅 전송 | dlv_... | webhook-id로 전송되며, 재시도해도 바뀌지 않습니다. |
| 요청 | req_... | 모든 응답에 X-Request-Id 헤더가 있습니다. 지원팀에 문의할 때 알려 주세요. |
{ "ok": true, "id": "sub_01k7v2c9t3e8m5q4x6z0a1b2c3" }