지원서 만들기
이력서를 올리고 내 시스템의 후보자를 공고에 추가하세요.
이미 운영 중인 시스템, 예를 들어 ATS, 소싱 도구, 채용 사이트가 후보자를 Tahoe 공고에 넣을 수 있도록 엔드포인트 두 개가 있습니다. POST /uploads는 이력서 파일을 올릴 자리를 예약하고, POST /applications는 지원서를 만듭니다. 둘 다 applications:write, Tahoe 사용자가 만든 API 키가 필요하며 expensive 속도 제한 등급에 속합니다. Sign in with Tahoe 토큰은 403 write_requires_api_key를 받습니다. 연결된 앱의 쓰기가 켜져 있으면 예외입니다. 둘 다 선택 사항인 Idempotency-Key 헤더를 받습니다.
지원서를 만든 뒤 옮기거나, 탈락시키거나, 메모를 남기려면 지원서 변경하기를 참고하세요.
호출 순서
- 후보자에게 이력서가 있으면
POST /uploads를 호출하고, 반환된 URL로 파일을PUT합니다. POST /applications에 공고, 후보자, 그리고 파일을 올렸다면 그upload_id를resume_upload_id로 넣어 호출합니다.
POST/uploadsapplications:write
이력서 파일 하나를 스토리지로 바로 올릴 수 있는 짧은 수명의 URL을 예약합니다. API의 요청 본문은 256KB로 제한되므로 이력서는 POST /applications 안에 담아 보낼 수 없습니다. 201을 반환합니다.
| 필드 | 타입 | 설명 |
|---|---|---|
content_type | string, 필수 | application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/msword, application/rtf, text/rtf, text/plain 중 하나. |
size_bytes | integer, 필수 | 파일의 정확한 길이입니다. 1~5,242,880(5MB). |
filename | string | 선택. 리크루터에게 이력서 이름으로 보이는 값입니다. 콘텐츠 타입과 확장자가 맞는 일반 파일 이름만 유지됩니다. 그 밖의 값은 resume.<ext>로 바뀝니다. |
curl -X POST https://tahoe.workonward.com/api/partner/v1/uploads \
-H "Authorization: Bearer $TAHOE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content_type": "application/pdf",
"size_bytes": 184320,
"filename": "ada-lovelace-cv.pdf"
}'{
"object": "upload",
"upload_id": "upl_3f9c2a7e1b5d4c8f9a0e6b7d2c1a8f54",
"content_type": "application/pdf",
"size_bytes": 184320,
"max_bytes": 5242880,
"upload": {
"method": "PUT",
"url": "https://storage.example.com/partner-uploads/...",
"headers": {
"Content-Type": "application/pdf",
"Content-Length": "184320"
},
"expires_at": "2026-10-08T12:15:00.000Z"
},
"expires_at": "2026-10-08T13:00:00.000Z"
}upload.url로 PUT 요청을 보내되, 본문은 파일 바이트이고 헤더는 upload.headers에 있는 그대로 보냅니다. 타입과 길이가 서명에 포함되므로, 선언한 것과 다른 파일은 스토리지가 거부합니다.
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: application/pdf" \
--data-binary @ada-lovelace-cv.pdf- URL은 15분 동안 유효합니다(
upload.expires_at).upload_id는 한 시간 동안(expires_at)POST /applications에 쓸 수 있으며, 한 번만 쓸 수 있습니다. - 업로드 URL은 Tahoe API가 아니라 스토리지를 가리킵니다.
PUT에Authorization헤더를 보내지 마세요. - 한 워크스페이스는 지원서에 연결되지 않았고 만료되지도 않은 업로드를 최대 500개까지 가질 수 있습니다. 넘으면
Retry-After: 600과 함께429 too_many_pending_uploads를 반환합니다. Idempotency-Key를 보내면 같은 호출을 반복할 때 같은 URL을 포함한 첫 응답이Idempotent-Replayed: true와 함께 돌아옵니다. 키가 없으면 호출할 때마다 새 업로드를 예약합니다. 이벤트는 보내지 않습니다.
POST/applicationsapplications:write
후보자를 공고에 추가합니다. 지원서가 만들어지면 201과 함께 지원서를, 이 후보자가 이미 그 공고에 있으면 200과 함께 기존 지원서를 반환합니다.
| 필드 | 타입 | 설명 |
|---|---|---|
job_id | 핸들, 필수 | 공고입니다. 게시 중이고 지원을 받는 공고여야 합니다. |
candidate | object, 필수 | first_name, last_name(각각 1~100자), email이 필수입니다. phone에는 숫자, 공백, + ( ) . -만 쓸 수 있습니다. linkedin_url은 linkedin.com의 https 주소여야 합니다. |
resume_upload_id | string | 선택. 파일을 올린 뒤 POST /uploads가 준 upload_id입니다. |
source | object, 필수 | system은 ^[a-z0-9_]{2,40}$ 형식입니다. tahoe로 시작하는 이름과 Tahoe가 직접 쓰는 몇몇 이름은 예약되어 있어 거부됩니다. external_id는 선택이며 1~200자입니다. |
answers | 배열 | field_id와 value로 된 항목을 최대 100개까지 보냅니다. 각 항목은 공고의 지원서 양식에 있는 질문에 대한 답이어야 합니다. 값은 문자열, 불리언, 또는 복수 선택 질문의 문자열 목록입니다. 선택지가 있는 질문에는 그 선택지 중 하나를 보냅니다. |
stage_id | 핸들 | 선택. 기본값은 공고의 첫 단계입니다. 그 공고에 속한 단계여야 하며 Hired나 Rejected 같은 마지막 단계는 안 됩니다. |
curl -X POST https://tahoe.workonward.com/api/partner/v1/applications \
-H "Authorization: Bearer $TAHOE_API_KEY" \
-H "Idempotency-Key: push-cand-991-0001" \
-H "Content-Type: application/json" \
-d '{
"job_id": "job_7Kd2mXq4Rp8v",
"candidate": {
"first_name": "Ada",
"last_name": "Lovelace",
"email": "[email protected]",
"phone": "+1 415 555 0142",
"linkedin_url": "https://www.linkedin.com/in/ada"
},
"resume_upload_id": "upl_3f9c2a7e1b5d4c8f9a0e6b7d2c1a8f54",
"source": { "system": "acme_ats", "external_id": "cand-991" },
"answers": [
{ "field_id": "field_1696_1", "value": "I like data pipelines." },
{ "field_id": "field_1696_2", "value": ["Python", "SQL"] }
],
"stage_id": "stg_3Rp8vKd2mXq4"
}'{
"object": "application",
"id": "app_6Qm2xKd4Rp8v",
"workspace_id": "wsp_4Kd8sPm2Qx7L",
"job_id": "job_7Kd2mXq4Rp8v",
"applicant_id": "apl_5Nx3jLm7Qd2s",
"stage_id": "stg_3Rp8vKd2mXq4",
"status": "new",
"source": "partner_api:acme_ats",
"applied_at": "2026-10-08T12:00:00.000Z",
"updated_at": "2026-10-08T12:00:00.000Z",
"parse_status": "pending",
"has_resume": true,
"voice_screening_opted_out": false,
"resume_access": { "state": "open", "...": "..." },
"restricted": ["rejection_reason"],
"restricted_reason": { "rejection_reason": "scope_required:applications:internal:read" },
"links": { "self": "/api/partner/v1/applications/app_6Qm2xKd4Rp8v" }
}응답은 민감한 스코프가 없는 키에 GET /applications/{application_handle}가 반환하는 것과 같은 객체입니다. 보낸 이름, 이메일, 전화번호, LinkedIn 주소는 되풀이하지 않습니다. 키에 연락처 스코프가 있다면 지원자 조회로 다시 읽을 수 있습니다.
같은 후보자를 두 번 보내도 안전합니다
기존 지원서는 다음 순서로 찾으며, 찾으면 200과 함께 반환합니다.
source.external_id를 보냈고, 그 공고에 같은source.system과external_id로 만든 지원서가 이미 있는 경우.- 후보자의 이메일(대소문자 구분 없음)이 이미 그 공고의 지원자인 경우. 그 지원서의 출처는 상관없습니다.
중복 여부는 요청을 검증하기 전에 판단하므로, 공고의 양식이 바뀐 뒤에도 재시도가 성공합니다. 중복 요청은 아무것도 바꾸지 않습니다. 후보자 정보를 갱신하지 않고, 중복 요청에 적힌 이력서도 쓰지 않습니다. 이 두 키는 “ATS가 배치 전체를 다시 보낸 경우”를 막고, Idempotency-Key 헤더는 HTTP 재시도 한 번을 막습니다.
이 호출이 하지 않는 일
사람을 대신해 넣는 시스템은 그 사람의 동의를 대신 줄 수 없습니다. 그래서 이 호출은 어떤 동의도 기록하지 않으며, 그런 값을 보낼 수 있는 필드도 없습니다.
- 동의를 기록하지 않습니다. 음성 스크리닝 동의, 이용 약관 동의, 평등 고용 기회 답변이 없습니다. 그런 필드가 들어 있는 본문은 알 수 없는 필드로 거부됩니다. 스크리닝 전화에 쓰는 지원서의 전화번호는 비어 있으므로, 이렇게 만든 지원서는 걸려 오는 사전 스크리닝 전화와 연결되지 않습니다. 후보자는 공고의 공개 페이지에서 직접 지원해 참여할 수 있습니다.
- 후보자에게는 아무것도 가지 않습니다. 확인 이메일, 상태 링크, 문자 메시지, 전화가 없습니다. 내 시스템이 알려 주지 않으면 후보자는 지원서가 만들어졌는지 모릅니다. 공고 담당자에게도 이메일이 가지 않습니다.
- 이미 있는 정보는 유지됩니다. 후보자가 워크스페이스에 이미 있으면 그 사람에게 지원서가 연결되고, 이미 가진 정보는 유지되며 비어 있는 곳만 요청 내용으로 채웁니다. 공개 지원 양식은 반대로 작동합니다. 거기서는 본인이 자기 정보의 권위자이기 때문입니다.
- 게시 중인 공고만 지원서를 받습니다. Tahoe에는 리크루터가 초안이나 마감된 공고에 후보자를 추가하는 방법이 없으므로, 이 호출도 추가하지 않습니다.
- 삭제되었거나 수신 거부한 사람은 거부됩니다. 이 확인은 삭제 통지 피드가 공개하는 처리 중단 목록을 읽습니다. 목록을 읽을 수 없으면 아무것도 만들지 않고 재시도할 수 있는 오류를 반환합니다.
{
"detail": {
"code": "applicant_suppressed",
"type": "conflict",
"message": "This person has asked Tahoe to stop processing their data. No application was created, and you should stop sending this person.",
"param": null
}
}리크루터가 보는 것
지원서는 공고의 파이프라인에서 고른 단계(기본은 첫 단계)에 상태 new, 출처 partner_api:<system>로 나타납니다. partner_api: 접두사는 Tahoe가 붙이므로 acme_ats만 보내면 됩니다. 활동 기록에는 키를 만든 사람이 한 것으로 “지원서 접수” 항목이 남습니다. 이력서는 채용 게시판 지원서와 같은 백그라운드 작업이 읽고, 리크루터는 같은 접근 규칙에 따라 Tahoe에서 이력서를 봅니다. 워크스페이스에는 앱 안 알림 “새 지원서가 접수되었습니다”가 갑니다.
파일은 지원서를 만들 때 다시 확인합니다. 파일이 있어야 하고, 비어 있지 않아야 하며, 5MB 이하여야 합니다. 확인이 끝나면 채용 게시판이 쓰는 것과 같은 비공개 위치로 복사됩니다. 지원서에 연결되지 않은 업로드는 한 시간 뒤 만료됩니다.
이벤트
지원서가 만들어질 때만 보내며, 200일 때는 보내지 않습니다. source가 partner_api:<system>인 application.created를 보내고, 후보자가 워크스페이스에 없었다면 applicant.created도 보냅니다. 둘 다 식별자만 담고 origin이 들어 있습니다.
오류
| 상태 | 코드 | 조치 |
|---|---|---|
| 400 | invalid_request | 본문 형식이 잘못되었거나, 알 수 없는 필드, 잘못된 이메일, 잘못된 source.system, 예약된 이름, 너무 긴 값이 있습니다. |
| 400 | invalid_upload | POST /uploads에서만 발생합니다. 파일 형식이나 크기를 받을 수 없습니다. |
| 400 | invalid_stage | 그 공고의 단계가 아니거나, 마지막 단계이거나, 핸들 형식이 잘못되었습니다. |
| 400 | unknown_answer_field | 답변이 공고 양식에 없는 질문을 가리킵니다. param은 answers[i].field_id입니다. |
| 400 | duplicate_answer_field | 같은 질문에 두 번 답했습니다. |
| 400 | invalid_answer_value | 값의 형태가 틀렸거나 질문의 선택지에 없습니다. |
| 400 | answer_field_not_accepted | 평등 고용 기회 질문, 동의 체크박스, 섹션 제목, 이력서 필드입니다. |
| 400 | answer_field_reserved | 이름, 이메일, 전화번호, LinkedIn 필드입니다. 이 값은 candidate에 넣어 보내세요. |
| 400 | missing_required_answers | 양식의 필수 질문에 답이 없습니다. 메시지에는 질문이 표시되며 후보자 정보는 나오지 않습니다. 필수 전화번호와 이력서는 세지 않습니다. |
| 400 | upload_not_found | resume_upload_id가 없거나, 만료되었거나, 다른 워크스페이스의 것입니다. |
| 400 | resume_rejected | 올린 파일이 비어 있거나 5MB를 넘습니다. 파일은 삭제되고 업로드 ID는 소모됩니다. |
| 403 | insufficient_scope | 키에 applications:write가 없습니다. |
| 403 | write_requires_api_key | Sign in with Tahoe 토큰이거나, Tahoe 사용자와 연결되지 않은 자격 증명입니다. |
| 404 | not_found | 키의 워크스페이스에 그 공고가 없거나 핸들 형식이 잘못되었습니다. |
| 409 | job_not_accepting_applications | 공고가 게시 중이 아니거나 지원 받기 스위치가 꺼져 있습니다. |
| 409 | applicant_suppressed | 그 사람이 삭제되었거나 Tahoe에 데이터 처리 중단을 요청했습니다. 아무것도 만들지 않았습니다. 이 사람을 더 보내지 마세요. |
| 409 | applicant_unsubscribed | 그 사람이 이 워크스페이스에서 수신 거부했습니다. 지원서를 만들지 않았습니다. 이미 그 공고에 있는 사람은 그대로 200으로 반환됩니다. |
| 409 | candidate_consent_required | 공고 양식에 후보자만 체크할 수 있는 필수 동의 항목이 있습니다. 메시지에 그 항목이 표시됩니다. |
| 409 | upload_already_used | 그 업로드는 이미 지원서에 연결되었습니다. 파일을 다시 올리세요. |
| 409 | upload_not_completed | 업로드 URL에 아직 파일이 도착하지 않았습니다. 올린 뒤 같은 ID로 다시 시도하세요. |
| 409 | application_conflict | 동시 요청이 같은 지원서를 만들었고 다시 읽을 수 없었습니다. 다시 시도하세요. |
| 409 | idempotency_key_reused | 같은 키를 다른 본문과 함께 썼습니다. |
| 409 | idempotency_in_flight | 같은 키의 첫 요청이 아직 실행 중입니다. 잠시 후 다시 시도하세요. |
| 429 | too_many_pending_uploads | POST /uploads에서만 발생합니다. Retry-After(600초)만큼 기다리세요. |
| 429 | suppression_check_unavailable | Tahoe가 처리 중단 목록을 읽지 못했습니다. 아무것도 만들지 않았습니다. 잠시 후 다시 시도하세요. |
| 429 | rate_limit_exceeded | Retry-After만큼 기다리세요. |