계약관리시스템 API 가이드
계약관리시스템에 저장된 계약서 및 첨부파일 데이터를 외부 시스템, 스크립트, 앱에서 직접 호출하는 방법을 안내합니다.
개요
계약관리시스템은 RESTful Table API를 사용하여 데이터를 저장합니다.
아래의 엔드포인트를 통해 계약서 데이터를 생성(POST) · 조회(GET) · 수정(PUT/PATCH) · 삭제(DELETE)할 수 있습니다.
별도의 인증 없이 호출 가능하며, 모든 요청/응답은 JSON 형식입니다.
Base URL
모든 API 요청의 기준 URL입니다. 이 사이트가 배포된 도메인 뒤에 경로를 붙여서 사용합니다.
https://contract.sinwoodnc.com 기준으로 모든 예제가 작성되어 있습니다.
📦 사용 가능한 테이블
| 테이블명 | 설명 | 엔드포인트 예시 |
|---|---|---|
contracts | 계약서 데이터 | tables/contracts |
contract_files | 첨부파일 데이터 (Base64) | tables/contract_files |
데이터 스키마
계약서 데이터의 전체 필드 목록입니다. * 표시가 없는 필드는 선택 입력입니다.
계약서 목록 조회
Query Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
page optional | number | 1 | 페이지 번호 |
limit optional | number | 20 | 페이지당 건수 (최대 200) |
search optional | string | - | 키워드 검색 |
sort optional | string | created_at | 정렬 기준 필드명 |
{
"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"
}
단건 조회
| Path 파라미터 | 타입 | 설명 |
|---|---|---|
id required | string (UUID) | 계약서 고유 ID |
{
"id": "uuid-xxxx-xxxx",
"title": "웹사이트 리뉴얼 개발 용역계약",
"contract_number": "CT-2025-001",
"status": "진행중",
"amount": 25000000,
"end_date": "2025-07-15"
}
계약서 생성
Content-Type: application/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": "클라우드 서버 마이그레이션 및 구축"
}
계약서 수정
PATCH를 사용하세요.// PATCH tables/contracts/{id}
{
"status": "서명완료",
"signed_date": "2025-04-05"
}
계약서 삭제
첨부파일 API
특정 계약서의 파일만 필터링하려면 응답 데이터에서 contract_id로 클라이언트 측 필터링합니다.
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);
file_data 필드에 담아 전송합니다. (DataURL 형식: data:mime;base64,...){
"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": "홍길동"
}
파일 레코드의 id를 Path에 포함하여 삭제합니다. HTTP 204를 반환합니다.
대금청구현황 API
계약별 대금 청구 및 입금 현황을 관리하는 billing_records 테이블 API입니다. 청구 예정·완료·입금·연체 상태를 추적할 수 있습니다.
billing_records 스키마
| 필드명 | 타입 | 설명 | 예시 |
|---|---|---|---|
id | text | UUID (자동생성) | uuid-... |
contract_id | text | 계약서 ID (contracts.id 참조) | uuid-... |
contract_number | text | 계약번호 (표시용 캐시) | CT-2025-018 |
billing_seq | number | 청구 회차 | 1, 2, 3 |
billing_type | text | 청구 유형 | 선급금 / 기성금 / 중도금 / 잔금 / 기타 |
billing_amount | number | 청구 금액 (원) | 79200000 |
billing_date | text | 청구일 (YYYY-MM-DD) | 2025-06-05 |
due_date | text | 입금 기한 (YYYY-MM-DD) | 2025-06-30 |
paid_date | text | 실제 입금일 (YYYY-MM-DD) | 2025-06-28 |
paid_amount | number | 입금 금액 (원, 부분입금 포함) | 59400000 |
status | text | 청구 상태 | scheduled / billed / partial / paid / overdue |
tax_invoice_no | text | 세금계산서 번호 | 20250605-001 |
note | text | 메모 | 자유 입력 |
scheduled 청구예정 |
billed 청구완료(미입금) |
partial 부분입금 |
paid 입금완료 |
overdue 연체(기한초과)
쿼리 파라미터
특정 계약의 청구 내역만 조회
// 전체 청구 목록 (페이지네이션)
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);
// 청구 등록
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);
// 입금 확인 처리 (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);
const billingId = 'BILLING_RECORD_ID';
await fetch(
`https://contract.sinwoodnc.com/tables/billing_records/${billingId}`,
{ method: 'DELETE' }
);
// 성공 시 HTTP 204 No Content 반환
활용 예제 - 계약별 입금 현황 집계
// 계약서 + 청구 데이터를 조합하여 입금 현황 집계
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 스키마
| 필드명 | 타입 | 설명 | 예시 |
|---|---|---|---|
id | text | UUID (자동생성) | uuid-... |
tx_date | text | 거래일 (YYYY-MM-DD) *필수 | 2025-04-01 |
tx_type | text | 구분 *필수 | income (입금) / expense (출금) |
category | text | 카테고리 *필수 | 대금입금 / 부가세납부 / 정기결제 / 소모품 / 인건비 / 공과금 / 임대료 / 기타 |
title | text | 거래 내용/적요 *필수 | 부가세 2기 확정신고 납부 |
amount | number | 금액 (원) *필수 | 3850000 |
department | text | 사업부서 | 감리부 / 철도부 / 도로부 / 안전진단 / 공통 |
counterpart | text | 거래처 / 상대방 | 국세청, ㈜테크솔루션 |
account | text | 계좌 / 은행 | 국민은행 123-456-789 |
contract_id | text | 연관 계약서 ID (선택, contracts.id 참조) | uuid-... |
tax_invoice_no | text | 세금계산서 번호 | 20250401-001 |
note | text | 비고 | 자유 입력 |
대금입금 계약 대금 수령 |
부가세납부 부가세 신고 납부 |
정기결제 구독·정기 서비스 |
소모품 사무용품 등 소모품 구입 |
인건비 급여·외주비 등 |
공과금 전기·수도·통신비 등 |
임대료 사무실 임대 |
기타 분류 불명
엔드포인트
tables/transactions?page=1&limit=100
거래 목록 조회
tables/transactions/{id}
거래 단건 조회
tables/transactions
거래 등록
tables/transactions/{id}
거래 수정
tables/transactions/{id}
거래 삭제
코드 예제
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 환경에서 사용할 수 있는 완전한 예제입니다.
// ─────────────────────────────────────────
// 계약관리시스템 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 라이브러리를 사용하는 예제입니다.
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 "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"
| python3 -m json.tool 또는 | jq를 추가하세요.