> 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-events.md).

# 주문 이벤트 조회 (Pull)

보관 중인 주문 이벤트를 단일 필터와 발생 기간으로 조회합니다. Push 누락 대사와 특정 추적 식별자·subTag의 이벤트 확인에 사용합니다.

```
GET /openapi/order-events
```

필요 스코프: `sharelink:read`

> 기본 전달은 Push이고, 이 API는 **5분 주기로 돌려 누락을 채우는 용도**입니다. Push는 실패한 전송을 다시 보내지 않으므로 Push를 쓴다면 이 대사도 반드시 구현해 주세요. 회원 수만큼 폴링 루프를 늘리는 용도는 아닙니다.

***

## 요청

쿼리 파라미터는 6개이며 모두 선택 항목입니다. `partnerRefId`와 `subTagId`는 둘 중 하나만 지정할 수 있습니다. 필터를 생략하면 인증된 거래처 전체 이벤트를 조회합니다. 기간은 필터 유무와 관계없이 지정할 수 있습니다.

| 파라미터           | 타입     | 기본값 | 설명                                                                               |
| -------------- | ------ | --- | -------------------------------------------------------------------------------- |
| `cursor`       | string | 없음  | 이전 응답의 `nextCursor`. **최초 조회는 생략**합니다. 빈 문자열을 보내지 마세요                            |
| `partnerRefId` | string | 없음  | 정확히 일치하는 값만 조회. `subTagId`와 동시 지정 불가. 링크에 붙인 값과 같은 규칙(영문 소문자·숫자·`-`·`_` 1\~128자) |
| `size`         | number | 50  | 한 페이지 개수. 1\~100                                                                 |
| `subTagId`     | string | 없음  | 정확히 일치하는 subTag만 조회합니다. 영문 대소문자·숫자·`-`·`_`·`.` 1\~64자이며 대소문자를 구분합니다              |
| `occurredFrom` | string | 없음  | 이벤트 발생 시각의 조회 시작. 이 시각을 포함합니다. UTC 오프셋을 포함한 ISO 8601 형식입니다                       |
| `occurredTo`   | string | 없음  | 이벤트 발생 시각의 조회 종료. 이 시각은 포함하지 않습니다. `occurredFrom`과 함께 지정합니다                      |

| 입력                                                               | 처리                                                           |
| ---------------------------------------------------------------- | ------------------------------------------------------------ |
| `size`가 정수가 아니거나 1\~100 밖                                        | HTTP 200 + `errorCode: "INVALID_ARGUMENT"`                   |
| `partnerRefId`에 대문자·허용 외 문자·129자 이상                              | HTTP 200 + `errorCode: "INVALID_ARGUMENT"` (소문자로 바꿔 주지 않습니다) |
| `subTagId`가 빈 문자열이거나 허용 외 문자·65자 이상                              | HTTP 200 + `errorCode: "INVALID_REQUEST"`                    |
| `partnerRefId`와 `subTagId`를 동시에 지정                               | HTTP 200 + `errorCode: "INVALID_ARGUMENT"`                   |
| 기간 한쪽만 지정, 오프셋 누락·형식 오류, 시작 시각이 종료 시각과 같거나 늦음, 31일 초과, 종료 시각이 미래 | HTTP 200 + `errorCode: "INVALID_ARGUMENT"`                   |
| 기간 커서의 거래처·필터·기간이 다르거나 기간 유무를 바꿔 커서를 사용                          | HTTP 200 + `errorCode: "INVALID_CURSOR"`                     |
| `cursor`가 빈 문자열                                                  | HTTP 200 + `errorCode: "INVALID_ARGUMENT"`                   |
| `cursor`가 응답으로 받은 값이 아닌 손상된 값                                    | HTTP 200 + `errorCode: "INVALID_CURSOR"`                     |
| 인증 정보·출발지 IP·거래처 상태 문제                                           | HTTP 200 + `errorCode: "SHARELINK_OPENAPI_ACCESS_DENIED"`    |
| 조건에 맞는 이벤트가 없음                                                   | 정상 응답. `events`가 빈 배열, `hasMore`는 `false`                    |

```bash
# 최초 조회: 최신 기록부터 최대 50건
curl --get 'https://sharelink.toss.im/openapi/order-events' \
  -H 'Authorization: Bearer {액세스 토큰}' \
  --data-urlencode 'size=50'
```

```bash
# 특정 추적 식별자 재확인: 새 조회이므로 cursor 생략
curl --get 'https://sharelink.toss.im/openapi/order-events' \
  -H 'Authorization: Bearer {액세스 토큰}' \
  --data-urlencode 'partnerRefId=cashback_20260914_a1'
```

```bash
# 과거 페이지로 이어서 조회: 같은 필터를 유지하고 cursor 만 추가
curl --get 'https://sharelink.toss.im/openapi/order-events' \
  -H 'Authorization: Bearer {액세스 토큰}' \
  --data-urlencode 'partnerRefId=cashback_20260914_a1' \
  --data-urlencode 'cursor={이전 응답의 nextCursor}'
```

***

## 발생 기간 조회

* `occurredFrom`과 `occurredTo`를 함께 지정하며, 시작보다 종료가 뒤여야 합니다.
* 조회 구간은 **시작 포함·종료 미포함**이며 최대 31일입니다. 더 긴 기간은 여러 구간으로 나누어 조회합니다.
* 종료 시각은 현재 시각을 넘을 수 없습니다.
* `2026-10-01T00:00:00+09:00` 또는 `2026-09-30T15:00:00Z`처럼 오프셋이 있는 값을 사용합니다. 같은 순간이면 같은 경계로 처리합니다.
* 기간 조회에서는 발생 시각 내림차순으로 반환하며, 발생 시각이 같으면 기록 ID 내림차순으로 반환합니다. 기간을 생략하면 기존 기록 ID 내림차순 조회를 유지합니다.

```bash
# 특정 subTag의 10월 1일 발생 이벤트 조회
curl --get 'https://sharelink.toss.im/openapi/order-events' \
  -H 'Authorization: Bearer {액세스 토큰}' \
  --data-urlencode 'subTagId=Creator_A1' \
  --data-urlencode 'occurredFrom=2026-10-01T00:00:00+09:00' \
  --data-urlencode 'occurredTo=2026-10-02T00:00:00+09:00' \
  --data-urlencode 'size=50'
```

```bash
# 같은 조건의 다음 페이지: 필터와 기간을 유지하고 응답 커서를 추가
curl --get 'https://sharelink.toss.im/openapi/order-events' \
  -H 'Authorization: Bearer {액세스 토큰}' \
  --data-urlencode 'subTagId=Creator_A1' \
  --data-urlencode 'occurredFrom=2026-10-01T00:00:00+09:00' \
  --data-urlencode 'occurredTo=2026-10-02T00:00:00+09:00' \
  --data-urlencode 'cursor={이전 응답의 nextCursor}'
```

기간 전체 조회는 위 예시에서 `subTagId`를 생략합니다. 추적 식별자로 조회하려면 `subTagId` 대신 `partnerRefId`를 지정합니다. `+`가 들어 있는 오프셋과 커서는 예시처럼 `--data-urlencode`로 인코딩해 주세요.

***

## 응답

```json
{
  "resultType": "SUCCESS",
  "success": {
    "events": [
      {
        "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"
      }
    ],
    "nextCursor": "MTIzNDU",
    "hasMore": false
  }
}
```

| 필드           | 타입      | 설명                                                                                          |
| ------------ | ------- | ------------------------------------------------------------------------------------------- |
| `events`     | array   | 이벤트 객체 배열. 기간 미지정 시 기록 ID 내림차순, 기간 지정 시 발생 시각·기록 ID 내림차순으로 내려갑니다                            |
| `nextCursor` | string  | 마지막 반환 이벤트보다 **과거** 기록을 조회하는 위치. 다른 목록 API와 달리 `null`이 되지 않으며, 더 볼 것이 있는지는 `hasMore`로 판단합니다 |
| `hasMore`    | boolean | 같은 조건에서 과거 기록의 다음 페이지가 있는지 여부. 다른 목록 API의 `hasNext`에 해당합니다                                  |

`events[]`의 각 항목은 아래 15개 필드입니다. 공통 필드의 의미는 [주문 이벤트 수신](/developers/open-api/api/order-event-callback.md)의 **이벤트 필드** 표에 있습니다.

| 필드                                                                                                                 | 타입             |
| ------------------------------------------------------------------------------------------------------------------ | -------------- |
| `eventId` `eventType` `orderId` `orderProductId` `productId` `productName` `attribution` `occurredAt` `recordedAt` | string         |
| `quantity` `commissionBaseAmount` `expectedCommissionAmount`                                                       | number         |
| `partnerRefId` `subTagId` `purchaseEventId`                                                                        | string \| null |

### Push와의 차이

이 API는 주문상품 단위로 이벤트를 반환합니다. 구매 Push의 `orderSummary`는 반환하지 않습니다.

보관 중인 이벤트가 하나도 없을 때의 정상 응답입니다.

```json
{
  "resultType": "SUCCESS",
  "success": {
    "events": [],
    "nextCursor": "MA",
    "hasMore": false
  }
}
```

***

## 커서 사용 규칙과 조회 범위

커서는 **선택한 정렬 기준에서 과거 방향으로만** 넘어가는 위치 표시입니다. 형식을 해석하거나 직접 만들지 말고, 응답으로 받은 값을 그대로 사용합니다. 새 이벤트는 커서 없이 새로 조회해야만 보입니다.

1. 최초 호출은 `cursor` 없이 시작합니다. 기간을 생략하면 기록 ID 내림차순, 기간을 지정하면 발생 시각·기록 ID 내림차순으로 돌려드립니다.
2. 한 페이지를 저장한 뒤 `nextCursor`로 다음 페이지를 조회합니다. 처리 중 실패하면 같은 조건·같은 커서로 다시 조회하고 `eventId`로 중복을 걸러 주세요.
3. 커서를 보내고 빈 페이지가 오면 `nextCursor`는 보내신 커서와 같은 위치입니다. 최초 조회부터 비어 있으면 그 커서는 "더 과거가 없다"는 뜻이라, 저장해 재사용하면 이후 조회가 항상 비게 됩니다.
4. `partnerRefId`·`subTagId`·기간 조건을 바꾸거나 기간 조회와 무기간 조회 사이를 전환할 때는 `cursor`를 생략하고 새로 시작합니다. 기간 커서는 인증된 거래처·필터·기간에 연결되어 있어 조건이 다르면 `INVALID_CURSOR`로 거절됩니다. 무기간 조회의 기존 커서는 그대로 사용할 수 있습니다.
5. 커서에 만료는 없고, 오래된 커서로 조회하면 그 위치보다 과거이면서 **아직 보관 중인** 이벤트만 옵니다.
6. 조회에서 `recordedAt` 기준 90일 제한을 추가로 적용하지 않습니다. 다만 실제로 보관 중인 이벤트만 조회하며, 삭제된 이벤트는 복구하지 않습니다. 이 API가 특정 보관 기간을 보장하는 것은 아닙니다.

**커서를 영속 저장할 필요는 없습니다.** 대사 한 번의 흐름은 이렇습니다.

```
cursor = 없음
반복:
  응답 = GET /openapi/order-events?size=100[&cursor=cursor]
  응답.events 를 eventId 유일 제약으로 저장 (새로 저장된 건수 = n)
  n == 0 인 페이지가 두 번 연속이면 이미 받은 구간 → 종료
  hasMore 가 false 면 마지막 페이지 → 종료
  cursor = 응답.nextCursor, 1초 대기
대사 종료 후 cursor 는 버립니다. 다음 대사도 없음에서 시작합니다.
```

한 페이지 더 읽는 것은 반영 지연에 대비하기 위한 운영 방법이며, 모든 지연을 복구한다고 보장하지는 않습니다.

**기간 대사는 최근 발생 기간이 겹치도록 다시 조회하고 `eventId`로 중복을 제거합니다.** 늦게 기록된 이벤트는 이미 읽은 발생 시각 구간에 들어올 수 있어, 이전 커서만 이어 읽으면 놓칠 수 있습니다. 같은 기간을 다시 읽을 때도 커서 없이 시작합니다. 기간 조건에 사용한 종료 시각은 페이지를 넘기는 동안 고정해 주세요.

***

## 권장 조회 주기

| 상황                          | 권장                                                                           |
| --------------------------- | ---------------------------------------------------------------------------- |
| 정기 대사 (전체 조회)               | **5분** 간격, `cursor` 없이 시작. 대사 배치는 인스턴스 하나에서만 돌리세요                            |
| `hasMore`가 `true`일 때 다음 페이지 | 1초 뒤                                                                         |
| 구매 직후 `partnerRefId` 재확인    | 2초 간격으로 짧게. 빈 결과가 이어지면 간격을 늘리고 상한을 두세요(예: 최대 5회, 총 30초). 상한에 닿으면 정기 대사에 맡깁니다 |

이 간격은 권장값이며 강제 제한이나 실시간 반영 SLA가 아닙니다. 기존 호출 제한(거래처 단위 10 rps)이 그대로 적용되므로 여러 `partnerRefId`의 병렬 조회를 합산해 한도 안에서 제어하고, 같은 `partnerRefId`의 동시 루프는 하나로 합치세요.

***

## 오류 처리

애플리케이션 오류는 **HTTP 200이어도** `error.errorCode`를 확인해야 합니다. 형식은 공통 규약의 **공통 응답 형식**과 같습니다.

```json
{
  "resultType": "FAIL",
  "error": {
    "errorCode": "INVALID_CURSOR"
  }
}
```

| `errorCode`                       | 확인할 것                                                 | 재시도                          |
| --------------------------------- | ----------------------------------------------------- | ---------------------------- |
| `INVALID_ARGUMENT`                | `size`·`partnerRefId`·`cursor` 입력 형식, 필터 동시 지정, 기간 조건 | 하지 마세요. 값을 고쳐야 합니다           |
| `INVALID_REQUEST`                 | `subTagId` 입력 형식                                      | 하지 마세요. 값을 고쳐야 합니다           |
| `INVALID_CURSOR`                  | 커서가 손상됐거나 기간 유무·거래처·필터·기간 조건이 달라졌는지                   | 하지 마세요. `cursor` 없이 새로 조회하세요 |
| `SHARELINK_OPENAPI_ACCESS_DENIED` | 인증 정보·거래처 활성 상태·등록한 출발지 IP                            | 하지 마세요                       |

게이트웨이 인증(HTTP 401)·호출 제한(HTTP 429)·네트워크 오류는 HTTP 상태나 전송 오류로 별도 발생합니다.

***

## 꼭 확인해 주세요

**빈 조회 결과는 미구매의 증거가 아닙니다.** 아직 기록되지 않았거나, 반영 지연으로 최신 이벤트가 잠시 보이지 않을 수 있습니다.

**Push로 받은 이벤트를 근거로 커서를 전진시키지 마세요.** 커서는 이 API의 응답으로만 갱신합니다.

**취소·구매확정이 구매보다 먼저 조회될 수 있고, 구매 이벤트 없이 취소나 구매확정만 올 수도 있습니다.** 처리 규칙은 주문 이벤트 수신의 **설계 전에 알아 둘 것**과 같습니다. 구매확정된 주문상품에는 이후 취소 이벤트가 발행되지 않는 점도 같은 표에 있습니다.

**이벤트는 2026-09-16부터 기록됩니다.** 그 이전 주문은 조회되지 않습니다.


---

# 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
