개발자 문서
개발자 문서

기업 리스크 브리프 (MCP 도구)

사업자등록번호 하나로 사업자상태, 진위확인, 나라장터 제재, 건설업, 폐기물 행정처분 다섯 공공데이터 조회를 한 번의 MCP 호출로 묶어 받는 도구의 준비, 요청, 응답, 오류 안내입니다.

이 도구가 하는 일

사업자등록번호 하나를 기준으로 공공데이터 다섯 조회(사업자상태, 진위확인, 나라장터 제재, 건설업 행정처분, 폐기물 행정처분)를 한 번의 호출로 실행해 하나의 요약으로 돌려주는 MCP 도구입니다. 다섯 조회는 함께 제출되어 가장 오래 걸리는 쪽만큼만 기다립니다. 다섯 조회 모두 XBOSS 의 플랫폼 키로 도니 고객의 기관 계정을 연동할 필요가 없습니다.

이 문서에서는 다섯 조회 하나하나를 leg 이라고 부릅니다. 응답의 legs 에는 leg 마다 원래 조회의 결과가 그대로 담기고, computed 에는 다섯 결과를 사업자 하나 기준으로 간추린 값이 담깁니다.

도구 이름company_info_pack__brief_v1__generate
호출 방식tools/call (JSON-RPC 2.0)
엔드포인트POST https://api.xdata.kr/mcp
인증Authorization: Basic
환경 헤더X-MCP-Env-Scope (선택)
최대 대기조회 하나 60초, 호출 전체 90초

이 도구는 MCP 로 부를 수 있습니다. REST 로 부르는 법은 아직 이 페이지에 없습니다(준비 중입니다). 다섯 조회를 REST 로 따로 부르는 방법도 아직 개발자 문서에 없습니다(공공데이터 조회 페이지가 준비 중입니다). 주소, 인증, 환경 헤더는 MCP 연동 개요와 같습니다.

다섯 조회(leg)

leg원자 액션따로 부르는 MCP 도구
사업자상태opendata.nts.statusopendata__nts__status
진위확인opendata.nts.validateopendata__nts__validate
나라장터제재opendata.g2b.sanctionopendata__g2b__sanction
건설행정처분opendata.kiscon.sanctionopendata__kiscon__sanction
폐기물행정처분opendata.env.waste_sanctionopendata__env__waste_sanction

다섯 조회는 개발자 문서에 아직 자기 페이지가 없습니다(공공데이터 조회 페이지 준비 중). 위 표의 「따로 부르는 MCP 도구」는 같은 조회를 이 팩 없이 단독으로 부르고 싶을 때 쓰는 이름입니다 - 인자와 응답 모양은 이 팩과 다를 수 있습니다.

부르기 전에 준비할 것

  1. MCP 서버를 연결합니다

    MCP 연동 개요의 순서대로 AI 앱이나 MCP 클라이언트에 XBOSS MCP 서버를 연결합니다. 기관 연동이나 자산 선택 같은 사전 준비는 없습니다.

요청

도구 이름과 arguments 를 담아 tools/call 을 보냅니다. bizno 는 다섯 조회 모두의 기준이 되고, start_dt, p_nm 은 진위확인 조회가, sDate, eDate 는 건설행정처분 조회가 씁니다. corp_name 은 건설행정처분 조회를 보조하는 선택 항목입니다.

필드타입필수설명예시
biznostring필수조회할 사업자등록번호입니다(10자리, 하이픈 없이). 다섯 조회 모두 이 번호를 기준으로 합니다.1234567890
start_dtstring필수진위확인 조회에 쓰는 개업일입니다(YYYYMMDD). 보내신 사업자등록번호와 이 개업일이 실제와 다르면 진위확인 결과도 달라집니다.20200101
p_nmstring필수진위확인 조회에 쓰는 대표자 이름입니다. 사업자등록증의 대표자명과 같아야 합니다.홍길동
sDatestring필수건설행정처분 조회 기간의 시작일입니다(YYYYMMDD).20200101
eDatestring필수건설행정처분 조회 기간의 종료일입니다(YYYYMMDD). 시작일과 같거나 그 이후여야 합니다.20261231
corp_namestring선택건설행정처분 조회에서 이름을 맞춰 보는 선택 항목입니다(상호명). 생략할 수 있습니다.—
account_link_id-받지 않음받지 않는 칸입니다. 보내도 무시됩니다. 다섯 조회 모두 플랫폼 키로 도는 opendata 조회라 고객의 기관 연동이 필요 없습니다.—
idempotency_keystring선택이 실행의 멱등 키입니다(비어 있지 않은 문자열, 앞뒤 공백은 지우고 128자까지 씁니다). 같은 키와 같은 조회 조건으로 다시 부르면 새 작업을 만들지 않고 이전 작업을 그대로 돌려받아 같은 결과가 옵니다. 같은 키로 조회 조건을 바꾸면 조회가 오류 번호 -32602(error_code IDEMPOTENCY_KEY_CONFLICT)로 실패하니, 같은 요청을 다시 보내는 것이면 처음 보낸 인자 그대로 보내고 새 요청이면 새 키를 쓰세요. 넣지 않으면 서버가 호출마다 새 키를 만들어 매번 새 작업으로 처리합니다.brief-2026-09
bash
# 기업정보 브리프 호출
# echo -n "<client_id>:<client_secret>" | base64
export MCP_URL="https://api.xdata.kr/mcp"
export MCP_AUTH_B64="<base64_결과_붙여넣기>"
curl -sS -X POST "$MCP_URL" \
  -H "Authorization: Basic $MCP_AUTH_B64" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "company_info_pack__brief_v1__generate",
      "arguments": {
        "bizno": "1234567890",
        "start_dt": "20200101",
        "p_nm": "홍길동",
        "sDate": "20200101",
        "eDate": "20261231",
        "idempotency_key": "brief-2026-09"
      }
    }
  }'

응답 읽기

호출이 성공하면 result.content[0].text 에 응답 본문이 JSON 문자열로 담겨 옵니다. JSON.parse 같은 함수로 풀어 씁니다. 데모 환경에서는 남은 호출량이 두 번째 content 블록과 _meta 에 함께 실립니다(MCP 연동 개요의 「응답에 실리는 _meta」).

필드타입있음설명
compositeboolean항상항상 true 입니다. 일반 도구 결과와 이 칸으로 구분합니다.
tool_namestring항상호출한 MCP 도구 이름입니다.
providerstring항상팩의 provider 이름입니다.
actionstring항상팩의 action 이름입니다.
idempotency_keystring항상이 실행의 멱등 키입니다. arguments 에 idempotency_key 를 넣었으면 그 값(앞뒤 공백을 지우고 128자까지), 넣지 않았으면 서버가 만든 값입니다.
partialboolean항상보고가 완전하지 않으면 true 입니다. leg 하나라도 성공하지 못했거나 계산을 하지 못했을 때입니다. true 여도 받은 자료는 그대로 쓸 수 있습니다 - 무엇이 비었는지는 legs 와 computed 가 알려 줍니다.
legsarray항상leg 다섯(사업자상태, 진위확인, 나라장터제재, 건설행정처분, 폐기물행정처분)의 원본 결과입니다. 정의 순서로 옵니다.
legs[].leg_indexnumber항상leg 의 순번입니다(0 부터, 정의 순서).
legs[].providerstring항상이 leg 이 부른 기관입니다.
legs[].actionstring항상이 leg 이 실행한 원자 액션입니다.
legs[].label_kostring항상이 leg 을 화면에 보일 한국어 이름입니다.
legs[].statusstring항상leg 의 결과 상태입니다. SUCCEEDED, FAILED, SKIPPED 중 하나입니다. SKIPPED 는 진위확인 leg 이 필수 바깥 인자(start_dt, p_nm)를 못 받았을 때만 나옵니다.
legs[].job_idstring | null항상이 leg 이 실행한 개별 작업의 아이디입니다. 제출하지 못한 leg 은 null 입니다.
legs[].resultobject | null항상성공한 leg 의 결과 본문입니다 - 그 원자 액션(opendata)의 응답과 같습니다. 실패했거나 건너뛰었으면 null 입니다.
legs[].errorobject | null항상실패한 leg 의 오류입니다. 성공했으면 null 입니다.
legs[].error.codenumber조건부오류 번호입니다(JSON-RPC 코드).
legs[].error.messagestring조건부이 leg 의 오류 문장입니다. 프로그램의 분기에는 code 와 data.error_code 를 쓰고, 문장은 사람이 읽는 용도로만 쓰세요.
legs[].error.dataobject | null조건부오류 종류별 부가 정보입니다. 보통 error_code 가 들어 있고 종류에 따라 다른 칸이 더해집니다. 부가 정보가 없는 오류는 이 칸이 없거나 null 입니다. 시간 초과일 때의 칸은 「시간 초과」 절의 표에 있습니다.
succeeded_countnumber항상성공한 leg 수입니다.
failed_countnumber항상성공하지 못한 leg 수입니다.
computedobject | null항상다섯 leg 을 사업자 하나 기준으로 요약한 칸입니다. 계산을 하지 못했으면 null 이고 이유는 computed_error 에 있습니다.
computed.biznostring조건부요청한 사업자등록번호를 그대로 돌려줍니다.
computed.as_ofstring조건부이 요약을 만든 시각입니다(UTC).
computed.sDatestring조건부건설행정처분 조회 시작일입니다. 보내지 않았으면 null 입니다.
computed.eDatestring조건부건설행정처분 조회 종료일입니다. 보내지 않았으면 null 입니다.
computed.legsarray조건부다섯 leg 결과를 계산이 다시 간추린 칸입니다(leg_index, provider, action, status, job_id, match_count, 실패하거나 건너뛴 leg 은 error).
computed.legs[].leg_indexnumber조건부봉투의 legs 와 같은 순번입니다.
computed.legs[].providerstring조건부이 leg 이 부른 기관입니다(opendata).
computed.legs[].actionstring조건부이 leg 이 실행한 원자 액션입니다.
computed.legs[].statusstring조건부이 leg 의 상태입니다. SUCCEEDED, FAILED, SKIPPED 중 하나입니다.
computed.legs[].job_idstring | null조건부이 leg 이 실행한 개별 작업의 아이디입니다. 제출하지 못했으면(SKIPPED) null 입니다.
computed.legs[].match_countnumber조건부성공한 leg 의 결과에 matches 배열이 있으면 그 길이입니다.
computed.legs[].errorobject조건부실패하거나 건너뛴 leg 의 오류입니다(code, message, data).
computed.legs[].error.codenumber조건부오류 번호입니다.
computed.legs[].error.messagestring조건부이 leg 의 오류 문장입니다. 프로그램의 분기에는 code 와 data 안의 error_code 를 쓰고, 문장은 사람이 읽는 용도로만 쓰세요.
computed.legs[].error.dataobject조건부오류 종류별 부가 정보입니다. 봉투 위쪽의 legs[].error.data 와 같은 값입니다(계산이 leg 오류를 그대로 옮깁니다) - 그 칸의 설명을 따릅니다.
computed.flagsobject조건부다섯 조회 각각을 한 낱말로 요약한 칸입니다 - 사업자상태, 진위확인, 나라장터제재, 건설행정처분, 폐기물행정처분 다섯 키가 늘 있습니다.
computed.flags.nts_statusobject조건부사업자상태 조회의 요약입니다(status, claim, 성공했으면 match_count 도).
computed.flags.nts_status.statusstring조건부사업자상태 조회 leg 의 상태입니다 - SUCCEEDED 또는 MISSING(성공하지 못함) 또는 SKIPPED 입니다.
computed.flags.nts_status.match_countnumber조건부claim 이 matches_reported 일 때만 있습니다 - 그 조회의 matches 배열 길이입니다.
computed.flags.nts_status.claimstring조건부그 결과를 어떻게 읽어야 하는지입니다 - matches_reported(정상 조회), payload_without_matches_list(모양이 다름), unavailable(못 읽음) 중 하나입니다.
computed.flags.nts_validateobject조건부진위확인 조회의 요약입니다. 그 leg 이 SKIPPED 면 status 가 SKIPPED 이고 claim 은 unavailable 입니다.
computed.flags.nts_validate.statusstring조건부진위확인 조회 leg 의 상태입니다 - SUCCEEDED 또는 MISSING 또는 SKIPPED 입니다. 필수 바깥 인자(start_dt, p_nm)가 없으면 SKIPPED 입니다.
computed.flags.nts_validate.match_countnumber조건부claim 이 matches_reported 일 때만 있습니다.
computed.flags.nts_validate.claimstring조건부그 결과를 어떻게 읽어야 하는지입니다 - matches_reported, payload_without_matches_list, unavailable 중 하나입니다.
computed.flags.g2b_sanctionobject조건부나라장터제재 조회의 요약입니다.
computed.flags.g2b_sanction.statusstring조건부나라장터제재 조회 leg 의 상태입니다 - SUCCEEDED 또는 MISSING 입니다(이 leg 은 SKIPPED 되지 않습니다 - 바깥 인자 리셰이프가 없습니다).
computed.flags.g2b_sanction.match_countnumber조건부claim 이 matches_reported 일 때만 있습니다.
computed.flags.g2b_sanction.claimstring조건부matches_reported, payload_without_matches_list, unavailable 중 하나입니다.
computed.flags.kiscon_sanctionobject조건부건설행정처분 조회의 요약입니다.
computed.flags.kiscon_sanction.statusstring조건부건설행정처분 조회 leg 의 상태입니다 - SUCCEEDED 또는 MISSING 입니다.
computed.flags.kiscon_sanction.match_countnumber조건부claim 이 matches_reported 일 때만 있습니다.
computed.flags.kiscon_sanction.claimstring조건부matches_reported, payload_without_matches_list, unavailable 중 하나입니다.
computed.flags.waste_sanctionobject조건부폐기물행정처분 조회의 요약입니다.
computed.flags.waste_sanction.statusstring조건부폐기물행정처분 조회 leg 의 상태입니다 - SUCCEEDED 또는 MISSING 입니다.
computed.flags.waste_sanction.match_countnumber조건부claim 이 matches_reported 일 때만 있습니다.
computed.flags.waste_sanction.claimstring조건부matches_reported, payload_without_matches_list, unavailable 중 하나입니다.
computed.coverage_notesarray조건부조회 결과를 읽을 때 반드시 알아야 하는 한국어 안내 네 문장입니다 - 빈 결과가 무혐의를 뜻하지 않는다는 것을 포함합니다.
computed_errorobject | null항상계산을 하지 못했을 때만 값이 있고(computed 는 null), 그 밖에는 null 입니다.
computed_error.reasonstring조건부계산하지 못한 이유입니다. 값마다 뜻과 결과(보고가 partial 로 오는지, 오류로 끝나는지)는 「시간 초과와 계산 실패」 절의 표에 있습니다.
computed_error.messagestring조건부이유를 설명하는 문장입니다. 마감으로 계산을 건너뛴 봉투(deadline_exhausted)는 한국어 안내 문장이고, 계산에 필요한 leg 자료가 없는 봉투(dependency_missing)는 어느 leg 의 결과가 없는지 알리는 영어 진단 문장입니다. 사람이 읽는 문장이니 프로그램의 분기에는 reason 을 쓰세요.

아래는 다섯 조회가 모두 성공했을 때의 응답 본문입니다. legs[].result 는 원래 조회의 응답과 같아서 예시에서는 줄여 적었습니다.

json
{
  "composite": true,
  "tool_name": "company_info_pack__brief_v1__generate",
  "provider": "company_info_pack",
  "action": "company_info_pack.brief_v1.generate",
  "idempotency_key": "brief-2026-09",
  "partial": false,
  "legs": [
    {
      "leg_index": 0,
      "provider": "opendata",
      "action": "opendata.nts.status",
      "label_ko": "사업자상태",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN10",
      "result": {
        "...": "opendata.nts.status 의 응답과 같습니다"
      },
      "error": null
    },
    {
      "leg_index": 1,
      "provider": "opendata",
      "action": "opendata.nts.validate",
      "label_ko": "진위확인",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN11",
      "result": {
        "...": "opendata.nts.validate 의 응답과 같습니다"
      },
      "error": null
    },
    {
      "leg_index": 2,
      "provider": "opendata",
      "action": "opendata.g2b.sanction",
      "label_ko": "나라장터제재",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN12",
      "result": {
        "...": "opendata.g2b.sanction 의 응답과 같습니다"
      },
      "error": null
    },
    {
      "leg_index": 3,
      "provider": "opendata",
      "action": "opendata.kiscon.sanction",
      "label_ko": "건설행정처분",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN13",
      "result": {
        "...": "opendata.kiscon.sanction 의 응답과 같습니다"
      },
      "error": null
    },
    {
      "leg_index": 4,
      "provider": "opendata",
      "action": "opendata.env.waste_sanction",
      "label_ko": "폐기물행정처분",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN14",
      "result": {
        "...": "opendata.env.waste_sanction 의 응답과 같습니다"
      },
      "error": null
    }
  ],
  "succeeded_count": 5,
  "failed_count": 0,
  "computed": {
    "bizno": "1234567890",
    "as_of": "2026-09-29T00:00:00Z",
    "sDate": "20200101",
    "eDate": "20261231",
    "legs": [
      {
        "leg_index": 0,
        "provider": "opendata",
        "action": "opendata.nts.status",
        "status": "SUCCEEDED",
        "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN10",
        "match_count": 0
      },
      {
        "leg_index": 1,
        "provider": "opendata",
        "action": "opendata.nts.validate",
        "status": "SUCCEEDED",
        "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN11",
        "match_count": 0
      },
      {
        "leg_index": 2,
        "provider": "opendata",
        "action": "opendata.g2b.sanction",
        "status": "SUCCEEDED",
        "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN12",
        "match_count": 0
      },
      {
        "leg_index": 3,
        "provider": "opendata",
        "action": "opendata.kiscon.sanction",
        "status": "SUCCEEDED",
        "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN13",
        "match_count": 0
      },
      {
        "leg_index": 4,
        "provider": "opendata",
        "action": "opendata.env.waste_sanction",
        "status": "SUCCEEDED",
        "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN14",
        "match_count": 0
      }
    ],
    "flags": {
      "nts_status": {
        "status": "SUCCEEDED",
        "match_count": 0,
        "claim": "matches_reported"
      },
      "nts_validate": {
        "status": "SUCCEEDED",
        "match_count": 0,
        "claim": "matches_reported"
      },
      "g2b_sanction": {
        "status": "SUCCEEDED",
        "match_count": 0,
        "claim": "matches_reported"
      },
      "kiscon_sanction": {
        "status": "SUCCEEDED",
        "match_count": 0,
        "claim": "matches_reported"
      },
      "waste_sanction": {
        "status": "SUCCEEDED",
        "match_count": 0,
        "claim": "matches_reported"
      }
    },
    "coverage_notes": [
      "조회 결과가 비어 있다고 해서 제재 이력이 없다고 단정하지 마세요. '확인된 제재 없음' 과 '제재가 없음' 은 다릅니다.",
      "건설업 행정처분은 요청하신 조회 기간 안만 봅니다. 기간 밖의 이력은 이 결과에 들어 있지 않습니다.",
      "환경(폐기물) 제재는 수집된 범위 안에서만 조회됩니다. 비어 있다는 것이 곧 이력이 없다는 뜻은 아닙니다.",
      "사업자 진위확인은 보내 주신 개업일과 대표자명을 기준으로 한 결과입니다. 그 값이 다르면 결과도 달라집니다."
    ]
  },
  "computed_error": null
}

조회 결과가 비어 있다고 해서 제재나 처분 이력이 없다고 단정하지 마세요. computed.coverage_notes 에 이 판단을 돕는 한국어 안내 네 문장이 함께 옵니다 - 빈 결과와 「확인된 제재 없음」은 다르고, 건설업 행정처분과 환경 제재는 조회 기간과 수집 범위 안에서만 보인다는 것을 포함합니다.

진위확인 조회를 건너뛰었을 때

start_dt 나 p_nm 을 비워 보내면(빈 문자열) 스키마는 통과하지만 진위확인 leg 만 건너뜁니다(legs[].status 가 SKIPPED). 나머지 네 조회는 그대로 진행되고 응답은 오류가 아니라 성공이며 partial 이 true 입니다. computed.flags.nts_validate 는 {"status": "SKIPPED", "claim": "unavailable"} 로 옵니다.

json
{
  "composite": true,
  "tool_name": "company_info_pack__brief_v1__generate",
  "provider": "company_info_pack",
  "action": "company_info_pack.brief_v1.generate",
  "idempotency_key": "brief-2026-09",
  "partial": true,
  "legs": [
    {
      "leg_index": 0,
      "provider": "opendata",
      "action": "opendata.nts.status",
      "label_ko": "사업자상태",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN10",
      "result": {
        "...": "opendata.nts.status 의 응답과 같습니다"
      },
      "error": null
    },
    {
      "leg_index": 1,
      "provider": "opendata",
      "action": "opendata.nts.validate",
      "label_ko": "진위확인",
      "status": "SKIPPED",
      "job_id": null,
      "result": null,
      "error": {
        "code": -32004,
        "message": "Composite dependency unavailable: p_nm is required for nts.validate",
        "data": {
          "error_code": "COMPOSITE_DEPENDENCY_UNAVAILABLE",
          "leg_index": 1
        }
      }
    },
    {
      "leg_index": 2,
      "provider": "opendata",
      "action": "opendata.g2b.sanction",
      "label_ko": "나라장터제재",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN12",
      "result": {
        "...": "opendata.g2b.sanction 의 응답과 같습니다"
      },
      "error": null
    },
    {
      "leg_index": 3,
      "provider": "opendata",
      "action": "opendata.kiscon.sanction",
      "label_ko": "건설행정처분",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN13",
      "result": {
        "...": "opendata.kiscon.sanction 의 응답과 같습니다"
      },
      "error": null
    },
    {
      "leg_index": 4,
      "provider": "opendata",
      "action": "opendata.env.waste_sanction",
      "label_ko": "폐기물행정처분",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN14",
      "result": {
        "...": "opendata.env.waste_sanction 의 응답과 같습니다"
      },
      "error": null
    }
  ],
  "succeeded_count": 4,
  "failed_count": 1,
  "computed": {
    "bizno": "1234567890",
    "as_of": "2026-09-29T00:00:00Z",
    "sDate": "20200101",
    "eDate": "20261231",
    "legs": [
      {
        "leg_index": 0,
        "provider": "opendata",
        "action": "opendata.nts.status",
        "status": "SUCCEEDED",
        "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN10",
        "match_count": 0
      },
      {
        "leg_index": 1,
        "provider": "opendata",
        "action": "opendata.nts.validate",
        "status": "SKIPPED",
        "job_id": null,
        "error": {
          "code": -32004,
          "message": "Composite dependency unavailable: p_nm is required for nts.validate",
          "data": {
            "error_code": "COMPOSITE_DEPENDENCY_UNAVAILABLE",
            "leg_index": 1
          }
        }
      },
      {
        "leg_index": 2,
        "provider": "opendata",
        "action": "opendata.g2b.sanction",
        "status": "SUCCEEDED",
        "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN12",
        "match_count": 0
      },
      {
        "leg_index": 3,
        "provider": "opendata",
        "action": "opendata.kiscon.sanction",
        "status": "SUCCEEDED",
        "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN13",
        "match_count": 0
      },
      {
        "leg_index": 4,
        "provider": "opendata",
        "action": "opendata.env.waste_sanction",
        "status": "SUCCEEDED",
        "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN14",
        "match_count": 0
      }
    ],
    "flags": {
      "nts_status": {
        "status": "SUCCEEDED",
        "match_count": 0,
        "claim": "matches_reported"
      },
      "nts_validate": {
        "status": "SKIPPED",
        "claim": "unavailable"
      },
      "g2b_sanction": {
        "status": "SUCCEEDED",
        "match_count": 0,
        "claim": "matches_reported"
      },
      "kiscon_sanction": {
        "status": "SUCCEEDED",
        "match_count": 0,
        "claim": "matches_reported"
      },
      "waste_sanction": {
        "status": "SUCCEEDED",
        "match_count": 0,
        "claim": "matches_reported"
      }
    },
    "coverage_notes": [
      "조회 결과가 비어 있다고 해서 제재 이력이 없다고 단정하지 마세요. '확인된 제재 없음' 과 '제재가 없음' 은 다릅니다.",
      "건설업 행정처분은 요청하신 조회 기간 안만 봅니다. 기간 밖의 이력은 이 결과에 들어 있지 않습니다.",
      "환경(폐기물) 제재는 수집된 범위 안에서만 조회됩니다. 비어 있다는 것이 곧 이력이 없다는 뜻은 아닙니다.",
      "사업자 진위확인은 보내 주신 개업일과 대표자명을 기준으로 한 결과입니다. 그 값이 다르면 결과도 달라집니다."
    ]
  },
  "computed_error": null
}

사업자등록번호를 비워 보냈을 때

bizno 를 빈 문자열로 보내면 다섯 조회의 성공 여부와 무관하게 요약(computed)을 만들지 못합니다 - computed 가 null 이고 computed_error.reason 이 dependency_missing 입니다. 받은 leg 결과는 그대로 legs 에 남고 partial 은 true 입니다.

computed_error.reason결과뜻
deadline_exhausted보고가 오고 partial호출 전체 마감 시간이 다 돼 계산을 건너뛰었습니다. 받은 자료는 legs 에 있고 봉투는 partial 로 옵니다(computed 는 null).
dependency_missing보고가 오고 partial계산에 필요한 leg 자료가 없습니다. 받은 자료는 legs 에 있고 봉투는 partial 로 옵니다.
validation_failed오류로 끝남계산 중 값 검증에서 실패했습니다(우리 쪽 결함). 봉투를 주지 않고 오류로 끝나며 받은 leg 결과는 오류의 data 에 실립니다.
builder_raised오류로 끝남계산 중 예외가 났습니다(우리 쪽 결함). 봉투를 주지 않고 오류로 끝나며 받은 leg 결과는 오류의 data 에 실립니다.

시간 초과

조회 하나는 제출한 뒤 최대 60초까지 기다리고, 호출 전체는 최대 90초 안에 끝납니다. 60초 안에 끝나지 않은 조회는 그 조회만 시간 초과로 실패하고 나머지 결과는 그대로 옵니다. 그 조회의 error.code 는 -32003 이고 error.data 에 다음 칸이 옵니다. 다섯 조회는 모두 읽기라 다시 불러도 됩니다.

error.data 의 칸뜻
job_id시간 안에 끝나지 않은 작업의 아이디입니다. 작업은 서버에서 계속 돌 수 있습니다.
timeout_seconds이 조회를 기다린 한도(초)입니다. 보통 60 이고, 호출 전체 마감(90초)이 가까웠으면 남은 시간이라 더 작습니다(소수일 수 있습니다).
safe_to_retry다시 불러도 되는지 알려 줍니다. 다섯 조회는 모두 읽기라 true 입니다.
status_url그 작업의 결과를 GET 으로 읽을 수 있는 주소입니다(REST 로 연동한 서비스에서 같은 API 키를 씁니다).
json
{
  "code": -32003,
  "message": "요청이 60초 안에 끝나지 않았습니다(job job_01JC8YH8R0E1ABCDEFGHJKMN10). 서버가 바쁠 수 있습니다. 같은 API 키로 연동한 서비스는 GET https://api.xdata.kr/v1/jobs/job_01JC8YH8R0E1ABCDEFGHJKMN10 로 결과를 읽을 수 있습니다. 잠시 뒤 다시 시도하거나, 요청 범위를 줄일 수 있으면 줄이세요.",
  "data": {
    "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN10",
    "timeout_seconds": 60,
    "safe_to_retry": true,
    "status_url": "https://api.xdata.kr/v1/jobs/job_01JC8YH8R0E1ABCDEFGHJKMN10"
  }
}

호출 전체 마감(90초)에 조회를 제출하거나 기다리기 시작하기 전이었다면 그 조회의 error.data.error_code 가 COMPOSITE_TIMEOUT 이고 error.data 에 다음 칸이 옵니다. error.message 는 사람이 읽는 한국어 문장이라 분기에는 error.data.error_code 를 쓰세요.

error.data 의 칸뜻
error_code항상 COMPOSITE_TIMEOUT 입니다. 이 값으로 호출 전체 마감을 가려냅니다.
timeout_seconds호출 전체 대기 한도(초)입니다.
phase마감이 된 단계의 이름입니다(진단용 - 분기에 쓰지 않습니다).
submitted그 조회의 작업을 이미 제출했는지 알려 줍니다. true 면 job_id 가 함께 옵니다.
job_id제출한 작업의 아이디입니다. 제출하지 못했으면 이 칸이 없습니다.
json
{
  "code": -32003,
  "message": "요청이 90초 안에 끝나지 않았습니다. 일부 조회는 서버에서 계속 진행 중입니다. 같은 API 키로 연동한 서비스는 GET https://api.xdata.kr/v1/jobs/job_01JC8YH8R0E1ABCDEFGHJKMN10 로 결과를 읽을 수 있습니다. 잠시 뒤 다시 시도해 주세요. 조회 기간이나 범위를 줄일 수 있으면 줄이면 더 빨리 끝납니다.",
  "data": {
    "error_code": "COMPOSITE_TIMEOUT",
    "timeout_seconds": 90,
    "phase": "Job polling",
    "submitted": true,
    "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN10"
  }
}

같은 요청을 다시 보낼 때

arguments 에 idempotency_key 를 문자열로 넣으면 그 값이 이 실행의 멱등 키가 됩니다(앞뒤 공백은 지우고 128자까지 씁니다). 같은 키와 같은 조회 조건으로 다시 부르면 새 작업을 만들지 않고 이전 작업을 그대로 돌려받아 같은 결과가 옵니다. 이전 작업이 실패했다면 실패한 결과가 그대로 오고, 아직 진행 중이면 그 작업을 이어서 기다립니다. 다시 시도하려면 새 키를 쓰세요.

같은 키로 조회 조건(인자)을 바꿔 부르면 조회가 실패합니다. 그 조회의 오류는 code 가 -32602 이고 data.error_code 가 IDEMPOTENCY_KEY_CONFLICT 입니다. 인자는 다섯 조회에 모두 전달되므로 대개 다섯 조회가 모두 이렇게 실패하고, 그러면 호출이 JSON-RPC error(번호 -32602)로 끝나며 조회별 오류는 error.data.legs[] 에 있습니다. 같은 요청을 다시 보내는 것이면 처음 보낸 인자 그대로 보내고, 새 요청이면 idempotency_key 를 새 값으로 바꾸거나 넣지 마세요(넣지 않으면 서버가 만듭니다). 오류의 safe_to_retry 는 false 입니다 - 같은 인자로 다시 불러도 같은 오류가 납니다.

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "같은 idempotency_key 로 내용이 다른 요청이 이미 들어왔습니다. 같은 요청을 다시 보내는 것이면 처음 보낸 인자 그대로 보내고, 새 요청이면 idempotency_key 를 새 값으로 바꾸거나 아예 넣지 마세요(넣지 않으면 서버가 만듭니다).",
    "data": {
      "composite": true,
      "tool_name": "company_info_pack__brief_v1__generate",
      "idempotency_key": "brief-2026-09",
      "legs": [
        {
          "leg_index": 0,
          "provider": "opendata",
          "action": "opendata.nts.status",
          "label_ko": "사업자상태",
          "status": "FAILED",
          "job_id": null,
          "result": null,
          "error": {
            "code": -32602,
            "message": "같은 idempotency_key 로 내용이 다른 요청이 이미 들어왔습니다. 같은 요청을 다시 보내는 것이면 처음 보낸 인자 그대로 보내고, 새 요청이면 idempotency_key 를 새 값으로 바꾸거나 아예 넣지 마세요(넣지 않으면 서버가 만듭니다).",
            "data": {
              "error_code": "IDEMPOTENCY_KEY_CONFLICT",
              "safe_to_retry": false
            }
          }
        },
        {
          "leg_index": 1,
          "provider": "opendata",
          "action": "opendata.nts.validate",
          "label_ko": "진위확인",
          "status": "FAILED",
          "job_id": null,
          "result": null,
          "error": {
            "code": -32602,
            "message": "같은 idempotency_key 로 내용이 다른 요청이 이미 들어왔습니다. 같은 요청을 다시 보내는 것이면 처음 보낸 인자 그대로 보내고, 새 요청이면 idempotency_key 를 새 값으로 바꾸거나 아예 넣지 마세요(넣지 않으면 서버가 만듭니다).",
            "data": {
              "error_code": "IDEMPOTENCY_KEY_CONFLICT",
              "safe_to_retry": false
            }
          }
        },
        {
          "leg_index": 2,
          "provider": "opendata",
          "action": "opendata.g2b.sanction",
          "label_ko": "나라장터제재",
          "status": "FAILED",
          "job_id": null,
          "result": null,
          "error": {
            "code": -32602,
            "message": "같은 idempotency_key 로 내용이 다른 요청이 이미 들어왔습니다. 같은 요청을 다시 보내는 것이면 처음 보낸 인자 그대로 보내고, 새 요청이면 idempotency_key 를 새 값으로 바꾸거나 아예 넣지 마세요(넣지 않으면 서버가 만듭니다).",
            "data": {
              "error_code": "IDEMPOTENCY_KEY_CONFLICT",
              "safe_to_retry": false
            }
          }
        },
        {
          "leg_index": 3,
          "provider": "opendata",
          "action": "opendata.kiscon.sanction",
          "label_ko": "건설행정처분",
          "status": "FAILED",
          "job_id": null,
          "result": null,
          "error": {
            "code": -32602,
            "message": "같은 idempotency_key 로 내용이 다른 요청이 이미 들어왔습니다. 같은 요청을 다시 보내는 것이면 처음 보낸 인자 그대로 보내고, 새 요청이면 idempotency_key 를 새 값으로 바꾸거나 아예 넣지 마세요(넣지 않으면 서버가 만듭니다).",
            "data": {
              "error_code": "IDEMPOTENCY_KEY_CONFLICT",
              "safe_to_retry": false
            }
          }
        },
        {
          "leg_index": 4,
          "provider": "opendata",
          "action": "opendata.env.waste_sanction",
          "label_ko": "폐기물행정처분",
          "status": "FAILED",
          "job_id": null,
          "result": null,
          "error": {
            "code": -32602,
            "message": "같은 idempotency_key 로 내용이 다른 요청이 이미 들어왔습니다. 같은 요청을 다시 보내는 것이면 처음 보낸 인자 그대로 보내고, 새 요청이면 idempotency_key 를 새 값으로 바꾸거나 아예 넣지 마세요(넣지 않으면 서버가 만듭니다).",
            "data": {
              "error_code": "IDEMPOTENCY_KEY_CONFLICT",
              "safe_to_retry": false
            }
          }
        }
      ]
    }
  }
}

키를 넣지 않으면 서버가 호출마다 새 키를 만들어 매번 새 작업으로 처리합니다. 응답의 idempotency_key 에는 실제로 쓴 키가 실립니다.

호출량

일일 한도가 있는 환경(데모)에서는 이 도구를 한 번 부를 때 새 작업이 만들어지면 1회로 셉니다. 다섯 조회를 각각 따로 부르면 5회입니다. 새 작업이 만들어지지 않은 호출은 세지 않습니다(제출 전에 모두 막혔거나 같은 멱등 키로 이전 작업을 돌려받은 경우).