livewells
Menu

API

Use the API to submit links and files from your own tools and to read finished cases. The API calls a session a run. An analyst drives the browser by hand, so a run waits for an analyst to start it. A file is read at once, with no analyst needed.

Templates for Microsoft Sentinel and Tines make runs with this API. See Integrations.

API keys

An administrator makes keys in Settings. See Administering livewells.

  • A key looks like lw_live_ followed by 43 characters.
  • Send the key in a header: Authorization: Bearer lw_live_....
  • A key with the access level "Read only" can read runs and cannot make them.
  • A key with the access level "Queue links only" can queue runs (POST /v1/runs) and can call no other route.
  • A key can be limited to IP ranges.
  • A missing, expired or revoked key returns 401.
  • A key that nobody has used for 90 days returns 401.
  • Each key can make 60 calls a minute. Over the limit, you get 429 and a Retry-After header.
  • A key reaches the runs of its own organisation only.

Start limit

Each organisation can start 10 browser sessions and file checks in any 15 minutes. Each person can start 5 browser sessions in any 15 minutes. A start counts when livewells accepts it. Ending a session does not give a start back. Over a limit, you get 429 with the error start-rate-limited, the field scope (tenant or user, the limit that was reached), the field retryAfter and a Retry-After header in seconds. Send the request again after that time.

Download limit

Each organisation can export 10 case ZIPs a minute and make 120 video reads a minute. Over a limit, you get 429 with the error rate-limited and a Retry-After header of 60 seconds.

Pause after heavy use

If your organisation uses more than 150 minutes of browser time in one hour, livewells pauses new browser sessions and file checks for it. You get 503 with the error tenant-paused and a Retry-After header in seconds. Sessions that run continue, and you can still read cases. Write to support@livewells.io to have the pause lifted.

Treat a key as a password. Keep it in a secret store and out of your repositories.

Limits

  • An API key is lw_live_ followed by 43 letters, digits, hyphens or underscores.
  • An API key name has 1 to 60 characters, starts with a letter or a digit, and holds letters, digits, spaces, dots, hyphens or underscores.
  • A run id, submission id or item id is a lower-case version 4 UUID.
  • An organisation id is one lower-case letter or digit followed by 1 to 39 lower-case letters, digits or hyphens. The id ops is reserved.
  • A link is an absolute http or https link of up to 2048 characters with no spaces, no control characters, no user name and no password.
  • A verdict is malicious, suspicious, benign or possible-cloak, and pending clears it.
  • A run artifact name is session.mp4, report.json, report.csv, netlog.json, meta.json, egress.json, egress.csv or enrichment.json.
  • A file case serves report.json, report.csv and the page pictures page-01.png to page-20.png.
  • An Idempotency-Key has 1 to 64 printable ASCII characters with no space.
  • An archive password has up to 256 bytes.
  • A key has the access level read, runs, samples or queue. The default is read.
  • A key with the access level read, runs or samples is valid for 30 to 365 days. The default is 365 days.
  • A key with the access level queue is valid for 1 to 72 hours. The default is 24 hours.
  • A request with expiresInDays for the access level queue, or with expiresInHours for another access level, returns the error expiresInHours-queue-only.
  • A key can list up to 20 IP ranges. A key with no IP ranges accepts calls from any address.
  • An IP range is an address with an optional prefix length, in up to 64 characters. The prefix length is 0 to 32 for a dotted address and 0 to 128 for an address with colons.
  • A list of IP ranges that does not follow this rule returns the error bad-cidrs.
  • An organisation can make 30 keys in an hour. Over that, you get 429 with the error rate-limited and a Retry-After header of 3600 seconds.

Routes

Base URL: https://app.livewells.io

POST /v1/runs

Send a JSON body with the field url. The field takes a link, a defanged link or text that holds a link. The answer 201 holds id, state, link, createdAt and expiresAt.

  • Send link to your analyst. They open it, sign in and start the browser.
  • A run counts against your monthly limit when you make it. Over the limit, you get 429.
  • A run nobody starts expires after 24 hours, or at the start of the next UTC month when that comes first. livewells gives the submission back to your monthly count.
  • A run can be started twice. The second start is possible only when livewells could not start the browser the first time. A third start returns 409 with the error start-attempts-used. Create a new run.
  • A run stays started when the person ends the session while the browser is starting. Create a new run to try again.

Idempotency-Key

Add the header Idempotency-Key to POST /v1/runs when your tool may send a request again, for example after a timeout. The value is 1 to 64 printable ASCII characters with no space.

  • The first request with a key makes the run. A later request with the same key and the same body returns the same status and body, with the header Idempotent-Replayed: true. It makes no second run, counts nothing and writes no second audit entry.
  • livewells keeps the answer for 24 hours. After that, the same key makes a new run.
  • The same key with a different body returns 422 with the error idempotency-key-reuse.
  • A key that is not valid returns 400 with the error idempotency-key-invalid.
  • A second request that arrives while the first one is still running returns 409 with the error idempotency-key-in-progress. Send it again after a moment.
  • A first request that returns an error stores nothing. You can send the same key again.
  • A key belongs to one API key. Another API key, or another organisation, can use the same value for its own request.

Queued runs

A run that a person queued from the page opens only for that person and for an administrator. A run that an API key queued opens for every member of the organisation.

GET /v1/runs/{id}

Returns the run: id, state, createdAt, expiresAt, url, and when they exist startedAt, endedAt, endReason, evidence, verdict, report, enrichment and artifacts.

A run is asynchronous. Poll this call until state is ended or expired, and wait at least the Retry-After value after any 429.

The field state is one of awaiting_analyst, running, ended, expired and deleted. The field evidence is null when the case saved all its evidence. Otherwise it holds saved as false, a failure code and the missing files. The fields url, report and enrichment are defanged.

Hunt queries: huntSnippets

When the verdict of an ended run is malicious, the answer holds huntSnippets: ready-made searches for seven SIEM platforms. A run with another verdict, or with no verdict set, has no huntSnippets field.

The searches use the live domains, IP addresses and file hashes of the case. They hold no link. The field is not defanged, so a query can go straight into your tool. Handle it like live data about an attacker. A value that is not a valid domain, IP address or file hash is left out and counted in counts.invalid.

  • liveValues is always true.
  • note is the usage note: "These are broad wildcard searches across all your data. Adapt them to your own indexes, tables and time ranges before you run them. Your SIEM can charge for the data a search reads, and you are responsible for those costs."
  • platforms has one entry for each of sentinel, crowdstrike, exabeam, rapid7, sumo, splunk and alienvault.
  • Each entry holds id, name, language, match, searches, note, queries, included and leftOut.
  • queries lists the queries to run. A long list of values needs more than one query.
  • searches says in plain words what the query searches. match is contains or exact.
  • Each platform holds up to five queries, and the field has a size limit. included counts the values in the queries. leftOut counts the values that did not fit.
  • counts holds the number of domains, IP addresses and file hashes that follow the form of their type, and invalid for the values left out.
  • valueList holds the same values, one on each line, for a tool that takes a plain list. valueListOmitted is true and valueList is empty when the case holds too many values for the list.

An administrator can switch huntSnippets off for the organisation in Settings. Then no run and no webhook holds the field, and the field leaves the webhook events that livewells has stored. The setting is on by default. The audit log lists each change.

GET /v1/runs/{id}/artifacts/{name}

The name is one of report.json, report.csv, session.mp4, meta.json and enrichment.json. JSON is defanged before it is sent. One key can make 1500 reads in a clock hour. A read is a report or page file, a run or case view that returns a report, or a case export; over that, you get 429 with the error rate-limited and a Retry-After header.

A request for session.mp4 can carry a Range header. The answer is 206 with a Content-Range header, and it holds at most 8 MiB. When you ask for more, the answer holds the first 8 MiB of the range, and you ask again from the next byte.

The file enrichment.json

The field version is 2. The file holds sessionId, generatedAt, status, message, counts, signals, indicators, errors, enabledProviders, providers and dataAsOf. The field status is one of ok, partial, disabled, error, no-report and report-too-large. The field enabledProviders lists the sources that ran for the case.

The object reputation holds state, checked and notChecked. The field state is one of ok, partial, not-configured, case-cap, budget-used, provider-budget-used, org-day-limit, org-hour-limit, platform-pressure and unavailable. The value case-cap means the case reached its limit of checks. The values budget-used and provider-budget-used mean the monthly limit is used. The values org-day-limit and org-hour-limit mean your organisation reached its limit of checks for the day or for the hour. The value platform-pressure means the service was busy, so the case checked the link host, the final host and the first hosts of the redirect path.

Each entry in indicators has kind and value. An entry for an IP address or a domain can hold result: score from 0 to 100, detections, enginesCount, engines (up to 12 names) and moreEngines. An IP address result also holds countryCode, asn, org and flags. A domain result also holds created, ageDays and checks. A link entry holds the result of its domain and viaHost as true. An entry that livewells did not check holds notChecked with the reason. The field notChecked is one of case-cap, budget-used, provider-budget-used, org-day-limit, org-hour-limit, platform-pressure, same-site-limit and no-result. Each entry in errors has provider, code and message. The field code is one of case_cap, budget_used, provider_budget_used, no_result, same_site_limit, org_day_limit, org_hour_limit, platform_pressure and report_too_large. The field dataAsOf is an empty object.

Each signal has indicator, code, severity and source. The source is reputation. The codes are reputation_hit, flag_tor, flag_vpn, flag_proxy, flag_hosting, flag_relay, flag_free_hosting, flag_url_shortener, flag_dynamic_dns, flag_typosquatting, flag_homoglyph and flag_recently_registered.

GET /v1/runs/{id}/export

Returns the case ZIP of an ended run, as described in the cases guide. HEAD answers with the same status and headers and no body.

GET /v1/usage

Returns the plan, the month, the submissions used, the monthly submissions, the top-up, the limit and the concurrency.

POST /v1/submissions

Send a file in a multipart/form-data body. The request needs a Content-Length header.

  • Put the archive_password fields before the file field. A file takes 0 to 3 passwords.
  • The file is 25 MiB at most. A larger file returns 413 with the error file-too-large.
  • A part header has 4096 bytes at most and a field name has 64 bytes at most. A larger one returns 400 with the error bad-multipart.
  • A file name has 255 bytes at most. A longer name returns 400 with the error bad-name.
  • An empty file returns 400 with the error empty-file.
  • The answer 201 holds id, state, message and link. The state is queued.
  • A file counts against your monthly limit when you send it. Over the limit, you get 429.
  • Your organisation can send 10 files a minute. Over that rate, you get 429 and a Retry-After header.
  • Your organisation can keep 20 GiB of stored files. The total holds uploaded files, files a browser case captured, pages, reports and evidence. When a file would pass that, you get 413 with the error storage-full. A new browser case needs room for its evidence: 512 MiB for the recording and 16 MiB for each of the six other files. When the free space is smaller, starting the case also returns 413 with the error storage-full. Delete older cases to make room, or write to support for more storage.

POST /v1/submissions/{id}/password

Send a JSON body with the field passwords, a list of 1 to 3 passwords of 1 to 256 bytes each. The answer 202 holds id and state. This route takes passwords for cases the same key created. A file that a person uploaded in the app, or that another key created, returns 403 with the error not-your-submission; the person sends the password of an app upload on the case page. A case takes 5 password tries. After that, the answer is 429 with the error too-many-tries. A case that is not waiting for a password returns 409 with the error not-waiting.

GET /v1/runs/{id} for a file

The answer holds kind with the value submission, state, createdAt, startedAt, endedAt, endReason, summary, items, report and artifacts. The field state is one of queued, running, needs-password, done, failed and deleted. The report is defanged.

GET /v1/submissions/{id}/items/{item}/artifacts/{name}

The name is report.json, report.csv or the name of a page picture that the report lists. JSON is defanged before it is sent.

Sample downloads

A key with the access level "Download samples only" can call these routes and no other route. A key with another access level gets 403 with the error insufficient-scope.

  • GET /v1/runs/{id}/samples lists the stored files of a case by id, SHA-256, size and type.
  • GET /v1/runs/{id}/samples.zip returns one password-protected ZIP of all stored files.
  • GET /v1/runs/{id}/samples/{item}/sample.zip returns one stored file in a password-protected ZIP.

The ZIP password is infected. One ZIP holds up to 20 files and 100 MiB. Your organisation can download 10 ZIPs a minute. One key can download 60 ZIPs in a clock hour; over that, you get 429 with the error rate-limited and a Retry-After header. When an administrator switches sample downloads off, the routes return 403 with the error samples-off.

Webhooks

A webhook tells your system when a run ends. An administrator adds endpoints in Settings.

  • livewells sends run.ended when a session ends.
  • livewells sends run.verdict when a verdict is set or cleared on an ended run. The data holds runId, source, url (defanged), verdict and setAt (Unix milliseconds).
  • livewells sends run.enriched when enrichment exists for an ended session.
  • The payload is defanged, except the field huntSnippets.
  • When the verdict of the run is malicious at the time livewells makes the event, run.ended also holds huntSnippets. It is the field that GET /v1/runs/{id} returns, with live values and the usage note.
  • A run with another verdict, or an organisation that switched the field off, has no huntSnippets in its events.
  • POST /api/webhooks answers 409 with the code endpoint-exists and the field existingId when the organisation has an endpoint with that address. livewells creates no endpoint and writes no audit entry.
  • livewells compares addresses after it lowercases the host, drops the port 443 and removes the fragment.

Headers

  • Livewells-Event holds the event type.
  • Livewells-Delivery holds the delivery id. Use it to drop duplicates.
  • Livewells-Signature holds t=<unix seconds>,v1=<hex>.
  • Livewells-Signature-V2 holds t=<unix seconds>,v2=<hex>.

v1 is the hex HMAC-SHA256 of the text <t>.<raw body>, keyed with your endpoint secret. Check the signature against the raw body and reject an old timestamp.

v2 is the hex HMAC-SHA256 of the text <t>.<event type>.<delivery id>.<raw body>, keyed with your endpoint secret. It covers the values of Livewells-Event and Livewells-Delivery, so use v2 when your system reads either value from a header. During a rotation v2 also holds two values, new secret first.

During a secret rotation the header holds two v1 values: t=<unix seconds>,v1=<hex with the new secret>,v1=<hex with the old secret>. Accept the request when any v1 value matches the secret you hold.

Rotate the signing secret

An administrator rotates the secret of an endpoint without a gap in deliveries.

  1. Call POST /api/webhooks/{id}/rotate. The optional field overlapHours is a whole number from 1 to 168. The default is 24.
  2. The answer holds the new secret once, and previousValidUntil (milliseconds since 1970). The request needs a sign-in from the last 15 minutes.
  3. Until previousValidUntil, livewells signs each delivery with both secrets. Put the new secret on your receiver in that time.
  4. After previousValidUntil, livewells signs with the new secret only. The daily job then deletes the previous secret from storage.

A second rotation inside an overlap ends the oldest secret at once.

Addresses livewells accepts

  • The address uses https, port 443 or 8443, and a host name. It has no credentials and no IP address.
  • The host name resolves only to public addresses.
  • The host name is not a livewells.io name. livewells does not send webhooks to its own hosts. An address like that answers 400 with the code own-host-not-allowed.
  • Before each delivery, livewells looks up the host name and checks it again right before it sends. If the answer changed, livewells sends nothing, disables the endpoint and records blocked:dns-changed on the delivery. If the lookup itself fails, livewells sends nothing, keeps the endpoint on and retries the delivery on the normal schedule.
  • livewells does not follow redirects. A 3xx answer counts as a failed attempt.

Delivery and retries

  • Any 2xx answer counts as delivered.
  • When a delivery fails, livewells retries it for about a day, so the same event can arrive more than once.
  • Make your handler safe to repeat.