Documentation menu

API 레퍼런스

POST /api/v1/send가 발송 API의 전부입니다 — 하나의 버전 관리되는 안정적인 엔드포인트입니다. MailInApp의 다른 라우트들(오직 우리 자신의 프런트엔드만 호출하며 자유롭게 바뀔 수 있는)과 달리, 이 엔드포인트는 서드파티 코드가 의존하는 계약이므로 처음부터 버전이 관리됩니다.

인증

Authorization: Bearer mia_live_...

키가 없거나 유효하지 않으면 401이 반환됩니다. 키는 대시보드의 개발자에서 관리합니다 — 빠른 시작을 참고하세요. 취소된 키는 즉시 작동을 멈춥니다.

요청

Content-Type: application/json입니다. 이 엔드포인트는 두 가지 요청 형태를 공유하며, projectId가 있는지 여부로 어느 쪽이 적용될지 결정됩니다.

자유 형식

| 필드 | 타입 | 필수 | 비고 | | --- | --- | --- | --- | | to | string | 예 | 단일 수신자 주소입니다. | | subject | string | 예 | 200자로 잘립니다. | | html | string | 예 | 그대로 발송됩니다 — 렌더링도, 병합 태그도, 스튜디오 파이프라인도 없습니다. | | text | string | 예 | 일반 텍스트 파트입니다. |

템플릿

| 필드 | 타입 | 필수 | 비고 | | --- | --- | --- | --- | | to | string | 예 | 단일 수신자 주소입니다. | | projectId | string | 예 | 여러분이 소유한 프로젝트여야 합니다 — 스튜디오 URL은 /studio/<projectId>입니다. | | mergeData | object | 아니요 | 평면 { key: value } 객체이며 최대 100개 필드입니다. 데이터 소스 행을 대신하며 — 프로젝트의 {{field}} 병합 태그로 해석됩니다. 중첩된 객체/배열은 제거되고, null/undefined는 빈 문자열이 되며, 그 외에는 모두 문자열로 변환됩니다. | | subject | string | 아니요 | 생략하면 프로젝트 이름이 기본값이 됩니다. 제목의 병합 태그는 mergeData에서 해석됩니다. 200자로 잘립니다. |

두 형태 중 어느 쪽에도 맞지 않는 본문(예: to가 없거나, html/textprojectId가 모두 없는 경우)은 400을 반환합니다.

공통 필드

| 필드 | 타입 | 필수 | 비고 | | --- | --- | --- | --- | | type | "transactional" | "marketing" | 아니요 | 기본값은 "transactional"입니다. 아래를 참고하세요. | | from | object | 아니요 | 발신 주소/이름을 호출별로 재정의합니다 — { "email": string, "name"?: string }. 아래를 참고하세요. | | senderId | string | 아니요 | from을 인라인으로 풀어 쓰는 대신 계정에 저장된 발신자 아이덴티티 중 하나를 선택해 호출별로 재정의합니다. 반드시 계정 소유여야 하며, 그렇지 않으면 400입니다. 둘 다 주어지면 from이 우선합니다. | | replyTo | string | 아니요 | 호출별 Reply-To 주소입니다. 아래를 참고하세요. |

헤더

| 헤더 | 필수 | 비고 | | --- | --- | --- | | Authorization | 예 | Bearer <apiKey>. | | Idempotency-Key | 아니요 | 멱등성을 참고하세요. |

응답

{ "id": "abc123", "status": "sent" }

status는 다음 중 하나입니다.

| 상태 | 의미 | | --- | --- | | sent | 발송 방식(SMTP 릴레이 또는 네이티브 발송)에 성공적으로 전달되었습니다. | | suppressed | 수신자가 억제 목록에 있습니다 — 트랜잭션 vs. 마케팅을 참고하세요. 이메일은 발송되지 않았습니다. 오류가 아닙니다. | | failed | 발송 시도가 실패했습니다(예: SMTP 릴레이가 설정되지 않았거나 메시지를 거부함). error 필드에 사람이 읽을 수 있는 사유가 담깁니다. |

오류 코드

| 상태 | 의미 | | --- | --- | | 400 | 잘못된 형식의 JSON, 자유 형식이나 템플릿 형태 어느 쪽에도 맞지 않는 요청, 유효하지 않은 type, 유효하지 않거나 허용되지 않는 from(발신 주소 참고), 유효하지 않은 replyTo, 또는 (템플릿 모드에서) 존재하지 않거나 소유하지 않은 projectId입니다. 계정에 작동하는 발송 방식이 설정되어 있지 않을 때도 반환됩니다. | | 401 | Authorization 헤더가 없거나 유효하지 않거나, 키가 취소된 경우입니다. | | 404 | (템플릿 모드) 프로젝트가 존재하지 않거나 계정이 소유하지 않은 경우입니다 — 다른 계정에 어떤 프로젝트 ID가 유효한지 노출되지 않도록 의도적으로 "존재하지 않음"과 동일한 응답입니다. | | 429 | 속도 제한을 초과했습니다 — 속도 제한을 참고하세요. |

요청 자체는 유효했지만 실제 발송 시도가 다운스트림에서 실패한 경우에는 (2xx가 아닌 상태 대신) status: "failed"와 함께 200이 반환됩니다. "우리가 요청을 거부했음"과 "시도했지만 나가지 않았음"을 구분해야 한다면, HTTP 상태 코드가 아니라 본문의 status를 확인하세요.

트랜잭션 vs 마케팅

type 필드는 ESP들이 트랜잭션과 마케팅 스트림을 분리하는 것과 마찬가지로, 어떤 억제 목록이 확인되는지를 제어합니다.

  • type: "transactional"(기본값) — 수신 거부 억제를 우회합니다. 비밀번호 재설정이나 주문 영수증은 수신자가 뉴스레터를 수신 거부했다는 이유만으로 차단되어서는 안 됩니다. 반송(bounce) 억제는 절대 우회하지 않습니다 — 의도와 무관하게 죽은 주소는 죽은 주소입니다.
  • type: "marketing" — 대시보드에서의 캠페인 발송과 정확히 동일하게 동작합니다: 수신 거부 억제와 반송 억제 모두에 의해 차단됩니다.

두 경우 모두 차단되면 오류가 아니라 status: "suppressed"를 반환합니다.

발신 주소

기본적으로 모든 발송은 계정에 설정된 발신자 아이덴티티를 사용합니다 — 네이티브(SES) 발송을 사용 중이라면 설정 → 도메인의 "발신 주소" 카드, 그렇지 않다면 SMTP 릴레이에 설정된 발신 주소입니다. 한 번의 호출에 대해 이를 재정의하려면 from을 전달하세요.

{
  "to": "[email protected]",
  "subject": "Your one-time code",
  "html": "<p>Your code is 123456</p>",
  "text": "Your code is 123456",
  "from": { "email": "[email protected]", "name": "FitConsent Sales Team" }
}

from이 존재하는 한 from.email은 항상 필수입니다 — 이름만 재정의하는 방법은 없으므로, 여기에 값을 넣으면 그 호출에 대해 주소와 표시 이름이 항상 완전히 대체됩니다. from.name은 선택 사항이며, 생략하면 단순한 주소만으로 발송됩니다.

계정이 검증된 도메인(네이티브/SES 발송)을 통해 발송하는 경우, from.email은 반드시 자신이 검증한 발송 도메인 중 하나에 속한 주소여야 합니다(예: [email protected]은 되지만 [email protected]은 안 됩니다) — 계정은 둘 이상의 도메인을 검증할 수 있으므로 그중 어느 것이든 허용되지만, 다른 사람의 도메인은 절대 허용되지 않습니다. 네이티브 발송은 모든 MailInApp 고객이 공유하는 하나의 플랫폼 AWS 계정에서 실행되므로, 이 제한이 바로 한 계정이 다른 계정의 검증된 도메인에서 온 것처럼 보이는 메일을 발송하지 못하도록 막는 장치입니다. SMTP 발송에는 이런 제한이 없습니다: 발송이 여러분 자신의 릴레이/자격 증명을 통해 나가므로, 이미 여러분의 릴레이 자체의 발신자 검증 규칙과 같은 수준으로 신뢰됩니다.

senderId(공통 필드 표 참고)는 대시보드에 발신자 아이덴티티를 이미 설정해 두었다면 보통 더 간단한 선택지입니다. 매 호출마다 반복 입력할 필요 없이 그 아이덴티티 자체의 {email, name, reply-to}로 해석됩니다.

유효하지 않거나 허용되지 않는 from은 발송이 시도되기 전에 400을 반환합니다.

회신 주소

기본적으로 답장은 설정 아래 발송 방식에 따라 발송 또는 도메인 페이지에 설정된 계정의 기본 Reply-To로 가거나, 아무것도 설정하지 않았다면 어디로도 가지 않습니다. 한 번의 호출에 대해 이를 재정의하려면 replyTo를 전달하세요 — 화면에 보이는 from이 no-reply 주소이지만 그래도 실제 사람이 답장을 보길 원할 때 유용합니다.

{
  "to": "[email protected]",
  "subject": "Your order shipped",
  "html": "<p>Your order is on its way.</p>",
  "text": "Your order is on its way.",
  "from": { "email": "[email protected]", "name": "FitConsent" },
  "replyTo": "[email protected]"
}

from.email과 달리 replyTo는 네이티브(SES) 발송에서 도메인 제한이 없습니다 — 발송 아이덴티티나 전달성 평판에 전혀 영향을 주지 않으며, 그저 수신자의 메일 클라이언트가 답장을 누를 때 따르는 헤더일 뿐입니다. 유효하지 않은 replyTo는 발송이 시도되기 전에 400을 반환합니다.

멱등성

재시도될 수 있는 호출(체크아웃 웹훅 재전달, 큐 컨슈머의 메시지 재처리 등)에는 Idempotency-Key 헤더를 전달하세요. 같은 계정에 대해 같은 키로 재시도된 호출은, 첫 번째 호출이 아직 진행 중이더라도 두 번째 이메일을 발송하지 않고 원래 호출의 {id, status}를 반환합니다.

키는 계정별로 범위가 지정되며 만료되지 않습니다. 이미 사용한 키를 다른 페이로드에 재사용하지 않는 것은 여러분의 책임입니다(그렇게 해도 새 페이로드를 발송하는 대신 첫 번째 호출의 결과가 반환됩니다). 키를 전달하지 않으면 매 호출이 발송됩니다.

속도 제한

세 가지 독립적인 상한이 적용됩니다.

  • API 키당: 분당 요청 수 상한입니다. 초과하면 해당 키에 대해서만 429가 반환됩니다 — 같은 계정의 다른 키는 영향을 받지 않습니다.
  • 계정당, 발송 API 할당량: 요금제에는 월별 발송 API 호출 횟수가 포함됩니다(Free는 월 500회). 초과하면 매월 1일에 할당량이 초기화될 때까지 429가 반환됩니다.
  • 계정당, 발송량: 대시보드 발송, 예약 발송, 라이프사이클 이메일, 그리고 이 API를 포함한 모든 발송 경로에 걸쳐 공유되는, 요금제의 누적 월간 발송량 상한입니다. 이는 계정의 나머지 발송을 보호하는 것과 동일한 상한입니다 — 발송 API가 별도의 예산을 받는 것은 아닙니다.

템플릿 모드 세부사항

템플릿 모드 발송은 일반적인 수신자 발송과 정확히 동일하게 연결된 프로젝트를 렌더링합니다: 폴백 엔진의 3단계 티어, 인터랙티브 블록, 그리고 저장된 연락처 행이 아니라 mergeData로부터 만들어진 개인화된 서명 라이브 뷰 링크입니다. 이후의 모든 과정은 스튜디오 기반 발송과 동일하게 동작합니다.

  • 투표, 평점, 폼 제출, 열람은 기록되며 연락처 행이 아니라 이 특정 API 호출에 귀속되어 프로젝트의 응답 뷰에 표시됩니다.
  • 프로젝트 웹훅은 다른 수신자와 마찬가지로 인터랙션 이벤트마다 발동합니다.
  • 최근 호출(상태, 수신자, 타임스탬프)은 감사(audit) 기록으로 개발자 대시보드 페이지에 나열됩니다.

대시보드 발송과 다른 한 가지: 인터랙션 뒤에 저장된 데이터 소스 행이 없으므로, 여러분 쪽에서의 조인(join)은 행 인덱스가 아니라 응답의 수신자 식별자를 기준으로 삼아야 합니다.