(라이트)EDI 수량·금액 자동정리
EDI·처방전 사진을 올리면 약품별 수량·처방량·단가·금액을 표로 정리해 드립니다 — 저비용·고속 라이트 버전입니다. 깨끗한 단일 표에 최적화했으며, 표가 불완전하면 자동 보정 없이 '확인 필요'로 표시합니다. 최고 정확도(자동 재추출·보정)가 필요하면 상위 버전(EDI 수량·금액 자동정리)을 사용하세요.
바로 실행
이미지를 올려 바로 실행해 결과를 확인하세요. (비로그인은 하루 실행 횟수 제한)
이미지를 자동으로 정렬·보정한 뒤 표를 읽어, 약품별 수량·일수·총처방량·단가·총금액을 추출합니다. 값은 이미지에서 읽어낸 원본이며, 계산·기준가 대조는 검증에만 씁니다. 약가코드가 비표준(대학병원 자체코드 등)이어도 약품명은 제공되고 라인별 needsReview로 확인 여부를 표시합니다.
엔드포인트
POST https://marketapi.nadoo.ai/api/v1/hira-extract-lite/extract인증
| 헤더 | 타입 | 필수 | 설명 |
|---|---|---|---|
x-api-key | string | 필수 | 대시보드 발급 API 키 (pk_live_…) |
Content-Type | string | 필수 | application/json |
Idempotency-Key | string | 선택 | 재시도 이중 과금 방지 (권장: UUID). 재시도 시 같은 키 + 같은 본문이면 재처리 없이 최초 결과를 반환하고, 같은 키로 다른 본문을 보내면 422. 키를 안 보내면 매 요청이 별건 처리됨. |
요청 본문 (JSON)
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
image | string | 택1 | base64 인코딩 이미지 |
imageUrl | string(uri) | 택1 | 이미지 https URL (서버 다운로드) |
templateId | string | 선택 | 특정 프롬프트 템플릿 버전으로 추출 (미지정 시 활성 최신본) |
응답 200 OK
| 필드 | 타입 | 설명 |
|---|---|---|
requestId | string | 요청 식별자 |
documentType | string | drug_table=약품표. 아니면 business_registration/receipt/other 등 표 없음 사유 |
foundTable | boolean | 약품 표 검출 여부 |
documentMeta | object | null | 표 밖 머리글·바닥글에서 읽은 문서 정보. 이미지에 없으면 해당 필드는 null |
documentMeta.title | string | null | 문서 제목 — 상단에 인쇄된 그대로(예 제약회사별 약소모 통계, 원외처방 통계) |
documentMeta.queryPeriod | string | null | 조회기간 — 인쇄된 그대로(예 2026-01-01~2026-01-31) |
documentMeta.printDate | string | null | 출력일자 — YYYY-MM-DD |
documentMeta.institutionName | string | null | 병의원명 — 인쇄된 그대로(예 OO내과의원, OO피부과). 법인 접두어·띄어쓰기 유지 |
documentMeta.dispensingChannel | string | null | 원내/원외 구분 — in_house=원내 | pharmacy=원외 | all=원내·원외 함께 집계 | null=표기 없음 |
documentMeta.subjectDrug | object | null | 대상 약품 — 머리글에 약품 하나가 지정되고 표는 진료과·의사·보험구분별로 나뉜 리포트(예 출력내용 : ESOP[에스올플러스정(삼익제약)])일 때만. 일반 약품표면 null |
documentMeta.subjectDrug.raw | string | 인쇄된 그대로(예 ESOP[에스올플러스정(삼익제약)]) |
documentMeta.subjectDrug.name | string | null | 약품명(코드·제약사 제외, 예 에스올플러스정) |
documentMeta.subjectDrug.manufacturer | string | null | 함께 인쇄된 제약사(예 삼익제약) |
documentMeta.subjectDrug.code | string | null | 함께 인쇄된 코드 — 병원 내부코드(예 ESOP) 또는 9자리 약가코드 |
documentMeta.documentCount | number | null | 사진에 찍힌 문서 장수. 2 이상이면 다른 문서의 품목이 빠졌을 수 있어 모든 품목을 needsReview로 표시(GREEN→YELLOW, review에 사유)하고 meta.imageIssues에 multi_document를 넣음 — 문서를 1장씩 올려주세요 |
items | object[] | 약품 라인아이템 (아래 스키마) |
items[].drugCode | string | null | 약가코드 — 보이는 그대로(9자리 아닐 수 있음) |
items[].drugName | string | null | 약품명 (코드 비표준이어도 제공). 표에 약품 컬럼이 없고 documentMeta.subjectDrug가 있으면 그 약품명을 행마다 채움(review에 출처 표시, drugCode는 비워 두고 마스터 매칭 결과는 suggestedDrugCode로만) |
items[].quantity | number | null | 수량(처방횟수·환자수 포함) |
items[].days | number | null | 일수(투약일수) |
items[].prescribedQty | number | null | 총처방량(총사용량/총투여량) |
items[].unitPrice | number | null | 단가 |
items[].totalAmount | number | null | 총금액 |
items[].codeInMaster | boolean | 약가 마스터 조회 성공 여부 |
items[].suggestedDrugCode | string | null | 코드 미조회 시 약품명 매칭으로 추정한 약가코드(교정 제안). 약품명 일치 → 같은 문서에서 원내코드·단가가 같은 행의 확정 코드 → 단가가 같은 품목 중 유사한 약품명(수량×단가=총금액 검산 통과 행만, 행에 제약사가 인쇄돼 있으면 그 제약사 품목 우선) 순으로 찾고, 출처는 review에 표시. 약품명이 행에 인쇄된 제약사와 다른 회사 품목과 일치하면 제안하지 않음. drugCode(OCR 원본)를 대체하지 않고 status도 올리지 않음. 오독·절단·원내코드 복구용 |
items[].priceCheck | string | 단가 검증 — current(현재가 일치)/historical(과거가·단가변동)/mismatch/none |
items[].status | string | GREEN(정상)/YELLOW(확인 권장)/RED(오류·확인 필요) |
items[].needsReview | boolean | 사람 확인 필요 여부 |
items[].review | string[] | 확인 사유 |
summary | object | items(행수)·needsReview(확인필요 수)·byStatus(green/yellow/red)·completeExtraction(합계 대조 전체추출 여부) |
geometry.rotation | number | 적용된 총 회전각(0/90/180/270), 반시계 방향 — 서비스 보정과 표 검출기 자체 보정을 합산한 값. 0=보정 없음. source→oriented 를 재현하려면 반시계로 이 각도만큼 회전 |
geometry.source / geometry.oriented | object | 업로드 원본 / 회전 보정 후 크기 { width, height } |
geometry.crop | object | null | 표 크롭 영역. 크롭본이 정본일 때만 채워지고, 크롭 실패·원본 재추출 시 null |
geometry.crop.corners | object[] | 회전 보정본 기준 꼭짓점 4개 { x, y } — 시계방향 좌상·우상·우하·좌하 |
geometry.crop.sourceCorners | object[] | 업로드 원본 기준 꼭짓점 4개(역회전 적용). 회전 없으면 corners와 동일 |
geometry.crop.box / sourceBox | object | 같은 영역의 { x, y, width, height } 표현 |
geometry.crop.normalized | object | 회전 보정본 기준 0~1 비율 좌표(해상도 무관) |
geometry.crop.score | number | null | 표 검출 신뢰도 |
meta | object | 진단(참고) — imageReadable·imageIssues[](사진에 문서 여러 장이면 multi_document)·rotationApplied·cropped·droppedSummaryRows·template. 외부 처리에 불필요하면 무시 가능 |
cost.krw / cost.free | number / boolean | 과금액 / 무료 처리 여부 |
balanceKrw | number | 처리 후 잔액 |
응답 예시
{
"requestId": "3f9a1c2e-...",
"documentType": "drug_table",
"foundTable": true,
"documentMeta": { "title": "원외처방 통계", "queryPeriod": "2026-01-01~2026-01-31", "printDate": "2026-02-03",
"institutionName": "OO내과의원", "dispensingChannel": "pharmacy", "subjectDrug": null, "documentCount": 1 },
"items": [
{ "drugCode": "658106350", "drugName": "OO정",
"quantity": 83, "days": null, "prescribedQty": 2355,
"unitPrice": 312, "totalAmount": 734760,
"codeInMaster": true, "priceCheck": "current",
"status": "GREEN", "needsReview": false, "review": [] }
],
"summary": { "items": 4, "needsReview": 0,
"byStatus": { "green": 4, "yellow": 0, "red": 0 },
"completeExtraction": true },
"geometry": {
"rotation": 90,
"source": { "width": 3024, "height": 4032 },
"oriented": { "width": 4032, "height": 3024 },
"crop": {
"score": 0.93,
"corners": [{"x":210,"y":340},{"x":3820,"y":340},{"x":3820,"y":2660},{"x":210,"y":2660}],
"box": { "x": 210, "y": 340, "width": 3610, "height": 2320 },
"sourceCorners": [{"x":340,"y":212},{"x":2660,"y":212},{"x":2660,"y":3822},{"x":340,"y":3822}],
"sourceBox": { "x": 340, "y": 212, "width": 2320, "height": 3610 },
"normalized": { "x": 0.052, "y": 0.112, "width": 0.895, "height": 0.767 }
}
},
"meta": { "imageReadable": true, "imageIssues": [],
"rotationApplied": 0, "cropped": true, "droppedSummaryRows": 1,
"template": { "key": "edi-extract", "version": 3 } },
"cost": { "krw": 200, "free": false },
"balanceKrw": 49700
}예시 (cURL)
curl -X POST https://marketapi.nadoo.ai/api/v1/hira-extract-lite/extract \
-H "x-api-key: pk_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"imageUrl":"https://.../edi.jpg"}'대용량 업로드 (presigned, 권장)
base64는 요청 크기 한계(본문 25MB·원본 ~18MB, 초과 시 413 payload_too_large)가 있어, 대용량·대량은 presigned 업로드가 안정적입니다(용량 무제한 — 이미지를 GCS로 직접 PUT). ① 업로드 URL 발급 → ② 이미지 PUT → ③ imageUrl로 추출.
# 1) 업로드 URL 발급
curl -X POST https://marketapi.nadoo.ai/api/v1/uploads \
-H "x-api-key: pk_live_xxxxxxxx" -H "Content-Type: application/json" \
-d '{"contentType":"image/jpeg"}'
# → { "uploadUrl": "https://storage.googleapis.com/...(PUT)", "imageUrl": "https://...(read)", "expiresIn": 3600 }
# 2) 이미지 업로드(PUT) — Content-Type 일치
curl -X PUT "<uploadUrl>" -H "Content-Type: image/jpeg" --data-binary @edi.jpg
# 3) 추출 — imageUrl 로 전달(base64 불필요)
curl -X POST https://marketapi.nadoo.ai/api/v1/hira-extract-lite/extract -H "x-api-key: pk_live_xxxxxxxx" -H "Content-Type: application/json" \
-d '{"imageUrl":"<imageUrl>"}'대량 비동기 (수천 장)
이미지 배열을 접수하고 { jobId }를 즉시 반환합니다(백그라운드 처리). GET /api/v1/jobs/{jobId}로 진행률·신호등 집계·항목별 상태를 폴링합니다. 대량은 imageUrls 권장.
curl -X POST https://marketapi.nadoo.ai/api/v1/hira-extract-lite/extract-batch-async \
-H "x-api-key: pk_live_xxxxxxxx" -H "Content-Type: application/json" \
-d '{"imageUrls":["https://.../a.jpg","https://.../b.jpg"], "templateId":"..."}'
# → { "jobId": "...", "status": "queued", "pollUrl": "/api/v1/jobs/..." }폴링 · 결과 조회
GET /api/v1/jobs/{jobId} 는 기본적으로 경량 응답(항목별 itemCount·foundTable·documentMeta·byStatus·extractionId)을 반환합니다. 수천 장 배치에서 폴링마다 전체 items를 싣지 않기 위함입니다. 실제 약품 라인아이템은 아래 두 방법으로 가져옵니다.
| 방법 | 설명 |
|---|---|
GET /api/v1/jobs/{jobId}?items=1 | 폴링 응답의 각 항목에 items(단건 /extract와 동일 스키마)를 병합해 반환. 소·중량 배치에 편리. |
GET /api/v1/extractions/{extractionId} | 항목별 extractionId로 개별 추출 상세를 단건 조회(items·summary). 대량 배치에서 필요한 항목만 골라 받을 때. |
# 1) 진행률 폴링(경량)
curl https://marketapi.nadoo.ai/api/v1/jobs/<jobId> -H "x-api-key: pk_live_xxxxxxxx"
# → { status, done, total, ok, trafficLights,
# results: [ { index, status, itemCount, foundTable, documentMeta, byStatus, extractionId } ] }
# 2) 폴링에 items 포함
curl "https://marketapi.nadoo.ai/api/v1/jobs/<jobId>?items=1" -H "x-api-key: pk_live_xxxxxxxx"
# → results[].items 에 약품 라인아이템(단건 /extract 와 동일 스키마) 포함
# 3) 개별 항목 상세(extractionId 로 온디맨드)
curl https://marketapi.nadoo.ai/api/v1/extractions/<extractionId> -H "x-api-key: pk_live_xxxxxxxx"
# → { extractionId, documentType, foundTable, documentMeta, items: [...], summary: {...} }참고: 대량 추출은 비동기(extract-batch-async)만 제공합니다. 동기 extract-batch는 없습니다(단건 /extract를 병렬 호출하거나 비동기 배치를 사용하세요).
에러 응답
| HTTP | error | 의미 |
|---|---|---|
| 401 | invalid_key | API 키 누락/무효 |
| 402 | insufficient_credit | 무료 소진 + 잔액 부족 |
| 400 | no_image / bad_json / invalid_image | 이미지 또는 JSON 본문 오류(과금 전 거절, 과금 없음) |
| 413 | payload_too_large | 요청 본문 25MB 초과 → presigned 업로드 사용 |
| 502 | processor_error | 처리 실패(과금분 자동 환불) |
| 415 | unsupported_image_format | HEIC·BMP 등 미지원 형식 → JPG·PNG·WEBP·GIF·TIFF·AVIF 로 변환(과금 없음/자동 환불) |
| 422 | idempotency_key_reuse | 같은 Idempotency-Key 로 다른 본문 → 새 키 사용 |
| 429 | rate_limited | API 키별 요청 한도 초과 → Retry-After 초 뒤 재시도(과금 없음) |
| 503 | server_busy / upstream_busy / db_unavailable | 일시적 처리 불가 → 과금분 자동 환불, Retry-After 초 뒤 같은 키·같은 본문으로 재시도 |
