아직 MID 가 없는 하위 판매자의 PG 가맹 신청을 받습니다. 화면이 필요하면 등록 폼을, 이미 받은 정보로 낼 때는 등록 API 를 사용합니다. B형(영업대행) 리셀러만 쓸 수 있습니다 — 리셀러 가입·유형.
| API | 하는 일 |
|---|---|
| 1. 등록 폼 세션 발급 | 등록 폼 토큰 발급 |
| 2. 등록 폼 열기 (JS SDK) | 리셀러 화면에서 등록 폼 열기 |
| 3. 등록 상태 조회 | 하위 가맹점의 PG 신청 진행 상태 |
| 4. 등록 API 접수 | 화면 없이 신청서 접수·보완, 약관 조회, 서류 업로드 허가 |
| 5. 신청서 서류 열기 | 신청서 서류 목록과 3분 열람 주소 |
인증·권한 오류는 각 표에 없어도 공통 오류 가 날 수 있습니다.
1등록 폼 세션 발급
하위 가맹점 한 곳의 PG 가맹 신청 화면(등록 폼)을 열 수 있는 토큰을 발급합니다. 받은
token을 등록 폼 열기 (JS SDK) 에 넘기거나url을 그대로 열면, 판매자는 부트페이 계정 없이 신청서를 작성해 접수합니다.
동작 규칙
- 토큰은 하위 가맹점 1곳 · 30분 · 접수 1회로 묶입니다. 접수하면 소진되고, 30분이 지나면 열리지 않습니다.
- 토큰 평문은 이 응답에서만 나옵니다. 부트페이는 해시만 저장하므로 다시 조회할 수 없습니다. 필요하면 새로 발급합니다.
- 같은 하위 가맹점·같은 종류로 다시 발급하면 아직 쓰지 않은 이전 토큰은 바로 만료됩니다.
- 지금 열 수 있는 종류(
kind)는pg_join(PG 가맹 신청) 하나입니다. - 신청 단계가 바뀔 때마다 리셀러 웹훅 이 오고, 놓친 단계는 등록 상태 조회 로 다시 맞춥니다.
이 API 는 리셀러 계정 키(Client Key·Secret Key)로 부릅니다. 브라우저·앱에 리셀러 키를 두지 말고, 리셀러 서버에서 발급한 token 만 판매자 화면에 넘깁니다. 토큰을 로그에 남기면 30분 동안 누구나 그 가맹점 이름으로 신청서를 낼 수 있습니다.
API 정보
https://api.bootpay.co.kr/v2/reseller/seller/:provider_id/onboarding/sessionBasic Auth (리셀러 계정 키)1-1. 요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
provider_id |
String | 필수 | 신청을 받을 하위 가맹점의 provider_id (URL 경로) |
kind |
String | 필수 | 등록 폼 종류. 지금은 pg_join 만 |
mode |
String | 선택 | new 새 신청(기본) · edit 기존 신청서 수정·재제출 |
pg_wait_id |
String | 선택 | mode=edit 일 때 필수. 고칠 신청서의 onboarding_id |
allowed_pgs |
Array | 선택 | 신청할 PG 별칭. new 는 정확히 1개 필수, edit 는 비우면 신청서의 PG |
allowed_methods |
Array | 선택 | 고를 수 있는 결제수단 별칭. 비우면 그 PG 의 결제수단 전부 |
app_id |
String | 선택 | PG 승인 뒤 MID 를 연결할 하위 프로젝트. 비우면 그 가맹점에서 가장 먼저 만든 프로젝트 |
prefill |
Object | 선택 | 신청서에 미리 채울 값 ({키: 값}, 값은 문자열·숫자, 1000자 이하) |
locked |
Array | 선택 | 판매자가 고칠 수 없게 잠글 키. prefill 에 있는 키만 |
return_url |
String | 선택 | 접수 뒤 돌아갈 주소. https:// 만, 2048자 이하 |
brand |
Object | 선택 | 신청 화면 상단 브랜드 {name(30자 이하), logo_url(https)} |
prefill·locked 에 쓸 수 있는 키는 아래와 같습니다. 이 밖의 키를 보내면 400 ONBOARDING_PREFILL_INVALID 로 거부됩니다(오타를 바로 알 수 있게 조용히 버리지 않습니다). pg_alias 를 보내면 allowed_pgs 의 PG 와 같아야 합니다.
| 묶음 | 키 |
|---|---|
| 사업자 | company_name seller_type regist_no corp_reg_no owner_name owner_birth_date corp_capital zip addr1 addr2 biz_email biz_tel biz_fax tax_type |
| 서비스 | service_name site_url business_type business_item real_type good_type desc max_term month_price good_price policy type |
| 담당자 | name email phone tel |
| 정산 계좌 | bank_code account_number account_owner |
| PG | pg_alias pay_method expect_mid |
1-2. 코드 예제
import fetch from 'node-fetch'
const RESELLER_AUTH = 'Basic ' + Buffer
.from(`${process.env.BOOTPAY_RESELLER_CLIENT_KEY}:${process.env.BOOTPAY_RESELLER_SECRET_KEY}`)
.toString('base64')
const providerId = '65a1c0aa8f1b5b00367a0001'
const res = await fetch(`https://api.bootpay.co.kr/v2/reseller/seller/${providerId}/onboarding/session`, {
method: 'POST',
headers: {
'Authorization': RESELLER_AUTH,
'Content-Type': 'application/json'
},
body: JSON.stringify({
kind: 'pg_join',
allowed_pgs: ['nicepay'],
prefill: { company_name: '예시상점', regist_no: '1234567890' },
locked: ['company_name', 'regist_no'],
return_url: 'https://reseller.example.com/onboarding/done',
brand: { name: '예시 리셀러', logo_url: 'https://reseller.example.com/logo.png' }
})
})
const { token, url, expires_at } = await res.json()
// token 만 판매자 화면에 넘긴다 — 로그에 남기지 않는다javascriptimport base64, os, requests
auth = 'Basic ' + base64.b64encode(
f"{os.environ['BOOTPAY_RESELLER_CLIENT_KEY']}:{os.environ['BOOTPAY_RESELLER_SECRET_KEY']}".encode()
).decode()
provider_id = '65a1c0aa8f1b5b00367a0001'
res = requests.post(
f'https://api.bootpay.co.kr/v2/reseller/seller/{provider_id}/onboarding/session',
headers={'Authorization': auth},
json={
'kind': 'pg_join',
'allowed_pgs': ['nicepay'],
'prefill': {'company_name': '예시상점', 'regist_no': '1234567890'},
'locked': ['company_name', 'regist_no'],
'return_url': 'https://reseller.example.com/onboarding/done'
}
)
session = res.json() # token 은 판매자 화면에만 넘긴다pythonRESELLER_AUTH=$(printf '%s' "$BOOTPAY_RESELLER_CLIENT_KEY:$BOOTPAY_RESELLER_SECRET_KEY" | base64)
PROVIDER_ID='65a1c0aa8f1b5b00367a0001'
curl -X POST "https://api.bootpay.co.kr/v2/reseller/seller/$PROVIDER_ID/onboarding/session" \
-H "Authorization: Basic $RESELLER_AUTH" \
-H 'Content-Type: application/json' \
-d '{"kind":"pg_join","allowed_pgs":["nicepay"]}'bash1-3. 응답
{
"token": "rst_example_token",
"url": "https://admin.bootpay.co.kr/onboarding/s/rst_example_token",
"expires_at": "2026-10-27T15:30:00+09:00"
}json응답 파라미터
| 파라미터 | 타입 | 설명 |
|---|---|---|
| token | String | 등록 폼 토큰(rst_ 로 시작). 이 응답에서만 나옵니다 |
| url | String | 토큰으로 바로 여는 신청 화면 주소. SDK 없이 새 창·리다이렉트로 열 때 사용합니다 |
| expires_at | String | 만료 시각(ISO 8601, 발급 + 30분) |
1-4. 판매자가 열었을 때
| 상황 | 신청 화면 |
|---|---|
| 30분이 지남 | "신청 주소를 쓸 수 있는 시간이 지났습니다" — 새로 발급합니다 |
| 이미 접수함 | "이미 접수한 신청 주소입니다" — 고칠 내용은 mode=edit 로 새로 발급합니다 |
| 같은 가맹점에 새 토큰을 발급함 | 이전 토큰은 만료로 안내됩니다 |
| 리셀러 사용이 멈춤 | 열리지 않습니다 |
1-5. 에러 코드
| 코드 | 메시지 | 대처 방법 |
|---|---|---|
API_ (601) |
리셀러만 이용이 가능한 API입니다 | 리셀러 권한 계정 키로 호출합니다 |
API_ |
허용되지 않은 API 권한의 키입니다 (HTTP 401) | 키에 이 API 의 리셀러 스코프 를 주거나 전체 권한(supervisor) 키로 호출합니다 |
RESELLER_ (343) |
본사가 리셀러 사용을 멈췄습니다 (HTTP 403) | 부트페이에 문의합니다 |
ONBOARDING_ |
kind 가 없거나 모르는 값입니다 |
pg_join 으로 보냅니다 |
ONBOARDING_ |
아직 열지 않은 종류입니다(sub_mall) |
지금은 pg_join 만 사용합니다 |
ONBOARDING_ (8168) |
이 리셀러 유형으로는 만들 수 없는 등록 폼입니다 (HTTP 403). PG 가맹 신청(pg_join)은 B형(영업대행) 리셀러만 만들 수 있습니다 — 메시지에 이 계정의 유형이 나옵니다 |
지금 유형은 리셀러 내 정보 의 reseller_contract.type 으로 확인합니다. 바꾸려면 부트페이에 문의합니다 |
PROVIDER_ (300) |
본인 하위 가맹점이 아닙니다 (HTTP 404) | provider_id 가 본인 하위 가맹점인지 확인합니다 |
ONBOARDING_ |
mode 가 new·edit 가 아닙니다 |
new 또는 edit 로 보냅니다 |
PG_ (11000) |
그 가맹점의 신청서가 아닙니다 (HTTP 404) | pg_wait_id 를 등록 상태 조회 의 onboarding_id 로 보냅니다 |
PW_ (2232) |
신청이 끝난 건은 고칠 수 없습니다 | 새 신청은 mode=new 로 발급합니다 |
APP_ (200) |
그 가맹점의 사용 중인 프로젝트가 아닙니다 (HTTP 404) | app_id 를 확인하거나 비웁니다 |
ONBOARDING_ |
allowed_pgs 가 1개가 아니거나 신청서의 PG 와 다릅니다 |
PG 별칭을 하나만 보냅니다 |
PG_ |
없거나 쓰지 않는 PG입니다 (HTTP 404) | PG 코드 에서 별칭을 확인합니다 |
ONBOARDING_ |
그 PG 에서 쓸 수 없는 결제수단이 있습니다 | 결제수단 별칭을 확인하거나 비웁니다 |
ONBOARDING_ |
prefill·locked 에 받을 수 없는 키나 값이 있습니다 |
위 허용 키 표를 확인합니다 |
ONBOARDING_ |
return_url 이 https 가 아니거나 너무 깁니다 |
2048자 이하의 https:// 주소로 보냅니다 |
ONBOARDING_ |
brand 값이 올바르지 않습니다 |
name 30자 이하, logo_url 은 https:// |
2등록 폼 열기 (JS SDK)
리셀러 서버가 등록 폼 세션 발급 으로 받은
token을 판매자 화면에 넘기고,Bootpay.requestOnboarding({ token })으로 부트페이 PG 가맹 신청 화면을 엽니다. 판매자가 접수하면 Promise 가done으로 끝납니다.
동작 규칙
- 결제창과 같은 JS SDK(
@bootpay/client-js)의 함수입니다. 웹 브라우저에서만 열립니다. - 여는 방식(
display)은iframe(기본, 지금 페이지 위 덮개) ·popup(새 창) ·redirect(지금 창 이동) 셋입니다. - 접수하면 resolve
{event: 'done', id, kind, pg_wait_id, status}, 판매자가 닫거나 「나중에 할게요」를 누르면 reject{event: 'cancel'}입니다. - 화면 결과는 표시용입니다. 최종 상태는 리셀러 서버가 리셀러 웹훅 이나 등록 상태 조회 로 확인합니다.
- 토큰은 리셀러 서버에서 발급합니다. 리셀러 키를 브라우저에 두지 않습니다.
2-1. 함수
Bootpay.requestOnboarding(data: {
token: string // 등록 폼 토큰(rst_…) — 필수
display?: 'iframe' | 'popup' | 'redirect' // 기본 iframe
url?: string // 세션 발급 응답의 url 을 그대로 쓸 때만(부트페이 신청 화면 주소만 받는다)
}): Promise<{
event: 'done' | 'cancel' | 'error'
id?: string // 신청서 식별자(= onboarding_id)
kind?: string // 'pg_join'
pg_wait_id?: string // id 와 같은 값
status?: number | null // 접수 직후 신청 상태
error_code?: string
message?: string
}>typescript2-2. 코드 예제
import { Bootpay } from '@bootpay/client-js'
// 리셀러 서버가 등록 폼 세션 발급 API 로 받은 token 만 화면에 넘긴다
const { token } = await fetch('/api/seller/onboarding-session', { method: 'POST' }).then((r) => r.json())
try {
const result = await Bootpay.requestOnboarding({ token, display: 'iframe' })
// result.event === 'done'
showMessage('PG 신청을 접수했어요. 심사 진행은 알림으로 알려 드릴게요.')
console.log(result.pg_wait_id, result.status)
} catch (e) {
if (e.event === 'cancel') {
// 판매자가 닫았다 — 나중에 다시 열 수 있게 둔다
} else {
console.error(e.error_code, e.message)
}
}javascript2-3. 여는 방식
| display | 열리는 모양 | 끝날 때 |
|---|---|---|
iframe (기본) |
지금 페이지 위에 덮개로 엽니다 | 접수하면 done, 덮개를 닫으면 cancel |
popup |
새 창으로 엽니다. 버튼 클릭 같은 사용자 동작 안에서 불러야 팝업이 막히지 않습니다 | 접수하면 done, 창을 닫으면 cancel. 팝업이 막히면 POPUP_BLOCKED |
redirect |
지금 창을 신청 화면으로 옮깁니다. Promise 는 끝나지 않습니다 | 세션의 return_url 로 돌아옵니다(아래) |
redirect 로 돌아올 때
세션을 발급할 때 return_url 을 넣으면, 접수 뒤 그 주소에 결과 쿼리를 붙여 돌려보냅니다. 토큰은 쿼리에 넣지 않습니다.
https://reseller.example.com/onboarding/done?event=done&id=6718a0aa8f1b5b00367a0101&kind=pg_join&pg_wait_id=6718a0aa8f1b5b00367a0101&status=0
https://reseller.example.com/onboarding/done?event=cancel쿼리 값은 누구나 바꿔 붙일 수 있습니다. 화면 안내에만 쓰고, 상태는 서버에서 등록 상태 조회 로 다시 확인합니다.
앱(웹뷰)에서 열 때
앱용 SDK 에는 아직 이 함수가 없습니다. 세션 발급 응답의 url 을 웹뷰나 외부 브라우저로 열고, return_url 을 앱이 받을 수 있는 https 주소로 넣어 돌아오는 시점을 잡습니다.
2-4. 신청 화면이 열리지 않을 때
| 상황 | 화면·결과 | 할 일 |
|---|---|---|
| 30분이 지난 토큰 | "신청 주소를 쓸 수 있는 시간이 지났습니다" | 서버에서 새로 발급합니다 |
| 이미 접수한 토큰 | "이미 접수한 신청 주소입니다" | 고칠 내용이 있으면 mode=edit 로 새로 발급합니다 |
| 같은 가맹점에 새 토큰을 발급함 | 이전 토큰은 만료로 안내됩니다 | 마지막에 받은 토큰을 사용합니다 |
| 리셀러 사용이 멈춤 | 열리지 않습니다 | 부트페이에 문의합니다 |
2-5. 에러 코드
| 코드 | 메시지 | 대처 방법 |
|---|---|---|
ONBOARDING_ |
token 이 없습니다 |
세션 발급 응답의 token 을 넘깁니다 |
ONBOARDING_ |
url 이 부트페이 신청 화면 주소가 아닙니다 |
세션 발급 응답의 url 을 그대로 넘기거나 url 을 빼고 token 만 넘깁니다 |
POPUP_ |
브라우저가 팝업을 막았습니다 | 버튼 클릭 안에서 부르거나 display: 'iframe' 을 사용합니다 |
ONBOARDING_ |
브라우저가 아닌 곳(서버 렌더링 등)에서 불렀습니다 | 브라우저에서 부릅니다 |
3등록 상태 조회
하위 가맹점 한 곳의 PG 가맹 신청서를 최신순으로 읽습니다. 각 항목의 값은 리셀러 웹훅 의
data와 같은 이름이라, 웹훅을 놓쳤거나 순서가 뒤바뀌었을 때 이 API 로 최신 상태를 다시 맞춥니다.
동작 규칙
- 그 가맹점의 신청서를 전부 보여 줍니다. 리셀러가 발급한 등록 폼으로 낸 신청(
channel=hosted)뿐 아니라 판매자가 부트페이 콘솔에서 직접 낸 신청(console)도 나옵니다. - 최신순 20건까지.
onboarding_id를 주면 그 1건만 옵니다. - 신청이 없으면
{"list": []}로 200입니다. - 본사 담당자의 메모·연락 기록·제출 서류·담당자 연락처는 내려 주지 않습니다.
API 정보
https://api.bootpay.co.kr/v2/reseller/seller/:provider_id/onboardingBasic Auth (리셀러 계정 키)3-1. 요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
provider_id |
String | 필수 | 하위 가맹점의 provider_id (URL 경로) |
onboarding_id |
String | 선택 | 신청서 하나만 볼 때 (쿼리) |
3-2. 코드 예제
import fetch from 'node-fetch'
const RESELLER_AUTH = 'Basic ' + Buffer
.from(`${process.env.BOOTPAY_RESELLER_CLIENT_KEY}:${process.env.BOOTPAY_RESELLER_SECRET_KEY}`)
.toString('base64')
const providerId = '65a1c0aa8f1b5b00367a0001'
const res = await fetch(`https://api.bootpay.co.kr/v2/reseller/seller/${providerId}/onboarding`, {
headers: { 'Authorization': RESELLER_AUTH }
})
const { list } = await res.json()
console.log(list[0]?.stage, list[0]?.stage_owner)javascriptimport base64, os, requests
auth = 'Basic ' + base64.b64encode(
f"{os.environ['BOOTPAY_RESELLER_CLIENT_KEY']}:{os.environ['BOOTPAY_RESELLER_SECRET_KEY']}".encode()
).decode()
provider_id = '65a1c0aa8f1b5b00367a0001'
res = requests.get(
f'https://api.bootpay.co.kr/v2/reseller/seller/{provider_id}/onboarding',
headers={'Authorization': auth}
)
print(res.json()['list'])pythonRESELLER_AUTH=$(printf '%s' "$BOOTPAY_RESELLER_CLIENT_KEY:$BOOTPAY_RESELLER_SECRET_KEY" | base64)
PROVIDER_ID='65a1c0aa8f1b5b00367a0001'
curl "https://api.bootpay.co.kr/v2/reseller/seller/$PROVIDER_ID/onboarding" \
-H "Authorization: Basic $RESELLER_AUTH"bash3-3. 응답
{
"list": [
{
"provider_id": "65a1c0aa8f1b5b00367a0001",
"onboarding_id": "6718a0aa8f1b5b00367a0101",
"kind": "pg_join",
"status": 1,
"status_name": "구비서류 전달 필요",
"stage": "doc_wait",
"stage_owner": "merchant",
"due_at": "2026-10-30T18:00:00+09:00",
"reject_reason": null,
"pg_wait_id": "6718a0aa8f1b5b00367a0101",
"pg": "nicepay",
"overdue": false,
"required_documents": ["doc"],
"channel": "hosted",
"c_at": "2026-10-27T10:00:00+09:00",
"u_at": "2026-10-27T11:20:00+09:00"
}
]
}json응답 파라미터
| 파라미터 | 타입 | 설명 |
|---|---|---|
| list | Array | 신청서 목록(최신순, 최대 20건) |
| list[].provider_id | String | 하위 가맹점 provider_id |
| list[].onboarding_id | String | 신청서 식별자. mode=edit 세션의 pg_wait_id 로 사용합니다 |
| list[].pg_wait_id | String | onboarding_id 와 같은 값 |
| list[].kind | String | pg_join |
| list[].status | Number | 신청 상태 번호 |
| list[].status_name | String | 신청 상태 이름 |
| list[].stage | String | 단계 — 아래 단계 표 |
| list[].stage_owner | String | 지금 할 차례: ours 부트페이 · merchant 판매자 · pg PG사 · 끝난 단계는 null |
| list[].due_at | String | 이 단계의 기한(ISO 8601). 기한이 없는 단계는 null |
| list[].overdue | Boolean | 기한이 지났는지 |
| list[].required_documents | Array | 서류 대기 단계일 때의 대기 사유 코드(doc 서류 · esign 전자계약 · fee 가맹비 · insurance 보증보험 · service_fix 서비스 보완 · review_fix PG 심사 보완 · card_fix 카드사 보완). 그 밖의 단계는 [] |
| list[].reject_reason | String | 반려 사유(반려일 때만, 그 밖은 null) |
| list[].pg | String | 신청한 PG 별칭. 아직 정해지지 않았으면 null |
| list[].channel | String | 접수 입구: hosted 리셀러 등록 폼 · console 부트페이 콘솔 · api 리셀러 API |
| list[].c_at · u_at | String | 접수·마지막 변경 시각(ISO 8601) |
3-4. 단계 표
| stage | 뜻 (status) | stage_owner |
|---|---|---|
intake |
접수됨 — 부트페이가 확인 중 (0 미확인) | ours |
doc_wait |
판매자의 서류·정보 보완을 기다림 (1 구비서류 전달 필요 · 3 미비사항 전달 필요 · 4 구비서류 원본 등기 필요) | merchant |
doc_review |
부트페이가 서류 검토 중 (2 구비서류 확인중) | ours |
pg_submit |
PG사에 접수함 (10 PG사 접수) | pg |
pg_review |
PG사 심사 중 (11 PG사 접수 확인 · 5 PG사 심사 진행중 · 7 진행중) | pg |
done |
신청 완료 (6 신청완료) | merchant |
closed |
끝남 (8 심사반려 · 9 신청 철회) | null |
doc_wait 이면 판매자에게 보완을 안내합니다. 고친 신청서는 같은 onboarding_id 로 mode=edit 세션을 발급 해 다시 내게 합니다.
3-5. 에러 코드
| 코드 | 메시지 | 대처 방법 |
|---|---|---|
API_ (601) |
리셀러만 이용이 가능한 API입니다 | 리셀러 권한 계정 키로 호출합니다 |
RESELLER_ (343) |
본사가 리셀러 사용을 멈췄습니다 (HTTP 403) | 부트페이에 문의합니다 |
PROVIDER_ (300) |
본인 하위 가맹점이 아닙니다 (HTTP 404) | provider_id 가 본인 하위 가맹점인지 확인합니다 |
PG_ (11000) |
그 가맹점의 신청서가 아닙니다 (HTTP 404) | onboarding_id 를 목록에서 다시 확인합니다 |
4등록 API 접수
리셀러가 자기 화면에서 판매자의 사업자 정보·서류·약관 동의를 받아, 리셀러 서버가 하위 가맹점의 PG 가맹 신청서를 직접 냅니다. 부트페이 신청 화면을 띄우지 않는 길입니다. 부트페이 화면으로 받고 싶다면 등록 폼 세션 발급 을 사용합니다.
동작 규칙
- 순서는 약관 조회 → (서류가 있으면) 업로드 허가 받기 → storage 에 서류 올리기 → 신청서 접수입니다. 진행 상태는 등록 상태 조회 로 읽습니다.
- 신청서와 약관 동의 기록은 한 번에 저장됩니다. 동의가 빠지거나 버전이 다르면 신청서도 남지 않습니다.
- 약관 동의는 리셀러 화면에서 받으므로, 약관마다 동의 시각·동의한 사람의 IP·동의자를 함께 보냅니다. 이 값이 그대로 동의 기록이 됩니다.
- 접수 응답은 등록 상태 조회의 한 항목과 같은 모양입니다(
channel은api). - 지금 낼 수 있는 신청은 PG 가맹 신청(
pg_join) 하나입니다. A형(대표가맹점) 리셀러는 아직 쓸 수 없습니다.
이 API 로 신청서를 내면 "접수" 리셀러 웹훅(seller.onboarding.changed)과 리셀러 담당자 알림톡이 나가지 않습니다. 접수 결과는 이 API 의 응답으로 바로 확인합니다. 그 뒤 부트페이가 심사 상태를 바꾸면(서류 요청·반려·승인 등) 그때부터는 다른 입구와 똑같이 웹훅·알림톡이 갑니다.
리셀러 계정 키(Client Key·Secret Key)로 부르므로 리셀러 서버에서만 호출합니다. 사업자등록증·정산 계좌·담당자 연락처를 리셀러가 직접 받는 길이라, 영업대행계약의 개인정보 처리 위탁 조항을 지키고 요청 본문을 로그·에러 리포팅에 남기지 않습니다.
4-1. 전체 흐름
| 단계 | 호출 | 받는 것 |
|---|---|---|
| 1. 약관 조회 | GET /v2/reseller/agreements |
지금 동의받아야 할 약관의 code·version·원문 |
| 2. 업로드 허가 | POST /v2/reseller/seller/:provider_id/files/grant |
10분짜리 grant·upload_url |
| 3. 서류 올리기 | POST {upload_url} (storage, 헤더 X-Upload-Grant) |
file_id 와 파일 정보 |
| 4. 접수 | POST /v2/reseller/seller/:provider_id/onboarding |
신청서 상태(pg_wait_id·status·stage·due_at …) |
| 5. 보완·재제출 | PUT /v2/reseller/seller/:provider_id/onboarding/:onboarding_id |
신청서 상태 |
4-2. 약관 조회
판매자에게 보여 줄 약관의 현재 버전과 원문을 읽습니다. 접수 때 agreements[] 에는 여기서 받은 code·version 을 그대로 넣습니다. 약관이 바뀌면 이전 버전으로 낸 접수는 거부됩니다.
https://api.bootpay.co.kr/v2/reseller/agreementsBasic Auth (리셀러 계정 키)| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
codes |
String | 선택 | 읽을 약관 코드를 쉼표로 (pg_terms,privacy_third_party). 비우면 전부 |
[
{
"code": "pg_terms",
"version": 2,
"title": "PG 서비스 이용약관",
"body": "(약관 원문)",
"effective_at": "2026-10-01T00:00:00+09:00",
"hash": "sha256:example_hash",
"required": true
}
]json| 파라미터 | 타입 | 설명 |
|---|---|---|
| code | String | 약관 코드 — pg_terms PG 서비스 이용약관 · privacy_third_party 개인정보 제3자 제공 동의 |
| version | Number | 지금 동의받아야 하는 버전 |
| title · body | String | 화면에 보여 줄 제목·원문(마크다운) |
| effective_at | String | 이 버전의 시행 시각(ISO 8601) |
| hash | String | 원문 해시. 동의 기록에 함께 남습니다 |
| required | Boolean | 빠지면 접수가 거부되는 필수 약관인지 |
4-3. 서류 업로드 허가
사업자등록증을 올릴 수 있는 10분짜리 허가를 받습니다. 파일은 부트페이 API 서버를 거치지 않고 리셀러 서버가 upload_url 로 직접 올립니다.
https://api.bootpay.co.kr/v2/reseller/seller/:provider_id/files/grantBasic Auth (리셀러 계정 키)| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
provider_id |
String | 필수 | 하위 가맹점의 provider_id (URL 경로) |
kind |
String | 필수 | 서류 종류. 지금은 business_license(사업자등록증)만 |
app_id |
String | 선택 | 서류를 둘 하위 프로젝트. 비우면 그 가맹점에서 가장 먼저 만든 프로젝트 |
{
"grant": "eyJ2IjoxLCJwX2lkIjoi...example.0f1e2d3c...",
"upload_url": "https://s.bootapi.com/api/v1/secure_documents",
"expires_at": "2026-10-27T15:10:00+09:00"
}json받은 grant 를 X-Upload-Grant 헤더에 넣고, upload_url 로 multipart 업로드합니다. 파일은 PDF·JPG·PNG, 10MB 이하입니다.
| 필드 | 값 |
|---|---|
헤더 X-Upload-Grant |
받은 grant 그대로 |
doc_type |
business_license (허가의 kind 와 같아야 합니다) |
file |
서류 파일 |
storage 응답의 file_id 를 접수의 file_ids[] 에 넣습니다. 접수할 때 부트페이가 그 파일이 이 하위 가맹점이 올린 사업자등록증인지 확인하고, 파일 이름·형식·크기는 저장된 값으로 신청서에 남깁니다. 다른 가맹점이 올린 파일 id 는 받지 않습니다. 나머지 값(file_name·ext·mime_type·size)은 business_license 로 함께 보내도 되지만 저장된 값이 우선합니다.
{
"file_id": "6718a0aa8f1b5b00367a0201",
"file_name": "사업자등록증.pdf",
"ext": "pdf",
"mime_type": "application/pdf",
"size": 182044
}json허가 하나로 여러 번 올릴 수 있지만 10분이 지나면 다시 받습니다. 하위 가맹점의 커머스 연동이 아직 없으면 storage 가 STORAGE_SECURE_DOC_GRANT_OWNER_NOT_FOUND(3037)로 거부합니다 — 커머스 연동 재시도 뒤 다시 올립니다.
4-4. 신청서 접수
https://api.bootpay.co.kr/v2/reseller/seller/:provider_id/onboardingBasic Auth (리셀러 계정 키)| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
provider_id |
String | 필수 | 신청할 하위 가맹점의 provider_id (URL 경로) |
pg_alias |
String | 필수 | 신청할 PG 별칭 하나. 부트페이 추천(suggest)은 받지 않습니다 |
agreements |
Array | 필수 | 약관 동의 — 아래 동의 항목 표. 필수 약관(required: true)마다 1개 |
file_ids |
Array | 선택 | 업로드한 사업자등록증 file_id (지금은 1개까지) |
business_license |
Object | 선택 | 업로드 응답의 file_name·ext·mime_type·size |
company_name |
String | 필수 | 상호 |
seller_type |
Number | 필수 | 사업자 구분 (3 개인사업자 · 4 법인사업자) |
regist_no |
String | 필수 | 사업자등록번호 |
corp_reg_no |
String | 필수 | 법인사업자는 법인등록번호, 개인사업자는 대표자 주민등록번호(숫자 13자리) |
owner_name |
String | 필수 | 대표자명 |
business_type · business_item |
String | 필수 | 업종 · 업태 |
biz_email · biz_tel |
String | 필수 | 회사 이메일 · 회사 전화번호 |
name · email · phone |
String | 필수 | 담당자 이름 · 이메일 · 휴대폰 |
site_url |
String | 필수 | 서비스 주소(본인인증 신청 type=4 는 선택) |
real_type |
Number | 선택 | 판매 상품 — 1 실물 · 0 비실물 · -1 둘 다. 비우면 1 |
그 밖의 칸 |
— | 선택 | service_name zip addr1 addr2 biz_fax tel tax_type good_type desc type pay_method max_term month_price good_price policy test_user_id test_user_password bank_code account_number account_owner expect_mid owner_birth_date corp_capital — 부트페이 콘솔 신청서와 같은 칸·같은 검사 |
라이트페이(lightpay)는 희망 MID(expect_mid, 영문·숫자 10자 이내)와 정산 계좌(bank_code·account_number·account_owner)가 필수이고, 법인이면 대표자 생년월일(owner_birth_date, YYYYMMDD)·자본금(corp_capital)도 필수입니다. 글자 칸에는 문자열이나 숫자만 보냅니다.
응답을 못 받아 같은 접수를 다시 보낼 때 신청서가 두 건 생기지 않게 하려면, 접수마다 새 값(예: UUID)을 Idempotency-Key 헤더에 넣습니다. 같은 키로 다시 보내면 새로 만들지 않고 처음 만든 신청서를 같은 응답 모양으로 돌려줍니다(약관 동의 기록도 다시 남기지 않습니다). 키는 64자 이내 영문·숫자·- _ : . 이고, 다른 가맹점 접수에 쓴 키를 다시 쓰면 IDEMPOTENCY_KEY_REUSED입니다. 헤더를 빼면 지금처럼 보낼 때마다 새 신청서입니다.
동의 항목 (agreements[] 한 개)
| 필드 | 필수 | 설명 |
|---|---|---|
| code | Y | 약관 코드 (1. 약관 조회의 code) |
| version | Y | 판매자에게 보여 준 버전 (1. 약관 조회의 version 과 같아야 합니다) |
| agreed_at | Y | 판매자가 동의한 시각, ISO 8601(시간대 포함 권장). 지금보다 5분 넘게 뒤면 거부 |
| ip | Y | 동의한 판매자의 IP(IPv4·IPv6 한 개). 리셀러 서버 IP 가 아닙니다 |
| signer.name | N | 동의자 이름(50자 이하). 비우면 신청서 담당자 이름 |
| signer.phone | N | 동의자 휴대폰(숫자, - 허용). 비우면 신청서 담당자 휴대폰. 부트페이는 암호화해 보관합니다 |
코드 예제
import fetch from 'node-fetch'
import fs from 'node:fs'
const RESELLER_AUTH = 'Basic ' + Buffer
.from(`${process.env.BOOTPAY_RESELLER_CLIENT_KEY}:${process.env.BOOTPAY_RESELLER_SECRET_KEY}`)
.toString('base64')
const API = 'https://api.bootpay.co.kr/v2/reseller'
const providerId = '65a1c0aa8f1b5b00367a0001'
// 1. 지금 동의받을 약관
const terms = await (await fetch(`${API}/agreements`, { headers: { Authorization: RESELLER_AUTH } })).json()
// 2·3. 업로드 허가 → storage 에 직접 올리기
const grant = await (await fetch(`${API}/seller/${providerId}/files/grant`, {
method: 'POST',
headers: { Authorization: RESELLER_AUTH, 'Content-Type': 'application/json' },
body: JSON.stringify({ kind: 'business_license' })
})).json()
const form = new FormData()
form.append('doc_type', 'business_license')
form.append('file', new Blob([fs.readFileSync('./license.pdf')], { type: 'application/pdf' }), 'license.pdf')
const file = await (await fetch(grant.upload_url, {
method: 'POST',
headers: { 'X-Upload-Grant': grant.grant },
body: form
})).json()
// 4. 접수 — 판매자가 리셀러 화면에서 동의한 시각·IP 를 그대로 보낸다
const consent = { agreed_at: '2026-10-27T10:15:00+09:00', ip: '198.51.100.7', signer: { name: '김대표', phone: '01011112222' } }
const submitKey = crypto.randomUUID() // 이 접수를 다시 보낼 때도 같은 값을 쓴다
const res = await fetch(`${API}/seller/${providerId}/onboarding`, {
method: 'POST',
headers: { Authorization: RESELLER_AUTH, 'Content-Type': 'application/json', 'Idempotency-Key': submitKey },
body: JSON.stringify({
pg_alias: 'nicepay',
company_name: '예시상점', seller_type: 4, regist_no: '1234567890', corp_reg_no: '1101111234567',
owner_name: '김대표', business_type: '서비스', business_item: '소프트웨어',
biz_email: 'biz@example.com', biz_tel: '0212345678', site_url: 'https://shop.example.com',
name: '이담당', email: 'manager@example.com', phone: '01033334444',
file_ids: [file.file_id],
business_license: { file_name: file.file_name, ext: file.ext, mime_type: file.mime_type, size: file.size },
agreements: terms.map((t) => ({ code: t.code, version: t.version, ...consent }))
})
})
const onboarding = await res.json() // { pg_wait_id, status, stage, due_at, ... }javascriptimport base64, os, requests
auth = 'Basic ' + base64.b64encode(
f"{os.environ['BOOTPAY_RESELLER_CLIENT_KEY']}:{os.environ['BOOTPAY_RESELLER_SECRET_KEY']}".encode()
).decode()
api = 'https://api.bootpay.co.kr/v2/reseller'
provider_id = '65a1c0aa8f1b5b00367a0001'
terms = requests.get(f'{api}/agreements', headers={'Authorization': auth}).json()
grant = requests.post(f'{api}/seller/{provider_id}/files/grant', headers={'Authorization': auth},
json={'kind': 'business_license'}).json()
with open('license.pdf', 'rb') as f:
file = requests.post(grant['upload_url'], headers={'X-Upload-Grant': grant['grant']},
data={'doc_type': 'business_license'},
files={'file': ('license.pdf', f, 'application/pdf')}).json()
consent = {'agreed_at': '2026-10-27T10:15:00+09:00', 'ip': '198.51.100.7'}
res = requests.post(f'{api}/seller/{provider_id}/onboarding', headers={'Authorization': auth}, json={
'pg_alias': 'nicepay',
'company_name': '예시상점', 'seller_type': 4, 'regist_no': '1234567890', 'corp_reg_no': '1101111234567',
'owner_name': '김대표', 'business_type': '서비스', 'business_item': '소프트웨어',
'biz_email': 'biz@example.com', 'biz_tel': '0212345678', 'site_url': 'https://shop.example.com',
'name': '이담당', 'email': 'manager@example.com', 'phone': '01033334444',
'file_ids': [file['file_id']],
'agreements': [{'code': t['code'], 'version': t['version'], **consent} for t in terms]
})
onboarding = res.json()pythonRESELLER_AUTH=$(printf '%s' "$BOOTPAY_RESELLER_CLIENT_KEY:$BOOTPAY_RESELLER_SECRET_KEY" | base64)
PROVIDER_ID='65a1c0aa8f1b5b00367a0001'
# 2. 업로드 허가
curl -X POST "https://api.bootpay.co.kr/v2/reseller/seller/$PROVIDER_ID/files/grant" \
-H "Authorization: Basic $RESELLER_AUTH" -H 'Content-Type: application/json' \
-d '{"kind":"business_license"}'
# 3. storage 에 올리기 — 위 응답의 grant·upload_url
curl -X POST "$UPLOAD_URL" -H "X-Upload-Grant: $UPLOAD_GRANT" \
-F doc_type=business_license -F file=@license.pdf
# 4. 접수
curl -X POST "https://api.bootpay.co.kr/v2/reseller/seller/$PROVIDER_ID/onboarding" \
-H "Authorization: Basic $RESELLER_AUTH" -H 'Content-Type: application/json' \
-d @onboarding.jsonbash응답
접수·보완 응답은 등록 상태 조회 목록의 한 항목과 같습니다.
{
"provider_id": "65a1c0aa8f1b5b00367a0001",
"onboarding_id": "6718a0aa8f1b5b00367a0101",
"kind": "pg_join",
"status": 0,
"status_name": "미확인",
"stage": "intake",
"stage_owner": "ours",
"due_at": "2026-10-28T10:20:00+09:00",
"reject_reason": null,
"pg_wait_id": "6718a0aa8f1b5b00367a0101",
"pg": "nicepay",
"overdue": false,
"required_documents": [],
"channel": "api",
"c_at": "2026-10-27T10:20:00+09:00",
"u_at": "2026-10-27T10:20:00+09:00"
}json| 파라미터 | 타입 | 설명 |
|---|---|---|
| pg_wait_id · onboarding_id | String | 신청서 식별자. 보완·재제출과 상태 조회에 사용합니다 |
| status · status_name | Number · String | 신청 상태 번호·이름. 접수 직후는 0 |
| stage · stage_owner | String | 단계와 지금 할 차례 — 등록 상태 조회 의 단계 표 |
| due_at | String | 이 단계의 기한(ISO 8601) |
| channel | String | 접수 입구. 이 API 로 낸 신청서는 api |
| 그 밖의 키 | — | 등록 상태 조회와 같습니다 |
4-5. 보완·재제출
서류 보완 요청을 받았거나 내용을 고칠 때 같은 신청서를 다시 냅니다. 판매자가 부트페이 콘솔이나 등록 폼으로 낸 그 가맹점의 신청서도 고칠 수 있습니다.
https://api.bootpay.co.kr/v2/reseller/seller/:provider_id/onboarding/:onboarding_idBasic Auth (리셀러 계정 키)| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
provider_id |
String | 필수 | 하위 가맹점의 provider_id (URL 경로) |
onboarding_id |
String | 필수 | 고칠 신청서 (URL 경로, 접수 응답의 pg_wait_id) |
agreements |
Array | 필수 | 약관 동의 — 다시 내는 것이라 동의도 다시 받습니다(접수와 같은 모양) |
pg_alias |
String | 선택 | 바꿀 PG. 비우면 신청서의 지금 PG |
file_ids |
Array | 선택 | 바꿀 사업자등록증 file_id. 비우거나 빈 배열이면 그대로 |
remove_business_license |
Boolean | 선택 | true 면 첨부한 사업자등록증을 뺍니다 |
신청서 칸 |
— | 선택 | 접수와 같은 칸. 보낸 칸만 바뀝니다 — real_type 도 안 보내면 그대로 |
신청이 끝난(status 6) 신청서는 고칠 수 없습니다. 새로 신청하려면 3. 신청서 접수로 다시 냅니다. 응답은 접수와 같습니다.
4-6. 에러 코드
| 코드 | 메시지 | 대처 방법 |
|---|---|---|
API_ (601) |
리셀러만 이용이 가능한 API입니다 | 리셀러 권한 계정 키로 호출합니다 |
API_ (1222) |
허용되지 않은 API 권한의 키입니다 (HTTP 401) | 키에 이 API 의 리셀러 스코프 를 주거나 전체 권한(supervisor) 키로 호출합니다 |
RESELLER_ (343) |
본사가 리셀러 사용을 멈췄습니다 (HTTP 403) | 부트페이에 문의합니다 |
ONBOARDING_ (8168) |
이 리셀러 유형으로는 할 수 없는 신청입니다 (HTTP 403). PG 가맹 신청은 B형(영업대행) 리셀러만 할 수 있습니다 — 메시지에 이 계정의 유형이 나옵니다 | 지금 유형은 리셀러 내 정보 의 reseller_contract.type 으로 확인합니다. A형(대표가맹점)용 신청은 아직 열리지 않았습니다. 유형을 바꾸려면 부트페이에 문의합니다 |
PROVIDER_ (300) |
본인 하위 가맹점이 아닙니다 (HTTP 404) | provider_id 가 본인 하위 가맹점인지 확인합니다 |
PG_ (11000) |
그 가맹점의 신청서가 아닙니다 (HTTP 404) | onboarding_id 를 등록 상태 조회로 다시 확인합니다 |
PW_ (2232) |
신청이 끝난 건은 고칠 수 없습니다 | 새 신청은 접수 API 로 냅니다 |
PG_ (1000) |
없거나 쓰지 않는 PG, 또는 suggest입니다 (HTTP 404) |
PG 코드 에서 별칭을 확인합니다 |
APP_ (200) |
그 가맹점의 사용 중인 프로젝트가 아닙니다 (HTTP 404) | 업로드 허가의 app_id 를 확인하거나 비웁니다 |
AGREEMENT_ (8120) |
동의받을 약관이 아직 준비되지 않았습니다 | 부트페이에 문의합니다 |
AGREEMENT_ (8121) |
약관 버전이 지금 버전과 다릅니다 (payload.codes) |
약관을 다시 조회해 바뀐 원문으로 동의를 다시 받습니다 |
AGREEMENT_ (8122) |
필수 약관 동의가 빠졌습니다 (payload.codes) |
빠진 약관 동의를 받아 넣습니다 |
AGREEMENT_ (8170) |
동의 시각·IP·동의자 값이 올바르지 않습니다 (payload.codes) |
agreed_at(ISO 8601, 미래 아님)·ip(한 개 주소)·signer 를 확인합니다 |
PW_ (8171) |
file_ids 가 2개 이상이거나 파일 id 모양이 아닙니다. 또는 이 하위 가맹점이 올린 사업자등록증이 아닙니다(없거나 지운 파일 포함) |
이 가맹점의 업로드 허가로 올린 응답의 file_id 하나만 보냅니다 |
COMMERCE_ (8182) |
서류 주인을 확인하지 못했습니다(연동 서버 오류). 신청서는 저장되지 않습니다 | 잠시 뒤 다시 보냅니다 |
IDEMPOTENCY_ (8210) |
이 Idempotency-Key 는 다른 가맹점의 접수에 이미 썼습니다 |
접수마다 새 키를 만듭니다 |
PARAMS_ (-33) |
약관 코드·버전 모양이 틀리거나 글자 칸에 객체·배열이 들어왔습니다. 업로드 허가의 kind 가 틀렸습니다 |
메시지의 칸을 고칩니다 |
PW_* (2201~2230) |
신청서 필수 칸이 비었거나 형식이 틀렸습니다 | 메시지의 칸을 채웁니다(부트페이 콘솔 신청서와 같은 검사) |
STORAGE_ (3036) |
storage 업로드 — 허가가 틀렸습니다 (HTTP 401) | 받은 grant 를 고치지 않고 그대로 넣습니다 |
STORAGE_ (3038) |
storage 업로드 — 허가 10분이 지났습니다 (HTTP 401) | 업로드 허가를 다시 받습니다 |
STORAGE_ (3037) |
storage 업로드 — 하위 가맹점의 커머스 연동이 없습니다 | 커머스 연동 재시도 뒤 다시 올립니다 |
5신청서 서류 열기
하위 가맹점의 PG 가맹 신청서에 올라간 서류 목록과, 서류마다 3분 동안만 열 수 있는 열람 주소를 돌려줍니다. 신청서 id 는 등록 상태 조회 의
onboarding_id입니다.
동작 규칙
- 지금 신청서에 붙는 서류는 사업자등록증 하나입니다. 올리지 않았으면
list가 빈 배열입니다. url은 3분 뒤 막힙니다. 저장해 두지 말고 열 때마다 이 API 를 다시 부릅니다.- 서류는 개인정보입니다.
url을 로그·에러 리포팅·브라우저 주소 공유에 남기지 않습니다. - 부를 때마다 열람 기록이 신청서에 남습니다(리셀러가 열었다는 기록).
- 판매자가 콘솔에서 직접 낸 신청서도 본인 하위 가맹점 것이면 열 수 있습니다.
- 다른 가맹점이나 다른 리셀러 하위의 신청서는 없는 것과 같은
PG_WAIT_NOT_FOUND입니다. - 열기 전에 서류가 그 하위 가맹점이 올린 파일인지 확인합니다. 아니거나 지워진 파일이면 그 서류는
list에 나오지 않습니다.
API 정보
https://api.bootpay.co.kr/v2/reseller/seller/:provider_id/onboarding/:onboarding_id/documentsBasic Auth (리셀러 계정 키)5-1. 요청 파라미터
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
provider_id |
String | 필수 | 하위 가맹점 provider_id (URL 경로) |
onboarding_id |
String | 필수 | 신청서 id (URL 경로) — 등록 상태 조회 의 onboarding_id |
5-2. 코드 예제
import fetch from 'node-fetch'
const RESELLER_AUTH = 'Basic ' + Buffer
.from(`${process.env.BOOTPAY_RESELLER_CLIENT_KEY}:${process.env.BOOTPAY_RESELLER_SECRET_KEY}`)
.toString('base64')
const providerId = '65a1c0aa8f1b5b00367a0001'
const onboardingId = '65a1c0aa8f1b5b00367a0050'
const res = await fetch(`https://api.bootpay.co.kr/v2/reseller/seller/${providerId}/onboarding/${onboardingId}/documents`, {
headers: { 'Authorization': RESELLER_AUTH }
})
const { list } = await res.json()
// list[0].url 을 바로 열어 보여 준다 — 저장하지 않는다javascriptimport base64, os, requests
auth = 'Basic ' + base64.b64encode(
f"{os.environ['BOOTPAY_RESELLER_CLIENT_KEY']}:{os.environ['BOOTPAY_RESELLER_SECRET_KEY']}".encode()
).decode()
res = requests.get(
'https://api.bootpay.co.kr/v2/reseller/seller/65a1c0aa8f1b5b00367a0001/onboarding/65a1c0aa8f1b5b00367a0050/documents',
headers={'Authorization': auth}
)
print([doc['file_name'] for doc in res.json()['list']])pythonRESELLER_AUTH=$(printf '%s' "$BOOTPAY_RESELLER_CLIENT_KEY:$BOOTPAY_RESELLER_SECRET_KEY" | base64)
curl "https://api.bootpay.co.kr/v2/reseller/seller/65a1c0aa8f1b5b00367a0001/onboarding/65a1c0aa8f1b5b00367a0050/documents" \
-H "Authorization: Basic $RESELLER_AUTH"bash5-3. 응답
{
"onboarding_id": "65a1c0aa8f1b5b00367a0050",
"list": [
{
"kind": "business_license",
"file_name": "사업자등록증.pdf",
"mime_type": "application/pdf",
"size": 482113,
"uploaded_at": "2026-10-20T11:02:00+09:00",
"url": "https://s.bootapi.com/api/v1/secure_documents/example_file_id/download?expires=1793090063&p=65a1c0aa8f1b5b00367a0001&sig=example_signature",
"expires_at": "2026-10-27T10:03:00+09:00",
"expires_in": 180
}
]
}json| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
onboarding_id |
String | 필수 | 신청서 id |
list |
Array | 필수 | 서류 목록. 서류가 없으면 [] |
kind |
String | 필수 | 서류 종류. 지금은 business_license(사업자등록증) |
file_name |
String | 선택 | 올린 파일 이름 |
mime_type |
String | 선택 | 파일 형식 |
size |
Number | 선택 | 파일 크기(바이트) |
uploaded_at |
String | 선택 | 올린 시각 |
url |
String | 필수 | 열람 주소. 3분 뒤 막힙니다 |
expires_at |
String | 필수 | url 이 막히는 시각 |
expires_in |
Number | 필수 | url 유효 시간(초). 180 |
5-4. 에러 코드
| 코드 | 메시지 | 대처 방법 |
|---|---|---|
API_ (601) |
리셀러만 이용이 가능한 API입니다 | 리셀러 권한 계정 키로 호출합니다 |
API_ |
허용되지 않은 API 권한의 키입니다 (HTTP 401) | 키에 이 API 의 리셀러 스코프 를 주거나 전체 권한(supervisor) 키로 호출합니다 |
PROVIDER_ (300) |
본인 하위 가맹점이 아닙니다 (HTTP 404) | provider_id 가 본인 리셀러 하위인지 확인합니다 |
PG_ (11000) |
그 가맹점의 신청서가 아닙니다 (HTTP 404) | 등록 상태 조회 에서 onboarding_id 를 다시 확인합니다 |
COMMERCE_ (8182) |
서류 주인을 확인하지 못했습니다(연동 서버 오류). 열람 주소를 주지 않습니다 | 잠시 뒤 다시 부릅니다 |



