본문으로 건너뛰기

지원서 만들기

이력서를 올리고 내 시스템의 후보자를 공고에 추가하세요.

이미 운영 중인 시스템, 예를 들어 ATS, 소싱 도구, 채용 사이트가 후보자를 Tahoe 공고에 넣을 수 있도록 엔드포인트 두 개가 있습니다. POST /uploads는 이력서 파일을 올릴 자리를 예약하고, POST /applications는 지원서를 만듭니다. 둘 다 applications:write, Tahoe 사용자가 만든 API 키가 필요하며 expensive 속도 제한 등급에 속합니다. Sign in with Tahoe 토큰은 403 write_requires_api_key를 받습니다. 연결된 앱의 쓰기가 켜져 있으면 예외입니다. 둘 다 선택 사항인 Idempotency-Key 헤더를 받습니다.

지원서를 만든 뒤 옮기거나, 탈락시키거나, 메모를 남기려면 지원서 변경하기를 참고하세요.

호출 순서

  1. 후보자에게 이력서가 있으면 POST /uploads를 호출하고, 반환된 URL로 파일을 PUT합니다.
  2. POST /applications에 공고, 후보자, 그리고 파일을 올렸다면 그 upload_id를 resume_upload_id로 넣어 호출합니다.

POST/uploadsapplications:write

이력서 파일 하나를 스토리지로 바로 올릴 수 있는 짧은 수명의 URL을 예약합니다. API의 요청 본문은 256KB로 제한되므로 이력서는 POST /applications 안에 담아 보낼 수 없습니다. 201을 반환합니다.

필드타입설명
content_typestring, 필수application/pdf, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/msword, application/rtf, text/rtf, text/plain 중 하나.
size_bytesinteger, 필수파일의 정확한 길이입니다. 1~5,242,880(5MB).
filenamestring선택. 리크루터에게 이력서 이름으로 보이는 값입니다. 콘텐츠 타입과 확장자가 맞는 일반 파일 이름만 유지됩니다. 그 밖의 값은 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"
  }'
응답: 201
{
  "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핸들, 필수공고입니다. 게시 중이고 지원을 받는 공고여야 합니다.
candidateobject, 필수first_name, last_name(각각 1~100자), email이 필수입니다. phone에는 숫자, 공백, + ( ) . -만 쓸 수 있습니다. linkedin_url은 linkedin.com의 https 주소여야 합니다.
resume_upload_idstring선택. 파일을 올린 뒤 POST /uploads가 준 upload_id입니다.
sourceobject, 필수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"
  }'
응답: 201
{
  "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과 함께 반환합니다.

  1. source.external_id를 보냈고, 그 공고에 같은 source.system과 external_id로 만든 지원서가 이미 있는 경우.
  2. 후보자의 이메일(대소문자 구분 없음)이 이미 그 공고의 지원자인 경우. 그 지원서의 출처는 상관없습니다.

중복 여부는 요청을 검증하기 전에 판단하므로, 공고의 양식이 바뀐 뒤에도 재시도가 성공합니다. 중복 요청은 아무것도 바꾸지 않습니다. 후보자 정보를 갱신하지 않고, 중복 요청에 적힌 이력서도 쓰지 않습니다. 이 두 키는 “ATS가 배치 전체를 다시 보낸 경우”를 막고, Idempotency-Key 헤더는 HTTP 재시도 한 번을 막습니다.

이 호출이 하지 않는 일

사람을 대신해 넣는 시스템은 그 사람의 동의를 대신 줄 수 없습니다. 그래서 이 호출은 어떤 동의도 기록하지 않으며, 그런 값을 보낼 수 있는 필드도 없습니다.

  • 동의를 기록하지 않습니다. 음성 스크리닝 동의, 이용 약관 동의, 평등 고용 기회 답변이 없습니다. 그런 필드가 들어 있는 본문은 알 수 없는 필드로 거부됩니다. 스크리닝 전화에 쓰는 지원서의 전화번호는 비어 있으므로, 이렇게 만든 지원서는 걸려 오는 사전 스크리닝 전화와 연결되지 않습니다. 후보자는 공고의 공개 페이지에서 직접 지원해 참여할 수 있습니다.
  • 후보자에게는 아무것도 가지 않습니다. 확인 이메일, 상태 링크, 문자 메시지, 전화가 없습니다. 내 시스템이 알려 주지 않으면 후보자는 지원서가 만들어졌는지 모릅니다. 공고 담당자에게도 이메일이 가지 않습니다.
  • 이미 있는 정보는 유지됩니다. 후보자가 워크스페이스에 이미 있으면 그 사람에게 지원서가 연결되고, 이미 가진 정보는 유지되며 비어 있는 곳만 요청 내용으로 채웁니다. 공개 지원 양식은 반대로 작동합니다. 거기서는 본인이 자기 정보의 권위자이기 때문입니다.
  • 게시 중인 공고만 지원서를 받습니다. Tahoe에는 리크루터가 초안이나 마감된 공고에 후보자를 추가하는 방법이 없으므로, 이 호출도 추가하지 않습니다.
  • 삭제되었거나 수신 거부한 사람은 거부됩니다. 이 확인은 삭제 통지 피드가 공개하는 처리 중단 목록을 읽습니다. 목록을 읽을 수 없으면 아무것도 만들지 않고 재시도할 수 있는 오류를 반환합니다.
409 Conflict
{
  "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이 들어 있습니다.

오류

상태코드조치
400invalid_request본문 형식이 잘못되었거나, 알 수 없는 필드, 잘못된 이메일, 잘못된 source.system, 예약된 이름, 너무 긴 값이 있습니다.
400invalid_uploadPOST /uploads에서만 발생합니다. 파일 형식이나 크기를 받을 수 없습니다.
400invalid_stage그 공고의 단계가 아니거나, 마지막 단계이거나, 핸들 형식이 잘못되었습니다.
400unknown_answer_field답변이 공고 양식에 없는 질문을 가리킵니다. param은 answers[i].field_id입니다.
400duplicate_answer_field같은 질문에 두 번 답했습니다.
400invalid_answer_value값의 형태가 틀렸거나 질문의 선택지에 없습니다.
400answer_field_not_accepted평등 고용 기회 질문, 동의 체크박스, 섹션 제목, 이력서 필드입니다.
400answer_field_reserved이름, 이메일, 전화번호, LinkedIn 필드입니다. 이 값은 candidate에 넣어 보내세요.
400missing_required_answers양식의 필수 질문에 답이 없습니다. 메시지에는 질문이 표시되며 후보자 정보는 나오지 않습니다. 필수 전화번호와 이력서는 세지 않습니다.
400upload_not_foundresume_upload_id가 없거나, 만료되었거나, 다른 워크스페이스의 것입니다.
400resume_rejected올린 파일이 비어 있거나 5MB를 넘습니다. 파일은 삭제되고 업로드 ID는 소모됩니다.
403insufficient_scope키에 applications:write가 없습니다.
403write_requires_api_keySign in with Tahoe 토큰이거나, Tahoe 사용자와 연결되지 않은 자격 증명입니다.
404not_found키의 워크스페이스에 그 공고가 없거나 핸들 형식이 잘못되었습니다.
409job_not_accepting_applications공고가 게시 중이 아니거나 지원 받기 스위치가 꺼져 있습니다.
409applicant_suppressed그 사람이 삭제되었거나 Tahoe에 데이터 처리 중단을 요청했습니다. 아무것도 만들지 않았습니다. 이 사람을 더 보내지 마세요.
409applicant_unsubscribed그 사람이 이 워크스페이스에서 수신 거부했습니다. 지원서를 만들지 않았습니다. 이미 그 공고에 있는 사람은 그대로 200으로 반환됩니다.
409candidate_consent_required공고 양식에 후보자만 체크할 수 있는 필수 동의 항목이 있습니다. 메시지에 그 항목이 표시됩니다.
409upload_already_used그 업로드는 이미 지원서에 연결되었습니다. 파일을 다시 올리세요.
409upload_not_completed업로드 URL에 아직 파일이 도착하지 않았습니다. 올린 뒤 같은 ID로 다시 시도하세요.
409application_conflict동시 요청이 같은 지원서를 만들었고 다시 읽을 수 없었습니다. 다시 시도하세요.
409idempotency_key_reused같은 키를 다른 본문과 함께 썼습니다.
409idempotency_in_flight같은 키의 첫 요청이 아직 실행 중입니다. 잠시 후 다시 시도하세요.
429too_many_pending_uploadsPOST /uploads에서만 발생합니다. Retry-After(600초)만큼 기다리세요.
429suppression_check_unavailableTahoe가 처리 중단 목록을 읽지 못했습니다. 아무것도 만들지 않았습니다. 잠시 후 다시 시도하세요.
429rate_limit_exceededRetry-After만큼 기다리세요.