SDK
배치 발송
`emails.sendBatch`: 최대 100개의 메시지와 항목별 결과.
emails.sendBatch
const result = await openemail.emails.sendBatch(invoices.map(toMessage)) console.log(result.sent, 'sent,', result.failed, 'failed') for (const item of result.items) { if (item.status === 'error') console.error(item.index, item.error.code, item.error.message) else console.log(item.index, item.email.id)}items에는 입력마다 하나씩 순서대로 항목이 들어가며, 각 항목은 메시지를 담은 ok이거나 그 메시지가 거부되었을 봉투를 담은 error입니다. 롤백은 없으므로 failed > 0은 배치를 다시 보내야 할 이유가 아니라 처리할 목록입니다.
하나의 멱등성 키가 배치 전체를 덮고 서버가 항목마다 그것을 확장하므로, 재시도된 배치는 모든 메시지를 재생할 뿐 첫 번째 메시지로 뭉뚱그려지지 않습니다.
매개변수: emails.sendBatch
emailsEmailSend[]필수- 1개에서 100개까지의 메시지이며, `{ "emails": [...] }`로 직렬화되어 주어진 순서대로 하나씩 처리됩니다. 빈 배열, 100개 초과, 또는 `translate`를 담은 항목이 10개를 넘으면 `emails`에 대한 `validation_error`로 호출 전체가 거부됩니다. `emails:send` 스코프 누락, 배열도 `{ emails: [...] }`도 아닌 본문, 형식이 잘못된 `Idempotency-Key`도 마찬가지이며, 모두 단 한 건의 메시지도 보내지기 전에 판정됩니다.
options.idempotencyKeystring- 프로세스를 가로질러 배치를 중복 제거합니다. 어떤 경우든 클라이언트가 호출마다 새로 생성한 키를 붙이므로 클라이언트 자체의 재시도는 결코 이중 발송을 하지 않으며, 서버는 받은 키가 무엇이든 항목마다 `key/0`, `key/1` 식으로 확장합니다. 구분자인 슬래시는 사용자 키에 들어갈 수 없는 문자이므로, 하나의 키가 100개의 메시지에 걸쳐 있어도 그것들이 첫 번째 메시지로 뭉뚱그려질 수 없습니다.
emails[].fromRecipientInput필수- 발신자입니다. 주소만 쓰거나 `Name <addr@host>` 또는 객체로 지정합니다. 기본 발신자는 없으며 키가 이 주소를 사용할 수 있어야 합니다. 거부되면 해당 항목 하나가 `from_address_forbidden` 코드의 `permission_error`로 실패합니다.
emails[].toRecipientInput | RecipientInput[]필수- 수신자는 최소 한 명이어야 하며, 하나만 주면 클라이언트가 배열로 감쌉니다. `to`, `cc`, `bcc`를 합쳐 최대 50개 주소이고, 배치 전체가 아니라 메시지 단위로 셉니다.
emails[].ccRecipientInput | RecipientInput[]- 기본값은 없음이며, `to`, `bcc`와 같은 50개 주소 합계에 포함됩니다.
emails[].bccRecipientInput | RecipientInput[]- 기본값은 없음이며, 같은 50개 주소 합계에 포함됩니다. `Bcc`는 `headers`로 설정할 수 없는 이름 중 하나이므로, 숨은 참조를 넣는 방법은 이것뿐입니다. 헤더 형태로 넣으면 주소를 가려 주는 수신자별 봉투가 무의미해집니다.
emails[].replyToRecipientInput- 답장이 가는 곳입니다. `headers` 이후에 적용되므로, 거기에 설정한 `Reply-To`가 있으면 두 번째로 추가되지 않고 덮어씁니다.
emails[].subjectstring- 최대 998자로 RFC 5322의 줄 길이 제한이며, 기본값은 빈 문자열입니다. `template`이 제목을 제공하는 경우, 빈 제목은 템플릿 자체의 제목으로 넘어갑니다.
emails[].htmlstring- HTML 파트이며 최대 100만 자이고, 두 본문이 모두 주어졌을 때 수신자가 보는 파트입니다. `html`, `text`, `template`, `draftId` 중 하나는 필수이며, 어느 것도 없는 항목은 `html`에 대한 `validation_error`로 실패합니다.
emails[].textstring- 일반 텍스트 파트이며 최대 100만 자입니다. 둘 다 보낼 수 있지만 이 경로의 모든 전송 계층은 하나의 문자열로 하나의 본문을 만들므로, `html`이 있으면 그쪽이 이깁니다.
emails[].headersRecord<string, string>- `X-*`, `List-*`, Reply-To, Precedence, Auto-Submitted, Importance, Priority, Feedback-ID만 허용됩니다. 전송 계층이 스스로 설정하는 것(From, To, Bcc, Subject, Message-ID, DKIM 및 ARC 헤더)은 조용히 버려지지 않고 `reserved_header`로 거부됩니다. 값은 최대 998자이고 CR, LF, NUL을 담을 수 없는데, 두 번째 줄은 곧 두 번째 헤더이기 때문입니다.
emails[].attachmentsAttachmentInput[]- 메시지당 파일 최대 20개이며, 인라인 파일은 디코딩 후 합계 5 MB까지이고 배치가 아니라 메시지 단위로 셉니다. `content`는 전송 시 base64이며, 바이트를 전달하면 클라이언트가 인코딩합니다. 손으로 만든 base64가 호출 스택을 터뜨리는 대표적인 지점이 바로 여기입니다. `{ fileId }` 항목은 워크스페이스에 이미 있는 파일을 가리키며 인라인 상한에 포함되지 않습니다.
emails[].threadIdstring- 기존 스레드에 답장합니다. 최대 256자입니다. 전송 계층이 이 값으로 In-Reply-To와 References를 쓰며, 답장이 대화 옆이 아니라 대화 안에 들어가게 하는 것이 바로 그것입니다.
emails[].draftIdstring- 저장된 초안의 내용을 이 봉투로 보냅니다. 최대 256자입니다. 여기서 만들어진 수신자, 제목, 헤더가 실제로 전송되는 값입니다.
emails[].template{ id, version?, props?, slots? }- 저장된 템플릿을 서버에서 렌더링합니다. id(`tpl_…`)나 slug로 지정하며, `version`으로 리비전을 고정하고 `props`/`slots`로 값을 채웁니다. 항목이 수락될 때 한 번 해석되며, `html`/`text`와 함께 쓰거나 `draftId`와 함께 쓰면 거부됩니다. 각각이 메시지 내용에 대한 또 하나의 답이기 때문입니다.
emails[].scheduledAtDate | string- `Date`, ISO-8601 시각, 또는 `PT1H` 같은 기간입니다. 최소 1초 뒤여야 하고 최대 365일 뒤까지 가능합니다. 항목마다 독립적으로 예약되므로 하나의 배치가 서로 다른 발송 시각 100개를 담을 수 있습니다.
emails[].cancellableForSecondsnumber- 즉시 발송에 대한 실행 취소 시간(초)으로, 0에서 900까지의 integer이고 기본값은 0입니다. 0보다 큰 값은 같은 항목의 `scheduledAt`과 함께 쓰면 거부되는데, 예약된 메시지는 나가기 전까지 이미 취소할 수 있기 때문입니다.
emails[].trackingTrackingRequest- `opens`와 `clicks`이며 각각 독립적으로 선택 사항이고, 각각 이 메시지 하나에 한해 설정을 덮어씁니다. 생략한 스위치는 메시지를 보내는 주소의 설정으로, 없으면 All addresses 설정으로 떨어지며, 그 둘 중 하나가 끄지 않았다면 켜져 있습니다.
emails[].tagsRecord<string, string>- 레이블은 최대 10개이며, 키는 `A-Za-z0-9_-`에서 뽑은 1자에서 64자, 값은 최대 256자입니다. 메시지에 그대로 되돌려 주고 해석하지 않습니다. `emails.list`는 `status`, `from`, `limit`, `cursor`만 받으므로, 태그는 메시지를 찾는 수단이 아니라 이미 가진 메시지에서 읽어 내는 값입니다.
emails[].translateSendTranslateOptions- 이 항목을 다른 언어로 보냅니다. 수락 시점에 해석되므로 승인된 문구가 그대로 나갑니다. 하나의 배치에서 이 값을 담을 수 있는 항목은 최대 10개입니다. 각각이 여러 번의 모델 호출을 소모하고 항목은 순서대로 처리되므로, 더 큰 배치는 발송 도중에 중단될 것입니다. 그 수를 넘으면 무엇이 보내지기 전에 `emails`에 대한 `too_many_items`로 호출 전체가 거부됩니다.
응답: BatchResultResource
itemsBatchItemResource[]- 보낸 순서대로 입력마다 하나씩 들어가는 항목입니다. 롤백은 없으므로 이것은 트랜잭션에 대한 보고가 아니라 각 메시지에 무슨 일이 있었는지에 대한 기록입니다. API는 전부 수락되었든 일부든 하나도 수락되지 않았든 207을 응답하므로 프로미스는 어느 쪽이든 resolve되며, 분기해야 할 대상은 항목별 `status`입니다.
sentnumber- 수락된 항목의 수이며, 실제로 나간 개수와는 다릅니다. 어떤 항목은 `ok`이면서도 `email.status`가 `failed`나 `partial`일 수 있는데, 행이 만들어진 뒤에 전송 계층이 메시지를 거부한 것은 거부된 요청이 아니라 배달 결과이기 때문입니다.
failednumber- `error`를 담은 항목의 수입니다. `failed > 0`은 배치를 다시 보낼 이유가 아니라 처리할 목록입니다. 수락된 메시지는 이미 나갔습니다.
items[].indexnumber- 이 항목의 메시지가 보낸 배열에서 차지하던 위치입니다. 순서뿐 아니라 필드로도 실려 있으므로, `items`를 필터링하거나 정렬하는 코드도 어떤 입력이 실패했는지 말할 수 있습니다.
items[].status'ok' | 'error'- 유니온의 판별자입니다. `ok`는 `email`을, `error`는 `error`를 담으며, 둘 다 담는 항목은 없습니다.
items[].emailSentEmailResource- 수락된 메시지이며 `ok` 항목에만 있고, 단건 발송이 반환하는 것과 같은 형태입니다. `tracking` 키는 담지 않는데, 참여 지표는 나중에 보고되고 수락 시점에는 보고할 것이 없기 때문입니다.
items[].email.replayedboolean- 파생된 `Idempotency-Key`가 이미 존재하는 발송과 일치해 새로 보내진 것이 없고 이것이 원래의 메시지일 때 true입니다.
items[].error{ type: string; code: string; message: string; param?: string }- 이 메시지 하나가 거부된 이유이며 `error` 항목에만 있습니다. API의 오류 봉투에서 `docUrl`과 `requestId`를 뺀 것으로, 그 둘은 요청을 설명하는데 요청 자체는 성공했기 때문입니다.
items[].error.typestring- 클라이언트가 분기해도 되는 분류입니다. `validation_error`, `permission_error`, `not_found_error`, `conflict_error` 등이 있습니다. 이 집합은 고정되어 있으며 `code`와 달리 늘어나지 않습니다.
items[].error.codestring- 구체적인 실패입니다. `from_address_forbidden`, `invalid_email_address`, `too_many_recipients`, `reserved_header`, `message_too_large`, `unknown_parameter` 등이며, 열려 있고 추가되므로 모르는 코드는 그 `type`으로 취급하세요.
items[].error.messagestring- 사람이 읽도록 쓰인 한 문장으로, 문제가 된 값이 있으면 그것을 짚어 줍니다. 안정적인 식별자가 아닙니다. 분기는 `code`로 하세요.
items[].error.paramstring- 거부된 필드를 해당 메시지 내부의 점 표기 경로로 나타냅니다. `to.0`, `from`, `attachments` 같은 식입니다. 실패가 특정 필드를 가리키지 않으면 없으며, 배치에서의 위치가 앞에 붙는 일은 없습니다. 그것은 `index`가 할 일입니다.