개발자 및 API
상태, 장애, 점검 정보를 제공하는 공개 REST API와 임베드 위젯, 배지, 피드, 캘린더를 제공합니다.
시작하기
모든 공개 엔드포인트는 JSON을 반환하는 HTTPS GET 요청이며 키가 필요 없습니다. 먼저 시스템 상태를 조회해 보세요.
curl -s "https://status.yucts.com/api/v2/status?locale=en"
# Conditional request — 304 Not Modified when nothing changed
curl -s -i "https://status.yucts.com/api/v2/services?locale=ja" -H 'If-None-Match: "<etag from the previous response>"'API 버전
- v1(/api/*)은 기존 앱과 위젯이 사용하는 안정적인 1.x API입니다. 사양이 고정되어 있어 필드가 삭제되거나 이름이 바뀌지 않습니다.
- v2(/api/v2/*)는 현재 API입니다. 일관된 응답 형식, 오류 코드, 요청 ID, ETag, 커서 기반 페이지네이션, 7개 언어를 지원합니다.
지원 중단 정책
호환되지 않는 변경은 새 버전 경로로만 제공합니다. 지원 중단 예정 버전은 최소 12개월 동안 유지되며, 제거 전에 Deprecation과 Sunset 헤더로 미리 알립니다.
인증
이 페이지의 엔드포인트는 모두 공개 읽기 전용이며 인증이 필요 없습니다. 관리용 엔드포인트는 이 문서에 포함되지 않으며, 로그인한 관리자가 관리 화면의 “API 문서”에서 볼 수 있습니다. 인증 정보를 URL에 넣지 마세요.
엔드포인트
API를 제공하는 코드에서 생성한 전체 레퍼런스를 OpenAPI 3.1 문서로 제공합니다. /openapi.json
오류
v2 오류는 모두 같은 형식입니다. 로직에는 message 대신 code를 사용하고, 문제를 보고할 때는 요청 ID를 함께 알려 주세요.
{
"error": {
"code": "not_found",
"message": "Incident not found.",
"status": 404,
"request_id": "c0ffee00-…"
}
}요청 제한
엣지 캐시에 적중하지 않은 요청은 클라이언트 IP별로 제한됩니다(기본 분당 600회). 제한을 넘으면 HTTP 429와 Retry-After 헤더를 받습니다.
캐시
공개 응답은 30초(v1은 60초) 동안 캐시할 수 있으며 ETag가 포함됩니다. If-None-Match로 다시 보내면 변경이 없을 때 304 Not Modified를 받습니다. 언어 감지 같은 개인화 응답은 캐시하지 않습니다.
언어
locale=zh-TW, zh-HK, zh-CN, en, ja, ko, vi를 지정할 수 있습니다. 번역이 없으면 일본어로 대체되며, 각 텍스트의 *_locale 필드에 실제로 제공된 언어가 표시됩니다.
웹훅
관리자는 장애와 점검 이벤트를 HTTPS 엔드포인트(일반 JSON, Discord, Slack)로 보낼 수 있습니다. 모든 요청에는 서명이 포함됩니다.
X-Yucts-Signature: t=1767225600,v1=5f2c…
X-Yucts-Event: incident.created
X-Yucts-Delivery: 42웹훅 시크릿으로 "<t>.<원본 본문>"의 HMAC-SHA256을 계산하고 v1과 상수 시간으로 비교하세요. 5분보다 오래된 타임스탬프는 거부하고 X-Yucts-Delivery로 중복을 제거하세요.
import { createHmac, timingSafeEqual } from 'node:crypto';
// header = X-Yucts-Signature, rawBody = the exact request body string
export function verifyYuctsSignature(rawBody, header, secret) {
const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header ?? '');
if (!m) return false;
const [, t, sig] = m;
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // replay window
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
return timingSafeEqual(Buffer.from(sig, 'hex'), expected);
}코드 예제
JavaScript
const res = await fetch('https://status.yucts.com/api/v2/status?locale=en');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { data } = await res.json();
console.log(data.status); // "operational" | "degraded" | "partial_outage" | ...
console.log(data.active_incidents); // ongoing incidents with their latest updateTypeScript
type ServiceStatus = 'operational' | 'degraded' | 'down' | 'maintenance' | 'unknown';
interface ServiceSummary {
id: number;
name: string;
status: ServiceStatus;
uptime: { h24: number | null; d30: number | null; d90: number | null }; // null = no data
}
async function services(locale = 'ja'): Promise<ServiceSummary[]> {
const res = await fetch(`https://status.yucts.com/api/v2/services?locale=${locale}`);
if (!res.ok) {
const { error } = await res.json(); // { code, message, status, request_id }
throw new Error(`${error.code} (${error.request_id})`);
}
return (await res.json()).data;
}Swift / SwiftUI
struct Envelope<T: Decodable>: Decodable { let data: T }
struct SystemStatus: Decodable {
let status: String
let counts: Counts
struct Counts: Decodable { let total, operational, degraded, down, maintenance, unknown: Int }
}
func fetchStatus(locale: String = "zh-TW") async throws -> SystemStatus {
var url = URLComponents(string: "https://status.yucts.com/api/v2/status")!
url.queryItems = [URLQueryItem(name: "locale", value: locale)]
let (data, response) = try await URLSession.shared.data(from: url.url!)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw URLError(.badServerResponse) }
return try JSONDecoder().decode(Envelope<SystemStatus>.self, from: data).data
}PHP (WordPress)
<?php
// WordPress: cache the status for 60 seconds with a transient.
function yucts_status() {
$cached = get_transient('yucts_status');
if ($cached !== false) return $cached;
$res = wp_remote_get('https://status.yucts.com/api/v2/status?locale=en', ['timeout' => 5]);
if (is_wp_error($res) || wp_remote_retrieve_response_code($res) !== 200) return null;
$body = json_decode(wp_remote_retrieve_body($res), true);
set_transient('yucts_status', $body['data'], 60);
return $body['data'];
}Swift/SwiftUI, JavaScript/TypeScript, WordPress, XenForo 단계별 가이드는 저장소 문서에 있습니다.
임베드 위젯
XenForo, WordPress, 일반 HTML 등 어떤 페이지에서도 실시간 상태를 보여 줄 수 있습니다. 1.x 위젯은 그대로 동작하며, v2 위젯은 새 디자인, 페이지 스타일과의 분리, 7개 언어를 지원합니다.
<!-- 2.0 widget: same attributes, Shadow DOM isolation, 7 languages -->
<div class="yucts-status" data-view="overview" data-locale="zh-TW"></div>
<script src="https://status.yucts.com/embed/v2.js" defer></script><!-- 1.x widget (unchanged) -->
<div id="yucts-status"></div>
<script src="https://status.yucts.com/embed.js" defer></script>변경 내역
- 2.0 — API v2, OpenAPI 3.1, 모든 콘텐츠의 베트남어 지원, 임베드 v2, 배지, 피드, iCalendar, 내보내기, OpenMetrics(각각 기능 스위치로 제어).
- 1.x — /api/overview, /api/status, /api/incidents, /api/slo, /api/maintenance, /api/geo, embed.js.