Store API 개요
관리(셀러) API 레퍼런스
온라인쇼핑몰 멀티테넌트 관리 Open API입니다. 스토어 = 테넌트.
베이스 URL
https://api.sayren.app/v1인증 — OAuth 2.1
두 가지 진입 경로가 있으며, 최종적으로 모든 테넌트 리소스는 storeToken으로 호출합니다 (경로에 storeId 불필요 — 토큰이 테넌트를 결정).
사람(인터랙티브) — authorization_code + PKCE:
GET /oauth2/authorize— PKCE(S256)·state 필수. 외부 IdP 로그인 화면을 거쳐redirect_uri로 code 전달POST /oauth2/token(grant_type=authorization_code) → userToken (+offline_accessscope 요청 시 refresh token, rotation 방식)GET /me/stores→ 소속 스토어 목록POST /oauth2/token(grant_type=urn:ietf:params:oauth:grant-type:token-exchange,audience={storeId}) → storeToken (role→scopes 자동)
서버(M2M) — client_credentials:
curl -u {client_id}:{client_secret} https://api.sayren.app/v1/oauth2/token \
-d "grant_type=client_credentials&scope=product:r order:r"클라이언트가 스토어에 바인딩되어 있어 storeToken이 곧바로 발급됩니다(scope로 다운스코프 가능).
데모 클라이언트: demo-client / demo-secret-key
API 토큰(PAT) — OAuth 클라이언트 없이 쓰는 사전 발급 Bearer 토큰입니다(스크립트·CI·LLM 연동).
curl https://api.sayren.app/v1/store/api-tokens \
-H "Authorization: Bearer {storeToken}" \
-d '{ "name": "ci-bot", "scopes": ["product:r", "order:r"], "expiresInDays": 90 }'- 평문 토큰(
sy_pat_…)은 발급 응답에서 한 번만 나옵니다. 목록(GET /store/api-tokens)에는 앞부분(prefix)과 발급자만 보입니다 - 발급한 토큰은 storeToken처럼
Authorization: Bearer로 씁니다.DELETE /store/api-tokens/{tokenId}로 즉시 폐기합니다 expiresInDays를 생략하면 90일,null이면 만료가 없습니다- 스코프는 발급자가 가진 범위 안에서만 지정할 수 있습니다. 읽기 전용(
product:r등)으로 최소한만 주세요
클라이언트 등록(DCR) — 자체 OAuth 클라이언트가 필요하면 RFC 7591 방식으로 등록합니다.
curl https://api.sayren.app/v1/oauth2/register \
-H "Authorization: Bearer {storeToken}" \
-d '{ "client_name": "내 연동 앱", "grant_types": ["client_credentials"], "scope": "product:r order:r" }'token_endpoint_auth_method: "none"이면 public 클라이언트(시크릿 없음, PKCE 로그인용)입니다. 이때redirect_uris는 https여야 합니다(개발용 루프백 http는 허용)- confidential 클라이언트는 등록한 스토어에 바인딩되고,
client_secret은 등록 응답에서 한 번만 나옵니다 - scope는 등록자가 가진 범위로 제한되고, 토큰은 등록한
grant_types로만 발급됩니다
API 토큰 발급과 클라이언트 등록은 OWNER/ADMIN의 storeToken으로만 할 수 있습니다. API 토큰이나 M2M 토큰으로는 호출할 수 없습니다. 셀러 콘솔에서는 개발자 메뉴의 API 토큰·앱 화면에서 같은 작업을 합니다.
구매자 위임 — 자체 회원 시스템을 가진 연동사가 구매자용 토큰을 위임 발급받는 경로:
curl -u {client_id}:{client_secret} https://api.sayren.app/v1/oauth2/token \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token_type=urn:sayren:params:oauth:token-type:member-ref" \
-d "subject_token=buyer@example.com"- 스토어에 바인딩된 confidential 클라이언트 +
member:delegatescope가 필요합니다 - 구매자를 이메일로 지목하면 해당 스토어의 회원 토큰(30분)이 발급됩니다 — 미등록 이메일은 자동으로 회원이 생성됩니다
- refresh token은 발급되지 않습니다 — 만료 시 서버가 같은 요청으로 재발급받으세요. 이 호출은 반드시 서버에서만 수행해야 합니다(클라이언트 시크릿 보호)
/oauth2/*엔드포인트는 RFC 6749 형식(평면 JSON)으로 응답합니다 — 표준 엔벨로프의 예외. 실패는{ "error", "error_description" }형식입니다.
크로스 테넌트 접근 시 403 TENANT_MISMATCH, 역할 권한 부족 시 403 INSUFFICIENT_ROLE.
TypeScript에서는 @sayren/store-sdk가 이 플로우를 캡슐화합니다.
스코프
형식은 {리소스}:{r|rw}이고 rw는 r을 포함합니다. 사람 로그인으로 받은 storeToken은 역할에 따라 스코프가
정해지고, API 토큰과 M2M 클라이언트는 발급할 때 지정한 스코프만 가집니다.
| 리소스 | 접근 수준 | OWNER | ADMIN | STAFF |
|---|---|---|---|---|
product category order claim inquiry review | r rw | rw | rw | rw |
settlement | r | r | — | — |
analytics | r | r | r | — |
webhook store member | r rw | rw | rw | — |
settlement:r은 매출 리포트(GET /sales-report)와 폐기 예정인 정산 API에 씁니다. 결제 설정 API는 store 스코프에
더해 콘솔에 로그인한 OWNER만 호출할 수 있습니다.
앱(OAuth 클라이언트) 관리
DCR로 등록한 클라이언트는 /store/api-clients에서 관리합니다. OWNER/ADMIN의 storeToken만 호출할 수 있고,
다른 스토어가 등록한 클라이언트에는 403 TENANT_MISMATCH가 반환됩니다.
| 요청 | 설명 |
|---|---|
GET /store/api-clients?status=ACTIVE | 등록한 클라이언트 목록(최신순). status를 생략하면 비활성화된 클라이언트도 포함 |
GET /store/api-clients/{clientId} | 클라이언트 상세 |
POST /store/api-clients/{clientId}/rotate-secret | confidential 클라이언트의 시크릿 재발급 |
POST /store/api-clients/{clientId}/deactivate | 클라이언트 비활성화(REVOKED) |
응답에는 시크릿 대신 앞부분(secretPrefix)과 등록자(createdByName·createdByEmail)가 담깁니다.
시크릿을 재발급하면 새 시크릿(clientSecret)은 그 응답에서 한 번만 나오고 기존 시크릿은 곧바로 쓸 수 없습니다.
유예 기간이 없으니 연동 서버의 설정을 바로 바꿔야 합니다. 이미 발급된 access token은 만료될 때까지 유효합니다.
비활성화는 되돌릴 수 없습니다. 그 클라이언트로 발급된 access token이 즉시 무효가 되고 refresh token도 폐기되며, 이후 토큰 발급 요청은 거부됩니다.
웹훅
주문·클레임·문의 등의 이벤트를 받을 HTTPS 주소를 /webhooks에 등록합니다. 스토어당 최대 10개이고
webhook:rw 스코프가 필요합니다(조회는 webhook:r).
curl https://api.sayren.app/v1/webhooks \
-H "Authorization: Bearer {storeToken}" \
-d '{ "url": "https://example.com/hooks/sayren", "eventTypes": ["ORDER.PAID", "CLAIM.RETURN_REQUESTED"] }'- 서명 시크릿(
secret)은 등록 응답에서 한 번만 나옵니다. 이후 조회에는 앞부분(secretPrefix)만 보입니다 POST /webhooks/{webhookId}/rotate-secret으로 시크릿을 바꾸면 이전 시크릿은oldSecretExpiresAt(24시간 뒤)까지 함께 유효합니다. 유효한 시크릿은 최대 2개라서, 이 기간에 다시 교체하면 가장 오래된 시크릿은 즉시 무효가 됩니다- 24시간 동안 전송이 한 번도 성공하지 못하면 구독이
SUSPENDED로 바뀝니다.POST /webhooks/{webhookId}/resume으로 재개하거나PUT /webhooks/{webhookId}로 수정하면ACTIVE로 돌아갑니다
페이로드 형식, 서명 검증, 재시도, 재전송은 웹훅 가이드에 정리되어 있습니다.
결제 설정
셀러가 직접 계약한 PG(토스페이먼츠·포트원)의 테스트 키·라이브 키, 사용 여부, 샌드박스 모드, 우선순위, 라우팅 방식을
/store/payment-settings와 /store/payment-providers/*에서 관리합니다. 콘솔에 로그인한 OWNER의 토큰만 받고,
ADMIN·STAFF와 API 토큰·M2M·연동 앱 토큰은 403 INSUFFICIENT_ROLE입니다. 비밀 값은 응답에 나오지 않습니다.
입력 항목과 에러는 결제 설정, 실결제 전환은
샌드박스에서 라이브로 전환하기에 정리되어 있습니다.
테스트 결제 주문
샌드박스 모드에서 받은 결제로 만든 주문은 테스트 결제 주문이고, 주문 응답의 testPayment가 true입니다. 주문 목록은
testPayment 쿼리로 실결제와 테스트 결제를 거를 수 있습니다. 필드와 쿼리는 GET /orders·GET /order-items 레퍼런스를 참고하세요.
주문 결제수단
실제 결제수단은 GET /orders/{orderId}의 payment.approvedMethod로 확인합니다. 값은 CARD·BANK_TRANSFER·VIRTUAL_ACCOUNT·
MOBILE·EASY_PAY이고, 결제 기록이 남아 있지 않은 오래된 주문은 null입니다. payment.method는 호환용이라 간편결제를 CARD로
표시합니다. 주문 목록(GET /orders, GET /order-items)에는 결제수단이 없습니다. 스토어프론트 API 주문 조회의 payment.method와
매출 리포트는 처음부터 실제 결제수단이라 간편결제를 EASY_PAY로 구분합니다.
매출 리포트
GET /sales-report는 기간·PG·결제수단별 결제·환불·순매출을 집계합니다(settlement:r). 테스트 결제는 기본으로 빠지고
includeTest=true로 포함합니다. 정산 API
/settlements는 폐기 예정이며 새 정산 내역이 쌓이지 않습니다. 매출 리포트를 참고하세요.
방문 분석
GET /analytics/overview·/analytics/products·/analytics/sources는 스토어프런트에서 수집한 방문 데이터를 날짜별로
집계해 돌려줍니다(analytics:r). 집계는 주기적으로 갱신되고 응답의 lastComputedAt이 마지막 집계 시각입니다.
방문 분석을 참고하세요.
API 로그
스토어로 들어온 관리 API 요청은 7일간 기록되고 셀러 콘솔에서 OWNER·ADMIN이 조회할 수 있습니다. 기록 범위와 저장하지 않는 정보는 API 로그를 참고하세요.
공통 규칙
- 모든 응답은 표준 엔벨로프 —
$.meta(status/code/message/isSuccess) + 성공$.data/ 실패$.error - 리소스는 복수형 명사, 상태 전이는 서브리소스 POST
- 목록 조회:
page/size페이지네이션 - 주문/발송/클레임은 OrderItem 단위 처리
- 일시는 ISO 8601 (KST 오프셋 포함)
- 일괄 처리 최대 100건, 부분 성공 응답
- 중복에 민감한 POST는
Idempotency-Key헤더 지원