
Nts Business Registration
Validate and submit Korean National Tax Service (NTS) business registration data through the K-SKILL proxy without hand-coding field limits and batch rules.
Overview
NTS Business Registration is an agent skill for the Validate phase that validates Korean business registration fields and submits them through the K-SKILL NTS proxy API.
Install
npx skills add https://github.com/nomadamas/k-skill --skill nts-business-registrationWhat is this skill?
- Normalizes and validates 사업자등록번호 (b_no) plus structured business fields with documented length limits (p_nm/b_nm up to 2
- Configurable KSKILL proxy base URL via KSKILL_PROXY_BASE_URL with a documented default endpoint.
- Batch-oriented API client with BATCH_LIMIT of 100 records per request.
- Field-level VALIDATE_TEXT_FIELD_LIMITS for nine registration attributes before proxy submission.
- Python CLI-style helpers with explicit ApiError status codes for solo debugging.
- Nine VALIDATE_TEXT_FIELD_LIMITS text fields including b_nm max 200 and b_adr max 500
Adoption & trust: 607 installs on skills.sh; 5.4k GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are incorporating or invoicing in Korea but lack a repeatable way to normalize 사업자등록번호 and pass NTS field validation before hitting the tax API.
Who is it for?
Solo builders or micro-SaaS founders registering or verifying a Korean business entity alongside their agent workflow.
Skip if: Teams outside Korea with no NTS obligation, or anyone needing certified tax/legal filing without human review of the proxy responses.
When should I use this skill?
You need to verify or submit Korean NTS business registration data via the K-SKILL proxy with enforced field lengths and normalized b_no.
What do I get? / Deliverables
You get proxy-ready, length-checked registration payloads and actionable ApiError messages instead of rejected NTS submissions.
- Normalized business number and validated field set ready for proxy POST
- Structured ApiError feedback when proxy or validation fails
Recommended Skills
Journey fit
Business registration and number verification happen while scoping a Korean entity before full product build and operations. Scope subphase covers legal entity setup, tax identifiers, and sector metadata that must be correct before you commit to stack and payments.
How it compares
Use instead of ad-hoc urllib scripts that skip documented field limits and batch caps for K-SKILL.
Common Questions / FAQ
Who is nts-business-registration for?
Indie and solo builders shipping paid products in Korea who want their agent to handle NTS registration data validation and proxy calls consistently.
When should I use nts-business-registration?
During Validate when scoping your legal entity, tax ID, and business metadata—and again in Operate if you refresh registration batches before compliance deadlines.
Is nts-business-registration safe to install?
Review the Security Audits panel on this Prism page and treat network calls to the configured proxy as sensitive; do not embed live secrets in prompts.
SKILL.md
READMESKILL.md - Nts Business Registration
from __future__ import annotations import argparse import datetime as dt import json import os import re import sys import urllib.error import urllib.request from typing import Any PROXY_BASE_URL_ENV_VAR = "KSKILL_PROXY_BASE_URL" DEFAULT_PROXY_BASE_URL = "https://k-skill-proxy.nomadamas.org" BATCH_LIMIT = 100 VALIDATE_TEXT_FIELD_LIMITS = { "p_nm": 30, "p_nm2": 30, "b_nm": 200, "b_sector": 100, "b_type": 100, "b_adr": 500, } class ApiError(RuntimeError): def __init__(self, message: str, *, status_code: int | None = None, url: str | None = None): super().__init__(message) self.status_code = status_code self.url = url def _text_or_none(value: Any) -> str | None: if value is None: return None text = str(value).strip() return text or None def resolve_proxy_base_url(explicit_base_url: str | None = None, env: dict[str, str] | None = None) -> str: env = os.environ if env is None else env candidate = _text_or_none(explicit_base_url or env.get(PROXY_BASE_URL_ENV_VAR)) if candidate and candidate.casefold() in {"off", "false", "0", "disable", "disabled", "none"}: raise ValueError("KSKILL_PROXY_BASE_URL 가 비활성화되어 있습니다.") if candidate and candidate != "replace-me": return candidate.rstrip("/") return DEFAULT_PROXY_BASE_URL def normalize_business_number(value: Any) -> str: raw = _text_or_none(value) if not raw: raise ValueError("사업자등록번호(b_no)를 입력하세요.") normalized = re.sub(r"\D", "", raw) if not re.fullmatch(r"\d{10}", normalized): raise ValueError("사업자등록번호는 숫자 10자리여야 합니다.") return normalized def normalize_start_date(value: Any) -> str: raw = _text_or_none(value) if not raw: raise ValueError("개업일자(start_dt)를 YYYYMMDD 형식으로 입력하세요.") normalized = re.sub(r"\D", "", raw) if not re.fullmatch(r"\d{8}", normalized): raise ValueError("개업일자는 YYYYMMDD 형식이어야 합니다.") try: dt.date(int(normalized[:4]), int(normalized[4:6]), int(normalized[6:8])) except ValueError as error: raise ValueError("개업일자는 유효한 날짜여야 합니다.") from error return normalized def normalize_validate_text(value: Any, field_name: str, *, required: bool = False) -> str | None: text = _text_or_none(value) if not text: if required: raise ValueError(f"{field_name}을(를) 입력하세요.") return None max_length = VALIDATE_TEXT_FIELD_LIMITS.get(field_name) if max_length and len(text) > max_length: raise ValueError(f"{field_name}은(는) {max_length}자 이하여야 합니다.") return text def normalize_corp_no(value: Any) -> str | None: raw = _text_or_none(value) if not raw: return None normalized = re.sub(r"\D", "", raw) if not re.fullmatch(r"\d{13}", normalized): raise ValueError("corp_no는 숫자 13자리여야 합니다.") return normalized def build_status_payload(business_numbers: list[Any]) -> dict[str, list[str]]: numbers = [normalize_business_number(value) for value in business_numbers] numbers = list(dict.fromkeys(numbers)) if not numbers: raise ValueError("사업자등록번호를 1개 이상 입력하세요.") if len(numbers) > BATCH_LIMIT: raise ValueError("한 번에 조회할 수 있는 사업자등록번호는 100개까지입니다.") return {"b_no": numbers} def build_validate_business(**kwargs: Any) -> dict[str, str]: p_nm = normalize_validate_text(kwargs.get("p_nm"), "p_nm", required=True) business = { "b_no": normalize_business_number(kwargs.get("b_no")), "start_dt": normalize_start_date(kwargs.get("start_dt")), "p_nm": p_nm, } for key in ("p_nm2", "b_nm", "b_sector", "b_type", "b_adr"): value = normalize_validate_text(kwargs.get(key), key) if value: business[key] = value corp_no = normalize_corp_no(kwargs.get("corp_no")) if corp_no: business["corp_no"] = corp_no return business def build_validate_payload(businesses: list[dict[str, Any]]) -> dict[str, list[dict[str, str]]