-
[OpenAI][Responses API] webhook 감사 로그에 event.id와 response_id를 어디까지 같이 남겨야 하나기타개발지식/풀스택개발 2026. 7. 9. 09:15
IT 리서치 노트
[OpenAI][Responses API] webhook 감사 로그에 event.id와 response_id를 어디까지 같이 남겨야 하나
Responses API를 background mode와 webhook으로 운영하면 `response.completed`를 한 번 받는 것으로 모든 기록 정책이 끝난다고 생각하기 쉽다. 하지만 2026년 7월 9일 기준 OpenAI 공식 문서를 다시 보면, webhook receipt에는 `webhook-id`, event 자체에는 `event.id`, 결과 객체에는 `response_id`가 따로 있고, response data는 polling을 위해 대략 10분 보관된다고 설명한다. 이 글은 수신 감사 로그를 남길 때 `event.id`와 `response_id`를 어디까지 같이 남기고, 어디서부터 TTL을 분리하는 편이 맞는지 정리한 것이다.
1. 개요
결론부터 말하면 OpenAI webhook 수신 감사 로그에는
event.id와response_id를 같이 남길 수 있지만, 같은 TTL로 묶는 기본값은 피하는 편이 맞다.event.id는 수신한 event body를 복기하는 키이고,response_id는responses.retrieve(), 결과 반영, 사후 보정 작업을 잇는 키이기 때문이다. 감사용 수신 기록과 결과 객체 조인을 한 테이블 한 TTL로 묶으면 나중에 어느 값을 왜 오래 남겼는지 설명이 어려워진다.이미 retrieve와 감사 로그 분리 글이 webhook 뒤 재조회와 감사 로그 저장 순서를 다뤘고, webhook-id와 response_id idempotency 글이 중복 차단 키를 다뤘다면, 이번 글은 그 둘 사이에 있는 기록 보존 기준을 좁히는 후속편이다.
2. 어디서 실제로 막히는가
실무에서 자주 꼬이는 지점은 세 가지다. 첫째, receipt 로그에
event.id만 남기고response_id를 빼서 며칠 뒤 같은 결과를 수동으로 다시 잇기 어려워진다. 둘째, 반대로response_id만 장기 저장하고 어떤 webhook event가 언제 왔는지, 중간에 중복 delivery가 있었는지 복기할 근거를 잃는다. 셋째, OpenAI가 polling을 위해 짧게 보관하는 response data 창과, 앱이 자체 정산을 위해 길게 들고 가야 하는 commit key 창을 같은 TTL로 둔다.OpenAI webhooks 가이드는 duplicate delivery를
webhook-id로 걸러내라고 설명하고, 공식 예시는event.data.id를 꺼내responses.retrieve(response_id)를 다시 호출한다. 타입 레퍼런스는 event 자체의id와 payload 안data.id를 별도 필드로 보여 준다. 이 세 문장을 같이 읽으면 수신한 body의 감사성과 결과 객체 조인성을 같은 값 하나로 대신하기 어렵다는 점이 드러난다.특히 수동 정산, 사람이 승인하는 후처리, 비동기 파이프라인 재시도, polling fallback이 섞이면
response_id는 receipt 창보다 더 오래 남아야 할 때가 많다. 반대로 모든 event body 전체를 같은 기간 그대로 보관하는 것이 꼭 필요한 것은 아니다. 결국 무엇을 재조회하고 무엇을 증적처럼 남길지 분리해야 한다.- 증상: 나중에 수신 감사와 결과 반영을 서로 이어 보기가 어렵다.
- 실패:
event.id와response_id를 같은 기록, 같은 TTL로 묶는다. - 막힘: OpenAI의 짧은 보관 창과 앱의 정산 보관 창을 구분하지 않는다.
- 누락: receipt 복기와 business commit 복기의 질문이 다르다는 점을 문서로 남기지 않는다.
질문 먼저 볼 키 이유 그날 어떤 event body가 실제로 왔나 event.id 수신 사실과 body 추적 질문이다 같은 결과를 다시 조회하거나 정산해야 하나 response_id retrieve와 commit을 잇는 질문이다 같은 delivery 사본이 또 왔나 webhook-id 전달 시도 중복 질문이다 3. 실무에서 적용하는 순서
가장 덜 꼬이는 구조는 세 레이어로 분리하는 것이다. 먼저 수신 계층에는
webhook-id,event.id,response_id, 수신 시각만 남긴다. 두 번째로 결과 계층에는response_id, 최종 상태, usage, 앱 내부 commit 시각을 남긴다. 세 번째로 body 전체 보존이 정말 필요한 환경이 아니면 raw event payload는 더 짧은 TTL 또는 별도 저장소로 둔다.- receipt log에는
webhook-id,event.id,response_id를 같이 남긴다. - result log에는
response_id와 실제 반영 결과를 남긴다. - raw event body는 필요 수준만 남기고 TTL을 더 짧게 둔다.
- background polling fallback 창과 앱 정산 창을 따로 적는다.
- 사후 정산이나 사람 승인 흐름이 있으면
response_id보관 기준을 더 길게 둔다.
이 구조가 좋은 이유는, 같은 값 세 개를 다 오래 들고 가자는 뜻이 아니기 때문이다. 반대로 어떤 질문을 나중에 풀어야 하는지 기준으로 필요한 최소 키만 남긴다는 뜻이다. OpenAI background 가이드의 대략 10분 보관은
retrievefallback이 어느 정도 가능하다는 플랫폼 창이고, 앱 쪽 정산 창은 업무 정책에 따라 그보다 길거나 짧을 수 있다.이렇게 적어 두면 운영자가 어느 표를 봐야 하는지도 즉시 정해진다. 수신 사고 조사면 receipt, 결과 보정이면 result, 재전송 중복이면 webhook-id다. 키는 겹쳐 보여도 질문은 서로 다르다.
4. 공식 문서와 예시 화면으로 확인하기
첫 자료는 Responses 전환 가이드다. 여기서는 Responses API가 자체 `response` 객체와 고유한 `id`를 돌려준다고 설명한다. webhook body 안의 `data.id`가 결국 어떤 결과 객체를 가리키는지 먼저 여기서 잡아야 한다.
이 문장을 기준으로 보면 `response_id`는 결과 조회와 결과 반영을 묶는 조인 키에 가깝다. receipt 자체를 추적하는 `event.id`와는 역할이 다르다.
두 번째 자료는 webhook 중복 전송 안내다. OpenAI는 드물게 같은 webhook event 사본이 다시 올 수 있고, 이 경우 `webhook-id` 헤더로 중복 delivery를 걸러내라고 적고 있다.
즉 receipt 계층의 1차 차단은 `webhook-id`다. 그다음에 body 안에 들어 있는 `event.id`와 `response_id`를 어떤 TTL로 남길지 따로 정해야 한다.
세 번째 자료는 webhook 이벤트 타입 레퍼런스다. 여기서는 event 자체의 `id`와 payload 안 `data.id`가 별도 필드로 존재한다는 점이 중요하다.
이 구분이 있어야 수신 감사 로그와 비즈니스 결과 저장소를 같은 스키마로 억지로 묶지 않게 된다. event는 수신 사실을, response는 결과 객체를 대표한다.
네 번째 자료는 background mode 보관 경계다. OpenAI는 polling을 위해 response data를 대략 10분 보관한다고 설명한다.
이 값은 앱의 장기 감사 로그 TTL과 같지 않다. retrieve fallback 창과 장기 정산 창을 같은 만료 규칙으로 두면 나중에 무엇을 다시 조회할 수 있는지 애매해진다.
실무에서는 키별 역할과 권장 TTL을 표로 먼저 쪼개는 편이 가장 빠르다. `event.id`와 `response_id`를 같이 남기더라도 왜 남기는지와 얼마나 오래 둘지를 다르게 적어 두어야 한다.
이 표처럼 남겨 두면 webhook 수신 감사와 결과 재적용, polling fallback, 사후 정산이 서로 다른 레이어라는 점이 분명해진다. 바로 앞 글인 webhook-id와 response_id idempotency 글이 중복 차단 기준을 다뤘다면, 이번 표는 그 기록 보존 기준을 다룬다.
마지막 자료는 저장 예시다. 수신 감사 테이블과 결과 커밋 테이블을 나눠 두면 어떤 키가 어느 레이어를 대표하는지 코드에서 바로 드러난다.
이 구조를 쓰면 며칠 뒤 같은 결과를 수동 보정할 때는 `response_id`를 잡고, 당시 어떤 webhook body가 왔는지 복기할 때는 `event.id`와 `webhook-id`를 잡으면 된다.
5. 주의사항과 리스크
첫 번째 리스크는
event.id없이response_id만 남겨 수신 증적을 약하게 만드는 것이다. 두 번째 리스크는 반대로 모든 raw event body를 같은 기간 오래 보관해 실제 필요한 키보다 과한 저장을 기본값으로 만드는 것이다. 세 번째 리스크는 OpenAI의 polling 가능 창을 앱의 장기 정산 가능 창으로 오해하는 것이다.운영 전에 확인할 때는 최소한 receipt log, raw event body, result commit의 TTL을 같은 문서에 한 줄씩 적는 편이 좋다. 그래야 사람이 나중에 왜 어떤 키는 남고 어떤 키는 지워지는지 설명할 수 있다.
event.id는 수신 감사 키다.response_id는 결과 조인 키다.- 플랫폼 보관 창과 앱 보관 창은 같은 값이 아니다.
6. 결론
OpenAI webhook 감사 로그에서
event.id와response_id를 함께 남기는 것은 유용하지만, 같은 TTL로 묶는 기본값은 피하는 편이 맞다.event.id는 수신 event를 복기하기 위한 키이고,response_id는 retrieve와 결과 반영을 잇는 키이기 때문이다. receipt, raw body, result commit을 서로 다른 보관 기준으로 나누면 webhook 운영과 사후 정산이 훨씬 덜 꼬인다.이 기준은 바로 앞 단계인 webhook-id와 response_id dedupe 기준 위에 얹는 후속 운영 규칙이다. 중복 차단이 끝난 뒤에는 무엇을 얼마나 남길지까지 분리해야 흐름이 완성된다.
여기서 한 단계 더 내려가면 completed가 아니었던 응답도 같은 바구니에 넣지 말아야 한다. 실제 receipt 로그에서
response.failed와response.incomplete를 어떤 상태 필드와 TTL 클래스로 나눌지는 response.failed와 response.incomplete 감사 로그 분리 글에서 이어서 볼 수 있다.- receipt 감사는
event.id중심이다. - 결과 조인은
response_id중심이다. - TTL은 레이어별로 다르게 둔다.
7. 참고 링크
'기타개발지식 > 풀스택개발' 카테고리의 다른 글