Sending resource logs
Prepare the destination
Cloud: select or create a resource in your workspace. Obtain its workspace/resource UUIDs and a credential with logs:write and access to that resource. Workspace API and Cloud Logs entitlements must be active. Check current availability in the portal rather than relying on a fixed plan name.
Desktop: open the resource's Logs → Send logs here, enable the localhost listener (default port 8765), create a sender token, and copy the supplied path. Synced resources use canonical portal IDs; local-only resources use local IDs.
Set ALSHIVAL_BASE_URL, ALSHIVAL_WORKSPACE_ID, ALSHIVAL_RESOURCE_ID, and ALSHIVAL_TOKEN in your application's environment. Choose https://api.alshival.ai for cloud or http://127.0.0.1:8765 for desktop. Do not append /v1 to this base URL.
JSON request
curl --fail-with-body -X POST \
"$ALSHIVAL_BASE_URL/v1/workspaces/$ALSHIVAL_WORKSPACE_ID/resources/$ALSHIVAL_RESOURCE_ID/logs" \
-H "Authorization: Bearer $ALSHIVAL_TOKEN" \
-H 'Content-Type: application/json' \
--data '{"events":[{"severity":"INFO","message":"Worker started","source":"worker","attributes":{"component":"jobs"}}]}'
A successful JSON ingestion response includes accepted and request_id. For this example, confirm accepted is 1. To see the event, open the resource's Logs panel or use a separate read credential with log queries.
Python (standard library)
import json
import os
from urllib.request import Request, urlopen
base = os.environ['ALSHIVAL_BASE_URL'].rstrip('/')
workspace = os.environ['ALSHIVAL_WORKSPACE_ID']
resource = os.environ['ALSHIVAL_RESOURCE_ID']
url = f'{base}/v1/workspaces/{workspace}/resources/{resource}/logs'
body = {'events': [{'severity': 'INFO', 'message': 'Worker started',
'source': 'python-worker', 'attributes': {'component': 'jobs'}}]}
request = Request(url, data=json.dumps(body).encode(), method='POST', headers={
'Authorization': 'Bearer ' + os.environ['ALSHIVAL_TOKEN'],
'Content-Type': 'application/json',
})
with urlopen(request, timeout=15) as response:
print(json.load(response))
Node.js (built-in fetch)
const { ALSHIVAL_BASE_URL: base, ALSHIVAL_WORKSPACE_ID: workspace,
ALSHIVAL_RESOURCE_ID: resource, ALSHIVAL_TOKEN: token } = process.env;
const response = await fetch(
`${base.replace(/\/$/, '')}/v1/workspaces/${workspace}/resources/${resource}/logs`,
{ method: 'POST', signal: AbortSignal.timeout(15000), headers: {
Authorization: `Bearer ${token}`, 'Content-Type': 'application/json'
}, body: JSON.stringify({ events: [{ severity: 'INFO', message: 'Worker started',
source: 'node-worker', attributes: { component: 'jobs' } }] }) }
);
if (!response.ok) throw new Error(`Log ingestion failed: ${response.status}`);
console.log(await response.json());
These examples send one batch; they are not background logging SDKs. Production senders should queue unsent batches and apply the retry policy below.
Payload and limits
| Field | Meaning |
|---|---|
message |
Event message. |
severity |
TRACE, DEBUG, INFO, WARN, ERROR, or FATAL. Use these canonical uppercase values. |
timestamp |
Optional timezone-aware timestamp; omitted timestamps use ingestion time. |
source |
Optional application/component label. |
attributes |
Optional structured attributes object. |
trace_id |
Optional 32-character hexadecimal trace ID. |
span_id |
Optional 16-character hexadecimal span ID. |
Send an events array containing 1–1000 events, at most 4 MiB compressed and decompressed, with at most 64 KiB per normalized event. Do not supply event IDs in this contract. Gzip and chunked HTTP are supported on these workspace/resource ingestion routes. See OTLP for telemetry exporters.
Delivery and retries
Retry network failures and retryable 429/5xx responses with bounded exponential backoff and respect Retry-After when present. Correct invalid payloads and authorization failures before retrying. A timeout can occur after acceptance, so retries can create duplicate events; do not assume exactly-once delivery or caller-supplied deduplication IDs.
Retention and storage quotas can expire accepted logs. Avoid passwords, tokens, personal data, and unbounded payloads in log messages and attributes.