SES 연동 API
워크스페이스에 AWS SES 이메일 공급자를 연결하는 온보딩 API입니다. 대시보드 로그인 세션(JWT) 인증을 사용하며, 인증 방법은 대시보드 인증을 참고하세요. 위저드 화면 흐름은 가이드 › 이메일 발송(SES)에서 다루며, 이 페이지는 API 명세만 설명합니다.
- 성공 응답은 별도 봉투 없이 본문 JSON을 그대로 반환합니다.
- 에러는
application/problem+json(RFC 7807) 형식입니다. 이 API에는 전용 에러 코드가 없어code는NOTI-{HTTP 상태}00형식(예: 404 →NOTI-40400)으로 채워집니다. - 요청 본문에 정의되지 않은 필드가 있으면 400으로 거부됩니다.
provision vs adopt
도메인을 연결하는 방법은 두 가지입니다.
| 모드 | 대상 | 동작 | 필요 IAM 액션 |
|---|---|---|---|
provision | SES를 처음 설정하는 경우 | 도메인 identity, custom MAIL FROM, 구성세트, SNS 전달추적을 일괄 생성합니다. | 전체 세트(17개) |
adopt | 이미 검증이 끝난 SES를 쓰는 경우 | AWS에 거의 읽기 전용으로 동작합니다. identity·DKIM·MAIL FROM은 변경하지 않으며, enableTracking: true일 때만 SNS 이벤트 대상을 부착합니다. | 경량 세트(6개, 추적 시 +9개) |
공통: AWS 자격증명 필드
본문을 받는 POST 5종(credentials/verify, identities, identities/email, domain/connect, domain/adopt)은 아래 공통 필드를 받습니다.
| 필드 | 타입 | 제약 |
|---|---|---|
accessKeyId | string | 선택. 최대 128자 |
secretAccessKey | string | 선택. 최대 256자 |
region | string | 필수. 최대 64자. SES identity는 리전 단위로 관리됩니다. 기본 추천 리전은 ap-northeast-2입니다. |
저장된 자격증명 폴백 — accessKeyId와 secretAccessKey를 둘 다 비우면 워크스페이스에 저장된(암호화) 자격증명으로 폴백하므로, 연결 후에는 키를 다시 입력할 필요가 없습니다.
둘 중 하나만 보내면 400, 저장본도 없으면 400이 반환됩니다.
region은 폴백 시에도 항상 본문 값을 사용합니다(리전 변경 허용).
필요 IAM 액션 목록
IAM 정책·CloudFormation 템플릿 구성에 필요한 최소권한 액션 목록을 조회합니다. (역할: owner / admin)
| 쿼리 파라미터 | 타입 | 설명 |
|---|---|---|
mode | string | 선택. adopt면 채택용 경량 세트, 그 외 모든 값은 provision(전체 세트)으로 처리됩니다. |
tracking | string | 선택. true 또는 1이면 전달추적용 SNS 액션을 추가합니다. adopt 모드에서만 의미가 있습니다. |
{
"actions": ["ses:GetAccount", "ses:GetEmailIdentity", "ses:ListEmailIdentities", "ses:GetConfigurationSet", "ses:SendEmail", "ses:SendRawEmail"]
}위 예시는 ?mode=adopt 응답입니다.
AWS를 호출하지 않는 정적 목록이므로 별도 에러가 없습니다.
자격증명 검증
AWS 키의 유효성과 샌드박스 여부·발신 한도를 확인합니다. (역할: owner / admin) 요청 필드는 공통 자격증명 필드 3개가 전부입니다.
curl -X POST https://api.posmit.io/v1/ses/credentials/verify \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "accessKeyId": "AKIA...", "secretAccessKey": "wJalr...", "region": "ap-northeast-2" }'{
"valid": true,
"sandbox": true,
"sendingEnabled": true,
"enforcementStatus": "HEALTHY",
"sendQuota": { "max24HourSend": 200, "sentLast24Hours": 12, "maxSendRate": 1 }
}sandbox: true면 아직 프로덕션 발송이 승인되지 않은 계정입니다(검증된 주소로만 발송 가능).
주요 에러 — 400 자격증명 무효(AWS 자격증명이 유효하지 않습니다)·부분 입력·저장본 없음, 403 IAM 권한 부족.
도메인 identity 목록
해당 AWS 계정에 등록된 도메인 identity 목록을 조회합니다.
채택(adopt) 시 도메인 선택용입니다. (역할: owner / admin)
요청 필드는 공통 자격증명 필드와 동일하며, DOMAIN 타입 identity만 반환합니다.
{
"domains": [
{ "domain": "example.com", "verifiedForSending": true },
{ "domain": "old.example.net", "verifiedForSending": false }
]
}주요 에러 — 400 자격증명 무효·부분 입력·저장본 없음, 403 IAM 권한 부족(ListEmailIdentities).
이메일 주소 검증 메일 발송
단일 이메일 주소를 SES identity로 등록해 검증 메일을 발송합니다. 샌드박스에서 테스트 발신·수신 주소를 빠르게 검증하는 용도입니다. (역할: owner / admin)
| 필드 | 타입 | 제약 |
|---|---|---|
| (공통 자격증명 필드) | — | accessKeyId / secretAccessKey / region |
email | string | 필수. 이메일 형식, 최대 254자 |
{ "email": "tester@example.com", "status": "pending" }이미 등록된 주소는 멱등 처리되어 동일하게 성공을 반환합니다. 도메인 인증과 무관하게 동작하며 워크스페이스의 SES 연결 설정은 변경하지 않습니다.
도메인 연결 (provision)
도메인 identity와 전달추적(구성세트 + SNS)을 일괄 셋업하고, 적용해야 할 DNS 레코드를 반환합니다. (역할: owner / admin)
| 필드 | 타입 | 제약 |
|---|---|---|
| (공통 자격증명 필드) | — | accessKeyId / secretAccessKey / region |
domain | string | 필수. 스킴·경로 없는 bare 도메인, 최대 255자 |
fromEmail | string | 필수. 이메일 형식 |
fromName | string | 선택. 최대 120자 |
customMailFrom | boolean | 선택. custom MAIL FROM 사용 여부. 기본 true |
mailFromDomain | string | 선택. bare 도메인, 최대 255자. 미지정 시 mail.{domain} |
curl -X POST https://api.posmit.io/v1/ses/domain/connect \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "region": "ap-northeast-2", "domain": "example.com", "fromEmail": "no-reply@example.com", "fromName": "Posmit" }'위 예시처럼 키를 생략하면 저장된 자격증명 폴백으로 동작합니다.
{
"domain": "example.com",
"region": "ap-northeast-2",
"fromEmail": "no-reply@example.com",
"configurationSet": "noti-ses-{workspaceId}",
"customMailFrom": true,
"dnsRecords": [
{ "purpose": "dkim", "type": "CNAME", "name": "token1._domainkey.example.com", "value": "token1.dkim.amazonses.com", "required": true, "status": "missing" }
],
"summary": "DKIM 0/3 적용 · SPF 누락 · MX 누락 · DMARC 누락"
}멱등하게 동작하며, 재호출 시 기존 identity·SNS 토픽·구성세트를 재사용하고 콜백 시크릿도 보존하므로, DNS 레코드를 다시 받아야 할 때 안심하고 다시 호출할 수 있습니다.
전달추적은 SEND / DELIVERY / BOUNCE / COMPLAINT / REJECT / OPEN / CLICK 이벤트를 SNS 토픽으로 수신합니다.
주요 에러 — 400 자격증명 무효·부분 입력·저장본 없음·AWS 호출 실패, 403 IAM 권한 부족(전체 세트 필요).
기존 SES 채택 (adopt)
이미 셋업된 SES를 있는 그대로 채택합니다.
identity·DKIM·MAIL FROM은 변경하지 않고 읽기만 하며, 응답 형태는 domain/connect와 동일합니다. (역할: owner / admin)
| 필드 | 타입 | 제약 |
|---|---|---|
| (공통 자격증명 필드) | — | accessKeyId / secretAccessKey / region |
domain | string | 필수. bare 도메인, 최대 255자. 발송 인증이 끝난 도메인만 채택할 수 있습니다. |
fromEmail | string | 필수. 이메일 형식 |
fromName | string | 선택. 최대 120자 |
configurationSet | string | 선택. 영숫자·-·_, 최대 64자. 입력 시 SES에 존재하는지만 검증합니다. |
enableTracking | boolean | 필수. true일 때만 SNS 토픽·이벤트 대상을 부착합니다. false면 SNS를 전혀 건드리지 않습니다. |
추적을 켜면서 configurationSet을 지정하면 그 구성세트에 이벤트 대상을 부착하고, 비워 두면 Posmit 관리 구성세트(noti-ses-{workspaceId})를 생성해 사용합니다.
주요 에러 — 400 미인증 도메인(도메인이 아직 SES에서 인증되지 않았습니다. 새로 설정으로 진행하세요.)·존재하지 않는 구성세트·자격증명 무효, 403 IAM 권한 부족.
DNS 레코드 응답 구조
domain/connect·domain/adopt의 dnsRecords와 diagnose의 records는 동일한 구조입니다.
| 필드 | 타입 | 설명 |
|---|---|---|
purpose | string | dkim | mx_mailfrom | spf_mailfrom | dmarc |
type | string | CNAME | TXT | MX |
name | string | 등록할 레코드 이름(호스트) |
value | string | 등록할 레코드 값 |
priority | int | MX 레코드에만 존재(값 10) |
required | boolean | 발송에 필수인지. DKIM·MAIL FROM은 필수, DMARC는 권장(false) |
status | string | 공개 DNS 조회 결과. ok | missing | mismatch | unknown |
반환되는 레코드 종류는 다음과 같습니다.
| 용도 | 타입 | 이름 | 값 | 필수 |
|---|---|---|---|---|
| DKIM (3개) | CNAME | {token}._domainkey.{domain} | {token}.dkim.amazonses.com | 필수 |
| MAIL FROM MX | MX | {mailFromDomain} | feedback-smtp.{region}.amazonses.com (priority 10) | 필수* |
| MAIL FROM SPF | TXT | {mailFromDomain} | v=spf1 include:amazonses.com ~all | 필수* |
| DMARC | TXT | _dmarc.{domain} | v=DMARC1; p=none; | 권장 |
* MAIL FROM 레코드 2종은 custom MAIL FROM을 사용할 때만 포함됩니다.
연결 상태 조회
도메인 검증·DKIM·MAIL FROM 상태를 조회하며, DNS 적용 후 폴링 용도입니다. (역할: owner / admin / operator / viewer)
{
"domain": "example.com",
"region": "ap-northeast-2",
"verifiedForSending": true,
"dkimStatus": "SUCCESS",
"dkimSigningEnabled": true,
"mailFromDomain": "mail.example.com",
"mailFromStatus": "SUCCESS"
}dkimStatus는 SES가 돌려주는 상태 문자열이며 값이 없으면 NOT_STARTED로 채워집니다.
custom MAIL FROM을 쓰지 않으면 mailFromDomain·mailFromStatus는 null입니다.
주요 에러 — 404 SES 미연결(SES가 아직 연결되지 않았습니다. connect-domain을 먼저 호출하세요.), 400 저장된 자격증명 불완전.
DNS 진단
필요한 DNS 레코드 각각을 공개 DNS로 조회해 적용 상태를 진단합니다. (역할: owner / admin / operator / viewer)
{
"records": [
{ "purpose": "dkim", "type": "CNAME", "name": "token1._domainkey.example.com", "value": "token1.dkim.amazonses.com", "required": true, "status": "ok" }
],
"summary": "DKIM 3/3 적용 · SPF 적용 · MX 적용 · DMARC 누락",
"allRequiredOk": true
}allRequiredOk는 필수 레코드가 모두 ok인지 나타냅니다.
DNS 전파 지연으로 방금 등록한 레코드가 missing으로 나올 수 있으며, 조회 자체가 실패하면 unknown입니다.
미연결 시 404를 반환합니다.
발신 한도 조회
연결된 계정의 샌드박스 여부와 발신 한도를 조회합니다. (역할: owner / admin / operator / viewer)
응답 형태는 자격증명 검증과 동일합니다(valid / sandbox / sendingEnabled / enforcementStatus / sendQuota).
저장된 자격증명을 사용하므로 본문 없이 호출하며, 미연결 시 404를 반환합니다.
DNS 원클릭 적용 URL
DNS 레코드를 제공자(예: GoDaddy)에 한 번에 적용하는 Domain Connect 서명 딥링크를 조회합니다. (역할: owner / admin / operator / viewer)
{
"url": "https://dns-provider.example/sync/v2/domainTemplates/providers/...?...&sig=...",
"provider": "godaddy"
}점진적 향상 방식으로 동작하며, SES 미연결, 제공자 미지원, 서버 미설정 등 모든 실패 케이스에서 에러 대신 { "url": null, "provider": null }을 200으로 반환하므로, url이 null이면 수동 등록 흐름을 안내하면 됩니다.
딥링크가 적용하는 레코드는 diagnose가 산출하는 레코드와 항상 동일합니다.
콜백 시크릿 회전
SES 전달 이벤트는 {API 베이스}/v1/callbacks/ses?ws={workspaceId}&token={시크릿} 형태의 SNS 구독으로 수신합니다.
이 토큰은 두 단계로 회전합니다.
회전 1단계 — 새 토큰을 발급해 새 엔드포인트로 SNS를 재구독합니다. 기존 토큰은 직전(previous) 슬롯으로 보존됩니다. (역할: owner / admin)
{
"rotated": true,
"graceActive": true,
"newEndpoint": "https://api.posmit.io/v1/callbacks/ses?ws=...&token=...",
"summary": "새 토큰으로 재구독했습니다. 구 토큰은 finalize 호출 전까지 유효합니다(콜백 무손실)."
}graceActive: false는 보존할 직전 토큰이 없어 유예 없이 즉시 전환됐다는 의미입니다.
회전 2단계 — 구 토큰의 SNS 구독을 해지하고 직전 토큰을 제거해 회전을 확정합니다. (역할: owner / admin)
{ "finalized": true, "removedSubscriptions": 1, "summary": "구 토큰 구독 1건을 해지하고 직전 토큰을 제거했습니다." }진행 중인 회전이 없으면 에러 대신 { "finalized": false, "removedSubscriptions": 0 }을 반환합니다.
주요 에러(두 엔드포인트 공통) — 404 SES 미연결, 400 SNS 토픽 없음(SNS 토픽이 없습니다. connect-domain을 먼저 실행하세요.) — 추적 없이 채택한 경우 발생할 수 있습니다.
회전 유예 기간 — rotate 호출 시점부터 commit 전까지는 현재·직전 토큰이 모두 유효해 콜백 유실이 없습니다.
새 구독으로 이벤트가 정상 수신되는 것을 확인한 뒤 commit을 호출하세요.
commit을 하지 않으면 구 토큰이 계속 유효한 상태로 남습니다.