물류 이메일 에이전트를 통한 배송 업데이트 전송 방법
요약
단방향 이메일 발송의 한계를 넘어, Nylas Agent Account를 활용해 고객의 문의에 자동으로 응답하는 양방향 물류 이메일 에이전트 구축 방법을 설명합니다. TMS 이벤트와 연동하여 마일스톤을 알리고, 고객의 답장을 분석해 배송 정보를 즉시 회신하는 워크플로우를 다룹니다.
핵심 포인트
- 단방향 ESP의 한계인 '답장 대응 불가' 문제 해결
- Nylas Agent Account를 통한 양방향 이메일 채널 구축
- 인바운드 이메일 웹훅을 활용한 자동화된 배송 정보 조회 및 회신
- Nylas CLI 및 curl 명령어를 이용한 실무 구현 가이드 제공
화물 고객에게 당신의 TMS(운송 관리 시스템, Transportation Management System)에서 실제로 무엇을 원하는지 물어본다면, 매번 두 가지 답변을 듣게 될 것입니다. 그들은 마일스톤(milestone)이 발생한 즉시, 선제적으로(proactively) 자신의 화물이 픽업되었고, 이동 중이며, 배송 완료되었다는 소식을 듣고 싶어 합니다. 그리고 무언가 잘못되었을 때, 티켓을 접수하거나 운송사 데스크의 연결을 기다릴 필요 없이 누군가에게 이메일을 보내 "내 화물이 어디에 있나요?"라는 질문에 빠르고 구체적인 답변을 받기를 원합니다.
대부분의 팀은 전반부만 구축하고 멈춥니다. 그들은 TMS의 마일스톤 이벤트를 SendGrid, Resend, 또는 SMTP 릴레이(relay)와 같은 단방향 이메일 서비스로 연결하여 "배송 중입니다"라는 템플릿 메시지를 발송합니다. 이는 고객이 '답장(Reply)'을 누르기 전까지는 잘 작동합니다. 하지만 답장을 보내면 메일이 반송되거나, 아무도 읽지 않는 no-reply@ 편지함으로 들어가 블랙홀처럼 사라지거나, 배차 담당자(dispatcher)가 하루에 두 번 확인하는 공유 편지함에 쌓이게 됩니다. 선제적인(proactive) 절반은 배송되었지만, 대응하는(responsive) 절반은 배송되지 않았습니다. 그리고 고객이 당신을 판단하는 지점은 바로 이 대응하는 절반입니다.
이 포스트에서는 코드가 소유하는 실제 편지함인 Nylas Agent Account를 사용하여 이 두 가지 측면을 모두 구축합니다. 아웃바운드(outbound) 측면은 마일스톤 중심을 유지합니다: 당신의 TMS 또는 운송사 피드(feed)가 이벤트를 발생시키면, 당신의 서비스가 일치하는 이메일을 보냅니다. 인바운드(inbound) 측면은 단방향 릴레이가 할 수 없는 부분입니다: "PRO 7741-022 화물은 어디에 있나요?"라는 메시지가 동일한 편지함에 도착하면, 당신의 에이전트가 이를 읽고, 당신의 시스템 내에서 해당 배송 정보를 조회한 뒤, 실제 상태를 포함하여 스레드 내에서 답장을 보냅니다. 저는 Nylas CLI를 담당하고 있으므로, 아래의 터미널 명령어들은 제가 실제로 사용하는 것들입니다. 또한 모든 작업에 대해 nylas 명령어와 원시(raw) curl 명령어를 모두 보여드릴 것입니다. 왜냐하면 당신의 마일스톤 워커(worker)는 아마도 하나를 쉘(shell)로 실행할 것이고, 당신의 서비스들은 다른 하나를 호출할 것이기 때문입니다.
왜 여기서는 실제 편지함이 단방향 ESP보다 나은가
처음부터 트레이드오프 (tradeoff)에 대해 솔직하게 말씀드리겠습니다. 이것이 바로 이 글을 계속 읽어야 하는 이유이기 때문입니다. 트랜잭션형 ESP (Transactional ESP)는 발신 측면에서는 매우 훌륭합니다. 만약 당신에게 필요한 것이 오직 "픽업됨 / 운송 중 / 배송 완료"와 같은 상태를 밀어내기(push)만 하는 것이고, 답장을 받을 필요가 전혀 없다면 ESP가 더 간단하며 그것을 사용해야 합니다. 하지만 이메일이 양방향 채널 (two-way channel)이 되어야 하는 순간, ESP 모델은 한계에 부딪힙니다.
- 마일스톤 (milestones)을 보낼 수는 있지만, 답장에 응답할 수는 없습니다. ESP는 보내고 잊어버립니다 (send and forget). 발신 주소 (From address) 뒤에 편지함 (inbox)이 없기 때문에, "내 화물은 어디에 있나요?"라는 질문은 갈 곳이 없습니다. 결국 별도의 고객 지원 시스템을 덧붙여 설치하고, 고객이 답장을 하는 대신 그 시스템을 사용하기만을 기도해야 하는 상황에 처하게 됩니다.
- 답장이 에이전트가 조치할 수 있는 곳으로 도착합니다. 에이전트 계정 (Agent Account)을 사용하면, 고객의 답장이 웹훅 (webhook)을 통해 당신의 코드가 읽을 수 있는 편지함으로 들어옵니다. 마일스톤을 보냈던 바로 그 서비스가 예외 상황에 응답할 수도 있습니다. 하나의 채널로 양방향 모두 처리하는 것입니다.
- 하나의 대화로 유지할 수 있습니다. 실제
In-Reply-To스레딩 (threading)을 수행하는 실제 편지함이기 때문에, 각 마일스톤 업데이트와 고객의 "그건 어디에 있나요?"라는 답장을 세 개의 단절된 발송 건이 아닌 하나의 스레드로 연결할 수 있습니다. (자동은 아니며, 아래에 설명된 것처럼 명시적으로 연결해야 합니다.) - 당신의 도메인에서 프로그래밍 방식으로 발송합니다. 사람이 직접 Google에 로그인하여
tracking@yourfreight.com에 대한 OAuth 동의를 부여할 필요가 없습니다. API 호출을 통해 편지함을 생성하고, 당신의 DKIM 서명으로 발송하며, 새벽 2시에 토큰이 만료되어 밤사이의 픽업 알림 배치 (batch) 작업이 중단되는 일도 없습니다.
이것이 핵심입니다. ESP는 일방향 마일스톤을 제공하지만, 에이전트 계정은 마일스톤에 답장 경로 (reply lane)까지 제공합니다.
권한 부여 (grant)가 곧 전체 데이터 평면 (data plane)입니다
이것이 처리 가능한 이유입니다. Agent Account는 그저 권한 부여(grant)일 뿐입니다. grant_id를 가지고 있으며, 이 ID는 Nylas가 이미 노출하고 있는 모든 grant 범위 엔드포인트—Messages, Threads, Folders, Drafts, Attachments—에서 작동합니다. 데이터 평면(data plane)에서 새로 배울 것은 없습니다. tracking@yourfreight.com과 같은 메일박스 하나를 프로비저닝하면, 그 이후로 마일스톤 이메일을 보내는 것은 어떤 메시지를 보낼 때 사용하는 것과 동일한 POST /v3/grants/{grant_id}/messages/send이며, 들어오는 질문을 읽는 것도 동일한 GET /v3/grants/{grant_id}/messages/{message_id}이고, 답장하는 것도 reply_to_message_id를 사용한 동일한 전송 엔드포인트입니다. 연결된 Gmail이나 Microsoft grant를 사용하여 이전에 구축해 본 적이 있다면, 이미 이 API를 알고 있습니다. 동일한 엔드포인트, 동일한 인증(auth), 동일한 페이로드입니다.
지금 명확히 해야 할 한 가지는 가장 흔한 오해 사항이기 때문입니다. 선적 데이터는 Nylas의 것이 아니라 사용자님의 것입니다. Nylas는 PRO 번호가 무엇인지 알지 못하고, 사용자의 화물이
-
Nylas API 키. 키가 없다면,
nylas init명령어를 통해 안내에 따라 계정을 생성하고 키를 발급(mint)받을 수 있습니다. 또한nylas init --api-key <your-key>를 사용하여 기존 키를 비대화형(non-interactively) 방식으로 전달할 수도 있습니다. -
발신자용 도메인. 모든 에이전트 계정(Agent Account)은 특정 도메인 상에서 동작합니다. 프로토타이핑을 위해 Nylas는 체험용
*.nylas.email서브도메인을 제공하므로, 즉시tracking@yourfreight.nylas.email과 같은 주소를 생성할 수 있습니다. 운영 환경(production)의 경우,tracking.yourfreight.com과 같은 전용 서브도메인을 등록하고 Nylas가 제공하는 DKIM 및 SPF 레코드를 게시해야 합니다. 새 도메인은 약 4주에 걸쳐 워밍업(warm up)이 필요하므로, 서비스를 시작하는 당일 아침에 도메인을 등록하지 마십시오. 또한 업데이트가 실제 화주에게 전송되는 경우, 대량 발송을 시작하기 전에 도달률 체크리스트 (deliverability checklist)를 확인하시기 바랍니다.
아래의 모든 API 예제는 미국 호스트 https://api.us.nylas.com와 베어러 토큰(bearer token) Authorization: Bearer <NYLAS_API_KEY>를 사용합니다. EU 지역의 경우 https://api.eu.nylas.com을 사용하십시오 (CLI는 동일한 목적으로 NYLAS_API_BASE_URL 환경 변수를 준수합니다).
트래킹 메일함 프로비저닝 (Provision the tracking mailbox)
이 단계는 에이전트 계정(Agent Accounts)에만 해당되는 유일한 단계이며, 단 한 줄의 명령어로 수행됩니다:
nylas agent account create tracking@tracking.yourfreight.com --name "YourFreight Tracking"
--name은 표시 이름(display name)을 설정하므로, 고객은 단순한 이메일 주소 대신 YourFreight Tracking <tracking@yourfreight.com>을 보게 됩니다. 이 명령은 새로운 권한(grant)의 id, 상태(status), 커넥터(connector) 세부 정보를 출력합니다. 아래의 모든 전송 및 읽기 작업에 사용될 핸들(handle)인 이 id를 저장해 두십시오. API는 계정을 위한 기본 워크스페이스(workspace)와 정책(policy)을 자동으로 생성하므로, 추가로 연결할 설정은 없습니다. (사용자 정의 전송/스팸 정책은 본 포스트의 범위를 벗어납니다.)
내부적으로 CLI는 provider: "nylas" 옵션과 함께 POST /v3/connect/custom을 호출하는 얇은 래퍼(thin wrapper)이며, 이는 프로비저닝 코드가 직접 호출하는 것과 동일한 호출입니다.
curl --request POST \
--url "https://api.us.nylas.com/v3/connect/custom" \
--header "Authorization: Bearer <NYLAS_API_KEY>"
...
응답에는 grant_id가 포함되어 있습니다. 이것이 귀하의 핸들(handle)입니다. 이를 다른 인프라 ID와 함께 저장하세요. 이 ID는 변경되지 않으며, 교체(rotate)해야 할 리프레시 토큰(refresh token)도 없습니다.
마일스톤 업데이트 전송하기
트리거는 귀하의 시스템에서 발생합니다. 귀하의 운송사 통합 시스템(carrier integration)이 EDI 214를 수신하거나, TMS(운송 관리 시스템)가 화물의 상태를 변경하거나, 드라이버 앱이 "배송 완료(delivered)" 이벤트를 게시할 때 발생합니다. 해당 이벤트 핸들러(event handler)가 전송 로직이 위치하는 곳입니다. Nylas는 언제 알림을 보낼지 결정하지 않으며, 귀하의 마일스톤 로직이 이를 결정합니다. 마일스톤이 발생하고 누구에게 알려야 할지 알게 되는 바로 그 순간, 귀하의 로직은 완성된 본문(body)을 Nylas에 전달합니다.
다음은 귀하가 담당하는 데이터 조립과 Nylas가 담당하는 전송 과정이 포함된 핸들러의 형태입니다:
# your_app/milestones.py — 마일스톤 이벤트가 발생할 때 실행됨
def on_milestone(event): # 귀하의 운송사/TMS 이벤트
shipment = db.get_shipment(event.pro) # 귀하의 조회(lookup)
...
render_milestone은 일반적인 템플릿("화물 {pro}가 {origin}에서 픽업되었으며 현재 운송 중입니다")이 될 수도 있고, 더 친근한 문체를 원한다면 LLM(대규모 언어 모델) 호출이 될 수도 있습니다. Nylas는 어느 쪽이든 상관하지 않습니다. body가 준비되면 프로세스에 참여하게 됩니다. 먼저 CLI 전송 예시입니다:
nylas email send tracking@tracking.yourfreight.com \
--to shipping@acmedist.com \
--subject "PRO 7741-022 picked up — in transit to Dallas, TX"
...
첫 번째 인자로 이메일 주소(또는 grant_id)를 통해 grant를 전달하며, --body는 HTML 또는 일반 텍스트를 받습니다. --from 플래그는 따로 없으며, Nylas는 발신자를 에이전트 계정(Agent Account)의 자체 주소와 표시 이름으로 기본 설정합니다. 이는 추적 통지(tracking notice)에 정확히 적합한 방식입니다.
API를 통한 동일한 작업은 POST /v3/grants/{grant_id}/messages/send입니다:
curl --request POST \
--url "https://api.us.nylas.com/v3/grants/<GRANT_ID>/messages/send" \
--header "Authorization: Bearer <NYLAS_API_KEY>"
...
그것이 픽업 (pickup) 전송입니다. 하지만 초기에 주의해야 할 점이 하나 있습니다: 개별적인 전송은 서로 스레드 (thread)로 연결되지 않습니다. 픽업, 운송 중 (in-transit), 배송 (delivery)은 세 개의 별개 messages/send 호출이며, 기본적으로 각 호출은 고객의 편지함에 완전히 새로운 이메일로 도착합니다. 즉, 하나의 추적 대화가 아니라 세 개의 단절된 메시지가 됩니다. 만약 이 메시지들을 그룹화하고 싶다면, 직접 체이닝 (chaining)을 해야 합니다. 픽업 전송이 반환하는 메시지 ID (message id)를 캡처하고, 운송 중 전송 시 reply_to_message_id를 해당 ID로 설정한 다음, 다시 그 ID를 캡처하여 배송을 그 뒤에 연결하십시오.
따라서 매 전송마다 반환되는 ID를 화물 (shipment)별로 저장하고, 이를 다음 마일스톤 (milestone)의 부모 (parent)로 전달하십시오. CLI에서는 스레드 연결 대상이 될 메시지 ID를 받는 --reply-to 플래그를 사용합니다:
# 저장해둔 픽업 메시지에 체이닝된 운송 중 (in-transit) 마일스톤
nylas email send tracking@tracking.yourfreight.com \
--to shipping@acmedist.com \
...
API에서의 동일한 체이닝 방식은 전송 본문 (send body)의 reply_to_message_id를 사용하는 것입니다:
curl --request POST \
--url "https://api.us.nylas.com/v3/grants/<GRANT_ID>/messages/send" \
--header "Authorization: Bearer <NYLAS_API_KEY>" \
...
동일한 방식으로 운송 중 ID에 배송을 체이닝하면, 고객은 화물이 이동함에 따라 확장되는 하나의 대화 내용을 보게 됩니다. 이는 또한 고객의 "물건이 어디에 있나요?"라는 답장(다음 섹션)이 동일한 스레드에 도착함을 의미합니다.
특정 시간에 마일스톤 예약하기
때로는 이벤트가 발생한 즉시 전송하고 싶지 않을 때가 있습니다. 예를 들어, 야간 픽업 건들을 일괄 처리하여 모두 오전 7시에 도착하게 하고 싶을 수 있습니다. CLI는 --schedule을 통해 전송을 보류하며, 이 플래그는 특정 시간, 2h와 같은 상대적 기간, 또는 2026-06-27 07:00 타임스탬프 (timestamp)를 허용합니다:
nylas email send tracking@tracking.yourfreight.com \
--to shipping@acmedist.com \
--subject "PRO 7741-022 picked up — in transit to Dallas, TX" \
...
API에서는 동일한 작업이 전송 본문의 send_at 필드를 통해 이루어지며, 이는 초 단위의 유닉스 타임스탬프 (Unix timestamp)입니다. 에포크 초 (epoch seconds)를 직접 계산하지 않도록 date 명령어를 사용하여 생성하십시오:
SEND_AT=$(date -d "tomorrow 07:00" +%s) # GNU date; macOS: date -j -f '%Y-%m-%d %H:%M' '2026-06-27 07:00' +%s
curl --request POST \
...
send_at은 최소 1분 이후부터 최대 30일 전까지여야 합니다. 이는 단일 메시지의 전송 시점을 변경하는 것이지, 반복 스케줄러 (recurring scheduler)가 아닙니다. 반복은 마일스톤 이벤트 (milestone events)를 통해 이루어집니다.
'내 화물은 어디 있나요?'라는 답장 받기
이 부분은 ESP (Email Service Provider)가 할 수 없는 영역입니다. 고객이 마일스톤에 대해 "PRO 7741-022 화물은 어디에 있나요? 어제 도착 예정이었는데요"라고 답장을 보내면, 해당 메시지는 트래킹 메일함 (tracking mailbox)에 도착하며 표준 message.created 웹훅 (webhook)을 발생시킵니다.
nylas webhook create \
--url https://yourfreight.com/webhooks/nylas \
--triggers message.created
--url은 핸들러 엔드포인트 (handler endpoint)이며, --triggers는 하나 이상의 트리거 (trigger) 유형을 받습니다 (쉼표로 구분하거나 반복 사용 가능). API에서의 동일한 구독은 POST /v3/webhooks입니다:
--url "https://api.us.nylas.com/v3/webhooks" \
--header "Authorization: Bearer <NYLAS_API_KEY>" \
...
이벤트에 따라 조치를 취하기 전에 올바르게 설정해야 할 두 가지 사항이 있습니다:
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기