문서로 건너뛰기
CLI

이메일 보내기와 추적

`emails` 명령으로 메일을 보내고, 일괄로 보내고, 번역하고, 예약하고, 취소한 뒤, `tracking`으로 배달, 열람, 클릭을 추적합니다.

개요

emails 네임스페이스는 발송 API를 명령으로 옮긴 것으로, SDK의 openemail.emails 메서드마다 명령이 하나씩 있습니다. 각 명령은 엔드포인트 하나를 호출하고 반환값을 출력합니다. tracking 네임스페이스는 보낸 메일의 열람과 클릭을 읽습니다. openemail emails 대신 openemail email을 써도 됩니다.

여기의 모든 명령에는 브라우저 로그인이나 API 키 로그인과 함께 두 범위 중 하나가 필요합니다. 보내기, 번역, 취소, 일정 변경에는 emails:send, 읽기만 하는 모든 명령에는 emails:read입니다.

어떤 보내기를 쓸지

openemail send는 메일 페이지에서 설명하는 직접 작성한 명령이며, emails send를 통해 보냅니다. 터미널 앞의 사람을 위해 만들어졌습니다. --from을 빼면 보내는 주소를 골라 주고, 파일, stdin, 편집기에서 본문을 읽고, 경로로 파일을 첨부하며, 무엇이든 나가기 전에 확인할 요약을 보여 줍니다. openemail emails send는 요청 본문을 필드마다 하나씩 플래그로 받고 아무것도 묻지 않으므로, 무엇을 보낼지 정확히 아는 스크립트에 알맞습니다.

sendemails send
--from <address>--data에 들어 있지 않으면 --to처럼 필수입니다. send는 이를 생략하고 주소를 대신 고를 수 있습니다
-f, --body-file <path>본문용 파일 플래그는 없습니다. --html "$(cat body.html)"을 넘기거나, 요청 전체를 --data @email.json으로 넘기세요
-a, --attach <path>--attachments. 파일의 JSON 배열이며, 각 파일에는 filename과 base64 content가 있거나, 이미 파일에 있는 파일의 fileId가 있습니다
--at <when>--scheduled-at <when>. ISO 8601 시각이나 PT1H, P2D 같은 기간입니다. send는 10m, 2h, 1d 같은 짧은 지연도 받습니다
--undo <seconds>--cancellable-for-seconds <n>, 0에서 900까지
--translate <language>--translate '{"to":"de"}'. from, includeOriginal, subject도 받습니다
--template <id> --props <json>--template '{"id":"welcome","props":{"name":"Ada"}}'. version을 고정할 수도 있습니다
--draft <id>--draft-id <id>
--thread <id>--thread-id <id>
--tag <key=value>--tags <key=value>. 반복하거나 JSON 객체로 줍니다

emails send에만 있는 플래그가 있습니다. 한 번의 보내기에서 열람이나 클릭 추적을 끄는 --tracking, --signature, 사용자 지정 헤더용 --headers, 파일을 첨부할지 링크할지 고르는 --attachment-delivery, 그리고 본문 전체를 JSON으로 받는 --data입니다. --data는 인라인, @path로 파일에서, -로 stdin에서 받습니다.

두 명령은 끝나는 방식이 다릅니다. send는 이메일이 failed로 돌아오면 코드 1로 종료합니다. emails send는 API가 응답하기만 하면 코드 0으로 종료하므로, 출력에서 status를 확인하세요.

모든 emails 명령

send, send-batch, translate, cancel, reschedule에는 emails:send가 필요합니다. list, get, list-events, get-tracking에는 emails:read가 필요합니다. 이메일 ID는 보내기가 반환하는 대로 msg_ 뒤에 16진수 문자 24개가 붙은 형태입니다.

명령하는 일
openemail emails send --from <value> --to <a,b>이메일 한 통을 지금 보내거나, --cancellable-for-seconds로 취소 가능 시간 동안 보류하거나, --scheduled-at으로 예약합니다. 본문은 --html, --text 또는 둘 다, 저장된 --template, 저장된 --draft-id 중 하나입니다
openemail emails send-batch <emails>서로 독립된 이메일을 요청 하나로 최대 100통 보냅니다. 파일, 인라인, 또는 -로 stdin에 있는 JSON 배열에서 읽습니다. 각 항목은 emails send의 본문과 같은 형태이며, 각자 성공하거나 실패합니다
openemail emails translate --to <value>번역 보내기가 --subject, --html, --text에 대해 무엇을 배달할지 미리 봅니다. 아무것도 저장하거나 보내지 않으며, AI 작업 하나를 씁니다
openemail emails list보낸 이메일 한 페이지, 최신순. --status, --from, --broadcast-id로 좁힙니다
openemail emails get <id>보낸 이메일 한 통. 받는 사람마다의 상태, 오류, 배달 시각과 함께, 추적된 경우 전체 추적 보고서도 나옵니다
openemail emails list-events <id>보내기 한 건의 이벤트 기록, 오래된 순: 수락, 예약, 발송, 배달, 반송, 신고, 열람, 클릭 등
openemail emails get-tracking <id>보내기 한 건의 참여 보고서: 합계, 추적된 사본마다 항목 하나, 그리고 다시 쓴 모든 링크와 그 클릭
openemail emails cancel <id>대기 중이거나 예약된 이메일을 나가기 전에 멈춥니다. 확인을 요청합니다
openemail emails reschedule <id> <scheduled-at>대기 중이거나 예약된 이메일을 ISO 8601 시각이나 PT30M 같은 기간 뒤로 옮깁니다. 1초 뒤부터 365일 뒤까지 가능합니다

모든 tracking 명령

다섯 명령 모두 emails:read가 필요합니다. tracking get, list-opens, list-clicks는 메시지가 가진 두 ID 중 어느 것이든 받습니다. 보내기가 반환한 msg_ ID, 또는 tracking list와 웹훅 페이로드에 담긴 tmsg_ 추적 ID입니다.

명령하는 일
openemail tracking list기간 안에 보낸 추적 메시지 한 페이지, 최신순이며 각각 전체 보고서가 함께 있습니다. --opened와 --clicked로 좁히고, --no-opened는 아무도 열지 않은 것만 남깁니다. --days나 --minutes로 달리 정하지 않으면 기간은 30일입니다
openemail tracking get-stats참여 패널 뒤의 수치: 추적, 열람, 클릭된 메시지 수, 열람률과 클릭률, --grain 단위로 나눈 시계열, 그리고 상위 링크, 메일 클라이언트, 국가
openemail tracking get <id>메시지 하나의 참여 보고서. emails get-tracking이 반환하는 것과 같은 문서입니다
openemail tracking list-opens <id>메시지의 열람 수를 이루는 개별 열람, 최신순이며 각각 human, proxy, machine으로 표시됩니다. --include-machine은 집계되지 않은 조회도 더합니다
openemail tracking list-clicks <id>메시지 링크의 개별 클릭, 최신순이며 각각 원래 url이 함께 나옵니다. --include-machine은 링크 스캐너와 합쳐진 반복 클릭도 더합니다

tracking list와 get-stats는 메일함이 보낸 모든 추적 메시지를 다룹니다. 웹 앱에서 쓴 메일과 MCP 도구나 어시스턴트가 보낸 메일도 포함됩니다. 반면 emails list에는 API가 만든 발송 기록이 있습니다. 발송 기록이 없는 보고서는 sendId가 null입니다.

예제

직접 정한 멱등 키로 스크립트에서 보냅니다. 같은 --idempotency-key로 다시 실행하면 두 번째 이메일을 보내는 대신 첫 이메일을 replayed: true와 함께 출력합니다.

스크립트에서 보내기
openemail emails send \  --from 'Acme Billing <[email protected]>' \  --to [email protected] \  --subject 'Your September invoice' \  --html '<p>The invoice is attached. Tell me if anything on it looks wrong.</p>' \  --attachments '[{"fileId":"file_6bb640f5b99e47deb758f1f5"}]' \  --tracking '{"opens":false}' \  --idempotency-key invoice:inv_2026_09_4192 \  --json | jq -r '.id + " " + .status'

번역이 나가기 전에 사람이 읽게 합니다. 승인된 문구는 --translate 없이 일반 --subject와 --html로 보내세요. 그렇지 않으면 한 번 더 번역됩니다. --no-include-original을 주지 않으면 번역된 html에는 이미 원문이 아래에 들어 있습니다.

번역을 미리 보고 보내기
openemail emails translate --to de \  --subject 'Your September invoice' \  --html "$(cat invoice.html)" \  --json > preview.jsonjq -r .html preview.jsonopenemail emails send --from [email protected] --to [email protected] \  --subject "$(jq -r .subject preview.json)" \  --html "$(jq -r .html preview.json)"

파일에서 일괄로 보냅니다. 일괄 처리가 이루어졌다면 일부 항목이 실패해도 명령은 코드 0으로 종료하므로, failed와 각 항목의 status를 읽으세요. 같은 키로 다시 실행하면 배열 순서가 그대로인 한 이미 나간 항목은 재생하고 나머지만 보냅니다.

receipts.json
[  { "from": "[email protected]", "to": "[email protected]", "subject": "Receipt 4192", "text": "Thanks for your order." },  { "from": "[email protected]", "to": "[email protected]", "subject": "Receipt 4193", "text": "Thanks for your order." }]
일괄 보내기
openemail emails send-batch receipts.json --idempotency-key receipts:2026-09-27 --json > result.jsonjq '{ sent, failed }' result.jsonjq -r '.items[] | select(.status == "error") | "\(.index) \(.error.code)"' result.json

이메일을 예약하고, 옮기고, 취소합니다. cancel이 요청하는 확인에는 스크립트가 답할 수 없으므로 --yes가 대신 답합니다.

예약, 이동, 취소
ID=$(openemail send --from [email protected] --to [email protected] --subject "Standup notes" \  --body-file notes.md --at 2026-10-01T09:00:00Z --json | jq -r .id)openemail emails reschedule "$ID" 2026-10-01T13:00:00Zopenemail emails get "$ID" --json | jq -r '.status + " " + .scheduledAt'openemail emails cancel "$ID" --yes

실패한 보내기를 찾고 그중 하나에 무슨 일이 있었는지 읽습니다. --json 없이 파이프로 보내면 --all은 한 줄에 JSON 객체 하나를 출력합니다.

실패한 보내기 찾기
openemail emails list --status failed,partial --from [email protected] --all | jq -r .idopenemail emails get msg_3f9a1c07d2b84e6a9c5b1f20openemail emails list-events msg_3f9a1c07d2b84e6a9c5b1f20 --all --json | jq -r '.items[] | .createdAt + " " + .type'

한 주의 참여를 UTC+2 자정에 끊기는 일 단위로 읽고, 아무도 열지 않은 것을 나열하고, 한 메시지의 링크별 클릭을 셉니다.

한 주의 열람과 클릭
openemail tracking get-stats --days 7 --offset-minutes 120 --json | jq '{ tracked, openRate, clickRate }'openemail tracking list --no-opened --days 7 --all | jq -r .subjectopenemail tracking list-clicks msg_3f9a1c07d2b84e6a9c5b1f20 --all | jq -r .url | sort | uniq -c

범위, 코드, 확인

  • 브라우저 로그인은 승인 페이지에서 범위를 요청하며, openemail login --scopes emails:send,emails:read는 둘 다 미리 선택합니다. 범위가 없는 명령은 종료 코드 4와 insufficient_scope로 멈추고 그 범위를 알려 줍니다.
  • 파일 합계가 5 MB를 넘는 send --attach는 먼저 파일을 파일에 업로드하므로 files:write도 필요합니다.
  • 이 명령들은 어느 것도 인증 코드를 요구하지 않으므로, 브라우저 로그인도 API 키와 똑같이 실행합니다.
  • emails cancel은 취소하기 전에 묻고, --yes가 대신 답합니다. 무인 실행에서 --yes가 없으면 Refusing to run unattended. Pass --yes to confirm.와 종료 코드 2로 멈춥니다.
  • emails send, send-batch, reschedule은 절대 묻지 않습니다. send는 요약을 보여 주고 터미널에서만 물으며, --yes는 그것도 건너뜁니다.
  • --dry-run은 명령이 보낼 요청을 출력하고, 아무것도 보내지 않은 채 코드 0으로 종료합니다. emails translate에서는 AI 작업을 쓰지 않고, emails cancel에서는 아무것도 묻지 않습니다.

결과 페이지

emails list, emails list-events, tracking list, list-opens, list-clicks는 한 페이지를 읽습니다. --limit로 크기를 정하며, 두 emails 목록은 1에서 100(기본 25), 세 tracking 목록은 1에서 200(기본 50)입니다. --cursor는 페이지가 출력한 커서부터 이어 갑니다.

  • --all은 모든 페이지를 읽어 항목을 스트리밍합니다. 터미널에서는 표로, 파이프로 보내거나 --ndjson을 주면 한 줄에 JSON 객체 하나로 출력합니다.
  • --max <n>은 그 개수만큼 읽고 멈추며, --all을 포함합니다.
  • --json은 --all일 때도 { items, hasMore, nextCursor } 문서 하나를 출력합니다.
  • 페이지는 오프셋이 아니라 커서로 넘기므로, 페이지를 넘기는 동안 보낸 메일 때문에 행이 밀리거나 반복되지 않습니다.

알아 둘 점

  • 실행할 때마다 자체 멱등 키를 만들며, 이 키는 그 실행 안의 재시도를 보호합니다. 보내기를 두 번 실행하면 두 번 보냅니다. 두 실행이 같은 --idempotency-key를 넘길 때만 예외입니다. 같은 키에 다른 본문을 주면 idempotency_key_reuse와 종료 코드 7로 거부됩니다.
  • queued와 scheduled 메일만 취소하거나 옮길 수 있습니다. 취소 가능 시간이 없는 즉시 보내기는 요청 안에서 나가므로, ID를 손에 쥘 때쯤에는 대개 너무 늦고, 호출은 email_not_cancellable과 종료 코드 6으로 끝납니다.
  • 취소된 이메일은 계속 취소된 상태입니다. 일정 변경은 시각만 바꾸며, 기간으로 주면 서버가 요청을 받은 시점부터 셉니다. 내용을 바꾸려면 취소하고 다시 보내세요.
  • 번역을 만들 수 없으면 보내기 전체가 거부되며, 번역되지 않은 채 나가는 것은 없습니다. 번역하는 일괄 보내기에는 translate를 담은 메시지가 최대 10개까지 들어갑니다.
  • 발송 할당량을 다 쓰면 다음 달 1일까지 보내기가 send_quota_exceeded로 멈추고, AI 할당량을 다 쓰면 UTC 자정까지 번역이 ai_quota_exceeded로 멈춥니다. 둘 다 종료 코드는 8입니다.
  • oe_test_ 키로 보낸 메일은 절대 배달되지 않습니다. 상태는 sent이고 transport는 test이며, 추적되지 않습니다.
  • 픽셀도 다시 쓴 링크도 없던 메시지에 대해 emails get-tracking과 tracking get은 404, 종료 코드 5로 응답합니다. 추적되지 않은 것과 열지 않은 것은 다르기 때문입니다. 추적은 메시지를 보낼 때의 설정을 따르므로, 나중에 켜도 이전 메일에는 적용되지 않습니다.
  • 모든 수치는 최솟값입니다. 메일 클라이언트가 이미지를 막는 독자는 열람으로 집계되지 않으며, 클릭은 열람보다 더 강한 읽음의 증거입니다.
  • list-opens와 list-clicks는 추적된 것이 없는 msg_ ID에는 404로 응답하지만, tmsg_ ID는 준 그대로 받으므로 알 수 없는 ID는 빈 목록으로 돌아옵니다.
  • 일부 주소로 제한된 키는 그 주소에서 보낸 메일만 보며, 도메인 전체를 가진 키는 그 도메인의 모든 주소를 다룹니다.

모든 플래그

이 페이지는 가장 중요한 플래그만 다룹니다. openemail <command> --help는 명령이 받는 모든 인수와 플래그를 타입, 필요한 범위, 메서드와 경로, 반환값, API 레퍼런스의 참고 사항과 함께 나열합니다. --json을 붙이면 같은 도움말을 하나의 JSON 문서로 받습니다.

터미널
openemail emails --helpopenemail emails send --helpopenemail tracking list-opens --help --json

받은편지함을,
내 방식대로.

기업, AI, 에이전트, 개인 메일을 위한 이메일 인프라. 규모와 프라이버시, 통제권을 위해 만들었습니다. 이메일이 처음부터 갖췄어야 할 모든 것.

OpenEmail

기업, AI, 에이전트, 개인 메일을 위한 이메일 인프라. 규모와 프라이버시, 통제권을 위해 만들었습니다. 이메일이 처음부터 갖췄어야 할 모든 것.

© 2026 OpenEmail. 모든 권리 보유.