> For the complete documentation index, see [llms.txt](https://sharelink-docs.toss.im/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sharelink-docs.toss.im/developers/open-api/api/order-event-callback.md).

# 주문 이벤트 수신 (Push)

발급한 링크로 발생한 구매·전체 취소·구매확정 이벤트를 제휴사 서버가 등록한 HTTPS 주소로 받습니다. 캐시백·리워드 연동의 기본 전달 경로입니다.

발급하신 쉐어링크로 발생한 **주문상품의 구매·전체 취소·구매확정 이벤트**를 토스가 제휴사 서버로 **HTTPS POST** 해 드립니다. 캐시백·리워드처럼 "누가 무엇을 샀는지"를 제휴사 시스템에서 바로 알아야 할 때 사용합니다.

```
POST {제휴사가 등록한 수신 URL}      (토스 → 제휴사 방향)
```

필요 스코프: **없음.** 토스가 제휴사 서버를 호출하는 방향이라 액세스 토큰을 쓰지 않습니다. 대신 아래 **서명 검증**으로 토스 발신임을 확인합니다.

> **Push와 Pull은 한 쌍입니다.** Push는 지연·중복·순서 역전·누락이 있을 수 있고 재전송이 없습니다. Push를 쓰신다면 [주문 이벤트 조회](/developers/open-api/api/order-events.md)(Pull)로 누락을 채우는 대사도 함께 구현해 주세요. 두 경로는 같은 `eventId`를 씁니다.

아래 **1. 설계 전에 알아 둘 것**에서 저장 구조와 확정 규칙을 정한 뒤, 이 순서로 진행하시면 됩니다.

1. 발급받은 `originUrl`에 `partner_ref_id`를 붙여 사용자에게 제공합니다.
2. 수신 URL을 준비해 등록하고, 방화벽에서 토스 발신 IP를 엽니다.
3. 서명을 검증하고 저장한 뒤 2xx로 응답합니다.
4. 5분마다 [주문 이벤트 조회](/developers/open-api/api/order-events.md)로 누락을 채웁니다.

이벤트 단위는 **주문상품**입니다. 한 주문에 상품이 여러 개면 이벤트도 여러 건 옵니다. 같은 주문상품에서도 구매·취소·구매확정이 각각 별개 이벤트로 옵니다. 부분 수량 취소 이벤트는 제공하지 않습니다.

***

## 1. 설계 전에 알아 둘 것

| 항목                       | 내용                                                                                                                                                                                                                            |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 이벤트 3종                   | `PURCHASE`(구매) · `CANCEL`(해당 주문상품 전체 취소) · `CONFIRM`(구매확정)이 옵니다. 세 종류 모두 같은 수신 URL로, 같은 구조의 본문으로 도착합니다                                                                                                                        |
| `eventId`는 고정값           | 주문상품 하나 + 이벤트 종류(구매/취소/구매확정)당 `eventId`가 하나로 정해집니다. 같은 이벤트가 다시 오거나 Pull로 조회돼도 값이 같으므로, **`eventId`에 유일 제약을 걸어 저장**하면 Push·Pull 중복이 모두 걸러집니다                                                                                   |
| 도착 순서를 보장하지 않음           | 취소나 구매확정이 구매보다 먼저 올 수 있습니다. 먼저 받은 쪽을 그대로 확정하고, 뒤늦게 온 구매로 되돌리지 마세요                                                                                                                                                             |
| 구매 이벤트가 없는 취소·구매확정도 있음   | 원래 구매가 2026-09-16 이전이면 구매 이벤트가 만들어진 적이 없고, 90일이 지났으면 이미 삭제되었습니다. `purchaseEventId`에 값이 있어도 그 구매 이벤트를 받지 못했을 수 있으니, **구매를 기다리지 말고 받은 이벤트만으로 처리**하세요                                                                            |
| 구매확정이 마지막 상태             | 구매확정된 주문상품에는 **이후 `CANCEL` 이벤트를 발행하지 않습니다.** 그래서 캐시백 확정 시점을 "구매 후 며칠 홀드" 대신 **`CONFIRM` 수신**으로 잡으실 수 있습니다. 다만 지급 확정(정산금 지급) 신호는 여전히 전달되지 않으며, [정산 실적 조회](/developers/open-api/api/settlement.md)는 월·상품 단위라 개별 주문까지 되짚을 수 없습니다 |
| 구매확정 이벤트의 값              | 수량·금액·`partnerRefId`·`subTagId`·`attribution`은 그 주문상품의 **구매 이벤트 값을 그대로 승계**합니다. 구매확정 시점에 다시 계산한 값이 아닙니다                                                                                                                       |
| 금액 2종의 의미                | `commissionBaseAmount`는 수익금 산정 기준액, `expectedCommissionAmount`는 예상 수익금(세전)입니다. 둘 다 실제 결제액·환불액이 아니고 확정 금액도 아닙니다. **판매가와 다르고 0원일 수도 있습니다** — 어떻게 정해지는지는 아래 **7. 이벤트 필드**의 *금액 2종이 정해지는 방식* 참고. 캐시백 산정 기준은 제휴사 정책으로 정하세요         |
| 보관 90일, 기록 시작 2026-09-16 | 이벤트는 종류별로 각자의 `recordedAt` 기준 90일 뒤 삭제됩니다. 더 긴 이력이 필요하면 자체 보관하세요. 2026-09-16 이전 주문은 이 방식으로 제공되지 않으며, **구매확정 이벤트는 기능이 적용된 이후 발생한 구매확정부터** 기록됩니다(그 이전에 이미 구매확정된 주문은 소급 생성하지 않습니다)                                               |

***

## 2. 링크에 `partner_ref_id` 붙이기 (선택)

제휴사의 사용자·유입·캐시백 신청을 이벤트와 연결하려면, 발급받은 `originUrl`에 `partner_ref_id` 쿼리 파라미터를 붙여 사용자에게 제공합니다. 붙이는 방법·허용 값·주의사항은 [쉐어링크 발급](/developers/open-api/api/link.md)의 **제휴사 추적 식별자 붙이기**에 있습니다.

이벤트에는 귀속된 방문의 값이 `partnerRefId`로 담깁니다. 값이 없던 방문이거나 **형식에 맞지 않는 값은 오류 없이 버려져** `null`로 옵니다. 사용자당 하나로 고정해도, 신청 건마다 새로 만들어도 됩니다. 이벤트를 사용자와 묶을지, 신청 건과 묶을지에 맞춰 정하세요. 같은 형식 규칙이 [주문 이벤트 조회](/developers/open-api/api/order-events.md)의 `partnerRefId` 필터에도 적용되는데, 링크에 붙일 때는 조용히 버려지고 조회 필터로 넣으면 거절됩니다.

***

## 3. 수신 URL 준비·등록

Push의 목적지는 토스 주소가 아니라 **제휴사가 직접 준비한 URL**입니다. 예를 들어 `https://partner.example.com/webhooks/toss-sharelink/orders` 처럼 경로는 제휴사가 정합니다. (예시 주소는 실제 수신 주소가 아닙니다.)

준비한 URL은 쉐어링크 크리에이터 어드민의 API 연동 화면(Access Key·Secret Key를 발급받는 곳) 아래 **주문 이벤트 수신** 카드에서 URL을 등록하고 수신을 켭니다. 화면에는 현재 상태가 **수신 중** / **수신 꺼짐**으로 표시됩니다. 거래처당 1개를 등록합니다.

| 항목         | 규칙                                                                                             |
| ---------- | ---------------------------------------------------------------------------------------------- |
| 프로토콜·길이    | HTTPS, 최대 400자. 포트는 생략하면 443이며 1\~65535를 지정할 수도 있습니다                                           |
| 허용하지 않는 것  | URL의 사용자명/비밀번호, 쿼리 문자열, fragment                                                               |
| 접근성        | 공개 인터넷 주소만 등록됩니다. **발송할 때마다 도메인을 다시 해석**하며, 결과가 사설·루프백·예약 대역이면 전송하지 않습니다                       |
| 리다이렉트      | 따라가지 않습니다. 최종 수신 URL을 등록하세요                                                                    |
| 인증 정보가 바뀌면 | 토스 운영자가 인증 정보를 폐기하거나 Secret Key를 재발급하면 수신이 **자동으로 비활성화**됩니다. 같은 카드의 **수신 다시 켜기**로 다시 활성화해야 합니다 |
| 설정 변경 시    | 변경 전에 이미 시작된 HTTP 요청은 도착할 수 있습니다                                                               |

***

## 4. 방화벽 열기

통신 방향은 **토스 서버 → 제휴사 수신 서버**입니다. IP 허용 목록을 운영하신다면 아래 토스 발신 IP **전체**를 인바운드 정책의 출발지로 등록하세요. 목적지는 제휴사 수신 서버, 포트는 등록한 URL의 포트(기본 443)입니다.

| 발신 IP / 범위                     | CIDR 표현            |
| ------------------------------ | ------------------ |
| 117.52.3.4                     | `117.52.3.4/32`    |
| 117.52.3.11                    | `117.52.3.11/32`   |
| 117.52.3.80 \~ 117.52.3.87     | `117.52.3.80/29`   |
| 211.115.96.4                   | `211.115.96.4/32`  |
| 211.115.96.11                  | `211.115.96.11/32` |
| 211.115.96.80 \~ 211.115.96.87 | `211.115.96.80/29` |
| 106.249.5.80 \~ 106.249.5.87   | `106.249.5.80/29`  |

`.80 \~ .87`은 8개 전체입니다. IP 허용은 서명 검증을 대체하지 않으므로 허용 IP에서 온 요청도 검증하세요.

Open API에는 IP 등록이 두 방향으로 있습니다. 헷갈리기 쉬우니 구분해 두세요.

| 구분                                                  | 방향       | 무엇을           | 어디에            |
| --------------------------------------------------- | -------- | ------------- | -------------- |
| 출발지 IP 등록 ([연동 시작하기](/developers/open-api/auth.md)) | 제휴사 → 토스 | 제휴사 서버의 공인 IP | 쉐어링크 크리에이터 어드민 |
| 발신 IP 허용 (이 절)                                      | 토스 → 제휴사 | 위 토스 발신 IP    | 제휴사 방화벽        |

***

## 5. 토스가 보내는 요청과 제휴사가 돌려줄 응답

| 항목         | 내용                                                                                              |
| ---------- | ----------------------------------------------------------------------------------------------- |
| 메서드·형식     | HTTPS `POST`, `Content-Type: application/json`                                                  |
| 본문         | 아래 **이벤트 객체 1건** 그 자체. `event`나 `partnerId`로 감싸지 않습니다                                           |
| 헤더         | `sharelink-webhook-transmission-time`, `sharelink-webhook-signature`. Secret 원문이나 인증키는 보내지 않습니다 |
| 성공 판정      | 제휴사 응답이 **HTTP 2xx**이면 성공. 본문은 필수가 아니며 `204 No Content`도 됩니다                                    |
| 토스 쪽 시간 제한 | 연결 1초, 응답 3초, 한 번의 전송 전체 5초. 이 값은 토스 발신 측 타임아웃이며 이벤트 전달 지연을 보장하는 SLA가 아닙니다                      |

**이벤트를 저장한 뒤 바로 응답하세요.** 캐시백 계산 같은 후속 처리는 비동기로 돌리시길 권장합니다.

| 상황                                     | 토스 동작                                                                                      |
| -------------------------------------- | ------------------------------------------------------------------------------------------ |
| 2xx 응답                                 | 성공으로 기록합니다. 다만 같은 이벤트가 다시 전송될 수는 있습니다                                                      |
| 2xx 외 응답(3xx 리다이렉트 포함) · 시간 초과 · 연결 실패 | 실패로 기록하고 **다시 보내지 않습니다.** 복구 수단은 [주문 이벤트 조회](/developers/open-api/api/order-events.md)뿐입니다 |
| 서명 키(Secret)를 가져오지 못함                  | 서명 없이 보내지 않습니다. 전송 자체를 하지 않습니다                                                             |

***

## 6. 서명 검증 (HMAC-SHA256)

토스는 제휴사의 **Secret Key(OAuth `client_secret`) 원문**으로 만든 서명을 헤더에 실어 보냅니다. 이벤트를 처리하기 전에 검증하세요.

| 헤더                                    | 값                                                                            |
| ------------------------------------- | ---------------------------------------------------------------------------- |
| `sharelink-webhook-transmission-time` | 서명 생성 시각. ISO 8601 KST 문자열 (예: `2026-09-14T12:00:00+09:00`). 소수 초가 붙을 수 있습니다 |
| `sharelink-webhook-signature`         | `v1:` 뒤에 HMAC-SHA256 결과를 **표준 Base64**로 인코딩한 값                               |

```
key       = UTF8(Secret Key 원문)
message   = raw HTTP body bytes  +  UTF8(":")  +  UTF8(transmission-time 헤더 원문)
signature = "v1:" + Base64( HMAC-SHA256(key, message) )
```

검증에서 자주 틀리는 지점입니다.

| 확인할 것                | 이유                                                                                       |
| -------------------- | ---------------------------------------------------------------------------------------- |
| 키는 Secret Key **원문** | Base64 디코딩하거나 앞뒤를 자르면 값이 달라집니다. 액세스 토큰·Access Key는 키가 아닙니다                               |
| 본문은 **수신한 원본 바이트**   | JSON을 파싱했다가 다시 만들거나 공백·필드 순서가 바뀌면 실패합니다. 프레임워크가 본문을 먼저 파싱해 버리면 원본을 따로 잡아 두어야 합니다 (아래 예시) |
| 시각은 **헤더 원문**        | 파싱해서 다시 만든 문자열이 아니라 `+09:00`이 포함된 원문 그대로                                                 |
| 헤더가 없거나 형식이 틀리면 거절   | 서명이 맞지 않는 요청을 "서명 없는 요청"으로 봐서 처리하면 안 됩니다                                                 |
| 전송 시각이 현재와 ±5분 이내인지  | 재전송 공격 완화. 서버 시계를 동기화하세요                                                                 |
| 상수 시간 비교             | 문자열 `==` 비교는 타이밍 공격에 노출됩니다                                                               |

### 수신 핸들러 예시 (Node.js / Express)

검증 → `eventId` 중복 제거 → 저장 → `204` 응답까지 이어지는 뼈대입니다. `saveIfNew`·`enqueueCashbackJob`은 제휴사가 구현합니다.

```javascript
const crypto = require("crypto");
const express = require("express");
const app = express();

// 원본 바이트를 그대로 받습니다. express.json() 으로 먼저 파싱하면 서명 검증에 쓸 원본이 남지 않습니다.
app.post("/webhooks/toss-sharelink/orders", express.raw({ type: "application/json" }), (req, res) => {
  if (!Buffer.isBuffer(req.body)) return res.status(400).end();
  const time = req.get("sharelink-webhook-transmission-time");
  const sig = req.get("sharelink-webhook-signature");
  if (!time || !sig || !sig.startsWith("v1:")) return res.status(401).end();

  const sentAt = Date.parse(time);
  if (Number.isNaN(sentAt) || Math.abs(Date.now() - sentAt) > 5 * 60 * 1000) return res.status(401).end();

  const expected = crypto
    .createHmac("sha256", Buffer.from(process.env.SHARELINK_CLIENT_SECRET, "utf8"))
    .update(req.body)            // 원본 Buffer
    .update(":")
    .update(Buffer.from(time, "utf8"))
    .digest();
  const received = Buffer.from(sig.slice(3), "base64");
  if (received.length !== expected.length || !crypto.timingSafeEqual(received, expected)) return res.status(401).end();

  const event = JSON.parse(req.body.toString("utf8"));   // 검증을 마친 뒤에 파싱
  const inserted = saveIfNew(event.eventId, event);       // eventId 유일 제약으로 중복 제거
  if (inserted) enqueueCashbackJob(event.orderProductId); // 후속 처리는 비동기
  return res.status(204).end();                           // 중복이어도 2xx
});
```

Spring이라면 `@RequestBody byte[]`로 받아 같은 순서로 처리하시면 됩니다.

### 서명 값 직접 계산해 보기 (터미널)

수신한 원본 본문을 `body.json`에 저장하고 실행하면 헤더의 `v1:` 뒤 값과 같아야 합니다. 편집기로 저장하면 끝에 개행이 붙어 값이 달라질 수 있고, Secret이 셸 이력에 남으므로 **운영 Secret으로는 실행하지 마세요.**

```bash
# TRANSMISSION_TIME: sharelink-webhook-transmission-time 헤더 원문 / CLIENT_SECRET: 테스트용 Secret Key
{ cat body.json; printf ':%s' "$TRANSMISSION_TIME"; } \
  | openssl dgst -sha256 -hmac "$CLIENT_SECRET" -binary \
  | base64
```

***

## 7. 이벤트 필드

Push 본문과 [주문 이벤트 조회](/developers/open-api/api/order-events.md)의 `events[]`는 같은 구조입니다.

| 필드                         | 타입             | 설명                                                                                                           |
| -------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------ |
| `eventId`                  | string         | 이벤트 식별자(UUID 36자). 주문상품+이벤트 종류당 하나로 고정. 형식에 의존하지 말고 문자열로 저장하세요                                               |
| `eventType`                | string         | `PURCHASE`(구매) / `CANCEL`(해당 주문상품 전체 취소) / `CONFIRM`(구매확정)                                                   |
| `orderId`                  | string         | 토스쇼핑 주문 식별자                                                                                                  |
| `orderProductId`           | string         | 토스쇼핑 주문상품 식별자. 이벤트의 기준 단위                                                                                    |
| `productId`                | string         | 상품 옵션 ID. [용어](/developers/open-api/glossary.md)의 `tacaItemId`와 같은 값이지만 실적 조회와 달리 **문자열**로 옵니다               |
| `productName`              | string         | 이벤트 발행 시점 상품명                                                                                                |
| `quantity`                 | number         | 이벤트 대상 수량. `CONFIRM`은 구매 이벤트 값을 승계합니다                                                                        |
| `commissionBaseAmount`     | number         | 수익금 산정 기준액 (원). 실제 결제액·환불액이 아닙니다. 산출 방식은 아래 *금액 2종이 정해지는 방식*                                                 |
| `expectedCommissionAmount` | number         | 예상 수익금 (원, 세전). 확정 금액이 아닙니다. 산출 방식은 아래 *금액 2종이 정해지는 방식*                                                      |
| `partnerRefId`             | string \| null | 귀속된 방문의 `partner_ref_id`. 없거나 형식 위반이면 `null`                                                                 |
| `subTagId`                 | string \| null | 귀속된 [subTag](/developers/open-api/api/sub-tags.md). 없으면 `null`                                               |
| `attribution`              | string         | `DIRECT`(직접) / `INDIRECT`(간접) / `UNKNOWN`(미확인)                                                               |
| `purchaseEventId`          | string \| null | `CANCEL`·`CONFIRM`이 대응하는 구매 `eventId`. `PURCHASE`는 `null`. 값이 있어도 구매 이벤트가 없을 수 있습니다 (**1. 설계 전에 알아 둘 것** 참고) |
| `occurredAt`               | string         | 구매·취소·구매확정 상태 전환 시각. ISO 8601 KST(`+09:00`)                                                                  |
| `recordedAt`               | string         | 이벤트 기록 시각. ISO 8601 KST(`+09:00`). 90일 보관의 기준                                                                |

### 금액 2종이 정해지는 방식

`commissionBaseAmount`는 **판매가에서 구매자가 실제로 쓴 쿠폰과 토스포인트를 뺀 금액**입니다. 상품 가격 그대로가 아닙니다.

```
commissionBaseAmount     = 판매가 − 쿠폰 할인 − 토스포인트 사용액
expectedCommissionAmount = commissionBaseAmount × 수수료율   (원 단위 반올림)
```

| 항목         | 기준액에 어떻게 반영되나                                                                                     |
| ---------- | ------------------------------------------------------------------------------------------------- |
| 판매가        | (+) 출발점. [공통 규약](/developers/open-api/convention.md)의 상품 카드 `displayPrice`와 같은 기준이며 배송비가 포함된 값입니다 |
| 수량         | (+) 판매가는 **주문한 수량 전체** 기준입니다. 2개를 사면 2개 값이 출발점입니다                                                 |
| 쿠폰 할인      | (−) 구매자가 사용한 셀러 쿠폰·토스 쿠폰 등                                                                        |
| 토스포인트      | (−) 구매자가 결제에 사용한 포인트                                                                              |
| 토스 부담 즉시할인 | 판매가에 **이미 반영**돼 있습니다. 여기서 또 빠지지 않습니다                                                              |
| 수수료율       | 거래처·상품에 적용된 율입니다. 이벤트 본문에는 담기지 않습니다                                                               |

그래서 이런 일이 생깁니다.

| 상황                    | 결과                                                                                |
| --------------------- | --------------------------------------------------------------------------------- |
| 같은 상품인데 주문마다 기준액이 다르다 | 정상입니다. 쿠폰·포인트 사용액은 구매자마다 다릅니다                                                     |
| 기준액이 판매가보다 작다         | 정상입니다. 기준액은 **판매가를 넘지 않습니다**                                                      |
| 기준액이 `0`으로 왔다         | 쿠폰과 포인트가 판매가 **전액**을 덮은 경우입니다. 이때 `expectedCommissionAmount`도 `0`이며, 데이터 누락이 아닙니다 |

판매가 20,000원 상품의 예입니다. 아래 값은 모두 가상 데이터이며 수수료율은 설명을 위해 10%로 가정했습니다.

| 쿠폰 할인 |  토스포인트 | `commissionBaseAmount` | `expectedCommissionAmount` |
| ----: | -----: | ---------------------: | -------------------------: |
|     0 |      0 |                 20,000 |                      2,000 |
| 3,000 |      0 |                 17,000 |                      1,700 |
| 3,000 | 15,000 |                  2,000 |                        200 |
| 5,000 | 15,000 |                      0 |                          0 |

> **판매가로 캐시백을 미리 고지하지 마세요.** `판매가 × 수수료율`로 계산해 사용자에게 안내하면, 그 사용자가 쿠폰이나 포인트를 쓴 순간 실제 기준액과 어긋납니다. 캐시백 금액은 이벤트로 도착한 `commissionBaseAmount`를 받은 뒤에 확정해 주세요.

`CANCEL`·`CONFIRM` 이벤트의 금액은 그 주문상품의 구매 이벤트 값을 그대로 승계합니다. 구매확정 시점에 다시 계산하지 않습니다.

### 구매 이벤트 예시

아래 값은 모두 가상 데이터입니다.

```json
{
  "eventId": "c6514386-e795-3b67-86ab-7c4174f95801",
  "eventType": "PURCHASE",
  "orderId": "900001",
  "orderProductId": "900002",
  "productId": "100003",
  "productName": "예시 상품",
  "quantity": 2,
  "commissionBaseAmount": 30000,
  "expectedCommissionAmount": 1500,
  "partnerRefId": "cashback_20260914_a1",
  "subTagId": "campaign_202609",
  "attribution": "DIRECT",
  "purchaseEventId": null,
  "occurredAt": "2026-09-14T10:00:00+09:00",
  "recordedAt": "2026-09-14T10:00:01+09:00"
}
```

### 전체 취소 이벤트 예시

```json
{
  "eventId": "f36944ca-97e8-3a3e-a2f0-e55cbf42a102",
  "eventType": "CANCEL",
  "orderId": "900001",
  "orderProductId": "900002",
  "productId": "100003",
  "productName": "예시 상품",
  "quantity": 2,
  "commissionBaseAmount": 30000,
  "expectedCommissionAmount": 1500,
  "partnerRefId": "cashback_20260914_a1",
  "subTagId": "campaign_202609",
  "attribution": "DIRECT",
  "purchaseEventId": "c6514386-e795-3b67-86ab-7c4174f95801",
  "occurredAt": "2026-09-14T11:00:00+09:00",
  "recordedAt": "2026-09-14T11:00:01+09:00"
}
```

### 구매확정 이벤트 예시

취소 없이 구매확정된 경우입니다. 수량·금액·`partnerRefId`·`subTagId`는 구매 이벤트와 같고, `occurredAt`만 구매확정 시각입니다.

```json
{
  "eventId": "3f0d5c22-9b41-3a77-ae65-2c8f6d1b4703",
  "eventType": "CONFIRM",
  "orderId": "900001",
  "orderProductId": "900002",
  "productId": "100003",
  "productName": "예시 상품",
  "quantity": 2,
  "commissionBaseAmount": 30000,
  "expectedCommissionAmount": 1500,
  "partnerRefId": "cashback_20260914_a1",
  "subTagId": "campaign_202609",
  "attribution": "DIRECT",
  "purchaseEventId": "c6514386-e795-3b67-86ab-7c4174f95801",
  "occurredAt": "2026-09-21T09:00:00+09:00",
  "recordedAt": "2026-09-21T09:00:01+09:00"
}
```

***

## 8. Secret Key가 바뀌면

서명은 전송 시점의 현재 Secret Key 하나로만 만듭니다. 이중 키 서명이나 구 키 유예 기간은 없습니다. 재발급 직후 제휴사 검증 키도 바로 교체하시고, 재발급 직전에 시작된 요청은 구 키로 서명됐을 수 있으니 검증 실패 건은 거절한 뒤 [주문 이벤트 조회](/developers/open-api/api/order-events.md)로 채우세요. 재발급은 토스 운영자에게 문의해 주세요([연동 시작하기](/developers/open-api/auth.md)).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://sharelink-docs.toss.im/developers/open-api/api/order-event-callback.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
