apick-api 4.11.0 → 4.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +321 -293
- package/README.md +638 -592
- package/docs/guide.en.md +1 -1
- package/docs/guide.ko.md +5 -5
- package/docs/skill-artifacts.md +2 -2
- package/docs/subagent.md +2 -2
- package/docs/tts.md +3 -3
- package/package.json +1 -1
- package/src/index.cjs +345 -4
- package/src/index.d.ts +282 -5
package/docs/guide.en.md
CHANGED
|
@@ -116,7 +116,7 @@ await client.ocr(bytes, {
|
|
|
116
116
|
Query `listGeminiTtsVoices()` and `listOpenAiTtsVoices()` for current voices; legacy `TTS_VOICE_IDS` are retired.
|
|
117
117
|
|
|
118
118
|
|
|
119
|
-
Normalization is on by default and adds a skill charge. Set `normalize_text: false` (SDK `normalizeText: false`) to skip execution and billing. The skill price is its base fee (seller amount and base commission) plus actual AI
|
|
119
|
+
Normalization is on by default and adds a skill charge. Set `normalize_text: false` (SDK `normalizeText: false`) to skip execution and billing. The skill price is its base fee (seller amount and base commission) plus the actual AI usage fee. Normalization failure stops synthesis; final server/provider failures refund normalization as well. ASS preserves the original spelling.
|
|
120
120
|
|
|
121
121
|
```js
|
|
122
122
|
const screenshot = await client.screenshot('https://example.com');
|
package/docs/guide.ko.md
CHANGED
|
@@ -116,7 +116,7 @@ await client.ocr(bytes, {
|
|
|
116
116
|
현재 목소리는 `listGeminiTtsVoices()`와 `listOpenAiTtsVoices()`로 조회합니다. 기존 `TTS_VOICE_IDS`는 폐기된 목소리입니다.
|
|
117
117
|
|
|
118
118
|
|
|
119
|
-
정규화는 기본 켜짐이며 발음을 다듬는 스킬 요금이 추가됩니다. `normalize_text: false`(SDK `normalizeText: false`)로 실행과 요금을 끕니다. 스킬 최종가는 기본요금(판매자 금액·기본 수수료) + AI
|
|
119
|
+
정규화는 기본 켜짐이며 발음을 다듬는 스킬 요금이 추가됩니다. `normalize_text: false`(SDK `normalizeText: false`)로 실행과 요금을 끕니다. 스킬 최종가는 기본요금(판매자 금액·기본 수수료) + 실제 AI 사용료입니다. 정규화 실패는 제작을 중단하며, 서버의 최종 제작 실패는 정규화까지 전액 환불합니다. ASS에는 원문을 표시합니다.
|
|
120
120
|
|
|
121
121
|
```js
|
|
122
122
|
const screenshot = await client.screenshot('https://example.com');
|
|
@@ -233,7 +233,7 @@ console.log(result.data.result.fields);
|
|
|
233
233
|
|
|
234
234
|
## 간편인증 데이터 조회
|
|
235
235
|
|
|
236
|
-
재직·소득·연금·면허·건강검진·현금영수증·국세 신고내역·주민등록표
|
|
236
|
+
재직·소득·연금·면허·건강검진·현금영수증·국세 신고내역·주민등록표 발급·민원서류 통합 조회는 본인 간편인증이 필요해 접수(`request*`)와 결과 조회(`get*`)가 나뉩니다.
|
|
237
237
|
|
|
238
238
|
아래 함수는 7종 모두에 공통으로 사용합니다. 같은 `transactionId`로 순차 조회하며 5→10→20→30초 간격으로 늘린 뒤 30초를 유지합니다. `resultAvailable === true`이면 즉시 결과를 반환합니다. `SUCCESS`는 전체 성공, `PARTIAL_SUCCESS`는 부분 성공이므로 `sources`에서 누락·실패 항목을 확인하세요. `AUTH_REJECTED`·`AUTH_EXPIRED`·`FAILED`는 실패 종료이며, `errorCode: 'RESULT_EXPIRED'`는 결과 보관 기간 만료입니다. 실패·만료 시 자동으로 재접수하지 않습니다.
|
|
239
239
|
|
|
@@ -309,7 +309,7 @@ try {
|
|
|
309
309
|
|
|
310
310
|
`authProvider`는 `AUTH_PROVIDERS`(13종: kakao, naver, toss, pass, samsung, kb, shinhan, hana, woori, ibk, nh, kakaobank, banksalad) 중 하나입니다. 접수는 정액 과금, 결과는 최초 반환에서만 과금되며 재조회는 무료입니다. `requestEmployment`는 `insuranceYears`(1~3), `requestPersonalIncome`은 `incomeYears`(1~5), `requestNpsJoinHistory`는 `from`/`to`(`YYYY-MM`) 선택 입력을 받습니다. `requestNpsDeductionCert`는 `years`(1~5), `requestNpsCareerHistory`는 `from`/`to`(`YYYY-MM`) 선택 입력을 받습니다. `requestCashReceiptDeduction`은 `incomeYears`(1~3), `requestTaxReturnHistory`는 `years`(1~10) 선택 입력을 받습니다. 나머지 상품은 `requestDrivingLicense`, `requestHealthCheckup`입니다.
|
|
311
311
|
|
|
312
|
-
주민등록표등본·초본은 `requestResidentRegistration()`·`requestResidentRegistrationCho()`로 발급하며 주민등록 주소지 `sido`·`sigungu`가 필수입니다. 발급 1건은 80P이고 결과의 `발급문서`는 PDF 원본의 base64, `문서해시`는 그 SHA-256입니다.
|
|
312
|
+
주민등록표등본·초본은 `requestResidentRegistration()`·`requestResidentRegistrationCho()`로 발급하며 주민등록 주소지 `sido`·`sigungu`가 필수입니다. 발급 1건은 80P이고 결과의 `발급문서`는 PDF 원본의 base64, `문서해시`는 그 SHA-256입니다. 민원서류 통합 조회 `requestGov24Bundle()`은 `products`에 고른 상품만 한 번의 인증으로 조회하며, 과금은 고른 상품 단가의 합입니다. 선택 가능한 13개 키는 `GOV24_BUNDLE_PRODUCTS`로 제공하고 `sido`·`sigungu`는 등본·초본을 고를 때만 필요합니다.
|
|
313
313
|
|
|
314
314
|
```js
|
|
315
315
|
const bundle = await client.requestGov24Bundle({
|
|
@@ -320,7 +320,7 @@ const bundle = await client.requestGov24Bundle({
|
|
|
320
320
|
const issued = await pollDataResult(id => client.getGov24Bundle(id), bundle);
|
|
321
321
|
```
|
|
322
322
|
|
|
323
|
-
|
|
323
|
+
세무 민원증명은 `requestIncomeAmountCert`(소득금액증명), `requestPensionCreditCert`(연금보험료등 소득세액 공제확인서), `requestFactCert`(납부내역증명), `requestTaxClearanceCert`(납세증명서)로 발급하며 발급 1건은 60P입니다. 모두 본인명의 발급이고 `year`·`use_purpose`·`submit_to`·`delivery`가 필수이며 `format`(`pdf`·`json`·`both`, 기본 `both`)으로 응답을 고릅니다. `json`·`both` 이면 발급된 PDF를 파싱한 `PDF데이터.필드`가 함께 오고, 표 구조에 따라 일부 필드는 비어 있을 수 있습니다. 결과 본문의 `문서확인번호`·`귀속연도` 같은 값이 정본입니다.
|
|
324
324
|
|
|
325
325
|
```js
|
|
326
326
|
const cert = await client.requestIncomeAmountCert({
|
|
@@ -427,7 +427,7 @@ if (status.data.status === "completed") {
|
|
|
427
427
|
|
|
428
428
|
## 상품별 조회 가이드
|
|
429
429
|
|
|
430
|
-
상품별 간편인증 조회 가이드에는 조회 데이터,
|
|
430
|
+
상품별 간편인증 조회 가이드에는 조회 데이터, 활용 사례와 자주 묻는 질문이 있습니다. 인증 요청은 해당 상품의 자료를 조회하기 위한 단계이며 결과 조회용 transactionId를 반환합니다. 요청·응답 계약과 인증 방식은 동일합니다. [공통 인증 흐름](https://apick.app/dev_guide/data)에서 절차와 오류 처리를 확인하세요.
|
|
431
431
|
|
|
432
432
|
|
|
433
433
|
TTS: [Gemini·ChatGPT·정규화 / engines and normalization](tts.md). `createOpenAiTtsJob`, `listOpenAiTtsVoices`, `getTtsOptions`를 포함한 최신 계약입니다.
|
package/docs/skill-artifacts.md
CHANGED
|
@@ -19,6 +19,6 @@ const downloaded = await apick.getSkillArtifact(run.data.run_id, file.id);
|
|
|
19
19
|
await downloaded.save('./result.png');
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
파일의 `name`, `mime_type`, `bytes`, `sha256`을 확인하고 실제 형식에 맞는 확장자로 저장하세요. 인증키 소유자의 실행 파일만 내려받을 수 있고, 실행 결과가 만료되면 파일도 만료됩니다. 실패·취소·시간 초과는 예약액을 반환하며, 가변 요금 상품은 성공 시 실제 사용료를 정산합니다.
|
|
22
|
+
파일의 `name`, `mime_type`, `bytes`, `sha256`을 확인하고 실제 형식에 맞는 확장자로 저장하세요. 인증키 소유자의 실행 파일만 내려받을 수 있고, 실행 결과가 만료되면 파일도 만료됩니다. 실패·취소·시간 초과는 예약액을 반환하며, 가변 요금 상품은 성공 시 실제 사용료를 정산합니다.
|
|
23
23
|
|
|
24
|
-
Use `name`, `mime_type`, `bytes`, and `sha256` to identify and verify each file; choose a matching extension. Downloads require the run owner's API key and expire with the result. Failed, cancelled, and timed-out runs release the reservation. Variable-price Skills settle recorded tool usage on success
|
|
24
|
+
Use `name`, `mime_type`, `bytes`, and `sha256` to identify and verify each file; choose a matching extension. Downloads require the run owner's API key and expire with the result. Failed, cancelled, and timed-out runs release the reservation. Variable-price Skills settle recorded tool usage on success.
|
package/docs/subagent.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# 서브에이전트 / Subagent
|
|
2
2
|
|
|
3
|
-
`apick-agent`는 설치형 상품이며 `quoteSkill`·`runSkill` 대신 전용 API를 사용합니다. 설치비·도입비·구독료는 없고,
|
|
3
|
+
`apick-agent`는 설치형 상품이며 `quoteSkill`·`runSkill` 대신 전용 API를 사용합니다. 설치비·도입비·구독료는 없고, 성공한 작업만 사용량대로 정산합니다(소수점 올림, 2026-11-06부터 AI 사용 작업 기본 5P). 승인된 동일 결과 캐시는 1P입니다. 충전 잔액 외 상품 사용량 한도는 없습니다.
|
|
4
4
|
|
|
5
|
-
`apick-agent` is an installed-agent product. Use the dedicated API rather than `quoteSkill` or `runSkill`. There is no installation, setup or subscription fee. Successful work is billed
|
|
5
|
+
`apick-agent` is an installed-agent product. Use the dedicated API rather than `quoteSkill` or `runSkill`. There is no installation, setup or subscription fee. Successful work is billed by actual usage (fractions rounded up, 5-point base fee for AI jobs from 2026-11-06); approved identical-result cache reuse costs 1 point. There are no product usage quotas beyond prepaid balance.
|
|
6
6
|
|
|
7
7
|
```js
|
|
8
8
|
import { ApickClient } from 'apick-api';
|
package/docs/tts.md
CHANGED
|
@@ -25,10 +25,10 @@ Style, emotion, tone, accent, pace, pitch and volume options can be set per requ
|
|
|
25
25
|
|
|
26
26
|
Normalization is enabled by default and costs extra. Disable it with `normalize_text: false` (`normalizeText: false` for the string SDK helper). Input limits are 2,000 characters including utterance separators with normalization, or 8,000 without it. Query a quote, engine-specific voices and supported options before submitting.
|
|
27
27
|
|
|
28
|
-
실제
|
|
28
|
+
실제 사용량에 정규화 요금을 더해 정산합니다. 소수점은 올림하며, 2026-11-06부터 합성 작업 1건의 기본요금 5P를 최소액으로 받습니다. 엔진별 요금이 다르며, 예상 금액은 견적 API(`POST /rest/tts/quote`)로 미리 확인할 수 있습니다.
|
|
29
29
|
|
|
30
|
-
AI usage is charged
|
|
30
|
+
AI usage is charged by actual usage, plus normalization. Fractions are rounded up, and from 2026-11-06 each synthesis job has a 5-point base fee. Rates differ by engine; check the estimate with the quote API (`POST /rest/tts/quote`) first.
|
|
31
31
|
|
|
32
|
-
두 엔진은 같은 작업 조회·취소·MP3·원문 ASS 계약을 사용합니다. `billing`의 예약·해제·확정·환불을 확인하세요. MP3는 24kHz 모노 48kbps, 결과는 완료 후 24시간 보관합니다. MP3와 ASS는 각각 완료된 다운로드 1회만 허용하며 전송 중단은 재시도할 수 있습니다. 접수 응답 유실 시 같은 입력과 멱등 키를 재사용하세요.
|
|
32
|
+
두 엔진은 같은 작업 조회·취소·MP3·원문 ASS 계약을 사용합니다. `billing`의 예약·해제·확정·환불을 확인하세요. MP3는 24kHz 모노 48kbps, 결과는 완료 후 24시간 보관합니다. MP3와 ASS는 각각 완료된 다운로드 1회만 허용하며 전송 중단은 재시도할 수 있습니다. 접수 응답 유실 시 같은 입력과 멱등 키를 재사용하세요. 서버 최종 실패는 정규화까지 환불하며, 연결 종료는 작업 취소가 아닙니다. 취소 시 이미 수행된 작업분을 정산합니다.
|
|
33
33
|
|
|
34
34
|
Both engines share job status, cancellation, MP3 and original-text ASS downloads. Check reserved, released, captured and refunded amounts in `billing`. MP3 is 24 kHz mono at 48 kbps. Files are kept for 24 hours; each permits one completed download, and interrupted transfers can be retried. Reuse the same input and idempotency key after a lost response. Final server/provider failures refund synthesis and normalization; disconnecting does not cancel work. Explicit cancellation settles work already performed.
|
package/package.json
CHANGED
package/src/index.cjs
CHANGED
|
@@ -16,7 +16,7 @@ const AUTH_PROVIDERS = Object.freeze([
|
|
|
16
16
|
'kakao', 'naver', 'toss', 'pass', 'samsung', 'kb', 'shinhan', 'hana', 'woori', 'ibk', 'nh', 'kakaobank', 'banksalad'
|
|
17
17
|
]);
|
|
18
18
|
const AUTH_PROVIDER_SET = new Set(AUTH_PROVIDERS);
|
|
19
|
-
//
|
|
19
|
+
// 민원서류 통합 조회에서 고를 수 있는 상품 키. 서버 공개 목록과 같다.
|
|
20
20
|
const GOV24_BUNDLE_PRODUCTS = Object.freeze([
|
|
21
21
|
'resident_registration', 'resident_registration_cho', 'driving_license', 'health_checkup',
|
|
22
22
|
'police_penalty', 'parking_fine_target', 'mortgage_loan', 'housing_refund_balance',
|
|
@@ -61,7 +61,36 @@ const SERVICE_DEFINITIONS = Object.freeze({
|
|
|
61
61
|
linkedinCompany: { endpoint: '/rest/linkedin_company', timeoutMs: 110_000, output: 'json' },
|
|
62
62
|
linkedinJob: { endpoint: '/rest/linkedin_job', timeoutMs: 110_000, output: 'json' },
|
|
63
63
|
courtAuction: { endpoint: '/rest/court_auction', timeoutMs: 45_000, output: 'json' },
|
|
64
|
+
bidNotice: { endpoint: '/rest/bid_notice', timeoutMs: 45_000, output: 'json' },
|
|
65
|
+
bidAward: { endpoint: '/rest/bid_award', timeoutMs: 45_000, output: 'json' },
|
|
66
|
+
rtmsTrade: { endpoint: '/rest/rtms_trade', timeoutMs: 45_000, output: 'json' },
|
|
67
|
+
rtmsRent: { endpoint: '/rest/rtms_rent', timeoutMs: 45_000, output: 'json' },
|
|
64
68
|
registryDocVerify: { endpoint: '/rest/registry_doc_verify', timeoutMs: 30_000, output: 'json' },
|
|
69
|
+
corpFinance: { endpoint: '/rest/corp_finance', timeoutMs: 35_000, output: 'json' },
|
|
70
|
+
buildingRegister: { endpoint: '/rest/bldrgst', timeoutMs: 35_000, output: 'json' },
|
|
71
|
+
appReviews: { endpoint: '/rest/app_reviews', timeoutMs: 35_000, output: 'json' },
|
|
72
|
+
vehicleEmission: { endpoint: '/rest/vehicle_emission', timeoutMs: 25_000, output: 'json' },
|
|
73
|
+
vehicleRecall: { endpoint: '/rest/vehicle_recall', timeoutMs: 45_000, output: 'json' },
|
|
74
|
+
hipassUnpaid: { endpoint: '/rest/hipass_unpaid', timeoutMs: 30_000, output: 'json' },
|
|
75
|
+
vehicleTaxCalc: { endpoint: '/rest/vehicle_tax_calc', timeoutMs: 15_000, output: 'json' },
|
|
76
|
+
// 공간정보는 배열·GeoJSON 객체를 그대로 보내도록 JSON 본문을 쓴다.
|
|
77
|
+
geoStandardize: { endpoint: '/rest/geo_standardize', timeoutMs: 30_000, output: 'json', json: true },
|
|
78
|
+
geoBatchGeocode: { endpoint: '/rest/geo_batch_geocode', timeoutMs: 35_000, output: 'json', json: true },
|
|
79
|
+
geoReverse: { endpoint: '/rest/geo_reverse', timeoutMs: 25_000, output: 'json', json: true },
|
|
80
|
+
geoAddressSearch: { endpoint: '/rest/geo_address_search', timeoutMs: 30_000, output: 'json', json: true },
|
|
81
|
+
geoPlaceSearch: { endpoint: '/rest/geo_place_search', timeoutMs: 30_000, output: 'json', json: true },
|
|
82
|
+
geoAdminCode: { endpoint: '/rest/geo_admin_code', timeoutMs: 25_000, output: 'json', json: true },
|
|
83
|
+
geoCompareAddress: { endpoint: '/rest/geo_compare_address', timeoutMs: 35_000, output: 'json', json: true },
|
|
84
|
+
geoMeasure: { endpoint: '/rest/geo_measure', timeoutMs: 25_000, output: 'json', json: true },
|
|
85
|
+
geoGeojsonAnalyze: { endpoint: '/rest/geo_geojson_analyze', timeoutMs: 25_000, output: 'json', json: true },
|
|
86
|
+
geoAddressQuality: { endpoint: '/rest/geo_address_quality', timeoutMs: 35_000, output: 'json', json: true },
|
|
87
|
+
geoAddressAutocomplete: { endpoint: '/rest/geo_address_autocomplete', timeoutMs: 25_000, output: 'json', json: true },
|
|
88
|
+
geoDistrictBoundary: { endpoint: '/rest/geo_district_boundary', timeoutMs: 35_000, output: 'json', json: true },
|
|
89
|
+
geoNeighborhood: { endpoint: '/rest/geo_neighborhood', timeoutMs: 35_000, output: 'json', json: true },
|
|
90
|
+
registryRealestateSearch: { endpoint: '/rest/registry_realestate_search', timeoutMs: 125_000, output: 'json' },
|
|
91
|
+
registryCorporateSearch: { endpoint: '/rest/registry_corporate_search', timeoutMs: 125_000, output: 'json' },
|
|
92
|
+
createRegistryRealestateJob: { endpoint: '/rest/registry_realestate_jobs', timeoutMs: 50_000, output: 'json' },
|
|
93
|
+
createRegistryCorporateJob: { endpoint: '/rest/registry_corporate_jobs', timeoutMs: 50_000, output: 'json' },
|
|
65
94
|
screenshot: { endpoint: '/rest/url_screenshot', timeoutMs: 75_000, output: 'binary', filename: 'screenshot.jpeg' },
|
|
66
95
|
youtubeMetadata: { endpoint: '/rest/youtube_metadata', timeoutMs: 60_000, output: 'json' },
|
|
67
96
|
youtubeThumbnail: { endpoint: '/rest/youtube_thumbnail', timeoutMs: 60_000, output: 'binary', filename: 'thumbnail.jpg' },
|
|
@@ -173,6 +202,57 @@ function normalizeBusinessNumber(value) {
|
|
|
173
202
|
return normalized;
|
|
174
203
|
}
|
|
175
204
|
|
|
205
|
+
const BID_BIZ_TYPES = ['물품', '공사', '용역', '외자'];
|
|
206
|
+
const RTMS_PROPERTY_TYPES = ['apt', 'rh', 'sh', 'offi'];
|
|
207
|
+
|
|
208
|
+
// 입찰공고·낙찰 조회 입력. bizType·기간·공고번호·업종코드·페이지를 정규화한다.
|
|
209
|
+
function bidQueryInput(options) {
|
|
210
|
+
const config = options || {};
|
|
211
|
+
const input = {};
|
|
212
|
+
if (config.bizType !== undefined) {
|
|
213
|
+
if (!BID_BIZ_TYPES.includes(config.bizType)) {
|
|
214
|
+
throw new TypeError('bizType must be one of 물품, 공사, 용역, 외자.');
|
|
215
|
+
}
|
|
216
|
+
input.bizType = config.bizType;
|
|
217
|
+
}
|
|
218
|
+
for (const key of ['startDate', 'endDate']) {
|
|
219
|
+
if (config[key] === undefined) continue;
|
|
220
|
+
const digits = requiredString(key, config[key], 12).replace(/[^0-9]/g, '');
|
|
221
|
+
if (digits.length !== 8 && digits.length !== 12) {
|
|
222
|
+
throw new TypeError(`${key} must be YYYYMMDD or YYYYMMDDHHMM.`);
|
|
223
|
+
}
|
|
224
|
+
input[key] = digits;
|
|
225
|
+
}
|
|
226
|
+
if (config.bidNtceNo !== undefined) {
|
|
227
|
+
input.bidNtceNo = requiredString('bidNtceNo', config.bidNtceNo, 20).replace(/[^0-9]/g, '');
|
|
228
|
+
}
|
|
229
|
+
if (config.indstrytyCd !== undefined) {
|
|
230
|
+
input.indstrytyCd = requiredString('indstrytyCd', config.indstrytyCd, 10).replace(/[^0-9]/g, '');
|
|
231
|
+
}
|
|
232
|
+
if (config.pageNo !== undefined) input.pageNo = positiveInteger('pageNo', config.pageNo);
|
|
233
|
+
if (config.numOfRows !== undefined) input.numOfRows = positiveInteger('numOfRows', config.numOfRows);
|
|
234
|
+
return input;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 실거래 분석 입력. 지역코드 5자리와 계약년월 6자리를 요구한다.
|
|
238
|
+
function rtmsQueryInput(lawdCd, dealYmd, options) {
|
|
239
|
+
const code = requiredString('lawdCd', lawdCd, 5).replace(/[^0-9]/g, '');
|
|
240
|
+
if (!/^\d{5}$/.test(code)) throw new TypeError('lawdCd must contain exactly 5 digits.');
|
|
241
|
+
const month = requiredString('dealYmd', dealYmd, 6).replace(/[^0-9]/g, '');
|
|
242
|
+
if (!/^\d{6}$/.test(month)) throw new TypeError('dealYmd must be YYYYMM.');
|
|
243
|
+
const config = options || {};
|
|
244
|
+
const input = { lawdCd: code, dealYmd: month };
|
|
245
|
+
if (config.propertyType !== undefined) {
|
|
246
|
+
if (!RTMS_PROPERTY_TYPES.includes(config.propertyType)) {
|
|
247
|
+
throw new TypeError('propertyType must be one of apt, rh, sh, offi.');
|
|
248
|
+
}
|
|
249
|
+
input.propertyType = config.propertyType;
|
|
250
|
+
}
|
|
251
|
+
if (config.pageNo !== undefined) input.pageNo = positiveInteger('pageNo', config.pageNo);
|
|
252
|
+
if (config.numOfRows !== undefined) input.numOfRows = positiveInteger('numOfRows', config.numOfRows);
|
|
253
|
+
return input;
|
|
254
|
+
}
|
|
255
|
+
|
|
176
256
|
function normalizeUrl(value) {
|
|
177
257
|
const input = requiredString('url', value);
|
|
178
258
|
let parsed;
|
|
@@ -242,6 +322,44 @@ function compact(payload) {
|
|
|
242
322
|
return Object.fromEntries(Object.entries(payload).filter(([, value]) => value !== undefined));
|
|
243
323
|
}
|
|
244
324
|
|
|
325
|
+
// 선택 입력 객체를 받아 정해진 키만 남긴다. 값은 서버가 검증하므로 여기서는 객체 모양만 본다.
|
|
326
|
+
function pickOptions(name, options, keys) {
|
|
327
|
+
if (options === undefined || options === null) return {};
|
|
328
|
+
if (typeof options !== 'object' || Array.isArray(options)) throw new TypeError(`${name} must be an object.`);
|
|
329
|
+
return compact(Object.fromEntries(keys.map(key => [key, options[key]])));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function nonEmptyList(name, value, max) {
|
|
333
|
+
if (!Array.isArray(value) || value.length === 0) throw new TypeError(`${name} must be a non-empty array.`);
|
|
334
|
+
if (value.length > max) throw new RangeError(`${name} must not have more than ${max} items.`);
|
|
335
|
+
return value;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// 차량번호는 공백을 빼고 보낸다. 형식(예: 12가3456)은 서버가 확인한다.
|
|
339
|
+
function carNumber(name, value) {
|
|
340
|
+
return requiredString(name, value, 16).replace(/\s+/g, '');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function registryCorporateTarget(input) {
|
|
344
|
+
const config = input || {};
|
|
345
|
+
const corp = config.corpRegNo === undefined || config.corpRegNo === '' ? '' : requiredString('corpRegNo', config.corpRegNo, 20).replace(/[^0-9]/g, '');
|
|
346
|
+
const biz = config.bizNo === undefined || config.bizNo === '' ? '' : requiredString('bizNo', config.bizNo, 20).replace(/[^0-9]/g, '');
|
|
347
|
+
if (!corp && !biz) throw new TypeError('corpRegNo or bizNo is required.');
|
|
348
|
+
return corp ? { corp_reg_no: corp } : { biz_no: biz };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function registryJobId(jobId) {
|
|
352
|
+
const id = requiredString('jobId', jobId, 64);
|
|
353
|
+
if (!/^[A-Za-z0-9_-]{8,64}$/.test(id)) throw new TypeError('jobId is invalid.');
|
|
354
|
+
return id;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function geoTarget(input, name) {
|
|
358
|
+
const config = input || {};
|
|
359
|
+
if (typeof config === 'string') return { address: requiredString(name, config, 300) };
|
|
360
|
+
return pickOptions(name, config, ['address', 'x', 'y', 'point', 'pnu', 'addrType', 'includeZipcode', 'radius', 'categories', 'limit']);
|
|
361
|
+
}
|
|
362
|
+
|
|
245
363
|
function optionalRangeInteger(name, value, min, max) {
|
|
246
364
|
if (value === undefined || value === null || value === '') return undefined;
|
|
247
365
|
const number = Number(value);
|
|
@@ -263,7 +381,7 @@ function dataRequestInput(input) {
|
|
|
263
381
|
};
|
|
264
382
|
}
|
|
265
383
|
|
|
266
|
-
//
|
|
384
|
+
// 세무 민원증명. 발급 조건은 REST 필드명 그대로 보낸다(서버가 최상위 평면 이름을 읽는다).
|
|
267
385
|
// 공통 인증 입력(name·birthDate·phone·authProvider)은 dataRequestInput 이 만든다.
|
|
268
386
|
const INCOME_AMOUNT_CERT_OPTIONS = Object.freeze(['year', 'use_purpose', 'submit_to', 'delivery', 'format', 'engl_yn', 'quantity', 'email', 'fax_no', 'wallet_address']);
|
|
269
387
|
const PENSION_CREDIT_CERT_OPTIONS = Object.freeze(['year', 'use_purpose', 'submit_to', 'delivery', 'format', 'address_open_yn', 'resno_open_yn', 'quantity', 'email', 'fax_no', 'wallet_address']);
|
|
@@ -828,6 +946,229 @@ class ApickClient {
|
|
|
828
946
|
return this._call('registryDocVerify', input);
|
|
829
947
|
}
|
|
830
948
|
|
|
949
|
+
// 입찰공고 분석. bizType 미지정 시 용역이며, 기간은 최대 31일이다.
|
|
950
|
+
bidNotice(options) {
|
|
951
|
+
return this._call('bidNotice', bidQueryInput(options));
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// 낙찰 경쟁 분석. 입력은 bidNotice 와 같다.
|
|
955
|
+
bidAward(options) {
|
|
956
|
+
return this._call('bidAward', bidQueryInput(options));
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// 매매 실거래 분석. 지역코드 5자리와 계약년월 6자리가 필요하다.
|
|
960
|
+
rtmsTrade(lawdCd, dealYmd, options) {
|
|
961
|
+
return this._call('rtmsTrade', rtmsQueryInput(lawdCd, dealYmd, options));
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// 전월세 실거래 분석. 같은 단지 매매가로 전세가율을 계산해 돌려준다.
|
|
965
|
+
rtmsRent(lawdCd, dealYmd, options) {
|
|
966
|
+
const input = rtmsQueryInput(lawdCd, dealYmd, options);
|
|
967
|
+
// includeTrade=false 면 매매가 비교(전세가율) 없이 임대 분석만 받는다.
|
|
968
|
+
if (options && options.includeTrade !== undefined) input.includeTrade = options.includeTrade === false ? 'false' : 'true';
|
|
969
|
+
return this._call('rtmsRent', input);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// 기업 재무정보. 법인등록번호 13자리(하이픈 허용)와 선택 사업연도·항목·연결 구분.
|
|
973
|
+
corpFinance(crno, options) {
|
|
974
|
+
return this._call('corpFinance', Object.assign(
|
|
975
|
+
{ crno: requiredString('crno', crno, 20) },
|
|
976
|
+
pickOptions('options', options, ['bizYear', 'type', 'scope'])
|
|
977
|
+
));
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// 건축물대장. pnu 또는 sigunguCd·bjdongCd·bun(·ji)으로 지번을 지정하고 type 으로 대장 종류를 고른다.
|
|
981
|
+
buildingRegister(options) {
|
|
982
|
+
const input = pickOptions('options', options, ['pnu', 'sigunguCd', 'bjdongCd', 'platGbCd', 'bun', 'ji', 'type', 'startDate', 'endDate', 'dongNm', 'hoNm', 'flrNo', 'pageNo', 'numOfRows']);
|
|
983
|
+
if (!input.pnu && !(input.sigunguCd && input.bjdongCd)) throw new TypeError('pnu or sigunguCd and bjdongCd are required.');
|
|
984
|
+
return this._call('buildingRegister', input);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// 앱스토어 리뷰. appId 는 숫자 앱 ID다.
|
|
988
|
+
appReviews(appId, options) {
|
|
989
|
+
return this._call('appReviews', Object.assign(
|
|
990
|
+
{ appId: requiredString('appId', String(appId == null ? '' : appId), 20) },
|
|
991
|
+
pickOptions('options', options, ['country', 'page'])
|
|
992
|
+
));
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// 배출가스 인증정보. 차량번호가 아니라 배출가스인증번호를 받는다.
|
|
996
|
+
vehicleEmission(certNo) {
|
|
997
|
+
return this._call('vehicleEmission', { cert_no: requiredString('certNo', certNo, 40) });
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
// 자동차 리콜 대상 여부. carNo 와 vin 중 하나만 보낸다. 리콜 대상이 아니면 과금되지 않는다.
|
|
1001
|
+
vehicleRecall(input) {
|
|
1002
|
+
const config = input || {};
|
|
1003
|
+
const hasCar = config.carNo !== undefined && config.carNo !== null && config.carNo !== '';
|
|
1004
|
+
const hasVin = config.vin !== undefined && config.vin !== null && config.vin !== '';
|
|
1005
|
+
if (hasCar === hasVin) throw new TypeError('Provide exactly one of carNo or vin.');
|
|
1006
|
+
return this._call('vehicleRecall', hasCar
|
|
1007
|
+
? { car_no: carNumber('carNo', config.carNo) }
|
|
1008
|
+
: { vin: requiredString('vin', config.vin, 17).replace(/\s+/g, '').toUpperCase() });
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// 하이패스 미납 여부. 미납이 없으면 과금되지 않는다.
|
|
1012
|
+
hipassUnpaid(carNo) {
|
|
1013
|
+
return this._call('hipassUnpaid', { car_no: carNumber('carNo', carNo) });
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// 자동차세 계산(비영업용 승용). cc 는 배기량, carAgeYears 는 차령(년)이다.
|
|
1017
|
+
vehicleTaxCalc(cc, options) {
|
|
1018
|
+
const config = options || {};
|
|
1019
|
+
if (cc === undefined || cc === null || cc === '') throw new TypeError('cc is required.');
|
|
1020
|
+
return this._call('vehicleTaxCalc', compact({
|
|
1021
|
+
cc: optionalRangeInteger('cc', cc, 50, 10000),
|
|
1022
|
+
car_age_years: optionalRangeInteger('carAgeYears', config.carAgeYears, 0, 40),
|
|
1023
|
+
year: optionalRangeInteger('year', config.year, 2000, 2100)
|
|
1024
|
+
}));
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// 주소 표준화·코드 변환. 주소 문자열 또는 { address | pnu, addrType, includeZipcode }.
|
|
1028
|
+
geoStandardize(input) {
|
|
1029
|
+
const body = geoTarget(input, 'input');
|
|
1030
|
+
if (!body.address && !body.pnu) throw new TypeError('address or pnu is required.');
|
|
1031
|
+
return this._call('geoStandardize', body);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// 대량 주소 좌표 변환(최대 50건).
|
|
1035
|
+
geoBatchGeocode(addresses, options) {
|
|
1036
|
+
return this._call('geoBatchGeocode', Object.assign(
|
|
1037
|
+
{ addresses: nonEmptyList('addresses', addresses, 50) },
|
|
1038
|
+
pickOptions('options', options, ['addrType', 'dedupe'])
|
|
1039
|
+
));
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// 좌표→주소·행정구역. { x, y } 또는 { point: '경도,위도' }.
|
|
1043
|
+
geoReverse(input) {
|
|
1044
|
+
return this._call('geoReverse', pickOptions('input', input, ['x', 'y', 'point']));
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
geoAddressSearch(query, options) {
|
|
1048
|
+
return this._call('geoAddressSearch', Object.assign(
|
|
1049
|
+
{ query: requiredString('query', query, 200) },
|
|
1050
|
+
pickOptions('options', options, ['category', 'page', 'size'])
|
|
1051
|
+
));
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// 장소 검색. 기준점(origin 또는 x·y)을 주면 거리순으로 정렬한다.
|
|
1055
|
+
geoPlaceSearch(query, options) {
|
|
1056
|
+
return this._call('geoPlaceSearch', Object.assign(
|
|
1057
|
+
{ query: requiredString('query', query, 200) },
|
|
1058
|
+
pickOptions('options', options, ['origin', 'x', 'y', 'radius', 'sort', 'page', 'size'])
|
|
1059
|
+
));
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// 행정구역 코드. 주소 문자열 또는 { address | x·y | point, addrType, includeZipcode }.
|
|
1063
|
+
geoAdminCode(input) {
|
|
1064
|
+
return this._call('geoAdminCode', geoTarget(input, 'input'));
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
geoCompareAddress(addressA, addressB, options) {
|
|
1068
|
+
return this._call('geoCompareAddress', Object.assign(
|
|
1069
|
+
{ addressA: requiredString('addressA', addressA, 300), addressB: requiredString('addressB', addressB, 300) },
|
|
1070
|
+
pickOptions('options', options, ['addrType'])
|
|
1071
|
+
));
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// 좌표 거리·면적. coordinates 는 ['경도,위도', ...] 또는 [[경도, 위도], ...](2~200개).
|
|
1075
|
+
geoMeasure(coordinates, options) {
|
|
1076
|
+
return this._call('geoMeasure', Object.assign(
|
|
1077
|
+
{ coordinates: nonEmptyList('coordinates', coordinates, 200) },
|
|
1078
|
+
pickOptions('options', options, ['mode', 'unit', 'matrix'])
|
|
1079
|
+
));
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// GeoJSON 면적·포함 분석. 본문은 1MB 이하여야 한다.
|
|
1083
|
+
geoGeojsonAnalyze(geojson, options) {
|
|
1084
|
+
if (!geojson || (typeof geojson !== 'object' && typeof geojson !== 'string')) throw new TypeError('geojson must be an object or a JSON string.');
|
|
1085
|
+
return this._call('geoGeojsonAnalyze', Object.assign(
|
|
1086
|
+
{ geojson },
|
|
1087
|
+
pickOptions('options', options, ['simplify', 'point', 'x', 'y', 'includeGeometry'])
|
|
1088
|
+
));
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// 주소 품질 진단(최대 20건).
|
|
1092
|
+
geoAddressQuality(addresses, options) {
|
|
1093
|
+
return this._call('geoAddressQuality', Object.assign(
|
|
1094
|
+
{ addresses: nonEmptyList('addresses', addresses, 20) },
|
|
1095
|
+
pickOptions('options', options, ['addrType'])
|
|
1096
|
+
));
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
geoAddressAutocomplete(query, options) {
|
|
1100
|
+
return this._call('geoAddressAutocomplete', Object.assign(
|
|
1101
|
+
{ query: requiredString('query', query, 200) },
|
|
1102
|
+
pickOptions('options', options, ['category', 'size'])
|
|
1103
|
+
));
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// 행정구역 경계·면적. query 는 이름(예: 강남구) 또는 코드(예: 11680).
|
|
1107
|
+
geoDistrictBoundary(query, options) {
|
|
1108
|
+
return this._call('geoDistrictBoundary', Object.assign(
|
|
1109
|
+
{ query: requiredString('query', String(query == null ? '' : query), 100) },
|
|
1110
|
+
pickOptions('options', options, ['level', 'simplify', 'includeGeometry'])
|
|
1111
|
+
));
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// 주변 생활시설. 기준 주소 문자열 또는 { address | x·y | point, radius, categories, limit }.
|
|
1115
|
+
geoNeighborhood(input) {
|
|
1116
|
+
return this._call('geoNeighborhood', geoTarget(input, 'input'));
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// 부동산 등기 검색. 주소(도로명·지번) 또는 부동산고유번호 14자리. 결과가 없으면 과금되지 않는다.
|
|
1120
|
+
registryRealestateSearch(query) {
|
|
1121
|
+
return this._call('registryRealestateSearch', { query: requiredString('query', query, 200) });
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// 법인 등기 검색. corpRegNo(법인등록번호 13자리) 또는 bizNo(사업자등록번호 10자리) 중 하나.
|
|
1125
|
+
registryCorporateSearch(input) {
|
|
1126
|
+
return this._call('registryCorporateSearch', registryCorporateTarget(input));
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
// 부동산 등기 열람·발급 접수. 접수 즉시 job_id 를 돌려주며 getRegistryJob 으로 상태를 확인한다.
|
|
1130
|
+
// 같은 요청을 다시 보낼 수 있게 idempotencyKey 를 넣어 두는 것을 권장한다.
|
|
1131
|
+
createRegistryRealestateJob(options) {
|
|
1132
|
+
const config = options || {};
|
|
1133
|
+
if (!config.query && !config.pin) throw new TypeError('query or pin is required.');
|
|
1134
|
+
return this._call('createRegistryRealestateJob', compact({
|
|
1135
|
+
query: config.query === undefined ? undefined : requiredString('query', config.query, 200),
|
|
1136
|
+
pin: config.pin === undefined ? undefined : requiredString('pin', config.pin, 20),
|
|
1137
|
+
index: optionalRangeInteger('index', config.index, 1, 100),
|
|
1138
|
+
usage: config.usage,
|
|
1139
|
+
record_scope: config.recordScope,
|
|
1140
|
+
include_cancelled: config.includeCancelled === undefined ? undefined : String(Boolean(config.includeCancelled)),
|
|
1141
|
+
publish_resident_no: config.publishResidentNo === undefined ? undefined : String(Boolean(config.publishResidentNo)),
|
|
1142
|
+
idempotency_key: config.idempotencyKey === undefined ? undefined : requiredString('idempotencyKey', config.idempotencyKey, 128)
|
|
1143
|
+
}));
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// 법인 등기 열람·발급 접수.
|
|
1147
|
+
createRegistryCorporateJob(options) {
|
|
1148
|
+
const config = options || {};
|
|
1149
|
+
return this._call('createRegistryCorporateJob', Object.assign(registryCorporateTarget(config), compact({
|
|
1150
|
+
usage: config.usage,
|
|
1151
|
+
idempotency_key: config.idempotencyKey === undefined ? undefined : requiredString('idempotencyKey', config.idempotencyKey, 128)
|
|
1152
|
+
})));
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// 등기 작업 상태. 조회는 무료다.
|
|
1156
|
+
getRegistryJob(jobId) {
|
|
1157
|
+
return this._call('createRegistryRealestateJob', null, null, { endpoint: '/rest/registry_jobs/' + registryJobId(jobId), method: 'GET', timeoutMs: 35_000 });
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// 완료된 등기 작업의 PDF. 전송이 끝난 다운로드만 과금된다.
|
|
1161
|
+
downloadRegistryPdf(jobId) {
|
|
1162
|
+
const id = registryJobId(jobId);
|
|
1163
|
+
return this._call('createRegistryRealestateJob', null, null, {
|
|
1164
|
+
endpoint: '/rest/registry_jobs/' + id + '?download=1',
|
|
1165
|
+
method: 'GET',
|
|
1166
|
+
output: 'binary',
|
|
1167
|
+
filename: 'registry-' + id + '.pdf',
|
|
1168
|
+
timeoutMs: 65_000
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
|
|
831
1172
|
screenshot(url) {
|
|
832
1173
|
return this._call('screenshot', { url: normalizeUrl(url) });
|
|
833
1174
|
}
|
|
@@ -1238,7 +1579,7 @@ class ApickClient {
|
|
|
1238
1579
|
return this._call('requestTaxReturnHistory', { transactionId: normalizeTransactionId(transactionId) }, null, { endpoint: '/rest/get_tax_return_history' });
|
|
1239
1580
|
}
|
|
1240
1581
|
|
|
1241
|
-
//
|
|
1582
|
+
// 세무 민원증명 발급. 신청인 본인명의이고 발급 1건 60P 정액이다.
|
|
1242
1583
|
// 응답은 format(pdf·json·both)으로 고른다. json 이면 발급된 PDF를 파싱한 PDF데이터가 함께 온다.
|
|
1243
1584
|
requestIncomeAmountCert(input) {
|
|
1244
1585
|
return this._call('requestIncomeAmountCert', hometaxCertInput(input, INCOME_AMOUNT_CERT_OPTIONS));
|
|
@@ -1348,7 +1689,7 @@ class ApickClient {
|
|
|
1348
1689
|
return this._call('requestResidentRegistrationCho', { transactionId: normalizeTransactionId(transactionId) }, null, { endpoint: '/rest/get_resident_registration_cho' });
|
|
1349
1690
|
}
|
|
1350
1691
|
|
|
1351
|
-
//
|
|
1692
|
+
// 민원서류 통합 조회. 한 번의 인증으로 고른 상품만 조회하고, 단가는 고른 상품의 합으로 정해진다.
|
|
1352
1693
|
requestGov24Bundle(input) {
|
|
1353
1694
|
const config = input || {};
|
|
1354
1695
|
const products = gov24BundleProductsInput(config.products);
|