For AI agents: markdown of this page — /docs-content-en/infra/deploy/logs.md documentation index — /llms.txt
Service logs
GET /v1/infra/servers/:id/logs
Returns the logs of a BLACKHOLE server via the journalctl system utility. Without the service parameter, the whole system journal is read (all services and system-wide messages). To read the logs of a specific systemd unit, pass service (the deploy creates app.service by default, unless you specified another name). Supports filtering by line count, time window, and substring.
Parameters
| Parameter | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
id |
path | string (UUID) | yes | — | BLACKHOLE server ID |
service |
query | string | no | — | The systemd unit name: app, crm-dashboard, etc. Do not specify the .service extension. By default the whole system journal is read |
lines |
query | number | no | 50 | Number of lines, 1–500 |
since |
query | string | no | — | Timestamp in the journalctl format: "1 hour ago", "2026-04-22 10:00:00", "10 minutes ago" |
grep |
query | string | no | — | Substring to filter by, up to 200 characters |
Examples
curl — personal key
# Application logs for the last hour with a filter on "error"
curl -H "X-Api-Key: YOUR_API_KEY" \
"https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/logs?service=app&lines=100&since=1%20hour%20ago&grep=error"
# The whole system journal, last 10 lines (for debugging cloud-init)
curl -H "X-Api-Key: YOUR_API_KEY" \
"https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/logs?lines=10"
curl — OAuth application
curl -H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN" \
"https://vibecode.bitrix24.com/v1/infra/servers/SERVER_ID/logs?service=app&lines=50"
JavaScript — personal key
const params = new URLSearchParams({
service: 'app',
lines: '100',
since: '30 minutes ago',
})
const res = await fetch(
`https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/logs?${params}`,
{ headers: { 'X-Api-Key': 'YOUR_API_KEY' } }
)
const { data } = await res.json()
data.logs.forEach(line => console.log(line))
JavaScript — OAuth application
const res = await fetch(
`https://vibecode.bitrix24.com/v1/infra/servers/${serverId}/logs?service=app&lines=50`,
{
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
},
}
)
Response fields
| Field | Type | Description |
|---|---|---|
success |
boolean | Always true on success |
data.service |
string | null | Echo of the ?service= parameter, or null if it was not passed |
data.logs |
array<string> | The main field. Log lines in arrival order (newest at the end of the array) |
data.lines |
array<string> | Alias of data.logs (the same array). Kept for backward compatibility — in new code use data.logs |
data.requestedLines |
number | Echo of the ?lines= parameter (validated 1–500, default 50) |
data.returnedLines |
number | Length of data.logs |
data.since |
string | null | Echo of ?since=, or null |
data.grep |
string | null | Echo of ?grep=, or null |
data.hint |
string | Optional. Diagnostics when data.logs.length === 0 |
Response example
{
"success": true,
"data": {
"service": null,
"logs": [
"Apr 22 11:17:02 epd65hdv07p8g89c0c06 CRON[1406]: pam_unix(cron:session): session closed for user root",
"Apr 22 11:17:27 epd65hdv07p8g89c0c06 vibe-agent[1098]: 2026/04/22 11:17:27 [exec] id= timeout=10s workdir=\"\" command=\"uname -a\"",
"Apr 22 11:17:28 epd65hdv07p8g89c0c06 cloud-init[975]: Reading package lists..."
],
"lines": [
"Apr 22 11:17:02 epd65hdv07p8g89c0c06 CRON[1406]: pam_unix(cron:session): session closed for user root",
"Apr 22 11:17:27 epd65hdv07p8g89c0c06 vibe-agent[1098]: 2026/04/22 11:17:27 [exec] id= timeout=10s workdir=\"\" command=\"uname -a\"",
"Apr 22 11:17:28 epd65hdv07p8g89c0c06 cloud-init[975]: Reading package lists..."
],
"requestedLines": 50,
"returnedLines": 3,
"since": null,
"grep": null
}
}
Error response example
400 — server in OPEN mode:
{
"success": false,
"error": {
"code": "NOT_BLACKHOLE",
"message": "Deployment API only available for BLACKHOLE servers"
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR |
The schema is violated: lines outside 1–500, grep longer than 200 characters |
| 400 | NOT_BLACKHOLE |
Server in OPEN mode — the Deploy API is unavailable |
| 401 | MISSING_API_KEY |
The X-Api-Key header was not provided |
| 401 | INVALID_API_KEY |
Invalid or expired API key |
| 404 | NOT_FOUND |
The server does not exist, was deleted, or belongs to another API key |
| 409 | SERVER_NOT_READY |
The server is not ready for the operation: it is not running or the tunnel is not connected. The response carries a hint field with the reason and the next step. Wake the server or call /repair and repeat the request. The "listed as connected but the Gateway has no live tunnel" case on this route arrives as 502 TUNNEL_NOT_FOUND (see below) |
| 429 | RATE_LIMITED |
The limit of 10 operations per minute per server was exceeded |
| 502 | TUNNEL_NOT_FOUND / GATEWAY_UNREACHABLE: … |
No live tunnel to the server, or the Gateway is unreachable — call /repair and retry. GATEWAY_UNREACHABLE carries details after the colon, so match error.code by prefix, not by an exact comparison |
| 503 | GATEWAY_TIMEOUT: … |
The Gateway did not respond in time. The code also carries details after the colon — compare by prefix |
| 502 | LOGS_UNAVAILABLE |
The agent could not read the system journal: no such unit, an invalid filter, access denied, or journalctl returned an error |
Full list of common API errors — Errors.
Known specifics
- Line format — exactly as
journalctlemits it. Each line already contains the time, host name, process name, and its PID — no extra processing on the client is needed. Output the lines as is. - The
grepfilter is a plain substring, not a regular expression. For complex patterns, fetch more lines and filter on the client. - The tunnel agent's logs are visible without
service— under the namevibe-agent. Useful for diagnostics when the application does not respond through the Deploy API. Simply do not pass theserviceparameter — you will see the whole system journal, includingvibe-agent, cloud-init, and other system-wide messages. - The 500-line limit combined with the cap of 10 calls per minute means you can read at most 5000 lines per minute. For a larger volume of logs, make several calls with different
sincevalues, staying within the limit.
Galaxy apps
If the server is a galaxy app (kind=GALAXY_APP), the endpoint returns the stdout/stderr of the application container itself (docker logs), not the host system journal. The service parameter does not apply to galaxy apps.
- Read-only. Requesting logs does not wake a sleeping galaxy. If the galaxy host is asleep or unreachable, the response is an empty
data.logsarray plus a diagnosticdata.hintfield. Wake the galaxy (by any request to the app) and retry. since— a duration or RFC3339, not a relative text form. For galaxy appssinceaccepts only a duration in seconds, minutes, or hours (10m,2h,24h) or an RFC3339 timestamp (2026-06-24T10:00:00Z) — use24hfor a one-day window. The relativejournalctltext forms ("1 hour ago","10 minutes ago") are accepted only for Black Hole servers — for a galaxy app such a value returns400 VALIDATION_ERROR.grepworks the same way — a plain substring, filtered platform-side.
See also
- Full deploy — see which service name is created.
- Run a command —
journalctl -u app -fto stream logs in real time viaexec. - Tunnel metrics — agent activity and HTTP connections.