에러 코드
Posmit API의 모든 에러 응답은 RFC 7807 표준을 따르는
application/problem+json 형식으로 반환됩니다.
성공 응답이 data / meta envelope로 감싸지는 것과 달리, 에러 응답은 envelope 없이 problem 객체 하나만 내려갑니다.
응답 구조
{
"type": "https://api.posmit.io/errors/noti-4014",
"title": "Unauthorized",
"status": 401,
"detail": "Invalid HMAC signature",
"code": "NOTI-4014",
"instance": "/v1/notifications",
"timestamp": "2026-07-10T09:00:00.000Z"
}| 필드 | 타입 | 설명 |
|---|---|---|
type | string | 에러 유형을 식별하는 URI입니다. code 소문자를 붙여 생성되며, 미설정 환경에서는 about:blank입니다. |
title | string | HTTP 상태에 대응하는 표준 문구입니다. (Bad Request, Unauthorized 등) |
status | number | HTTP 상태 코드와 동일한 값입니다. |
detail | string | 사람이 읽을 수 있는 상세 원인입니다. 유효성 검증 실패 시 여러 메시지가 ; 로 연결됩니다. |
code | string | 구조화된 에러 코드입니다. 클라이언트 분기는 이 값을 기준으로 하는 것을 권장합니다. |
instance | string | 에러가 발생한 요청 경로입니다. |
timestamp | string | 에러 발생 시각(ISO 8601)입니다. |
title은 상태 코드별 고정 문구이고 detail은 상황에 따라 달라질 수 있습니다.
프로그램 분기는 status와 code로 처리하고, detail은 로그·디버깅 용도로만 사용하세요.
에러 코드 체계
에러 코드는 NOTI- 접두어 뒤에 HTTP 상태 기반 번호가 붙는 형식입니다.
- 명시적 코드 — 인증(401 계열)과 요청 한도(429 계열) 에러는 세분화된 4자리 코드를 반환합니다. (예:
NOTI-4014) - 폴백 코드 — 그 외 에러는
NOTI-<HTTP상태>00형식의 5자리 폴백 코드를 반환합니다. 예를 들어 400 에러는NOTI-40000, 403은NOTI-40300, 404는NOTI-40400, 500은NOTI-50000입니다.
422 검증 계열(NOTI-4220), 404 세부 계열() 등 일부 코드는 예약만 되어 있고 아직 반환되지 않습니다.
현재 요청 본문 검증 실패는 422가 아니라 HTTP 400 + NOTI-4440NOTI-40000 으로 반환됩니다.
주요 에러 코드
401 — 인증 실패
| 코드 | 발생 조건 | 대응 방법 |
|---|---|---|
NOTI-4010 | API 키가 없거나 비활성 상태, 또는 시크릿 불일치 | X-API-Key 헤더의 keyId·keySecret을 확인합니다. |
NOTI-4011 | API 키 만료 | 대시보드에서 새 키를 발급합니다. |
NOTI-4013 | 인증 헤더 누락 | X-API-Key, X-Timestamp, X-Nonce, X-Signature 4종이 모두 있는지 확인합니다. detail에 누락된 헤더 이름이 표시됩니다. |
NOTI-4014 | HMAC 서명 불일치 | canonical string 구성과 raw body 해시를 점검합니다. 서명 후 본문을 재직렬화하면 서명이 깨집니다. |
NOTI-4015 | 타임스탬프 허용 오차(기본 ±300초) 초과 | 서버 시계 동기화(NTP)를 확인하고, X-Timestamp가 초 단위 epoch인지 확인합니다. |
NOTI-4016 | Nonce 재사용(리플레이) 감지 | 요청마다 새로운 X-Nonce 값을 생성합니다. |
429 — 요청 한도 초과
| 코드 | 발생 조건 |
|---|---|
NOTI-4290 | 워크스페이스 요청 한도 초과 |
NOTI-4291 | API 키 요청 한도 초과 |
자세한 한도 정책과 재시도 전략은 요청 한도 문서를 참고하세요.
폴백 코드로 반환되는 에러
| 코드 | HTTP | 대표 사례 |
|---|---|---|
NOTI-40000 | 400 | 요청 본문 검증 실패, 정의되지 않은 필드 포함, 템플릿 없음, scheduledAt 형식 오류 등 |
NOTI-40300 | 403 | API 키 스코프 부족, 템플릿 화이트리스트 위반 |
NOTI-40400 | 404 | 존재하지 않거나 다른 워크스페이스의 requestId 조회 |
NOTI-50000 | 500 | 서버 내부 오류 |
Posmit API는 정의되지 않은 필드를 조용히 무시하지 않고 400으로 거부합니다.
요청 스키마에 없는 필드를 보내면 NOTI-40000과 함께 해당 필드명이 detail에 표시됩니다.
자주 만나는 detail 메시지
detail은 변경될 수 있으므로 분기 조건으로 쓰지 말고, 원인 파악 참고용으로만 사용하세요.
| HTTP | detail | 원인 |
|---|---|---|
| 400 | content.text is required when templateCode is missing | 템플릿 없이 발송할 때 content.text 누락 |
| 400 | Template not found or disabled: {code} | 존재하지 않거나 비활성화된 템플릿 코드 |
| 400 | Published template version missing: {code} | 게시(published)된 템플릿 버전 없음 |
| 400 | Alimtalk template is not approved (status=...): {code} | 검수 승인되지 않은 알림톡 템플릿 |
| 400 | scheduledAt must be a valid ISO-8601 datetime | 예약 시각 형식 오류 |
| 400 | scheduledAt must be in the future | 예약 시각이 현재 또는 과거 |
| 400 | Validation failed (uuid is expected) | requestId 경로 파라미터가 UUID 형식이 아님 |
| 403 | API key missing required scope: {scope} | API 키에 필요한 스코프 없음 |
| 403 | API key is not allowed to send template: {code} | API 키 템플릿 화이트리스트 위반 |
| 404 | Notification request not found | 요청 미존재 또는 다른 워크스페이스 소속 |
처리 가이드
에러 처리는 다음 순서를 권장합니다.
- HTTP 상태 코드로 큰 분기를 나눕니다. (4xx는 요청 수정, 5xx·429는 재시도)
- 401·429처럼 세분화가 필요한 경우
code로 추가 분기합니다. detail·instance·timestamp는 로그에 남겨 디버깅에 활용합니다.
type ProblemDetails = {
type: string
title: string
status: number
detail: string
code: string
instance: string
timestamp: string
}
const res = await fetch('https://api.posmit.io/v1/notifications', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'YOUR_KEY_ID.YOUR_KEY_SECRET',
'X-Timestamp': timestamp,
'X-Nonce': nonce,
'X-Signature': signature,
},
body,
})
if (!res.ok) {
const problem = (await res.json()) as ProblemDetails
switch (problem.code) {
case 'NOTI-4015':
// 서버 시계 오차 — NTP 동기화 확인 후 재서명하여 재시도
break
case 'NOTI-4016':
// Nonce 재사용 — 새 nonce로 재서명하여 재시도
break
case 'NOTI-4290':
case 'NOTI-4291':
// 요청 한도 초과 — 윈도우가 지난 뒤 재시도
break
default:
if (problem.status >= 500) {
// 일시 장애 — 지수 백오프로 재시도
} else {
// 요청 자체의 문제 — 재시도하지 말고 요청을 수정
console.error(`[${problem.code}] ${problem.detail}`)
}
}
}4xx 에러(429 제외)는 같은 요청을 다시 보내도 같은 결과가 반환됩니다. 자동 재시도 대상에서 제외하세요.
재시도는 429와 5xx에만 적용하고, 발송 요청 재시도 시에는 idempotencyKey를 함께 사용해 중복 발송을 방지하는 것을 권장합니다.
발송 실패와의 구분
이 문서의 에러는 API 요청 자체가 거부된 경우입니다.
요청이 정상 접수(201)된 뒤 공급자 발송 단계에서 실패하는 경우는 HTTP 에러가 아니라,
요청 상태가 failed로 바뀌고 finalErrorCode에 실패 코드가 기록됩니다.
발송 단계의 실패 코드와 폴백 동작은 라우팅과 폴백을 참고하세요.