{"openapi":"3.0.3","info":{"title":"Toile API","version":"1.0.0","description":"## Toile 가상 피팅 API\n\n고객사 쇼핑몰에서 AI 가상 피팅 기능을 사용할 수 있는 공개 API입니다.\n\n### 인증\n보호된 엔드포인트는 `x-api-key` 헤더에 API 키를 포함해야 합니다.\n대시보드 → API 키 관리에서 발급할 수 있습니다.\n\n```\nx-api-key: YOUR_API_KEY\n```\n\n### 공유 API\n`/api/share/*` 엔드포인트는 인증 없이 공개 접근 가능합니다.\n\n### 응답 형식\n성공: `{ \"data\": { ... } }`\n에러: `{ \"error\": \"ErrorType\", \"message\": \"설명\", \"statusCode\": 400 }`"},"servers":[{"url":"https://api.toile.co.kr","description":"프로덕션"}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"대시보드에서 발급한 API 키"},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"대시보드 로그인 시 발급되는 JWT (관리 API 전용)"}},"schemas":{"ErrorResponse":{"type":"object","required":["error","message","statusCode"],"properties":{"error":{"type":"string","example":"BadRequest"},"message":{"type":"string","example":"요청 형식이 올바르지 않습니다."},"statusCode":{"type":"integer","example":400}}}},"responses":{"BadRequest":{"description":"요청 형식 오류","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"BadRequest","message":"요청 형식이 올바르지 않습니다.","statusCode":400}}}},"Unauthorized":{"description":"API 키 인증 실패","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"Unauthorized","message":"유효하지 않은 API 키입니다.","statusCode":401}}}},"Forbidden":{"description":"접근 권한 없음","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"Forbidden","message":"접근 권한이 없습니다.","statusCode":403}}}},"NotFound":{"description":"리소스 없음","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"NotFound","message":"요청한 리소스를 찾을 수 없습니다.","statusCode":404}}}},"Conflict":{"description":"중복 또는 상태 충돌","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"Conflict","message":"이미 처리된 요청입니다.","statusCode":409}}}},"Gone":{"description":"만료된 리소스","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"Gone","message":"만료된 공유 링크입니다.","statusCode":410}}}},"PayloadTooLarge":{"description":"파일 크기 초과 (10MB 제한)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"PayloadTooLarge","message":"파일 크기는 10MB를 초과할 수 없습니다.","statusCode":413}}}},"TooManyRequests":{"description":"요청 한도 초과","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"TooManyRequests","message":"월별 사용량 한도를 초과했습니다.","statusCode":429}}}},"InternalServerError":{"description":"서버 내부 오류","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"InternalServerError","message":"서버 내부 오류가 발생했습니다.","statusCode":500}}}}}},"security":[{"ApiKeyAuth":[]}],"tags":[{"name":"Tryon","description":"AI 가상 피팅 요청·상태 조회·피드백·공유 링크 발급"},{"name":"Share","description":"공유 링크 공개 조회 (인증 불필요)"},{"name":"Size","description":"사이즈 추천 및 피드백"},{"name":"Webhooks","description":"웹훅 설정·테스트·재발송 (대시보드 JWT 인증)"}],"paths":{"/api/tryon":{"post":{"tags":["Tryon"],"summary":"AI 가상 피팅 요청","description":"사용자 전신 사진과 상품 이미지 URL을 전달하여 피팅 작업을 시작합니다. 202 즉시 반환 후 백그라운드에서 FASHN AI가 처리합니다. jobId로 GET /api/tryon/:jobId를 폴링하여 결과를 확인하세요.","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["userImage","productImageUrl"],"properties":{"userImage":{"type":"string","format":"binary","description":"전신 사진 (JPEG/PNG, 10MB 이하)"},"productImageUrl":{"type":"string","format":"uri","description":"상품 이미지 URL"},"category":{"type":"string","enum":["upper_body","lower_body","dresses"],"default":"upper_body","description":"의류 카테고리"},"productTitle":{"type":"string","maxLength":200,"description":"상품명 (공유 페이지 표시용)"},"productPageUrl":{"type":"string","format":"uri","description":"상품 페이지 URL (공유 링크 발급 시 사용)"}}}}}},"responses":{"202":{"description":"피팅 작업 시작됨","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"jobId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PENDING"]}}}}},"example":{"data":{"jobId":"550e8400-e29b-41d4-a716-446655440000","status":"PENDING"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"413":{"$ref":"#/components/responses/PayloadTooLarge"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/tryon/{jobId}":{"get":{"tags":["Tryon"],"summary":"피팅 작업 상태 조회","description":"피팅 작업의 현재 상태와 완료 시 결과 이미지 URL을 반환합니다. PENDING/PROCESSING 상태일 때 3~5초 간격으로 폴링하세요. COMPLETED 상태가 되면 resultImageUrl이 포함됩니다.","security":[{"ApiKeyAuth":[]}],"parameters":[{"in":"path","name":"jobId","required":true,"schema":{"type":"string","format":"uuid"},"description":"POST /api/tryon에서 받은 jobId"}],"responses":{"200":{"description":"작업 상태 및 결과","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"jobId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["PENDING","PROCESSING","COMPLETED","FAILED"]},"resultImageUrl":{"type":"string","format":"uri","description":"COMPLETED 상태일 때만 포함 (presigned URL, 1시간 유효)"},"errorMessage":{"type":"string","description":"FAILED 상태일 때만 포함"}}}}},"examples":{"pending":{"summary":"처리 중","value":{"data":{"jobId":"550e8400-e29b-41d4-a716-446655440000","status":"PENDING"}}},"completed":{"summary":"완료","value":{"data":{"jobId":"550e8400-e29b-41d4-a716-446655440000","status":"COMPLETED","resultImageUrl":"https://toile-images.s3.ap-northeast-2.amazonaws.com/..."}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/tryon/{jobId}/feedback":{"post":{"tags":["Tryon"],"summary":"피팅 품질 피드백 제출","description":"피팅 결과에 대한 품질 피드백을 제출합니다. 방문자당 1회만 제출 가능합니다.","security":[{"ApiKeyAuth":[]}],"parameters":[{"in":"path","name":"jobId","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["vote","visitorId"],"properties":{"vote":{"type":"string","enum":["GOOD","BAD"]},"visitorId":{"type":"string","format":"uuid","description":"위젯 방문자 고유 ID"},"reason":{"type":"string","maxLength":500,"description":"BAD 선택 시 사유 (선택)"}}},"example":{"vote":"GOOD","visitorId":"550e8400-e29b-41d4-a716-446655440001"}}}},"responses":{"201":{"description":"피드백 등록 성공","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"이미 피드백을 제출한 방문자","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"Conflict","message":"이미 피드백을 제출했습니다.","statusCode":409}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/tryon/{jobId}/share":{"post":{"tags":["Tryon"],"summary":"공유 링크 발급","description":"완료된 피팅 결과에 대한 7일 만료 공유 링크를 발급합니다. 유효한 토큰이 이미 있으면 기존 링크를 반환합니다(idempotent). COMPLETED 상태의 피팅에만 발급 가능합니다.","security":[{"ApiKeyAuth":[]}],"parameters":[{"in":"path","name":"jobId","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["visitorId"],"properties":{"visitorId":{"type":"string","format":"uuid"}}},"example":{"visitorId":"550e8400-e29b-41d4-a716-446655440001"}}}},"responses":{"200":{"description":"기존 유효 토큰 재사용","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"shareUrl":{"type":"string","format":"uri"},"expiresAt":{"type":"string","format":"date-time"}}}}}}}},"201":{"description":"신규 공유 링크 발급","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"shareUrl":{"type":"string","format":"uri"},"expiresAt":{"type":"string","format":"date-time"}}}}},"example":{"data":{"shareUrl":"https://toile.co.kr/share/abc123XYZ","expiresAt":"2026-05-01T00:00:00.000Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"피팅이 COMPLETED 상태가 아님","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"error":"Conflict","message":"피팅 완료 후에만 공유 링크를 발급할 수 있습니다.","statusCode":409}}}},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/share/{token}":{"get":{"tags":["Share"],"summary":"공유 링크 공개 조회","description":"공유 토큰으로 피팅 결과를 공개 조회합니다. 호출마다 viewCount가 1 증가합니다. 인증이 필요 없습니다.","security":[],"parameters":[{"in":"path","name":"token","required":true,"schema":{"type":"string"},"description":"POST /api/tryon/:jobId/share로 발급받은 공유 토큰"}],"responses":{"200":{"description":"피팅 결과 조회 성공","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"resultImageUrl":{"type":"string","format":"uri"},"productTitle":{"type":"string","nullable":true},"productPageUrl":{"type":"string","format":"uri","nullable":true},"sharedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":"string","format":"date-time"},"viewCount":{"type":"integer"}}}}},"example":{"data":{"resultImageUrl":"https://toile-images.s3.ap-northeast-2.amazonaws.com/...","productTitle":"블루 린넨 셔츠","productPageUrl":"https://shop.example.com/product/123","sharedAt":"2026-04-24T00:00:00.000Z","expiresAt":"2026-05-01T00:00:00.000Z","viewCount":5}}}}},"404":{"$ref":"#/components/responses/NotFound"},"410":{"$ref":"#/components/responses/Gone"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/share/{token}/og-image":{"get":{"tags":["Share"],"summary":"OG 이미지 302 redirect","description":"S3 presigned URL로 302 redirect합니다. SNS 공유 시 og:image 메타태그에 사용됩니다. 인증이 필요 없습니다.","security":[],"parameters":[{"in":"path","name":"token","required":true,"schema":{"type":"string"},"description":"공유 토큰"}],"responses":{"302":{"description":"S3 presigned URL로 redirect","headers":{"Location":{"schema":{"type":"string","format":"uri"},"description":"S3 presigned URL (1시간 유효)"},"Cache-Control":{"schema":{"type":"string"},"example":"public, max-age=3600"}}},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/size/recommend":{"post":{"tags":["Size"],"summary":"사이즈 추천","description":"등록된 상품과 사용자 신체 치수를 기반으로 권장 사이즈를 추천합니다. 브랜드 사이즈 차트가 등록되지 않은 경우 recommendation이 null로 반환됩니다.","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["productId","measurement"],"properties":{"productId":{"type":"string","description":"고객사에서 등록한 상품 externalId"},"measurement":{"type":"object","required":["height","weight","gender"],"properties":{"height":{"type":"number","description":"키 (cm)","maximum":300},"weight":{"type":"number","description":"몸무게 (kg)","maximum":500},"gender":{"type":"string","enum":["M","F"]},"chest":{"type":"number","description":"가슴 둘레 (cm, 선택)"},"waist":{"type":"number","description":"허리 둘레 (cm, 선택)"},"hip":{"type":"number","description":"엉덩이 둘레 (cm, 선택)"}}},"visitorId":{"type":"string","format":"uuid","description":"방문자 ID (측정 데이터 저장용, 선택)"}}},"example":{"productId":"BLUE-SHIRT-001","measurement":{"height":170,"weight":65,"gender":"M","chest":90,"waist":75},"visitorId":"550e8400-e29b-41d4-a716-446655440001"}}}},"responses":{"200":{"description":"사이즈 추천 결과","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"recommendation":{"nullable":true,"type":"object","properties":{"size":{"type":"string","description":"추천 사이즈 라벨 (자유 문자열 — \"S\"/\"M\"/\"L\"/\"XL\", \"44\"/\"55\"/\"66\"/\"77\", \"90\"/\"95\"/\"100\"/\"105\", \"26\"/\"28\"/\"30\"/\"32\", \"Free\" 등)","example":"95"},"confidence":{"type":"integer","minimum":0,"maximum":100,"description":"추천 신뢰도 (%)"}}},"product":{"type":"object","properties":{"name":{"type":"string"},"category":{"type":"string"},"brand":{"type":"string"}}}}}}},"examples":{"withRecommendation":{"summary":"추천 성공","value":{"data":{"recommendation":{"size":"M","confidence":82},"product":{"name":"블루 린넨 셔츠","category":"TOP","brand":"BRAND_A"}}}},"noChart":{"summary":"사이즈 차트 미등록","value":{"data":{"recommendation":null,"product":{"name":"블루 린넨 셔츠","category":"TOP","brand":"BRAND_A"}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/size/feedback":{"post":{"tags":["Size"],"summary":"사이즈 추천 피드백 제출","description":"사이즈 추천 결과가 실제로 도움이 되었는지 피드백을 제출합니다.","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["tryOnRequestId","recommendedSize","confidence","helpful"],"properties":{"tryOnRequestId":{"type":"string","description":"연관된 피팅 요청 ID"},"recommendedSize":{"type":"string","pattern":"^[A-Za-z0-9가-힣/+\\- ]{1,10}$","description":"추천받은 사이즈 라벨 (자유 문자열, 길이 1~10)","example":"95"},"confidence":{"type":"integer","minimum":0,"maximum":100,"description":"추천 신뢰도 (%)"},"helpful":{"type":"boolean","description":"추천이 도움이 되었는지 여부"},"comment":{"type":"string","maxLength":500,"description":"선택적 코멘트"}}},"example":{"tryOnRequestId":"550e8400-e29b-41d4-a716-446655440000","recommendedSize":"95","confidence":82,"helpful":true}}}},"responses":{"201":{"description":"피드백 등록 성공","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/size/measurements":{"post":{"tags":["Size"],"summary":"신체 측정 데이터 저장","description":"위젯에서 수집한 방문자 신체 측정 데이터를 저장합니다. 통계 수집 및 추천 정확도 개선에 사용됩니다.","security":[{"ApiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["visitorId","measurement"],"properties":{"visitorId":{"type":"string","format":"uuid"},"measurement":{"type":"object","required":["height","weight","gender"],"properties":{"height":{"type":"number","maximum":300},"weight":{"type":"number","maximum":500},"gender":{"type":"string","enum":["M","F"]},"chest":{"type":"number"},"waist":{"type":"number"},"hip":{"type":"number"}}}}},"example":{"visitorId":"550e8400-e29b-41d4-a716-446655440001","measurement":{"height":170,"weight":65,"gender":"F","chest":85,"waist":65,"hip":90}}}}},"responses":{"201":{"description":"저장 성공","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"ok":{"type":"boolean"}}}}},"example":{"data":{"ok":true}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"500":{"$ref":"#/components/responses/InternalServerError"}}}},"/api/webhooks":{"get":{"tags":["Webhooks"],"summary":"웹훅 설정 목록 조회","description":"현재 사용자의 모든 webhook 설정을 조회합니다. secret 은 절대 노출되지 않습니다.","security":[{"BearerAuth":[]}],"responses":{"200":{"description":"설정 목록","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"tags":["Webhooks"],"summary":"웹훅 설정 생성","description":"새 webhook 설정을 생성합니다. 응답에 raw secret 이 1회만 포함되며 이후 절대 다시 노출되지 않습니다.\n이 secret 으로 Standard Webhooks 스펙(HMAC-SHA256)에 따라 서명을 검증해야 합니다.\n","security":[{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri","maxLength":2048},"events":{"type":"array","items":{"type":"string","enum":["tryon.completed","tryon.failed","usage.quota_exceeded"]}}}},"example":{"url":"https://hooks.example.com/toile","events":["tryon.completed","tryon.failed"]}}}},"responses":{"201":{"description":"생성 성공 (secret 1회 노출)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"},"secret":{"type":"string","description":"HMAC 서명 검증용 raw secret. 응답 시점 이후 다시 조회 불가.","example":"whsec_a1b2c3d4..."}}}}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/api/webhooks/{id}":{"patch":{"tags":["Webhooks"],"summary":"웹훅 설정 수정","description":"url/events/isActive 중 일부 또는 전부를 수정합니다. secret 은 회전되지 않습니다.","security":[{"BearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","maxLength":2048},"events":{"type":"array","items":{"type":"string","enum":["tryon.completed","tryon.failed","usage.quota_exceeded"]}},"isActive":{"type":"boolean"}}}}}},"responses":{"200":{"description":"수정 성공"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Webhooks"],"summary":"웹훅 설정 삭제","description":"설정 + 연결된 모든 발송 로그(deliveries) 가 함께 삭제됩니다.","security":[{"BearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"삭제 성공"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/webhooks/{id}/test":{"post":{"tags":["Webhooks"],"summary":"웹훅 테스트 발송","description":"해당 설정의 url 로 `webhook.test` 페이로드를 즉시 발송하고 결과(상태/응답코드/오류메시지)를\n동기적으로 반환합니다. fetch 타임아웃은 10초입니다.\n","security":[{"BearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"발송 결과","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["PENDING","DELIVERED","FAILED"]},"attemptCount":{"type":"integer"},"lastResponseStatus":{"type":"integer","nullable":true},"lastError":{"type":"string","nullable":true}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/webhooks/{id}/deliveries":{"get":{"tags":["Webhooks"],"summary":"웹훅 발송 로그 조회","description":"해당 설정의 발송 이력을 최신순으로 조회합니다. cursor 기반 페이지네이션.","security":[{"BearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}},{"in":"query","name":"cursor","schema":{"type":"string"}},{"in":"query","name":"take","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"로그 목록","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"event":{"type":"string"},"status":{"type":"string","enum":["PENDING","DELIVERED","FAILED"]},"attemptCount":{"type":"integer"},"nextAttemptAt":{"type":"string","format":"date-time"},"lastAttemptAt":{"type":"string","format":"date-time","nullable":true},"lastResponseStatus":{"type":"integer","nullable":true},"lastError":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"}}}},"nextCursor":{"type":"string","nullable":true}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/webhooks/deliveries/{id}/replay":{"post":{"tags":["Webhooks"],"summary":"발송 실패 건 재시도","description":"단일 delivery 의 status 를 PENDING, attemptCount 를 0, nextAttemptAt 을 now 로 리셋합니다.\n리셋된 row 는 다음 폴러 틱에서 즉시 재발송 시도됩니다.\n","security":[{"BearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"리셋된 delivery"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}}}}