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 (by default a deploy creates app.service, unless you specify 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 include the .service suffix. By default, the whole system journal is read |
lines |
query | number | no | 50 | Number of lines, 1–500 |
since |
query | string | no | — | Timestamp in 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 |
Schema violation: 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 |
| 402 | ACCOUNT_FROZEN |
The Vibecode balance is frozen. The request is rejected before the operation and a sleeping server is not woken — top up the balance and repeat |
| 403 | SERVER_WAKE_BLOCKED |
The server is asleep and waking is blocked by the platform — an ended trial or an administrative block. A journal read does not bring such a server up, and a retry will not help until the block is lifted. For details on the block, see Wake a server |
| 403 | WRONG_KEY |
The server exists but is not yours. Operations on the application's content — deploy, exec, upload and log reads — are open on any one of three grounds: the server's managing key, a key whose application is bound to that server (Application.serverId), or membership in that server's development team (both roles, Developer and Administrator). Access tokens and the icon upload require the managing key. Managing the machine itself is also open to the Administrator role on the team. The response carries a hint with a two-step recovery. See Server access recovery |
| 404 | NOT_FOUND |
There is no server with this id, or it was deleted. A server that exists but is not yours answers 403 WRONG_KEY — the code tells "no such server" apart from "no rights to it" |
| 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. This code does not describe a sleeping server — the platform wakes that one itself. Start a stopped server with /start or restore the tunnel with /repair, then 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) |
| 409 | EXEC_BUSY |
Galaxy applications only (kind: "GALAXY_APP"): the host's shared exec channel is busy — the log is read through that same channel. The response carries a Retry-After header and the retryable: true / retryAfter fields, retry at that interval. The application owner cannot release the shared channel, so contact support if the channel stays busy |
| 409 | WAKE_IN_PROGRESS |
Another request is already waking this sleeping server. Wait for it to finish and repeat |
| 422 | VM_MISSING |
The server record has no virtual machine on the provider side, so there is nothing to wake. Delete the server and create a new one |
| 502 | WAKE_FAILED |
While the sleeping server was being woken it entered an unexpected state. Repeat the request |
| 502 | PROVIDER_ERROR |
The cloud provider returned an error while starting the sleeping server's virtual machine. The tunnel is not involved here, so /repair will not help |
| 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 |
| 503 | WAKE_TIMEOUT |
The sleeping server did not come up within the allotted 6.5 minutes — either the machine never started or the tunnel never connected. The server returns to sleeping, so a repeat request is safe. See "Known specifics" |
| 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
- A sleeping standalone virtual machine is woken by a journal read. If the server (
kind: "STANDALONE") is insleepingstatus, the platform starts the wake and waits for it within the same request — up to 6.5 minutes — and only then reads the journal. The client's request timeout must be longer than that window. If the server does not come up,503 WAKE_TIMEOUTarrives and the server returns tosleeping, so a repeat request is safe. On a Galaxy app, a read behaves differently — see the "Galaxy apps" section below. - Line format — exactly as
journalctlemits it. Each line already contains the time, hostname, 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.
- Reading does not wake the machine. While the galaxy host is asleep or unreachable, the response arrives with status
200, an emptydata.logsarray and two diagnostic fields —data.hintanddata.recovery. Bring the app up withPOST /v1/infra/servers/:id/wakeand retry the request: the wake starts the same container, so lines written before the sleep stay in the log. A cold galaxy boots in minutes, its budget arrives in therecovery.coldBootBudgetSecondsfield, and readiness is checked by pollingGET /v1/infra/servers/:idon thereachability.effectiveStatusfield. Every response field and the order of actions — Galaxy app sleep and wake. 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.- The
grepfilter works the same way — a plain substring, filtered platform-side.