Skip to main content

1. Getting Started

This guide leads you through the steps to send your first call using the V5 Java API client.

Quick Start (60 Seconds)​

3 Requirements

ItemValueSource
Server Addresshttp://<server-ip>:80 (TLS: https://<server-ip>:443)REST API endpoint. This is the same address as the web console.
Account IDExample: apiUser account for API calls
TokenUUID formatIssued for the account via Console Security Management → API Authentication Token (/token/index) → Security Settings · Security Management

With these three values, you can make your first call using the single block below.

import java.util.List;
import plantpulse.api.v5.APIClient_V5;
import plantpulse.api.v5.dto.response.SiteResponseV5;

try (APIClient_V5 client = new APIClient_V5(
"HTTP", "100.68.69.41", 80, "api", "<issued-token>", false)) {
client.connect(); // 인증
System.out.println(client.system().ping().isPing()); // → true
for (SiteResponseV5 s : client.site().list()) // 사이트 목록
System.out.println(s.getSite_id() + " — " + s.getSite_name());
}

If this block outputs true and the site list, you are ready to go. Sections 1.1 to 1.7 below provide detailed explanations for each step.

1.1 Adding Dependencies​

Add the JAR file to your classpath.

# 빌드 (Gradle + Java 21)
cd plantpulse-api
./gradlew build

The artifact is created in target/.

FilePurpose
target/plantpulse-api.jarFor integration into existing projects along with dependencies

1.2 Creating the Client​

Pass options directly to the APIClient_V5 constructor.

import plantpulse.api.v5.APIClient_V5;

APIClient_V5 client = new APIClient_V5(
"HTTPS", // 프로토콜: "HTTP" 또는 "HTTPS"
"100.68.69.41", // host
443, // port (REST API: 80=HTTP, 443=HTTPS)
"api", // user_id
"19ba7a54-1132-49f2-967e-3c6f5cd989a0", // token (api_key 발급용)
false); // debug

client.connect(); // 인증 + HTTP 클라이언트 초기화

// ... API 호출 ...

client.close(); // 종료

Constructor Parameters​

ParameterTypeDescription
protocolString"HTTP" or "HTTPS"
hostStringServer host or IP
portintServer port
user_idStringAuthentication account ID
tokenStringIssued token (UUID format)
debugbooleanHTTP request/response debug logs

Authentication Flow: When calling connect(), an api_key is issued internally using user_id + token, and this key is automatically included in the header for all subsequent calls. The user does not need to handle this manually.

1.3 First API Call — Ping & Site List​

import java.util.List;
import plantpulse.api.v5.APIClient_V5;
import plantpulse.api.v5.dto.response.SiteResponseV5;
import plantpulse.api.v5.dto.response.SystemPingResponseV5;

public class FirstCall {
public static void main(String[] args) throws Exception {

try (APIClient_V5 client = new APIClient_V5(
"HTTP", "100.68.69.41", 80, "api",
"19ba7a54-1132-49f2-967e-3c6f5cd989a0", false)) {

client.connect();

// 1) 서버 헬스체크
SystemPingResponseV5 ping = client.system().ping();
System.out.println("Server alive: " + ping.isPing()
+ " (v" + ping.getVersion() + ")");

// 2) 사이트 목록 (typed DTO 리스트)
List<SiteResponseV5> sites = client.site().list();
for (SiteResponseV5 s : sites) {
System.out.println(s.getSite_id() + " — " + s.getSite_name());
}
}
}
}

Execution Result:

Server alive: true (v5.0)
SITE_000001 — 대전 1공장
SITE_000002 — 천안 2공장

1.4 try-with-resources Pattern​

APIClient_V5 implements AutoCloseable. The use of try-with-resources is recommended.

try (APIClient_V5 client = new APIClient_V5(proto, host, port, user, token, false)) {
client.connect();
// ... API 호출 ...
}
// close() 자동 호출 — HTTP 커넥션 정리

1.5 Overview of 13 Services​

Immediately after connect(), you can access all domains using the service methods below (lazy initialization).

client.system() // 헬스체크
client.site() // 사이트(공장)
client.customer() // 고객
client.employee() // 직원
client.product() // 제품
client.asset() // 자산 계층 (Area → Line → Equipment)
client.opc() // OPC 데이터 채널
client.tag() // 태그(데이터 포인트)
client.alarm() // 알람
client.order() // 작업지시
client.calendar() // 일정
client.path() // 자산 경로
client.flow() // Flow / Flow Node

1.6 V5 Method Signature Standard Pattern​

All domain services follow a consistent method pattern. The usage is identical; only the domain name differs.

PatternReturn TypeReturn Value on FailureDescription
create(req)*ResponseV5nullRegistration
update(id, req)*ResponseV5nullFull Update
delete(id)booleanfalseDeletion
get(id)*ResponseV5nullSingle Inquiry
list()List<*ResponseV5>Empty ListFull List
exists(id)booleanfalseExistence Check
count()long0Count

⚠️ Exceptions are not thrown. Communication errors or server ERROR responses are returned as the safe default values shown in the table above. Check the logs or BaseServiceV5.getErrorCode(env) for the cause of the error. For more details, refer to Response Format and Error Handling.

1.7 Debug Mode​

Setting the last argument of the constructor to true will print HTTP requests and responses to the console.

APIClient_V5 client = new APIClient_V5(
"HTTP", "100.68.69.41", 80, "api", TOKEN, true); // debug=true

Example Output:

[V5] GET https://server/api/v5/site headers={api_key=ab****cd}
[V5] response: 200 {"_status":"OK","data":[{"site_id":"SITE_000001",...}]}

1.8 When Authentication Fails — 503 is not "Wrong Key"​

connect() exchanges a token with POST /api/v5/auth for api_key. Success is not determined by HTTP 200 — it is a success only if meta.status is "OK" and data.api_key is not empty. The second condition exists because there are actual paths where token validation passes but key issuance fails (in which case the server returns 500).

curl -s -X POST "http://<server>/api/v5/auth" \
-H "Content-Type: application/json; charset=UTF-8" \
-d '{"token":"<issued-token>"}'
# 200: {"data":{"api_key":"..."},"meta":{"status":"OK"},"errors":null}
ResponseCodeMeaningWhat to fix
400E1001token is missing or emptyRequest body
422E1002Body is not JSONRequest body
401E1100Wrong tokenCredentials
401E1102Token expiredRecovered by re-issuance
401E1103Token revokedSame token cannot be recovered — obtain a new one
429E1101Accumulated failures from the same IPWait for Retry-After, then retry after fixing the value
500E5000Token is correct, but api_key issuance failedServer-side — not a credential issue
503E5500«No determination possible» due to inability to reach token storeServer-side — not a credential issue
Treating 503 as "Authentication Failure" leads to wasting eight days searching the wrong place

The server does not issue a 401 when it cannot reach the token store (PostgreSQL). 401 is a claim that «your credentials are wrong», and at that moment, the server does not know if the token is good or bad. The problem is that the caller has no way to distinguish between the two.

This actually happened — on 2026-09-07, nightly e2e reported «22/22 failed, E1100 Token authentication failed» for eight days, but the real reason in the platform logs was Connection to plantpulse-datalake:5432 refused. A PostgreSQL failure wore the face of a credential error for eight days. Now, the system traces the cause chain to the storage exception (SQLException · socket family) and responds with 503 E5500.

For the same reason, 503 is not counted as a failure. If it were, a DB failure would have locked out healthy callers with 429 — layering a lock on top of a failure.

The Java client returns this distinction exactly as an exception message. Determine where to fix based on the message.

MessageMeaning
V5 connect: /api/v5/auth call failed — …Request did not even reach the server (Address · Port · Firewall)
V5 connect: auth endpoint unavailable (http=503) — server reached no verdict about the tokenServer could not make a determination — do not suspect the token
V5 connect: authentication failed — …401/400 series — fix credentials or request
V5 connect: api_key issuance failed — response=…meta.status is OK, but key is empty — server-side
Authentication attempt locking is configurable

If authentication failures from the same IP accumulate to engine.api.v5.auth.bruteforce.ip_limit (default 30) times, it is locked with 429 for engine.api.v5.auth.bruteforce.window.minutes (default 15 minutes). This can be disabled via engine.api.v5.auth.bruteforce.enabled=false, but doing so leaves the system vulnerable to token brute-force attacks. Requests where the body is not JSON are not counted as credential attempts.

Next Steps​