감사 로그 조회
curl --request GET \
--url https://sajuapi.dev/v1/reports/audit \
--header 'X-API-Key: <api-key>'import requests
url = "https://sajuapi.dev/v1/reports/audit"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://sajuapi.dev/v1/reports/audit', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://sajuapi.dev/v1/reports/audit",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://sajuapi.dev/v1/reports/audit"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://sajuapi.dev/v1/reports/audit")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://sajuapi.dev/v1/reports/audit")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body감사 로그 조회
GET
/
v1
/
reports
/
audit
감사 로그 조회
curl --request GET \
--url https://sajuapi.dev/v1/reports/audit \
--header 'X-API-Key: <api-key>'import requests
url = "https://sajuapi.dev/v1/reports/audit"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://sajuapi.dev/v1/reports/audit', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://sajuapi.dev/v1/reports/audit",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://sajuapi.dev/v1/reports/audit"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://sajuapi.dev/v1/reports/audit")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://sajuapi.dev/v1/reports/audit")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_bodyv1 Enterprise API (Coming Soon)이 엔드포인트는 Enterprise 버전에서 제공될 예정입니다.
현재는 v0 API를 사용하세요.
감사 로그는 개인정보보호법 및 기업 컴플라이언스 준수를 위해 제공됩니다. 모든 복호화 요청, 삭제 작업이 자동으로 기록됩니다.
Query 파라미터
string
특정 액션으로 필터링합니다.
profile.unmasked, fortune.unmasked, profile.deleted, fortune.deleted 등입니다.string
리소스 타입으로 필터링합니다.
profile, fortune, webhook 중 하나입니다.string
특정 리소스 ID로 필터링합니다.
string
특정 IP 주소로 필터링합니다.
string
조회 시작 날짜입니다. ISO 8601 형식입니다.
string
조회 종료 날짜입니다. ISO 8601 형식입니다.
integer
default:"50"
한 페이지에 반환할 로그 수입니다. 최대 1000입니다.
string
다음 페이지를 조회하기 위한 커서입니다.
Response
성공
감사 로그 조회에 성공하면 AuditLog 배열이 반환됩니다.실패
| 상태 코드 | 에러 타입 | 설명 |
|---|---|---|
| 401 | authentication_error | API 키가 유효하지 않음 |
| 403 | forbidden | 감사 로그 조회 권한이 없음 |
| 429 | rate_limited | 요청 한도 초과 |
요청 예시
# 전체 감사 로그 조회
curl -X GET "https://api.sajuapi.dev/v1/reports/audit?limit=100" \
-H "X-API-Key: bs_live_xxx"
# 복호화 요청만 조회
curl -X GET "https://api.sajuapi.dev/v1/reports/audit?action=profile.unmasked" \
-H "X-API-Key: bs_live_xxx"
# 특정 프로필 관련 로그 조회
curl -X GET "https://api.sajuapi.dev/v1/reports/audit?resource_id=prf_abc123def456" \
-H "X-API-Key: bs_live_xxx"
// 전체 감사 로그 조회
const response = await fetch(
'https://api.sajuapi.dev/v1/reports/audit?limit=100',
{
headers: {
'X-API-Key': 'bs_live_xxx'
}
}
);
const { data, has_more, next_cursor } = await response.json();
// 복호화 요청만 조회
const unmaskedLogs = await fetch(
'https://api.sajuapi.dev/v1/reports/audit?action=profile.unmasked',
{
headers: { 'X-API-Key': 'bs_live_xxx' }
}
).then(r => r.json());
import requests
# 전체 감사 로그 조회
response = requests.get(
'https://api.sajuapi.dev/v1/reports/audit',
headers={'X-API-Key': 'bs_live_xxx'},
params={'limit': 100}
)
result = response.json()
logs = result['data']
# 복호화 요청만 조회
unmasked_response = requests.get(
'https://api.sajuapi.dev/v1/reports/audit',
headers={'X-API-Key': 'bs_live_xxx'},
params={'action': 'profile.unmasked'}
)
응답 예시
{
"data": [
{
"id": 12345,
"action": "profile.unmasked",
"resource_type": "profile",
"resource_id": "prf_abc123def456",
"actor_ip": "203.0.113.42",
"actor_api_key": "bs_live_xxx...",
"request_id": "req_xyz789",
"metadata": {
"user_agent": "Mozilla/5.0 ...",
"endpoint": "GET /v1/profiles/prf_abc123def456/unmasked"
},
"created_at": "2025-01-16T09:30:00Z"
},
{
"id": 12344,
"action": "fortune.unmasked",
"resource_type": "fortune",
"resource_id": "ftn_xyz789abc123",
"actor_ip": "203.0.113.42",
"actor_api_key": "bs_live_xxx...",
"request_id": "req_abc456",
"metadata": {
"profile_id": "prf_abc123def456"
},
"created_at": "2025-01-16T09:25:00Z"
},
{
"id": 12343,
"action": "profile.deleted",
"resource_type": "profile",
"resource_id": "prf_def456ghi789",
"actor_ip": "198.51.100.23",
"actor_api_key": "bs_live_yyy...",
"request_id": "req_def789",
"metadata": {
"fortunes_deleted": 23,
"cached_calculations_deleted": 5
},
"created_at": "2025-01-16T09:15:00Z"
}
],
"has_more": true,
"next_cursor": "eyJpZCI6MTIzNDJ9",
"total_count": 1523
}
AuditLog 객체
| 필드 | 타입 | 설명 |
|---|---|---|
id | integer | 로그 ID입니다. |
action | string | 액션 유형입니다. |
resource_type | string | 리소스 타입입니다. |
resource_id | string | 리소스 ID입니다. |
actor_ip | string | 요청자 IP 주소입니다. |
actor_api_key | string | 사용된 API 키 (앞 20자)입니다. |
request_id | string | 요청 ID입니다. |
metadata | object | 추가 메타데이터입니다. |
created_at | string | 로그 생성 시간입니다. |
액션 유형
| 액션 | 설명 |
|---|---|
profile.created | 프로필 생성 |
profile.updated | 프로필 수정 |
profile.deleted | 프로필 삭제 |
profile.unmasked | 프로필 복호화 조회 |
fortune.created | 운세 생성 |
fortune.deleted | 운세 삭제 |
fortune.unmasked | 운세 복호화 조회 |
webhook.created | 웹훅 생성 |
webhook.deleted | 웹훅 삭제 |
보안 모니터링
비정상적인 복호화 요청 패턴을 모니터링하여 보안 이슈를 조기에 발견하세요.// 특정 IP에서의 복호화 요청 빈도 확인
async function checkUnmaskedRequestFrequency(ip, hours = 24) {
const startDate = new Date();
startDate.setHours(startDate.getHours() - hours);
const response = await fetch(
`/v1/reports/audit?action=profile.unmasked&actor_ip=${ip}&start_date=${startDate.toISOString()}`,
{ headers: { 'X-API-Key': 'bs_live_xxx' } }
);
const { total_count } = await response.json();
if (total_count > 100) {
console.warn(`경고: IP ${ip}에서 ${hours}시간 동안 ${total_count}건의 복호화 요청`);
}
return total_count;
}
감사 로그는 90일간 보관됩니다. 장기 보관이 필요한 경우 주기적으로 데이터를 백업하세요.
⌘I