메시지
수신 거부 링크와 일일 한도가 적용되는, 지원자에게 보내는 이메일.
엔드포인트 하나로, 내 공고에 지원한 사람 한 명에게 이메일을 보냅니다. 연동 프로그램이 내 도구 안에서 후보자에게 연락할 수 있게 하되, Tahoe 제품과 같은 안전장치를 적용하기 위한 것입니다. 수신 거부 링크, 수신 거부 목록, 일일 한도가 그것입니다.
POST/messagesmessages:send
이메일 한 통을 보내고, 메일 제공자가 받아들이면 200을 반환합니다. Tahoe 사용자가 만든 API 키가 필요합니다. Sign in with Tahoe 토큰은 403 write_requires_api_key를 받습니다. 연결된 앱의 쓰기가 켜져 있으면 예외입니다. messages:send는 유료 플랜 스코프입니다.
| 필드 | 타입 | 설명 |
|---|---|---|
to | object, 필수 | application_id(app_ 핸들)와 applicant_id(apl_ 핸들) 중 정확히 하나. 둘 다 있거나 둘 다 없으면 400 invalid_request입니다. 이메일 주소를 넣는 필드는 없으며 받지도 않습니다. 주소는 내 워크스페이스에 있는 그 사람의 Tahoe 기록에서 읽습니다. |
subject | string, 필수 | 1~200자. 한 줄이어야 하며 공백만 있으면 안 됩니다. |
body_text | string, 필수 | 1~20,000자이며 공백만 있으면 안 됩니다. 이메일의 텍스트 부분이고, HTML을 표시하지 않는 메일 앱을 위한 대체 내용입니다. |
body_html | string | 선택. 최대 50,000자. Tahoe가 보내기 전에 기본 서식을 제외한 모든 것을 제거합니다. |
reply_to_user | boolean | 선택. 받아 주지만 무시합니다. 답장은 항상 키가 대신하는 Tahoe 사용자에게 갑니다. |
curl -X POST https://tahoe.workonward.com/api/partner/v1/messages \
-H "Authorization: Bearer $TAHOE_API_KEY" \
-H "Idempotency-Key: msg-6Qm2-next-steps-0001" \
-H "Content-Type: application/json" \
-d '{
"to": { "application_id": "app_6Qm2xKd4Rp8v" },
"subject": "Next steps for the Backend Engineer role",
"body_text": "Hi Jane,\n\nThanks for applying. Are you free for a call on Thursday?\n\nRecruiting team",
"body_html": "<p>Hi Jane,</p><p>Thanks for applying. Are you free for a call on Thursday?</p>"
}'{
"id": "msg_Zm9vQm1hcjR3",
"status": "sent",
"to": { "application_id": "app_6Qm2xKd4Rp8v" },
"sent_at": "2026-10-08T14:03:11.482913Z"
}id는 메시지의msg_핸들입니다. 저장해 두고 해석하지 마세요.status는 메일 제공자가 메시지를 받아들였을 때sent이고, 같은 키의 이전 요청이 아직 전달 중이면queued입니다.sent는 상대가 읽었다거나 받은편지함에 도착했다는 뜻이 아닙니다.to는 보낸 핸들의 종류를 표준 형태로 되풀이합니다. 응답에는 이메일 주소, 이름, 메시지 내용이 없습니다.sent_at은 제공자가 받아들인 시각(UTC)이며,status가queued인 동안은null입니다.- 이 호출은
expensive속도 제한 등급에 속합니다. 리크루터가 메시지를 보낼 때 제품이 이벤트를 보내지 않으므로 이 호출도 이벤트를 보내지 않습니다.
받는 사람이 보는 것
- 이메일은 Tahoe 제품이 보내는 모든 이메일과 마찬가지로 내 도메인이 아니라 Tahoe의 공용 발신 주소에서 갑니다.
Reply-To는 키를 만든 Tahoe 사용자의 이메일이므로 답장은 그 사람의 메일함에 도착하며, 요청마다 바꿀 수 없습니다. Tahoe에는 받은편지함이 없고 답장을 읽지 않습니다. - 모든 메시지 끝에 Tahoe의 수신 거부 안내가 붙고 원클릭
List-Unsubscribe헤더가 포함됩니다. 끄거나 수정할 수 없습니다. 안내의 링크를 누르면 그 워크스페이스에 대해 Tahoe를 통해 보내는 이메일의 수신이 거부됩니다. - 응답을 돌려주기 전에 메일 제공자에게 전달을 넘깁니다.
example.com같은 예약된 테스트 주소로는 메일을 보내지 않으며409 recipient_unsubscribed를 반환합니다.
팀이 보는 것
메시지는 제목, 텍스트, 정리된 HTML, 수신자, 발신자, 상태와 함께 저장되며, 키를 만든 사용자가 보낸 것처럼 Tahoe의 그 사람 보낸 이메일 목록에 나타납니다. 지원서 타임라인에, 또는 수신자를 지원자로 지정했다면 워크스페이스 활동에 email_sent 항목이 생깁니다. 수신자 주소, 제목, 전달 상태가 기록되고 키를 만든 사람이 한 것으로 표시됩니다.
이메일을 보낼 수 있는 사람
키의 워크스페이스에 지원한 사람, 즉 지원자와 그 지원서만 해당합니다. 지원하지 않은 소싱한 프로필에는 이 엔드포인트로 이메일을 보낼 수 없습니다. 세 가지 기록을 확인하며, 하나라도 해당하면 발송이 중단됩니다.
- 그 워크스페이스에서 본인이 한 수신 거부
- 워크스페이스의 아웃리치 수신 거부 목록
- Tahoe의 처리 중단 및 삭제 기록. 사람의 데이터를 지운 뒤에도 유지되며 워크스페이스를 넘어 적용됩니다.
이 중 하나라도 읽을 수 없으면 아무것도 보내지 않습니다. 거부된 발송은 409 recipient_unsubscribed를 반환하며 다시 시도하지 마세요.
{
"detail": {
"code": "recipient_unsubscribed",
"type": "conflict",
"message": "This person cannot be emailed. Nothing was sent.",
"param": null
}
}body_html 필드
Tahoe 제품의 발송 경로는 로그인한 리크루터만 쓸 수 있으므로 HTML을 정리하지 않습니다. 이 엔드포인트는 호출하는 쪽이 프로그램이므로 HTML을 정리합니다.
- 유지되는 것:
a(http,https,mailto링크만 가능하며rel="noopener noreferrer nofollow"로 바뀝니다),b,strong,i,em,u,p,br,hr,div,span,ul,ol,li,blockquote,h1~h4,code,pre. 링크 주소를 제외한 모든 속성이 제거됩니다. - 내용째 제거되는 것:
script,style,iframe,object,embed,svg,math,form과 입력 요소. 그 밖의 태그는 태그만 제거되고 텍스트는 남습니다. - 이미지는 허용되지 않습니다. 정리한 뒤 남는 것이 없으면 HTML 보기에도 텍스트 부분만 씁니다.
한도
키 하나는 기본적으로 UTC 하루에 최대 200통을 보낼 수 있습니다. 분당 요청 제한과는 별개입니다. 넘으면 429 message_quota_exceeded를 반환하고, Retry-After는 UTC 자정까지 남은 초입니다. 실패한 요청은 한도를 소모하지 않습니다.
재시도
- 키는 자격 증명, 워크스페이스, 경로에 묶이며 기록은 24시간 유지됩니다. 같은 키와 같은 본문이면 첫 응답이
Idempotent-Replayed: true와 함께 돌아오고 두 번째 이메일은 보내지 않습니다. 같은 키에 다른 본문이면409 idempotency_key_reused입니다. - 키는 Tahoe의 발송 이메일 기록에도 저장됩니다. 저장된 응답이 사라졌더라도(예: 24시간 기록이 만료) 같은 요청을 반복하면 Tahoe가 이전 발송을 찾아 그대로 반환하고, 아무것도 보내지 않으며 한도도 소모하지 않습니다. 이 확인은 수신 거부 확인보다 먼저 합니다. 첫 시도 뒤에 수신 거부한 사람에게도 거부되었다고 하지 않고 메시지가 발송되었다고 알려 주기 위해서입니다.
- 오류로 끝난 요청은 키를 놓아 주므로 원인을 고친 뒤 같은 키로 다시 시도할 수 있습니다. 예외는
delivery_failed입니다. 같은 키는 같은 실패를 다시 알려 주며, 실패한 발송이 실수로 반복되지 않도록 한 설계입니다. 새 키로 다시 시도하세요.
오류
| 상태 | 코드 | 조치 |
|---|---|---|
| 400 | invalid_request | 본문 형식이 잘못되었거나, 필드가 너무 길거나, 제목에 줄바꿈이 있거나, 핸들을 둘 다 보냈거나 하나도 보내지 않았거나, email이나 reply_to처럼 이 엔드포인트가 받지 않는 필드가 있습니다. errors 목록에 필드가 표시됩니다. |
| 400 | idempotency_key_required | Idempotency-Key 헤더를 추가하세요. |
| 400 | invalid_idempotency_key | 키가 허용된 문자 8~255자가 아닙니다. |
| 403 | insufficient_scope | 키에 messages:send가 없습니다. |
| 403 | write_requires_api_key | Sign in with Tahoe 토큰이거나, Tahoe 사용자와 연결되지 않은 자격 증명입니다. |
| 404 | not_found | 지원서나 지원자가 키의 워크스페이스에 없습니다. 다른 워크스페이스의 핸들, 종류가 다른 핸들, 형식이 잘못된 핸들 모두 같은 응답을 받습니다. |
| 409 | recipient_unsubscribed | 수신 거부했거나, 처리가 중단되었거나, 삭제되었거나, 메일을 받을 수 없는 주소입니다. 아무것도 보내지 않았습니다. 다시 시도하지 마세요. |
| 409 | sender_email_missing | 키가 대신하는 Tahoe 사용자에게 이메일 주소가 없어 답장이 갈 곳이 없습니다. |
| 409 | idempotency_key_reused | 같은 키를 이전에 다른 본문과 함께 썼습니다. |
| 409 | idempotency_in_flight | 같은 키의 요청이 아직 실행 중입니다. 잠시 후 다시 시도하세요. |
| 429 | rate_limit_exceeded | 분당 한도를 넘었습니다. Retry-After만큼 기다리세요. |
| 429 | message_quota_exceeded | 키가 하루 최대치를 보냈습니다. Retry-After만큼 기다리세요. |
| 429 | limiter_unavailable | 한도를 적용하는 카운터가 응답하지 못했습니다. 아무것도 보내지 않았습니다. Retry-After 뒤에 다시 시도하세요. |
| 429 | datastore_unavailable | 데이터베이스나 수신 거부 기록이 응답하지 못했습니다. 아무것도 보내지 않았습니다. Retry-After 뒤에 다시 시도하세요. |
| 429 | delivery_failed | 메일 제공자가 거부했거나 실패했습니다. 상대에게 아무것도 가지 않았습니다. 새 Idempotency-Key로 다시 시도하세요. |
마지막 세 가지는 type: "unavailable"이며 503이 아니라 429를 씁니다. 이유는 429가 항상 요청 한도 초과는 아닙니다를 참고하세요. detail.code로 분기하세요.