계약관리시스템 API 가이드

계약관리시스템에 저장된 계약서 및 첨부파일 데이터를 외부 시스템, 스크립트, 앱에서 직접 호출하는 방법을 안내합니다.

REST API 인증 불필요 JSON 응답 CORS 허용

개요

계약관리시스템은 RESTful Table API를 사용하여 데이터를 저장합니다.
아래의 엔드포인트를 통해 계약서 데이터를 생성(POST) · 조회(GET) · 수정(PUT/PATCH) · 삭제(DELETE)할 수 있습니다.
별도의 인증 없이 호출 가능하며, 모든 요청/응답은 JSON 형식입니다.

사용 시나리오: ERP 시스템과 계약 데이터 동기화, 사내 대시보드에 계약 현황 임베드, Python/Node 스크립트로 계약 만료 알림 발송 등에 활용하세요.

Base URL

모든 API 요청의 기준 URL입니다. 이 사이트가 배포된 도메인 뒤에 경로를 붙여서 사용합니다.

https://contract.sinwoodnc.com/tables/
운영 도메인 고정: https://contract.sinwoodnc.com 기준으로 모든 예제가 작성되어 있습니다.

📦 사용 가능한 테이블

테이블명설명엔드포인트 예시
contracts계약서 데이터tables/contracts
contract_files첨부파일 데이터 (Base64)tables/contract_files

데이터 스키마

계약서 데이터의 전체 필드 목록입니다. * 표시가 없는 필드는 선택 입력입니다.

title
text
계약서 제목 *
contract_number
text
계약번호 *
contract_type
text
사업번호 (예: DH25007-30)
status
text
계약 상태 * (초안/검토중/서명완료/진행중/종료/만료/해지)
client_name
text
거래처명 *
client_contact
text
거래처 담당자
amount
number
계약금액 (원)
start_date
text
시작일 (YYYY-MM-DD)
end_date
text
종료일 (YYYY-MM-DD)
signed_date
text
서명일 (YYYY-MM-DD)
manager
text
내부 담당자
department
text
사업부서 (감리부/철도부/도로부/안전진단)
priority
text
우선순위 (높음/보통/낮음)
tags
text
비고 / 인감도장 번호 (예: 1번인감, 2번인감)
description
rich_text
계약 내용
contract_id
text
연결된 계약서 ID *
file_name
text
원본 파일명 *
file_size
number
파일 크기 (bytes)
file_type
text
MIME 타입
file_ext
text
확장자 (pdf, xlsx 등)
file_data
rich_text
Base64 인코딩 데이터
description
text
파일 설명/메모
uploaded_by
text
업로더 이름
아래 필드는 API가 자동으로 채워주므로, 요청 시 포함하지 않아도 됩니다.
id
text (UUID)
자동 생성 고유 ID
created_at
number (ms)
생성 시각 (타임스탬프)
updated_at
number (ms)
수정 시각 (타임스탬프)
gs_project_id
text
프로젝트 ID
gs_table_name
text
테이블명

계약서 목록 조회

GET tables/contracts 전체 계약서 목록 (페이지네이션)

Query Parameters

파라미터타입기본값설명
page optionalnumber1페이지 번호
limit optionalnumber20페이지당 건수 (최대 200)
search optionalstring-키워드 검색
sort optionalstringcreated_at정렬 기준 필드명
Response JSON
{
  "data": [
    {
      "id": "uuid-xxxx-xxxx",
      "title": "웹사이트 리뉴얼 개발 용역계약",
      "contract_number": "CT-2025-001",
      "contract_type": "DH25007-30",
      "status": "진행중",
      "client_name": "(주)테크솔루션",
      "amount": 25000000,
      "start_date": "2025-01-15",
      "end_date": "2025-07-15",
      "manager": "이수진",
      "priority": "높음",
      "created_at": 1736000000000,
      "updated_at": 1736000000000
    }
  ],
  "total": 8,
  "page": 1,
  "limit": 20,
  "table": "contracts"
}

단건 조회

GET tables/contracts/{id} 특정 계약서 1건 조회
Path 파라미터타입설명
id requiredstring (UUID)계약서 고유 ID
Response JSON
{
  "id": "uuid-xxxx-xxxx",
  "title": "웹사이트 리뉴얼 개발 용역계약",
  "contract_number": "CT-2025-001",
  "status": "진행중",
  "amount": 25000000,
  "end_date": "2025-07-15"
}

계약서 생성

POST tables/contracts 새 계약서 생성 (HTTP 201)
Content-Type: application/json 헤더가 필요합니다.
Request Body (JSON)
{
  "title": "신규 서버 구축 계약",
  "contract_number": "CT-2025-009",
  "contract_type": "DH25009-30",
  "status": "초안",
  "client_name": "클라우드코리아",
  "client_contact": "홍길동 / 010-0000-0000",
  "amount": 18000000,
  "start_date": "2025-04-01",
  "end_date": "2025-09-30",
  "manager": "박지수",
  "department": "감리부",
  "priority": "높음",
  "tags": "1번인감",
  "description": "클라우드 서버 마이그레이션 및 구축"
}

계약서 수정

PUT tables/contracts/{id} 계약서 전체 업데이트
PUT은 전체 필드를 교체합니다. 일부만 수정하려면 PATCH를 사용하세요.
PATCH — 일부 필드만 수정
// PATCH tables/contracts/{id}
{
  "status": "서명완료",
  "signed_date": "2025-04-05"
}

계약서 삭제

DELETE tables/contracts/{id} 계약서 삭제 (HTTP 204)
삭제 성공 시 응답 본문 없이 HTTP 204 No Content를 반환합니다. 소프트 삭제(soft delete)로 처리됩니다.

첨부파일 API

GET tables/contract_files?limit=100 전체 파일 목록 조회

특정 계약서의 파일만 필터링하려면 응답 데이터에서 contract_id로 클라이언트 측 필터링합니다.

JavaScript 필터링 예시
const res = await fetch('tables/contract_files?limit=200');
const data = await res.json();

// 특정 계약서의 파일만 필터
const contractId = 'uuid-xxxx-xxxx';
const files = data.data.filter(f => f.contract_id === contractId);
console.log(files);
POST tables/contract_files 파일 업로드 (Base64)
파일을 Base64로 인코딩하여 file_data 필드에 담아 전송합니다. (DataURL 형식: data:mime;base64,...)
Request Body
{
  "contract_id": "uuid-xxxx-xxxx",
  "file_name": "계약서_초안.pdf",
  "file_size": 204800,
  "file_type": "application/pdf",
  "file_ext": "pdf",
  "file_data": "data:application/pdf;base64,JVBERi0xLjQK...",
  "description": "서명 전 초안",
  "uploaded_by": "홍길동"
}
DELETE tables/contract_files/{id} 파일 삭제 (HTTP 204)

파일 레코드의 id를 Path에 포함하여 삭제합니다. HTTP 204를 반환합니다.

대금청구현황 API

계약별 대금 청구 및 입금 현황을 관리하는 billing_records 테이블 API입니다. 청구 예정·완료·입금·연체 상태를 추적할 수 있습니다.

billing_records 스키마

필드명 타입 설명 예시
idtextUUID (자동생성)uuid-...
contract_idtext계약서 ID (contracts.id 참조)uuid-...
contract_numbertext계약번호 (표시용 캐시)CT-2025-018
billing_seqnumber청구 회차1, 2, 3
billing_typetext청구 유형선급금 / 기성금 / 중도금 / 잔금 / 기타
billing_amountnumber청구 금액 (원)79200000
billing_datetext청구일 (YYYY-MM-DD)2025-06-05
due_datetext입금 기한 (YYYY-MM-DD)2025-06-30
paid_datetext실제 입금일 (YYYY-MM-DD)2025-06-28
paid_amountnumber입금 금액 (원, 부분입금 포함)59400000
statustext청구 상태scheduled / billed / partial / paid / overdue
tax_invoice_notext세금계산서 번호20250605-001
notetext메모자유 입력
status 상태값 설명
scheduled 청구예정  |  billed 청구완료(미입금)  |  partial 부분입금  |  paid 입금완료  |  overdue 연체(기한초과)
GET /tables/billing_records
대금청구 목록 조회

쿼리 파라미터

pagenumber페이지 번호 (기본값: 1)
limitnumber페이지당 건수 (기본값: 20, 최대: 200)
sortstring정렬 기준 (예: billing_date, created_at)

특정 계약의 청구 내역만 조회

JavaScript
// 전체 청구 목록 (페이지네이션)
const res = await fetch('https://contract.sinwoodnc.com/tables/billing_records?limit=200');
const data = await res.json();
console.log(data.data);   // 청구 목록 배열
console.log(data.total);  // 전체 건수

// 특정 계약 ID의 청구 내역만 필터링
const contractId = 'YOUR_CONTRACT_ID';
const billings = data.data.filter(b => b.contract_id === contractId);

// 회차 순서로 정렬
billings.sort((a, b) => a.billing_seq - b.billing_seq);
POST /tables/billing_records
청구 등록
JavaScript
// 청구 등록
const res = await fetch('https://contract.sinwoodnc.com/tables/billing_records', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    contract_id:     'CONTRACTS_TABLE_ID',   // contracts 테이블의 id
    contract_number: 'CT-2025-018',          // 표시용 계약번호
    billing_seq:     1,                      // 회차
    billing_type:    '선급금',               // 선급금|기성금|중도금|잔금|기타
    billing_amount:  79200000,               // 청구 금액(원)
    billing_date:    '2025-06-05',           // 청구일
    due_date:        '2025-06-30',           // 입금기한
    status:          'billed',               // scheduled|billed|partial|paid|overdue
    paid_amount:     0,                      // 입금 금액(원)
    paid_date:       null,                   // 입금일
    tax_invoice_no:  '20250605-001',         // 세금계산서 번호
    note:            '1차 선급금 청구'
  })
});
const created = await res.json();
console.log('생성된 ID:', created.id);
PUT /tables/billing_records/{id}
청구 수정 (입금 확인 포함)
JavaScript
// 입금 확인 처리 (PATCH - 부분 업데이트)
const billingId = 'BILLING_RECORD_ID';
const res = await fetch(
  `https://contract.sinwoodnc.com/tables/billing_records/${billingId}`,
  {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      status:       'paid',          // 입금완료로 상태 변경
      paid_amount:  79200000,        // 입금 금액
      paid_date:    '2025-06-28',    // 입금일
    })
  }
);
const updated = await res.json();
console.log('수정 완료:', updated.status);
DELETE /tables/billing_records/{id}
청구 삭제
JavaScript
const billingId = 'BILLING_RECORD_ID';
await fetch(
  `https://contract.sinwoodnc.com/tables/billing_records/${billingId}`,
  { method: 'DELETE' }
);
// 성공 시 HTTP 204 No Content 반환

활용 예제 - 계약별 입금 현황 집계

JavaScript
// 계약서 + 청구 데이터를 조합하여 입금 현황 집계
async function getBillingStatus(contractId) {
  // 계약 정보 조회
  const cRes = await fetch(
    `https://contract.sinwoodnc.com/tables/contracts/${contractId}`
  );
  const contract = await cRes.json();

  // 해당 계약의 청구 내역 조회 (전체)
  const bRes = await fetch(
    `https://contract.sinwoodnc.com/tables/billing_records?limit=200`
  );
  const { data: allBillings } = await bRes.json();
  const billings = allBillings
    .filter(b => b.contract_id === contractId)
    .sort((a, b) => a.billing_seq - b.billing_seq);

  // 집계
  const totalAmount  = contract.amount || 0;
  const totalBilled  = billings.reduce((s, b) => s + (b.billing_amount || 0), 0);
  const totalPaid    = billings.reduce((s, b) => s + (b.paid_amount    || 0), 0);
  const remaining    = totalAmount - totalBilled;
  const collectRate  = totalAmount > 0
    ? Math.round(totalPaid / totalAmount * 100) : 0;

  // 연체 항목 확인
  const today    = new Date();
  const overdues = billings.filter(b =>
    b.due_date &&
    new Date(b.due_date) < today &&
    b.status !== 'paid'
  );

  return {
    contract,
    billings,
    summary: {
      totalAmount,   // 계약 총액
      totalBilled,   // 총 청구액
      totalPaid,     // 총 입금액
      remaining,     // 잔여 미청구액
      collectRate,   // 입금률(%)
      overdueCount: overdues.length  // 연체 건수
    }
  };
}

// 사용 예시
const result = await getBillingStatus('YOUR_CONTRACT_ID');
console.log(`입금률: ${result.summary.collectRate}%`);
console.log(`연체 ${result.summary.overdueCount}건`);

입출금현황 API

부가세 납부, 정기결제, 소모품 구입, 대금 입금 등 모든 통장 거래 내역을 등록·조회·관리하는 transactions 테이블 API입니다. 계약 대금과 무관한 일반 입출금도 자유롭게 등록할 수 있습니다.

transactions 스키마

필드명 타입 설명 예시
idtextUUID (자동생성)uuid-...
tx_datetext거래일 (YYYY-MM-DD) *필수2025-04-01
tx_typetext구분 *필수income (입금) / expense (출금)
categorytext카테고리 *필수대금입금 / 부가세납부 / 정기결제 / 소모품 / 인건비 / 공과금 / 임대료 / 기타
titletext거래 내용/적요 *필수부가세 2기 확정신고 납부
amountnumber금액 (원) *필수3850000
departmenttext사업부서감리부 / 철도부 / 도로부 / 안전진단 / 공통
counterparttext거래처 / 상대방국세청, ㈜테크솔루션
accounttext계좌 / 은행국민은행 123-456-789
contract_idtext연관 계약서 ID (선택, contracts.id 참조)uuid-...
tax_invoice_notext세금계산서 번호20250401-001
notetext비고자유 입력
카테고리 값 설명
대금입금 계약 대금 수령  |  부가세납부 부가세 신고 납부  |  정기결제 구독·정기 서비스  |  소모품 사무용품 등 소모품 구입  |  인건비 급여·외주비 등  |  공과금 전기·수도·통신비 등  |  임대료 사무실 임대  |  기타 분류 불명

엔드포인트

GET tables/transactions?page=1&limit=100 거래 목록 조회
GET tables/transactions/{id} 거래 단건 조회
POST tables/transactions 거래 등록
PUT tables/transactions/{id} 거래 수정
DELETE tables/transactions/{id} 거래 삭제

코드 예제

JavaScript (fetch)
const BASE = 'https://YOUR_DOMAIN/tables/transactions';

// ── 거래 목록 조회 (전체) ──
async function getTxList(page=1, limit=100) {
  const res = await fetch(`${BASE}?page=${page}&limit=${limit}`);
  return await res.json();
  // { data: [...], total: 50, page: 1, limit: 100 }
}

// ── 거래 등록 예시: 부가세 납부 ──
async function addTax() {
  const res = await fetch(BASE, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      tx_date:    '2025-04-01',
      tx_type:    'expense',       // 출금
      category:   '부가세납부',
      title:      '2025년 1기 부가세 확정신고 납부',
      amount:     3850000,
      counterpart:'국세청',
      account:    '국민은행 123-456',
      department: '공통',
      tax_invoice_no: '',
      note:       '홈택스 납부'
    })
  });
  return await res.json();
}

// ── 거래 등록 예시: 대금 입금 (계약 연결) ──
async function addPaymentReceived(contractId) {
  const res = await fetch(BASE, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      tx_date:    '2025-04-10',
      tx_type:    'income',        // 입금
      category:   '대금입금',
      title:      'DH25007-30 1회차 기성금 입금',
      amount:     55000000,
      counterpart:'㈜테크솔루션',
      account:    '국민은행 123-456',
      department: '감리부',
      contract_id: contractId,    // 계약서 연결
      tax_invoice_no: '20250405-001'
    })
  });
  return await res.json();
}

// ── 월별 입출금 집계 ──
async function getMonthlySummary(yearMonth) {
  // yearMonth: '2025-04'
  const res = await fetch(`${BASE}?limit=200`);
  const { data } = await res.json();
  const monthData = data.filter(t => (t.tx_date||'').startsWith(yearMonth));
  const income  = monthData.filter(t=>t.tx_type==='income' ).reduce((s,t)=>s+Number(t.amount||0),0);
  const expense = monthData.filter(t=>t.tx_type==='expense').reduce((s,t)=>s+Number(t.amount||0),0);
  return { income, expense, balance: income - expense, count: monthData.length };
}

JavaScript 예제

브라우저 또는 Node.js 환경에서 사용할 수 있는 완전한 예제입니다.

JavaScript (fetch)
// ─────────────────────────────────────────
// 계약관리시스템 API 클라이언트 (JavaScript)
// ─────────────────────────────────────────
const BASE = 'https://contract.sinwoodnc.com/tables';

// 1. 전체 계약서 조회
async function getContracts(page = 1, limit = 20) {
  const res = await fetch(`${BASE}/contracts?page=${page}&limit=${limit}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return await res.json();
  // { data: [...], total: N, page: 1, limit: 20 }
}

// 2. 특정 계약서 조회
async function getContract(id) {
  const res = await fetch(`${BASE}/contracts/${id}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return await res.json();
}

// 3. 계약서 생성
async function createContract(payload) {
  const res = await fetch(`${BASE}/contracts`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return await res.json(); // 생성된 레코드 반환 (id 포함)
}

// 4. 계약서 상태 변경 (PATCH)
async function updateContractStatus(id, status) {
  const res = await fetch(`${BASE}/contracts/${id}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ status }),
  });
  return await res.json();
}

// 5. 계약서 삭제
async function deleteContract(id) {
  const res = await fetch(`${BASE}/contracts/${id}`, {
    method: 'DELETE',
  });
  return res.status === 204; // true면 성공
}

// 6. 만료 임박 계약 (30일 이내) 조회
async function getExpiringContracts() {
  const { data } = await getContracts(1, 200);
  const today = new Date();
  return data.filter(c => {
    if (!c.end_date || c.status === '만료' || c.status === '종료') return false;
    const end  = new Date(c.end_date);
    const diff = Math.round((end - today) / 86400000);
    return diff >= 0 && diff <= 30;
  });
}

// ─── 사용 예시 ───
(async () => {
  // 전체 조회
  const { data, total } = await getContracts();
  console.log(`전체 계약 ${total}건:`, data);

  // 만료 임박 조회
  const expiring = await getExpiringContracts();
  console.log('30일 이내 만료:', expiring.map(c => c.title));

  // 새 계약서 생성
  const newContract = await createContract({
    title: 'API로 생성한 계약',
    contract_number: 'CT-2025-999',
    contract_type: 'DH25999-30',
    status: '초안',
    client_name: '테스트 회사',
    amount: 5000000,
    priority: '보통',
  });
  console.log('생성된 계약 ID:', newContract.id);
})();

Python 예제

Python requests 라이브러리를 사용하는 예제입니다.

Python (requests)
import requests
from datetime import date, timedelta

BASE = "https://contract.sinwoodnc.com/tables"

# ─── 1. 전체 계약서 조회 ───
def get_contracts(page=1, limit=50):
    r = requests.get(f"{BASE}/contracts", params={"page": page, "limit": limit})
    r.raise_for_status()
    return r.json()

# ─── 2. 계약서 생성 ───
def create_contract(payload: dict):
    r = requests.post(
        f"{BASE}/contracts",
        json=payload,
        headers={"Content-Type": "application/json"},
    )
    r.raise_for_status()
    return r.json()

# ─── 3. 상태 업데이트 (PATCH) ───
def update_status(contract_id: str, status: str):
    r = requests.patch(
        f"{BASE}/contracts/{contract_id}",
        json={"status": status},
        headers={"Content-Type": "application/json"},
    )
    r.raise_for_status()
    return r.json()

# ─── 4. 만료 임박 조회 → 알림 ───
def alert_expiring(days=30):
    data = get_contracts(limit=200)["data"]
    today = date.today()
    threshold = today + timedelta(days=days)

    expiring = []
    for c in data:
        if not c.get("end_date") or c.get("status") in ("만료", "해지"):
            continue
        end = date.fromisoformat(c["end_date"])
        if today <= end <= threshold:
            expiring.append(c)

    for c in expiring:
        end = date.fromisoformat(c["end_date"])
        remaining = (end - today).days
        print(f"⚠️  [{c['contract_number']}] {c['title']} — D-{remaining} ({c['end_date']})")
    return expiring

# ─── 실행 ───
if __name__ == "__main__":
    result = get_contracts()
    print(f"전체 계약 {result['total']}건")

    # 만료 임박 알림
    alert_expiring(days=30)

cURL 예제

터미널에서 바로 사용 가능한 cURL 명령어입니다.

cURL
# ─── 전체 계약서 조회 ───
curl "https://contract.sinwoodnc.com/tables/contracts?limit=50"

# ─── 특정 계약서 조회 ───
curl "https://contract.sinwoodnc.com/tables/contracts/uuid-xxxx-xxxx"

# ─── 새 계약서 생성 ───
curl -X POST "https://contract.sinwoodnc.com/tables/contracts" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "cURL로 생성한 계약",
    "contract_number": "CT-2025-010",
    "contract_type": "DH25010-30",
    "status": "초안",
    "client_name": "테스트 클라이언트",
    "amount": 10000000,
    "priority": "보통"
  }'

# ─── 상태 변경 (PATCH) ───
curl -X PATCH "https://contract.sinwoodnc.com/tables/contracts/uuid-xxxx-xxxx" \
  -H "Content-Type: application/json" \
  -d '{"status": "서명완료", "signed_date": "2025-04-10"}'

# ─── 계약서 삭제 ───
curl -X DELETE "https://contract.sinwoodnc.com/tables/contracts/uuid-xxxx-xxxx"

# ─── 첨부파일 목록 조회 ───
curl "https://contract.sinwoodnc.com/tables/contract_files?limit=100"
팁: cURL 응답을 보기 좋게 출력하려면 명령어 끝에 | python3 -m json.tool 또는 | jq를 추가하세요.