점검창과 https://doc.throw.im/api/v1 요청은 모두 실제 환경에서 실행됩니다.
비즈니스 플랜 토큰이 필요하며 문서·공유 생성은 저장 용량과 호출 한도에 반영됩니다.
테스트에는 GET /me로 확인한 가장 짧은 유효기간을 쓰고, 끝나면 테스트 공유를 회수하세요.
연동 설계
파일 재사용 여부에 따라 호출 방식을 고르고, 응답 식별자를 저장합니다.
문서와 공유의 차이
문서는 파일, 공유는 수신자별 링크입니다.
POST /documents
POST /shares
생성 단위
파일당 하나
수신자당 하나
멱등 키
안 받습니다
필수
형식
multipart 또는 JSON(url)
JSON · 파일을 실으면 multipart
호출 방식 선택
같은 파일을 여러 명에게:POST /documents 한 번 + 수신자별 POST /shares.
파일이 매번 다르면:POST /shares에 file 또는 url을 바로 보냅니다.
저장할 값
값
왜 필요한가
id (shr_…)
상태 조회와 회수
document.id (doc_…)
같은 파일 재사용
links.recipient
고객 발송·재안내
links.host
담당자 재입장
사용한 토큰
다른 계정의 토큰으로 조회하면 404입니다
id와 멱등 키를 발송 기록에 함께 저장하세요
응답을 놓쳤다면 24시간 안에 같은 멱등 키와 같은 요청으로 다시 호출해 결과를 재생할 수 있습니다.
24시간이 지났거나 멱등 키도 잃어버리면 목록·externalRef 검색 API가 없어 shr_…를 복구할 수 없습니다.
발송 기록에 멱등 키·공유 id·document.id·links.recipient·links.host·사용한 토큰을 함께 저장하세요.
현재 공유 상태는 필요할 때 GET /shares/{id}로 다시 확인합니다.
API로 생성한 links.host는 로그인 없이 열리는 짧은 주소입니다. 반환된 주소를 그대로 저장하고 재사용하세요. 주소를 가진 사람은 해당 문서의 호스트로 입장하므로 고객에게 전달하지 마세요.
구현 핵심
복사 가능한 요청 예시는 각 엔드포인트에 있습니다. 운영 코드에는 아래 규칙을 적용하세요.
POST /documents는 Idempotency-Key를 받지 않습니다. 대신 같은 계정이 같은 내용·같은 이름을 1분 안에 다시 올리면 앞서 등록된 문서를 반환합니다.
상황
결과
1분 안에 같은 파일·같은 이름
같은 documentId
1분이 지난 뒤 같은 파일·같은 이름
새 문서
같은 파일·다른 이름
새 문서
문서를 지운 뒤 다시 올림
새 문서
createdAt으로 재사용 여부를 확인하세요
재사용된 경우 처음 등록된 시각이 반환됩니다. 그 문서의 기존 공유는 GET /documents/{id}의 activeShares에서 확인하세요.
같은 파일을 다시 보낼 때는 documentId 재사용을 권장합니다
1분 재사용은 응답을 받지 못해 즉시 재시도할 때를 위한 안전장치입니다. 일반적인 재발송은 저장해 둔 documentId로 POST /shares를 호출하세요.
같은 이름이 이미 있을 때
새 문서의 이름 뒤에 번호가 붙습니다. 화면에 표시할 이름은 요청값이 아닌 응답의 name을 사용하세요.
계약서.pdf → 계약서 (2).pdf → 계약서 (3).pdf
파일 내용은 저장소에서 중복 제거되더라도 문서는 위 규칙에 따라 구분됩니다.
실패하면 unsupported_file_type · file_too_large ·
storage_quota_exceeded · file_required(파일도 url도 없음) ·
document_fetch_failed(url을 받지 못함) 중 하나가 돌아옵니다.
GET/documents/{id}documents:write
등록한 문서와 활성 공유 수를 조회합니다.
응답
{ "documentId": "doc_pJbchjs_mQ4QYKrkjTDkiA",
"name": "계약서.pdf",
"sizeBytes": 148231,
"createdAt": "2026-08-27T00:00:00Z",
"activeShares": 3 } // 살아 있는 공유 수
activeShares가 0이어야 force 없이 삭제할 수 있습니다.
DELETE/documents/{id}documents:write
문서를 휴지통으로 보냅니다.
활성 공유가 있으면 삭제할 수 없습니다
409 document_in_use와 details.activeShares가 반환됩니다. 공유를 먼저 회수하거나,
링크가 즉시 무효화되는 것을 확인한 뒤 ?force=true를 사용하세요.
평소
DELETE /api/v1/documents/doc_pJbchjs_mQ4QYKrkjTDkiA
살아 있는 공유가 있어도 지울 때
DELETE /api/v1/documents/doc_pJbchjs_mQ4QYKrkjTDkiA?force=true
응답
{ "id": "doc_pJbchjs_mQ4QYKrkjTDkiA",
"deleted": true,
"activeShares": 0, // force 로 지웠다면 방금 죽은 링크 수"restorableUntil": "2026-09-09T00:00:00Z" }
필드
타입
설명
activeShares
integer
삭제 시점의 활성 공유 수
restorableUntil
string
마이페이지에서 복구할 수 있는 기한
파일은 7일 뒤 영구 삭제됩니다. 그 전에는 마이페이지에서 복구할 수 있습니다.
POST/sharesshares:write
공유를 만들고 고객용 links.recipient와 담당자용 links.host를 반환합니다. 고객 링크 발송은 연동 시스템이 담당합니다.
404는 없는 공유와 남의 공유를 구분하지 않습니다. 다른 계정의 공유 존재 여부는 알려주지 않습니다.
웹훅
고객이 문서를 열면 throw가 지정한 서버로 알림을 보냅니다. 폴링 대신 쓰세요.
이벤트
언제
share.opened
고객이 문서에 실제로 들어왔을 때 발송됩니다. 링크를 누르기만 해서는 발송되지 않습니다
share.downloaded
원본을 내려받았을 때 발송됩니다
share.expired
유효기간이 끝났을 때 발송됩니다
share.memo_created
게스트가 메모를 만들었을 때 발송됩니다. 전체 공개 메모만 본문을 포함합니다
공유 생성
├─▶ 링크 열림 알림 없음
│ └─▶ 입장 ● share.opened
│ ├─▶ 다운로드 ● share.downloaded
│ └─▶ 게스트 메모 ● share.memo_created
├─▶ 유효기간 끝 ● share.expired
└─▶ 회수 알림 없음
●이 붙은 곳에서만 웹훅이 나갑니다.
share.opened는 실제 문서 진입 시 발생하며 링크 클릭과 회수는 알리지 않습니다.
공유를 생성하는 계정마다 웹훅을 등록하세요
웹훅은 등록한 계정이 만든 공유에만 동작합니다. 같은 계정에서 발급한 여러 토큰은 웹훅 등록을 공유하므로
토큰마다 중복 등록하지 않습니다. 여러 계정으로 공유한다면 계정마다 등록하되, 수신 URL은 같아도 됩니다.
X-Throw-Delivery로 중복 알림을 걸러내세요
10초 안에 2xx로 응답하고 처리는 뒤에서 하세요. 이미 처리한 전송 ID는 버립니다.
POST /webhooks — 등록
필요 권한은 shares:read입니다. 등록한 계정이 만든 공유에서만 이벤트를 받습니다.
CRM 백엔드에서 초기 연동 때 한 번 호출하세요
수신 주소를 먼저 배포한 뒤 공유를 생성하는 각 계정에서 한 번 등록합니다.
같은 계정에서 발급한 여러 토큰은 기존 등록을 함께 사용하므로 공유할 때마다 다시 등록하지 마세요.
브라우저에서 호출하면 API 토큰이 노출됩니다. 기존 등록은 GET /webhooks로 확인할 수 있습니다.
share.opened에만 포함. host는 담당자용, recipient는 입장한 고객의 링크(참가자 미확인 시 공개 게스트 링크). 사용할 수 없는 주소는 null. 기존 구독 그대로 받으며 호스트 주소는 CRM 내부에서만 보관하세요. 예시 입장권은 실제로 사용할 수 없습니다.
data.memo
object
share.memo_created에서만 반환. 전체 공개일 때만 text 포함
externalRef의 유무로 고객 링크를 구분합니다
고객 링크로 열거나 다운로드하면 고객 식별자를, 다른 경로면 null을 반환합니다.
share.expired는 특정 열람자 이벤트가 아니므로 externalRef·metadata·viewer가 모두 null입니다.
만료 건은 shareId로 저장한 발송 기록을 찾으세요.
수신 서버 예시
알림에 필요한 값만 확인해 받은 편지함에 저장한 뒤 응답합니다. 실제 채팅 알림은 작업 큐에서 보냅니다.
// 등록할 때 알려 준 그 주소
app.use(express.json())
app.post('/hooks/throw', async (req, res) => {
const deliveryId = String(req.headers['x-throw-delivery'] || '')
const payload = req.body
// API 점검 도구의 단발 테스트. 업무 이벤트로 저장하지 않는다.if (payload?.event === 'webhook.test') return res.status(204).end()
const allowed = ['share.opened', 'share.downloaded', 'share.expired', 'share.memo_created']
if (!deliveryId || !allowed.includes(payload?.event) || !payload?.data?.shareId) {
return res.status(400).end()
}
// 본문의 고객 정보는 그대로 믿지 않고, 저장해 둔 shareId로 발송 건을 찾는다.const shareRecord = await crmShares.findByThrowShareId(payload.data.shareId)
if (!shareRecord) return res.status(200).end()
// deliveryId에 UNIQUE를 걸어 같은 알림은 한 번만 저장한다.await webhookInbox.insertOnce({ deliveryId, event: payload.event, shareId: payload.data.shareId })
res.status(200).end() // 10초 안에 응답
})
받은 편지함을 처리하는 작업자가 이벤트별 문구를 만든 뒤 사내 채팅 API를 호출하세요. 자동 처리의 근거가 필요하면 저장한 shareId로 GET /shares/{id}를 조회해 현재 상태를 확인하세요.
수신 서버 체크 사항
항목
처리 방법
알려진 shareId 확인
저장된 발송 건과 대조합니다
X-Throw-Delivery 저장
재시도 중복을 차단합니다
빠르게 2xx
받은 편지함에 저장한 뒤 10초 안에 응답하고 실제 처리는 큐로 넘깁니다
재시도
2xx가 아니면 4xx를 포함해 다시 보냅니다.
1회차 즉시 → 1분 → 5분 → 30분 → 2시간 → 6시간 (총 6회 · 약 9시간)
연속 20회 실패하면 자동 비활성화됩니다. 해제 후 다시 등록하세요.
상태 확인과 관리는 마이페이지 → 웹훅에서 할 수 있습니다.
조회·해제
아래 API도 Authorization: Bearer {토큰}과 shares:read 권한이 필요합니다.
API 점검 도구의 05 웹훅에서 인증용 토큰을 입력하고 열람·다운로드·만료·메모 작성 중 테스트할 이벤트를 선택하세요. 메타데이터(키/값 최대 3쌍)를 입력한 뒤 테스트 실행을 누르면 선택한 이벤트의 임의 데이터를 점검 도구의 수신 서버로 실제 전송합니다. 같은 화면에서 수신 확인된 헤더·JSON·수신 시각을 볼 수 있습니다. 별도 CRM 수신 주소는 필요하지 않습니다.
열람에는 data.links, 메모 작성에는 data.memo가 포함됩니다. 만료 이벤트는 실제 규격과 동일하게 metadata·externalRef·viewer가 null이며 입력한 메타데이터도 전달하지 않습니다.
문서·공유·열람 기록이나 임시 웹훅 등록은 만들지 않으며, 기존 CRM에도 보내지 않습니다. JSON의 data.metadata에서 입력값을 확인하세요. 고객·담당자 정보와 data.links는 임의 데이터이며 링크는 사용할 수 없습니다. 테스트 요청에는 X-Throw-Test: true가 포함됩니다. 수신 결과는 이 화면에서만 표시하며 입력 변경·새로고침 시 지워집니다. 기존 API 토큰 사용 기록은 갱신됩니다. CRM 수신 서버 자체를 점검하려면 아래 등록 주소 테스트를 사용하세요.
POST /webhooks/{id}/test — 요청 본문은 없습니다.
등록 주소로 아래 요청을 즉시 한 번 보냅니다. 공유나 업무 이벤트를 만들지 않고 테스트 전달 이력은 DB에 기록하지 않으며, 실패해도 재시도하지 않습니다. 기존 API 토큰 사용 기록은 갱신됩니다.
실제 share.opened·share.downloaded·share.expired·share.memo_created 전달을 최신순으로 조회합니다. 각 항목에는 전송 헤더와 JSON 본문, 상태, 시도 횟수, 마지막 HTTP 응답과 실패 원인이 포함됩니다. 전송 대기 건은 전송 예정 내용이며, 수신 서버 내부 처리 완료 여부는 확인할 수 없습니다. limit은 1~100이며 기본값은 20입니다. 단발 테스트는 이 목록에 나타나지 않습니다.