API 키와 HMAC 인증
개인 REST API와 개인 WS 연결은 동일한 HMAC-SHA256 인증을 사용합니다. 키는 BTX 웹의 마이페이지 API 메뉴에서 생성·폐기합니다. GET /v1/account로 현재 키의 권한을 확인할 수 있습니다.
허용 IP와 인증 캐시
키를 만들 때 개별 IPv4·IPv6 주소를 최대 5개 등록할 수 있습니다. 한 줄에 하나씩 입력합니다.
비워 두면 모든 IP에서 사용할 수 있고, 하나라도 등록하면 목록에 있는 IP에서만 REST 요청과 개인 WS 연결을 사용할 수 있습니다.
CIDR 대역·도메인·포트는 지원하지 않으며, IPv4-mapped IPv6는 IPv4로 정규화합니다.
거절 시 HTTP 403 IP_NOT_ALLOWED를 반환하며 위반 점수는 추가하지 않습니다.
목록을 변경하려면 기존 키를 폐기하고 새 키를 발급합니다.
키 상태와 권한·IP 정책은 게이트웨이의 메모리 캐시를 공유합니다. 캐시를 사용하는 요청마다 보관 시간을 5분 연장하며, 사용하지 않은 채 5분이 지나면 제거합니다. 보관 시간과 별도로 30초마다 캐시에 남은 키만 일괄 조회해 권한·IP 제한·비활성화·삭제를 반영합니다. 폴링 자체는 보관 시간을 연장하지 않습니다. 키 변경 시 같은 프로세스의 해당 키를 무효화하고, 다른 프로세스에는 NATS 변경 신호를 전달해 바로 갱신하도록 합니다. 신호 누락은 주기적인 폴링이 보완합니다. 갱신이 실패하면 해당 캐시를 비웁니다. 요청이 계속 들어와도 마지막 DB 검증 이후 60초가 지난 정보는 그대로 사용하지 않으며, 원본 DB 확인도 실패하면 인증을 거절합니다.
인증 헤더
| 헤더 | 필수 | 값 |
|---|---|---|
BTX-API-KEY |
예 | Public Key |
BTX-TIMESTAMP |
예 | 현재 Unix 밀리초의 양의 정수 |
BTX-SIGNATURE |
예 | 서명의 소문자 16진수 문자열 |
BTX-RECV-WINDOW |
권장 | 허용 시간 차이(ms), 기본 5000, 최대 60000 |
BTX-NONCE |
아니요 | 폐기 예정 레거시 메타데이터. 새 클라이언트에서는 생략 |
서버 시간과 요청 timestamp의 절대 차이가 수신 시간 범위 안이어야 합니다. 호스트 시간을 동기화하고 GET /v1/time으로 시간 차이를 확인하세요. 헤더를 생략해도 서명에는 기본 receive window인 5000이 들어갑니다.
서명할 문자열
다음 7개 항목을 줄바꿈 \n으로 연결한 UTF-8 바이트를 서명합니다.
UPPERCASE_HTTP_METHOD
ESCAPED_PATH
SORTED_URL_ENCODED_QUERY
LOWERCASE_SHA256_HEX_OF_EXACT_BODY_BYTES
BTX-TIMESTAMP
BTX-RECV-WINDOW
BTX-NONCE_OR_EMPTY
signature = lowercase_hex(HMAC_SHA256(secretKey, canonical))
- 메서드는
GET,POST,DELETE처럼 대문자로 씁니다. - 경로는
/v1/account처럼/v1을 포함합니다. 별도의 프록시 경로 접두사가 있으면 포함하고, percent-escape된 경로를 그대로 서명합니다. 호스트는 서명에 포함하지 않습니다. - 쿼리는 Go
url.Values.Encode규칙입니다. 키를 정렬하고 공백은+, 값의/는%2F로 인코딩합니다. 반복 값의 순서는 유지됩니다. 실제 API 쿼리는 중복 키와 빈 값을 거절합니다. - 본문은 실제 전송하는 바이트 그대로 SHA-256 해시합니다. JSON을 다시 직렬화하면 바이트가 달라질 수 있습니다.
- 빈 본문 해시는
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855입니다. - 새 클라이언트는 nonce를 생략하고 7번째 항목을 빈 문자열로 둡니다. 따라서 최종 바이트는 receive window 뒤의
\n으로 끝납니다. 그 뒤에 줄바꿈을 더 붙이지 마세요.
검증용 고정 벡터
아래 Secret은 테스트용 공개 문자열입니다. timestamp도 고정된 과거 값이므로 운영 API를 호출하는 용도가 아닙니다.
secretKey: secret
method: POST
path: /v1/orders
query: (empty)
timestamp: 1783420800000
recvWindow: 5000
nonce: (empty)
body: {"symbol":"BTC-USDT","walletId":21,"side":"buy","type":"limit","timeInForce":"GTC","quantity":"0.100000000","price":"65000.000000000"}
bodySha256: f2f86635e268cfe58437fc6dc62a7e109e2b62337b41fbfd8caa66c92311c284
signature: ec5dcede7caa7214fca3f4f632cea4219a9b5c5e053ea088169bb0bed8e846e0
Node.js·Python 예제는 이 벡터를 검증하는 --self-test 옵션을 제공합니다.
개인 WebSocket 인증
다음 GET 업그레이드 요청에 같은 규칙으로 서명합니다.
GET wss://api-ws.btxpro.co.kr/v1/user/stream
서명 경로: /v1/user/stream
쿼리: 빈 문자열
본문: 빈 바이트
인증 헤더를 HTTP 업그레이드 요청에 보내야 합니다. 연결 뒤 인증 메시지를 보내는 방식이 아닙니다. 브라우저 표준 WebSocket 생성자는 임의 인증 헤더를 설정하지 못하므로 Go SDK 또는 서버용 WS 라이브러리를 사용합니다.
연결 시 read, 주문 명령 시 trade를 확인합니다. 서버는 주문 명령마다 키를 재검증하고, 유휴 연결도 60초마다 read 권한을 재검증합니다.
키 교체와 요청 중복
새 키 발급 → 서명된 조회와 개인 스트림 확인 → 클라이언트 교체 → 기존 키 폐기 순서로 교체합니다.
timestamp는 요청이 최근 생성됐는지 확인합니다. 요청을 유일하게 만들지는 않습니다. 같은 유효 요청을 다시 보내면 주문이 중복 실행될 수 있습니다. nonce나 WS 명령 id도 완료 후 재시도를 막는 멱등성 키가 아닙니다.