튜토리얼
HiAPI 콜백을 받을 수 없나요? Webhook 전송 문제 진단 및 수정 가이드
POST https://api.hiapi.ai/v1/tasks로 작업을 제출할 때 callback.url을 함께 넣어 HiAPI가 작업 완료 후 능동적으로 POST 알림을 보내주길 기대했는데, GET /v1/tasks/{taskId}에서 작업이 이미 success인데도 콜백 인터페이스에는 단 하나의 요청도 수신되지 않았습니다. 이런 문제는 대부분 HiAPI 쪽이 아니라 콜백 주소에 접근 가능한지, 수신 측이 약속대로 2xx를 반환하는지, 그리고 폴링과 콜백이 먼저 달려들지 않았는지에 있습니다. 아래에서는 실제 콜백 계약에 따라 항목별로 점검하고, 즉시 적용할 수 있는 수정 단계를 제시합니다.
작업이 좀처럼 종료 상태에 도달하지 않는 경우(
data.status가 오랫동안queued/handling/archiving에 머묾)는 다른 유형의 문제이므로 HiAPI 작업 멈춤 또는 타임아웃 진단 및 재시도 가이드를 참고하세요. 이 문서는 작업이 이미 완료되었지만 콜백 알림을 받을 수 없는 상황만 다룹니다.
문제 증상
전형적인 양상은 작업 측은 모두 정상이고 GET /v1/tasks/{taskId}가 반환하는 data.status가 success(또는 fail)인데, 공인 인터넷에 배포한 콜백 인터페이스 로그에는 HiAPI로부터 온 POST 기록이 전혀 없는 경우입니다. 또는 간헐적으로 수신되다 자주 유실되거나, 동일한 taskId가 두 번 전송되어 중복 제거 로직에서 예외로 처리되는 경우도 있습니다.
먼저 한 가지 사실을 확립하세요: 콜백은 결과를 가져오는 한 가지 방식일 뿐, 유일한 채널이 아닙니다. HiAPI의 비동기 작업은 언제나 GET /v1/tasks/{taskId}로 능동 폴링하여 종료 상태를 얻을 수 있으며, 콜백(Webhook)은 폴링을 생략해 주는 역할일 뿐입니다. 따라서 "콜백을 받을 수 없다"는 "결과를 얻을 수 없다"와 같지 않으므로, 먼저 폴링으로 작업 자체가 정상인지 확인한 뒤 전송 문제를 찾아야 합니다.
실제 콜백 계약(먼저 맞춘 뒤 문제 해결)
콜백을 등록할 때 callback은 POST /v1/tasks의 최상위 필드이며 model, input과 같은 레벨이고, input 안에 넣을 수 없습니다:
{
"model": "gpt-image-2",
"input": { "prompt": "a calm blue data center" },
"callback": {
"url": "https://your-domain.com/hiapi/callback",
"when": "final"
}
}
반드시 기억해야 할 몇 가지:
callback.url은 공인 인터넷에서 접근 가능한 http(s) URL이어야 하며, HiAPI가 공인 인터넷에서 해당 URL로 POST를 보냅니다.callback.when은 현재final만 허용합니다: 작업이 종료 상태에 도달하면 한 번 전송하며,success와fail모두 트리거됩니다. 진행률이나 단계별 콜백은 없습니다.- 전송 방식은
POST+Content-Type: application/json이며, 요청 본문은GET /v1/tasks/{taskId}의data필드와 완전히 동일합니다. 즉 작업 상세 객체로,status,output,error,taskId,created,completed등을 포함합니다. - 전송 실패(비 2xx / 타임아웃 / 네트워크 오류) 시 즉시 → +1분 → +5분 → +20분 순으로 재전송하며, 최대 4회입니다. 4회 모두 실패하면 더 이상 재시도하지 않으며, 작업 자체의 상태에는 영향을 주지 않습니다.
- 종료 상태 콜백은 **최소 한 번(at least once)** 의미론이며, 정확히 한 번이 아닙니다. 동일한
taskId가 여러 번 전송될 수 있으므로 수신 측은taskId기준으로 멱등 중복 제거를 해야 합니다.
계약 세부 사항은 공식 문서를 기준으로 합니다: 작업 생성 및 callback 필드, 비동기 API 개요(콜백 재시도 및 멱등성).
흔한 원인(발생 확률 순)
1. 콜백 주소에 공인 인터넷에서 접근 불가(가장 흔함)
http://localhost:3000/..., 내부 IP, 또는 내부 DNS에서만 해석되는 도메인으로 작성한 경우입니다. HiAPI가 공인 인터넷에서 여러분의 로컬 머신에 도달할 수 없으니 당연히 전송되지 않습니다. 로컬 개발에서 가장 쉽게 밟는 항목입니다.
2. 수신 측이 2xx를 반환하지 않음
handler가 예외를 던지거나, 3xx 리다이렉트를 반환하거나, 인증 미들웨어에 막혀 401/403이 되거나, 5xx가 되는 경우입니다. HiAPI는 HTTP 2xx만 전송 성공으로 인정하며, 나머지는 모두 재시도에 들어가고 재시도 창이 소진되면 "수신되지 않음"으로 나타납니다.
3. 처리 타임아웃
handler에서 무거운 작업(산출물 다운로드, 오브젝트 스토리지로 전송, 데이터베이스 쓰기, 메시지 발행)을 먼저 한 뒤 200을 반환하면, 시간이 너무 오래 걸려 타임아웃으로 판정되어 재시도가 트리거되고, 횟수가 누적되면 패킷 유실처럼 보입니다.
4. callback을 input 안에 넣음
이것은 최상위 필드이며, input에 넣으면 콜백 등록으로 인식되지 않습니다. 또한 각 모델의 input은 엄격한 스키마이므로 알 수 없는 키를 추가로 넣으면 보통 제출 시 동기적으로 invalid input: <root>: additional properties 'callback' not allowed 오류가 나고, 작업은 아예 제출되지 않습니다.
5. when에 final 이외의 값 입력
현재는 final만 지원하며, 다른 값을 넣으면 거부됩니다.
6. 폴링과 콜백의 경쟁
폴링도 켜고 콜백도 기다리는 상황에서 폴링이 먼저 종료 상태를 얻어 로컬 작업 기록을 닫아버리면, 콜백이 도착했을 때 해당 기록을 찾지 못해 "수신되지 않음"으로 처리됩니다.
7. 인증서 또는 WAF 차단
콜백 주소에 자체 서명/만료된 인증서를 사용했거나, WAF가 일반적인 UA도 없고 자체 인증 헤더도 없는 익명 POST를 의심스러운 트래픽으로 차단한 경우입니다.
수정 단계
- 먼저 작업 자체가 정상인지 확인하세요.
GET /v1/tasks/{taskId}로data.status를 조회하여success/fail이면 작업이 이미 종료 상태이므로 문제는 전송 단계에 있습니다. 아직 비종료 상태라면 다른 유형의 문제입니다(서두의 멈춤 진단 참고). - 콜백 주소를 공인 https 도메인으로 바꾸세요. 외부 머신에서
curl -i -X POST https://your-domain.com/hiapi/callback -d '{}'를 실행하여 연결 가능하고 2xx를 반환하는지 확인하세요. localhost 디버깅은 공인 터널로 로컬 포트를 외부에 매핑하세요. - 먼저 콜백을 신뢰성 있게 기록한 뒤 빠르게 2xx를 반환하세요. 요청을 받으면 먼저 콜백 내용을 데이터베이스나 영속 큐에 기록하고(이 단계는 빨라야 함), 기록 성공 후 2xx를 반환하세요. 다운로드, 전송 같은 무거운 작업은 백그라운드에서 비동기로 처리하세요. 기록 전에 200을 반환하지 마세요: 이후 프로세스가 크래시하면 해당 콜백은 유실되며, HiAPI는 2xx를 받으면 다시 전송하지 않습니다. 동시에 해당 경로는 인증 미들웨어에서 면제하세요. HiAPI의 콜백은 여러분의 자체 인증 헤더를 포함하지 않습니다.
callback의 위치와 값을 확인하세요. 요청 본문의 최상위(model,input과 같은 레벨)에 있는지,url이 공인 http(s)인지,when이final인지 확인하세요.taskId기준으로 멱등 중복 제거를 하세요. 콜백을 받으면 먼저 로컬에서 해당taskId를 이미 처리했는지 조회하고, 처리한 적이 없을 때만 기록/후속 동작을 트리거하세요. at-least-once 의미론에서는 중복 도착이 정상이므로, 중복을 예외로 보고 버리지 마세요.- 보조 폴링을 추가하세요. 예상 시간을 초과해도 콜백을 받지 못한
taskId에 대해서는 능동적으로GET /v1/tasks/{taskId}로 종료 상태를 보충 조회하세요. 콜백은 최적화 항목이고 폴링은 최저선이며, 둘을 동일한 멱등 로직으로 통합하세요. - 산출물을 가능한 한 빨리 전송하세요. 콜백 본문의
output[].url은 임시 링크입니다(storage가temp,expireAt은 Unix 시간이며 약 7일 후 만료). 받은 즉시 다운로드하거나 자체 스토리지로 전송하고, 핫링크를 그대로 저장하지 마세요.
최소 검증 예제
먼저 콜백은 건드리지 않고 "제출 + 폴링"으로 여러분의 key와 연동 자체가 정상인지 증명하세요(생 모델 id 사용):
# 1. 작업 제출
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-image-2","input":{"prompt":"a calm blue data center"}}'
# → {"code":200,"message":"success","data":{"taskId":"tk-hiapi-..."}}
# 2. 반환된 taskId로 종료 상태 폴링
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-xxxx \
-H "Authorization: Bearer $HIAPI_API_KEY"
# → data.status가 queued/handling/archiving에서 success(output 포함) 또는 fail(error 포함)로 진행
제출 시 HTTP 401 + {"error":{"code":"permission_denied","type":"hiapi_error","request_id":"..."}}가 반환되면 API key가 잘못된 것이므로, 콘솔에서 확인 후 재시도하세요. 연동이 정상임을 확인한 뒤 POST 본문에 최상위 callback을 추가하고, 위 수정 단계에 따라 수신 측을 항목별로 점검하세요.
FAQ
콜백을 계속 받을 수 없지만 작업은 실제로 성공했는데, 결과를 여전히 얻을 수 있나요?
네. 언제든지 GET /v1/tasks/{taskId}로 data.status와 data.output을 읽을 수 있으며, 콜백 유실은 능동적으로 결과를 가져오는 데 영향을 주지 않습니다.
콜백은 몇 번 재시도하며 간격은 얼마인가요?
비 2xx / 타임아웃 / 네트워크 오류 시 즉시 → +1분 → +5분 → +20분 순으로 재전송하며 최대 4회입니다. 4회 모두 실패하면 더 이상 재시도하지 않으며, 작업 자체의 success/fail 상태에는 영향을 주지 않습니다.
같은 작업에 대해 콜백을 두 번 받는 것이 정상인가요?
정상입니다. 종료 상태 콜백은 "최소 한 번" 의미론이므로 taskId 기준으로 멱등 중복 제거하면 되고, 중복 전송을 오류로 간주하지 마세요.
로컬 개발에서는 어떻게 콜백을 받나요?
localhost와 내부 주소는 HiAPI가 도달할 수 없습니다. 공인 터널로 로컬 포트를 공인 인터넷에 매핑하거나, 로컬 단계에서는 폴링으로 전환하고 배포 후 콜백으로 바꾸세요.
콜백 요청 본문에는 어떤 필드가 있나요?
GET /v1/tasks/{taskId}의 data와 완전히 동일하며, taskId, status, output(성공 시 산출물 배열), error(실패 시 code와 message), created, completed 등을 포함합니다. 자세한 내용은 작업 상세 문서를 참고하세요.
콜백을 받지 못하면 요금이 더 부과되나요? 아니요. 작업 실패 자체는 요금이 부과되지 않으며, 예약된 할당량은 반환됩니다. 콜백 전송 성공 여부도 작업의 과금 결과를 바꾸지 않으며, 과금은 작업이 최종적으로 성공적으로 생성되었는지에 따라서만 계산됩니다.
관련 글
모든 아티클 보기 →-
튜토리얼
HiAPI API로 hailuo-2.3-fast/image-to-video 호출하는 방법: curl과 Python 실전
빠르고 저렴한 이미지-투-비디오 등급의 첫 실행 요청 — 모델 ID부터 MP4 다운로드까지
-
튜토리얼
HiAPI API로 hailuo-2.3/text-to-video를 호출하는 방법: curl, Python 및 실행 가능한 요청 예시
-
튜토리얼
HiAPI에서 Kandinsky 6.0 Pro 텍스트-투-비디오 사용하기: curl 및 Python 튜토리얼
요청 우선 실전 데모: 프롬프트에서 비디오 클립까지의 호출 구성, 요청 제출, 완료 상태 폴링 및 결과 다운로드