Multiple log files and ingestion API¶
How to submit several log files at once from the browser, and how to submit and track jobs from a script with an API token.
Each file is an independent analysis job. A case can group any number of jobs; it does not become a combined analysis job. Existing compatible submissions can reuse a job under the same ownership, filename, privacy, workflow and log-type rules as the browser.
Browser¶
Drop or select files on the homepage, up to the limit shown there (50 by default). Administrators can change Settings → Submissions → Maximum files per submission to 1–500; reload the upload page to pick up the change. This controls one browser selection, independently of the API's per-file requests, upload concurrency and rate quotas. LogsTotal previews two files at a time, then selects a compatible workflow for each. Change individual types/workflows, or apply a workflow to all compatible files. Unknown types need a compatible workflow or a manual type choice; oversized files remain visible with an error and can be removed.
The selection stays in a compact, scrollable list with aligned type/workflow controls.
Resolve any invalid files, then submit the whole selection with Analyze. Two files
transfer at a time; the next transfer starts while earlier requests are being confirmed,
up to four in-flight requests (or the configured server limit when lower). Analysis starts
as each file is accepted. Per-file bars show transfer progress; the overall bar counts
confirmed submissions. The page explicitly confirms when it is safe to close the tab,
without waiting for analysis. A failed file does not discard successful submissions.
Quota/capacity responses pause the queue and retry according to Retry-After: five
seconds for a busy upload slot, sixty seconds for the per-minute quota.
For selections above 50 jobs, status polling rotates through groups of 50 to keep the
request rate constant; all accepted jobs continue processing between polls.
Members and admins can choose no case, an existing visible case, or a new unshared case. Jobs link immediately, including reused jobs. Entity import remains an explicit case action after analysis. Sharing a case does not grant access to private jobs.
Keep the tab open until uploads finish. There is no persistent batch page: closing the tab loses its unsubmitted files, but accepted jobs remain in Jobs and their case. Stop uploads stops waiting transfers and aborts active browser requests; it does not cancel accepted analyses. A lost/aborted response may have reached the server. Signed-in uploads use a receipt to check that outcome before retrying. Anonymous users should check Jobs before manually retrying an uncertain upload.
Authentication and scopes¶
Create tokens at /admin/api-tokens. Tokens delegate their creator's access, including
job privacy and case visibility; they expire when revoked, expired, or their creator
loses access. Supply Authorization: Bearer <token>. A supplied invalid token is rejected,
even if a valid browser cookie is also present. Only the Bearer scheme counts as a token:
the Authorization: Basic … header a browser resends behind the proxy profile's basic auth is
the proxy's credential, and requests carrying it fall back to the browser session. The same
header is why a token client cannot get through that basic auth at all — see
Known limitations.
Ingestion uses the job:submit, job:read, case:write and case:read scopes; what each
one grants is in the token scopes table. Attaching an uploaded
job to a case needs case:write as well as job:submit.
Browser cookies are also accepted. Ordinary signed-in users can submit jobs; case
operations require member-or-above access. Anonymous browser submissions use
POST /upload. Anonymous status requests return public jobs only.
Endpoints¶
All versioned endpoints return JSON. The OpenAPI schema is served only with DEBUG=true,
at /api/docs and /api/openapi.json, so a production instance does not expose it.
Submit one file¶
POST /api/v1/jobs, multipart form data, with a Content-Length header. From any machine
that can reach the instance:
| Field | Meaning |
|---|---|
file |
Exactly one file, required |
workflow_id |
Required workflow ID from GET /api/v1/workflows |
log_type_override |
auto by default, or a supported log type |
is_private |
false by default |
force_resubmit |
false by default; true requests a fresh analysis |
case_id |
Optional existing case ID; requires case:write as well as job:submit |
curl --fail-with-body "$LOGSTOTAL_URL/api/v1/jobs" \
-H "Authorization: Bearer $LOGSTOTAL_TOKEN" \
-H 'Idempotency-Key: collection-2026-09-host-a-security' \
-F 'file=@Security.evtx' \
-F 'workflow_id=1' \
-F 'case_id=42' \
-F 'is_private=true'
A new job returns 202; reuse and idempotent replay return 200:
{"job_id":123,"status":"pending","reused":false,"job_url":"/jobs/123","case_id":42}
202 means accepted for background analysis, not completed. Files are not concatenated.
To submit a collection, loop over files with at most two requests in flight; assign
one idempotency key per logical file submission. Keep keys in the client if recovery
across process restarts is needed. Mixed formats can use different workflow IDs.
Retry receipts¶
Idempotency-Key is optional for authenticated clients and must be 1–128 printable
ASCII characters without spaces. The browser supplies it automatically when signed in.
Keys are scoped to the creator's user and operation (job or case).
Repeating the same file and options with the same key returns the same job, even with
force_resubmit=true. Changed content, filename or options with that key returns 409.
Concurrent retries create one job and enqueue it once. Receipts are committed with the
job and its case link, before queueing. A queue failure marks that job failed and leaves
the receipt available; an intentional new attempt needs a new key.
GET /api/v1/submissions/{key} resolves a lost response without retransmitting the file.
A 404 means no committed receipt yet, so the original request may still be running;
retrying with the same key safely resolves that race. A 410 means its job was deleted.
Receipt tombstones remain after job/case deletion to prevent replay from recreating
resources. Receipts are deleted with their owning account.
Workflows, statuses and cases¶
GET /api/v1/workflowsreturns{"workflows":[{"id":1,"name":"…","log_types":["evtx"],"is_default":true}]}.GET /api/v1/jobs?ids=123,124accepts at most 50 IDs and returnsjobswithjob_id,status,total_findings,error, andjob_url. As on the job page, a failed job'serrordetail is shown to administrators only; others get a generic message.unavailable_idscovers missing and inaccessible jobs identically. Poll about every three seconds while active, less often in background tabs, and stop atcompleted,partial,failed, orcancelled.POST /api/v1/casesaccepts{"name":"Investigation","is_shared":false}and returns201withcase_idandcase_url. An optional idempotency key makes creation safe to retry (200on replay). Create once and pass the returned ID to each upload.GET /intel/cases/list.jsondiscovers existing visible cases (case:read).
Errors use {"detail":"…"} (validation errors may contain a detail list). Handle
400/422 per file, 401/403 as authentication/permission errors, 409 as a key conflict,
411/413 as transfer format/size errors, 429 by waiting for Retry-After, and 507
as insufficient temporary storage. After an uncertain network/server failure, look up
the receipt before retrying. Other successfully accepted jobs remain unaffected.
Capacity¶
| Setting | Default | Applies to |
|---|---|---|
MAX_UPLOAD_SIZE_MB |
500 | Each file |
UPLOAD_RATE_LIMIT_PER_MINUTE |
30 | Anonymous submissions, per IP |
AUTHENTICATED_UPLOAD_RATE_LIMIT_PER_MINUTE |
60 | Authenticated submissions, per creator across cookies and tokens |
PREVIEW_RATE_LIMIT_PER_MINUTE |
120 | Preview requests, per IP |
UPLOAD_MAX_CONCURRENT |
4 | Active upload requests across web processes sharing Redis |
API_TOKEN_RATE_LIMIT_PER_MINUTE |
120 | API reads and case creation, the IOC feed, TAXII and the case read APIs, per token |
A zero rate disables that quota. The concurrency setting has a minimum effective value
of one. Upload admission uses Redis before multipart parsing; if coordination is
unavailable, uploads return 503. It reserves room for both parser and storage spools,
retains 1 GiB of free headroom, and releases reservations on completion or failure.
Reservations are renewed while uploading and expire after a dead process disappears.
Local disk and S3 use the same admission rules because both need local temporary files.
Upload concurrency does not change worker or tool concurrency.
The default HUEY_QUEUE_EXPIRY=1800 discards jobs not started within 30 minutes. Size
the workers and that setting for the longest wait a large batch can cause, with headroom,
and watch the queue on /admin/tasks. A faster uploader cannot make an overloaded worker
fleet process more logs. See Scaling.
Related: Admin UI reference · Configuration · Scaling · Known limitations