API

관계 생성

작성일 2026.04.23 | 수정일 2026.04.29

POST/crm-core/v1/relationships

두 레코드를 사전에 정의한 관계 유형(Relationship Type)으로 연결합니다. MEMBER 이상 권한이 필요하며, 요금제 relationships.max 한도가 적용됩니다. self-link(동일한 source/target 레코드 ID)는 차단됩니다.

Body Params

NameTypeRequiredDescription
relationshipTypeIdstring*사전 정의된 관계 유형 ID (CRMRT1...)
sourceEntityIdstring*소스 레코드가 속한 개체 ID (CRMET1...)
sourceRecordIdstring*출발점 레코드 ID (CRMRC1...)
targetEntityIdstring*대상 레코드가 속한 개체 ID (CRMET1...)
targetRecordIdstring*도착점 레코드 ID (CRMRC1...)
metadataobject관계별 부가 정보 (role, isPrimary, startDate, endDate, notes 등 자유 형식)

Response

NameTypeRequiredDescription
relationshipIdstring*생성된 관계 ID (CRMRL1...)
accountIdstring*계정 ID
workspaceIdstring | null워크스페이스 ID (워크스페이스 환경에서만)
relationshipTypeIdstring*관계 유형 ID
sourceEntityIdstring*소스 개체 ID (유형 정의 기준 정규화 결과)
sourceRecordIdstring*소스 레코드 ID
targetEntityIdstring*대상 개체 ID
targetRecordIdstring*대상 레코드 ID
metadataobject*관계 메타데이터 (제공하지 않으면 빈 객체)
dateCreateddate*생성 시각 (UTC)
dateUpdateddate*마지막 수정 시각 (UTC)

Structure

코드 예제

const crypto = require('crypto');

const apiKey = 'NCSXXXXXXXXXXXXX';
const apiSecret = 'YOUR_API_SECRET';
const dateTime = new Date().toISOString();
const salt = crypto.randomBytes(16).toString('hex');
const signature = crypto.createHmac('sha256', apiSecret).update(dateTime + salt).digest('hex');
const authHeader = HMAC-SHA256 apiKey=${apiKey}, date=${dateTime}, salt=${salt}, signature=${signature};

const response = await fetch('https://api.solapi.com/crm-core/v1/relationships', {
method: 'POST',
headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' },
body: JSON.stringify({
"relationshipTypeId": "CRMRT1260423091530123RTP11122233",
"sourceEntityId": "CRMET1260423081530123XYZ11122233",
"sourceRecordId": "CRMRC1260423081530123REC11122233",
"targetEntityId": "CRMET1260423081530124XYZ11122234",
"targetRecordId": "CRMRC1260423081530124REC11122234",
"metadata": {
"role": "primary",
"isPrimary": true
}
})
});
const data = await response.json();

import hmac, hashlib, secrets, requests
from datetime import datetime, timezone

api_key = 'NCSXXXXXXXXXXXXX'
api_secret = 'YOUR_API_SECRET'
date_time = datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
salt = secrets.token_hex(16)
signature = hmac.new(api_secret.encode(), (date_time + salt).encode(), hashlib.sha256).hexdigest()
auth_header = f'HMAC-SHA256 apiKey={api_key}, date={date_time}, salt={salt}, signature={signature}'

response = requests.post('https://api.solapi.com/crm-core/v1/relationships', headers={
'Authorization': auth_header,
'Content-Type': 'application/json'
}, json={
"relationshipTypeId": "CRMRT1260423091530123RTP11122233",
"sourceEntityId": "CRMET1260423081530123XYZ11122233",
"sourceRecordId": "CRMRC1260423081530123REC11122233",
"targetEntityId": "CRMET1260423081530124XYZ11122234",
"targetRecordId": "CRMRC1260423081530124REC11122234",
"metadata": {
"role": "primary",
"isPrimary": True
}
})
data = response.json()

API_KEY="NCSXXXXXXXXXXXXX"
API_SECRET="YOUR_API_SECRET"
DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
SALT=$(openssl rand -hex 16)
SIGNATURE=$(echo -n "${DATE}${SALT}" | openssl dgst -sha256 -hmac "${API_SECRET}" | awk '{print $NF}')
AUTH="HMAC-SHA256 apiKey=${API_KEY}, date=${DATE}, salt=${SALT}, signature=${SIGNATURE}"

curl -X POST 'https://api.solapi.com/crm-core/v1/relationships' \
-H "Authorization: ${AUTH}" \
-H "Content-Type: application/json" \
-d '{"relationshipTypeId": "CRMRT1260423091530123RTP11122233", "sourceEntityId": "CRMET1260423081530123XYZ11122233", "sourceRecordId": "CRMRC1260423081530123REC11122233", "targetEntityId": "CRMET1260423081530124XYZ11122234", "targetRecordId": "CRMRC1260423081530124REC11122234", "metadata": {"role": "primary", "isPrimary": true}}'

lightbulb

요청의 `sourceEntityId`/`targetEntityId`가 관계 유형 정의와 반대 방향이어도 자동으로 정규화하여 저장합니다(콘솔 호환). 두 개체 모두 정의와 일치하지 않으면 `400 Bad Request`로 거절됩니다.

lightbulb

동일한 `(sourceRecordId, targetRecordId, relationshipTypeId)` 조합은 고유 인덱스로 차단되며 중복 요청 시 `409 Conflict`("동일한 관계가 이미 존재합니다.")가 반환됩니다. 요금제 한도를 초과하면 `403 Forbidden`이 반환됩니다.

lightbulb

**401 응답**: `{ "errorCode": "Unauthorized", "errorMessage": "권한이 없습니다." }`