/marketplace namespace 다
(예: https://staging-api.vpms.io/marketplace). 게이트웨이가 prefix 를 벗겨
marketplace-svc 로 프록시하므로, 이 문서의 모든 경로는 base URL 뒤에 그대로 붙인다
— 예: 토큰 발급은 POST {baseUrl}/oauth/token.
1. 인증 (OAuth2 client_credentials)
앱은 설치별 access token 을 발급받는다. 표준client_credentials 를 installation_id
파라미터로 확장한 흐름이다 — 토큰은 해당 설치의 accommodation 컨텍스트와 승인 scope 로
한정된다.
Authorization: Bearer <access_token> 를 실는다. scope 는 설치의
AccessGrant.effectiveScopes 기준이며, 부족하면 403 insufficient_scope 다.
OAuth client(
client_id/client_secret)는 앱 등록 후 콘솔에서 1회 발급된다. secret 은
그 시점에만 노출되므로 안전하게 보관하라. installation_id 는 테넌트가 앱을 설치·동의한
뒤 installation.activated webhook 으로 전달된다. webhook 외에도, 설치 완료 후 앱의
redirect URI 로 전달되는 handoff query parameter 의 1회용 code 를 POST /oauth/handoff
에 client_id/client_secret 과 함께 교환하면 {installation_id, accommodation_id, app_id}
를 얻는다 (code 는 1회용이며 10분 후 만료). 토큰 검증·폐기는 POST /oauth/introspect /
POST /oauth/revoke (둘 다 client_id/client_secret 인증).2. 멱등성 (명령형 쓰기 필수)
명령형 쓰기(예약·결제 —/api/v1/reservations*·/api/v1/payments* 의
POST/PATCH/DELETE)는 Idempotency-Key 헤더가 필수다. 없으면
400 invalid_request. 같은 키의 재요청은 최초 명령의 결과를 그대로 재응답하므로,
채널 매니저의 네트워크 재시도가 중복 예약/결제를 만들지 않는다. 그 밖의 쓰기
(dataset/document 푸시, setup-ready, action-run/upgrade 완료 보고 등)는 이 헤더를 받지
않는다 — upsert·상태 가드로 자체 멱등인 엔드포인트가 대부분이지만, POST /api/v1/events
는 중복 제거가 없으므로 재시도 시 이벤트가 중복 적재될 수 있다.
Ordering-Key 헤더(200자 이하, 초과 시
400 invalid_request)를 함께 보낼 수 있다. 같은 키의 지연(outbox) 명령은 설치 내에서
생성 순서대로 직렬 실행되고, 키가 다르면 병렬로 처리된다 (예: 같은 예약에 대한 수정
연쇄). 미지정 시 payload 의 id/reservation_id, 그것도 없으면 Idempotency-Key
기준으로 자동 직렬화된다.
3. 쓰기 모델 — command outbox (동기 확정 + WAL)
쓰기는 hybrid 로 처리된다: 요청을 durable 하게 journaling 한 뒤 인라인으로 내부 백엔드에 시도하고, 실패 시 워커가 at-least-once 로 재시도한다.202 를 받으면 최종 확정은 지연 webhook(예: reservation.created / reservation.failed)
으로 전달되며, 동일 outbox_id 로 요청과 상관관계를 맺는다. 완료 이벤트의 리소스
envelope 는 동기 성공 응답과 동일한 형태다. 단, 백엔드가 명령을 정책상 거부하면
(예: 감사 마감 데이터 수정 — ERR_AUDITTED_DATA) 재시도 없이 SKIPPED 로 종결되며
webhook 이 발행되지 않는다. 202 응답의 최종 상태는
GET /api/v1/commands?prefix=<키 prefix> (prefix 최소 8자)로 조회하라.
PMS 내부 변경 이벤트 (pms.reservation.*)
앱 명령 결과(reservation.*)와 별개로, 프런트데스크 등 VPMS 내부에서 발생한 예약 변경은
pms.reservation.* webhook 으로 구독 중인 설치에 전달된다 (채널매니저류 앱의 upstream 동기화용):
payload 는 식별자만 담는다(
{ reservation_id, actor, reason, occurred_at }) — 앱은
GET /api/v1/reservations/:id 로 전체 상태를 다시 읽는다(read-after-notify). actor 로
자기 쓰기 에코를 억제할 수 있으나, 정밀 억제는 앱이 자기 쓰기 예약 id 를 추적하는 것을 권장한다.
4. 예약 (Reservations)
POST /api/v1/reservations
reservations:write · Idempotency-Key 필수.
nightly_rates 지정 시 합계가 예약 총액이 되고, 요금제는 분류(리포팅) 용도로만 쓰인다.
그 밖의 예약 엔드포인트
5. 결제 (Payments)
POST /api/v1/payments
folios:write · Idempotency-Key 필수. 결제는 folio 에 귀속되므로 folio_id 또는
reservation_id(→folio 해석) 중 하나가 필요하다.
method: "platform" 은 VPMS 의 대외후불이다 — 예약에 연결된 채널(거래처)의 미수로
처리된다. 따라서 platform 결제는 예약이 channel_id(거래처 연결 채널)를 가질 때
의미가 있다.6. 조회 (Reads)
7. UI Surface 데이터 (매핑·문서)
UI surface(manifest.surfaces) 를 뒷받침하는 dataset·매핑·문서 API. 자세한 블록 선언은
UI Kit Manifest 가이드 참조.
PUT /api/v1/datasets/
key 는 ^[a-z0-9][a-z0-9_-]{0,63}$. 항목은 최대 5,000개.
GET /api/v1/mappings
?vpms_entity=<entity>&dataset=<key> 로 필터.
POST /api/v1/action-runs//complete
{ "status": "completed" | "failed", "message": "27건 동기화" } — 이미 종결된 run 은
현재 상태를 멱등 재응답한다.
PUT /api/v1/documents
설치 내(ref_type, data_id) 기준 upsert. body: ref_type · external_ref · data_id
(각 ^[a-zA-Z0-9._:-]{1,128}$), content_type(application/xml | application/json |
text/plain), content(UTF-8 최대 65,536 bytes), title(선택, 최대 200자).
external_ref 는 키가 아니라 예약 등 대상 리소스를 가리키는 갱신 데이터다. 응답에는
content 가 에코되지 않는다(id, ref_type, external_ref, data_id, size_bytes,
updated_at).
8. 설치 업그레이드
버전 업그레이드가 실행되면 항상
installation.upgraded webhook
({upgrade_id, from_version, to_version, app_migration, effective_scopes})이 발행된다.
manifest 의 upgrade 블록에 appMigration: "required" 인 홉이 포함된 업그레이드는
AWAITING_APP 상태로 유지되며(app_migration: true), 앱이 자체 마이그레이션을 마친 뒤
이 엔드포인트로 ack 해야 완료된다. 대상 업그레이드가 AWAITING_APP 이 아니면(이미
완료됐거나 다른 설치 소유) 404 not_found. upgrade 블록이 없거나 appMigration 이
none 이면 자동 완료된다(app_migration: false).