문서

SublimeKeys를 앱에 연동하는 데 필요한 모든 것. 읽는 데 계정은 필요 없습니다.

$SUBLIMEKEYS_API_KEY

아래의 모든 요청은 이것을 실제 키의 자리 표시자로 사용합니다. 로그인 (무료, 카드 불필요)하여 본인의 키를 받고 이 예제들이 본인의 제품으로 채워진 모습을 확인하세요.

빠른 시작 — 3번의 호출

1. 제품 등록 (제품에서 한 번만 수행)

curl -X POST https://api.sublimearts.io/v1/products \
  -H "Authorization: Bearer $SUBLIMEKEYS_API_KEY" \
  -d '{"slug":"my-app","key_prefix":"MYAPP"}'

2. 판매마다 키 발급

curl -X POST https://api.sublimearts.io/v1/licenses \
  -H "Authorization: Bearer $SUBLIMEKEYS_API_KEY" \
  -d '{"product":"my-app","email":"buyer@mail.com","max_activations":3}'

→ {"key":"MYAPP-K3F7Q-9WZ2M-P8RT4-XN5CB", ...}
product필수 — 1단계의 제품 slug
email선택 — 대시보드의 라이선스 목록에 표시됩니다
max_activations선택, 기본값 1 — 이 하나의 키가 동시에 몇 개의 서로 다른 machine_id에서 활성화될 수 있는지. 본인의 가격 정책에 맞게 설정하세요(예: "개인 사용, 3대" 플랜이라면 3으로 설정). 최종 고객의 플랜이 아니라 본인의 SublimeKeys 플랜에 따라 상한이 정해집니다 — Free는 5, Pro는 25, Business는 100(요금제 참고).
expires_at선택 — ISO 날짜; 영구 키의 경우 생략
notes선택 — 자유 텍스트, 본인 참고용

또는 이 호출 자체를 완전히 건너뛸 수도 있습니다 — 아래 AUTOMATE 참고.

3. 앱 내에서 검증

Python — 공식 SDK(권장)
# pip install sublimekeys
from sublimekeys import SublimeKeysClient

client = SublimeKeysClient(product_id="my-app")
result = client.activate(license_key=key)   # first run
result = client.verify(license_key=key)     # every launch after — offline-first,
                                             # verifies a signed lease locally,
                                             # zero network calls for up to 7 days
if result.valid:
    unlock_full_version()
Node.js / Electron — 공식 SDK(권장)
// npm install sublimekeys
import { SublimeKeysClient } from "sublimekeys";

const client = new SublimeKeysClient("my-app");
const activated = await client.activate(key);     // first run
const result = await client.verify(key);          // every launch after — offline-first,
                                                   // verifies a signed lease locally,
                                                   // zero network calls for up to 7 days
if (result.valid) unlockFullVersion();

Python이나 Node.js를 사용하지 않거나, 직접 raw API를 호출하고 싶으신가요? PyPI의 Python SDK · npm의 Node.js SDK 또는 아래 엔드포인트를 직접 사용하세요.

Python — raw API
import requests

r = requests.post("https://api.sublimearts.io/activate", json={
    "license_key": key,
    "machine_id": machine_id,
    "product_id": "my-app",
})
if r.json()["valid"]:
    unlock_full_version()
Node.js — raw API
const r = await fetch("https://api.sublimearts.io/activate", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ license_key, machine_id, product_id: "my-app" }),
});
const { valid } = await r.json();
if (valid) unlockFullVersion();
cURL
curl -X POST https://api.sublimearts.io/activate \
  -d '{"license_key":"...","machine_id":"...","product_id":"my-app"}'
▸ 처음이신가요? 이 호출들이 실제로 앱 어디에 들어가는지

위 예제는 /activate 만 단독으로 보여줍니다 — 실제 앱에서는 서로 다른 시점에 서로 다른 엔드포인트를 호출합니다:

앱 시작
├─ 아직 저장된 라이선스 키가 없음(최초 실행)
│    → "라이선스 키 입력" 화면 표시
│    → 사용자가 제출 → /activate를 한 번 호출 → 결과를 로컬에 저장
│
├─ 이미 라이선스 키가 저장되어 있음(이후 매 실행)
│    → 대신 /verify를 호출(다시 /activate가 아님) — 이 기기에서
│      여전히 유효한지 확인
│
├─ API에 접근할 수 없음(사용자가 오프라인 상태)
│    → /activate와 /verify 모두 서명된 "리스"를 반환(7일간 유효)
│    → 마지막 결과를 무조건 신뢰하는 대신 서명을 로컬에서
│      검증 — Python과 Node.js SDK는 이를 자동으로 수행하며, 다른
│      언어에서는 Ed25519 서명을 직접 검증할 수 있음(공개 키는
│      GET /public-key에서 확인)
│
└─ 사용자가 제거/로그아웃(선택)
     → /deactivate를 호출하여 다른 기기를 위해 자리를 비움

Python이나 Node.js/Electron에서는 공식 SDK(PyPI·npm)가 오프라인 리스에 대한 실제 암호학적 검증을 포함해 이미 이를 자동으로 처리합니다. 아래의 raw 버전은 이 단계를 건너뜁니다(오프라인 상태에서는 단순히 마지막으로 저장된 결과를 신뢰합니다) — 더 단순하지만 암호학적으로 검증되지는 않습니다. Python/Node.js를 사용하지 않거나 완전한 제어가 필요할 때 사용하세요.

Python — 전체 앱 시작 흐름(raw API, SDK 미사용)
import requests, json, os

STATE_FILE = "license_state.json"

def check_license(machine_id):
    saved = json.load(open(STATE_FILE)) if os.path.exists(STATE_FILE) else None

    try:
        if saved is None:
            # first run — ask the user for their key once
            key = prompt_for_license_key()
            r = requests.post("https://api.sublimearts.io/activate", json={
                "license_key": key, "machine_id": machine_id, "product_id": "my-app",
            }, timeout=5)
        else:
            # every later launch — re-check the saved key, don't ask again
            r = requests.post("https://api.sublimearts.io/verify", json={
                "license_key": saved["license_key"], "machine_id": machine_id, "product_id": "my-app",
            }, timeout=5)

        result = r.json()
        json.dump(result, open(STATE_FILE, "w"))
        return result["valid"]

    except requests.RequestException:
        # offline — trust the last known result instead of locking the user out
        return saved["valid"] if saved else False

이미 개념이 익숙하신가요? 이 부분은 건너뛰세요 — 위의 호출만으로 충분합니다.

공식 SDK

🐍 Python — sublimekeys

전체 API를 감싸고 오프라인 검증을 대신 처리합니다 — 로컬에서 검증되는 서명된 리스로 activate/verify/deactivate를 수행하므로 대부분의 실행에서 네트워크 호출이 전혀 발생하지 않으며, 오프라인 기간을 견디는 체험판 확인 기능도 있습니다. 터미널에서 통합을 테스트할 수 있는 sublimekeys CLI도 함께 설치됩니다.

pip install sublimekeys
PyPI에서 보기 →

⚡ Node.js / Electron — sublimekeys

동일하게 오프라인을 지원하는 설계이며 런타임 의존성이 전혀 없습니다 — Ed25519 검증은 Node 내장 crypto를 사용하므로 Electron의 asar 패키징이 걸림돌이 될 요소가 없습니다.

npm install sublimekeys
npm에서 보기 →

더 많은 언어가 로드맵에 있습니다. 그동안 아래의 모든 엔드포인트는 일반적인 REST 호출입니다 — HTTPS 요청을 보낼 수 있는 어떤 것에서든 호출할 수 있습니다.

엔드포인트 레퍼런스 — 퍼블릭(앱에서 호출)

POST /activate

body: { "license_key", "machine_id", "product_id" }

▸ 세부 정보

최초 실행 — 라이선스 키를 machine_id에 연결합니다. 동일한 기기에서 다시 호출해도 안전합니다 — 추가 활성화 슬롯을 소비하지 않습니다. 불가능한 경우(예: 다른 기기에서 활성화 한도에 도달한 경우) 메시지와 함께 valid:false를 반환합니다. 성공 시 응답의 lease 필드에 서명된 리스(7일 오프라인 신뢰 기간)도 함께 반환됩니다 — Python SDK는 이후 실행 시 매번 API를 호출하는 대신 이를 로컬에서 검증합니다.

POST /verify

body: { "license_key", "machine_id", "product_id" }

▸ 세부 정보

이후의 모든 실행마다 — 키가 이 기기에서 여전히 유효한지 확인합니다. 활성화 횟수에 영향을 주지 않으므로 원하는 만큼 자주 호출해도 됩니다. 성공적으로 호출될 때마다 서명된 리스(/activate 참고)도 갱신됩니다.

POST /deactivate

body: { "license_key", "machine_id", "product_id" }

▸ 세부 정보

사용자가 로그아웃/제거 — 다른 기기를 위해 자리를 비웁니다.

POST /trial/start

body: { "machine_id", "product_id" }

▸ 세부 정보

기기에 대해 7일 체험판을 가져오거나 생성합니다. 멱등성을 가집니다 — 재설치해도 절대 시간이 초기화되지 않습니다.

POST /trial/status

body: { "machine_id", "product_id" }

▸ 세부 정보

읽기 전용 체험판 확인입니다 — 체험판을 시작하지는 않습니다.

여기에는 Authorization 헤더가 없습니다 — license_key 자체가 자격 증명입니다. 계정 범위의 전체 API(생성/목록/취소)는 /v1/* 아래에 있으며, 대시보드를 참고하세요.

⚡ AUTOMATE — 백엔드 없는 키 전달

위의 2단계를 완전히 건너뛸 수 있습니다. 결제 제공업체의 웹훅을 제품의 URL로 연결하면 판매가 발생할 때마다 자동으로 키가 발급되어 구매자에게 이메일로 전송됩니다.

  1. Stripe 대시보드 → Developers → Webhooks → Add endpoint
  2. 제품의 URL을 붙여넣으세요 (로그인 후 제품을 생성하면 URL을 받을 수 있습니다)
  3. 이벤트 선택: checkout.session.completed
  4. 저장하세요. 이게 전부입니다 — 서버도, 코드도 필요 없습니다.
  5. 선택 사항이지만 권장: Stripe가 바로 다음에 Signing secret을 보여줍니다 — 이를 제품 페이지의 "Signature verification"에 붙여넣으면, 이 URL을 알고 있는 아무나가 아니라 실제로 Stripe에서 온 요청만 받아들이게 됩니다.

🔔 판매가 있을 때마다 본인도 이메일을 받고 싶으신가요? 제품 페이지에서 이 블록 옆의 "Notify me"를 켜세요.

GUMROAD, KEYGEN 또는 자체 서버에서 마이그레이션

기존 고객을 email 열 하나가 있는 CSV로 내보낸 다음, 제품마다 이를 한 번 실행하세요. 각 행마다 본인의 브랜드 접두사가 붙은 새 SublimeKeys 라이선스를 일괄 발급합니다.

migrate.py
import csv, os, requests

API_KEY = os.environ["SUBLIMEKEYS_API_KEY"]
PRODUCT = "my-app"

with open("customers.csv") as f:
    for row in csv.DictReader(f):
        r = requests.post("https://api.sublimearts.io/v1/licenses",
            headers={"Authorization": f"Bearer {API_KEY}"},
            json={"product": PRODUCT, "email": row["email"]})
        print(row["email"], "->", r.json()["key"])

이 방식은 완전히 새로운 키를 발급합니다 — 기존 키 문자열은 유지되지 않습니다(설계상 커스텀 키 값은 허용하지 않습니다). 고객에게 새 키를 이메일로 보내거나, 기존 시스템을 폐지하기 전 전환 기간 동안 앱에서 두 검증을 나란히 실행하세요.

플랜 한도

FREE

제품 1개 · 키 100개 · 키당 기기 5대

PRO

제품 5개 · 키 1,000개 · 키당 기기 25대

BUSINESS

제품·키 무제한 · 키당 기기 100대

한도에 도달해도 기존 키는 절대 손상되지 않습니다 — 영원히 계속 검증됩니다. 업그레이드하기 전까지 새 키를 발급할 수 없을 뿐입니다. 다운그레이드나 해지 역시 무엇도 삭제하지 않습니다.