Authentication
Send an API key as a bearer token: Authorization: Bearer lio_key_…. Owners and admins create keys under Settings, Integrations on the Agency and Enterprise plans. A key acts as the member who created it, with that member's current role: if the role changes, so does what the key may do, and if the member leaves the workspace, the key stops working.
Keys are shown once and stored only as a hash. Give each system its own key, set an expiry, and revoke keys you no longer use. Never put a key in a web page or a browser extension: anyone who can read it can act as you.
Limits
- Agency: 60 requests a minute per key. Enterprise: 300.
- Credits apply exactly as in the workspace; failed audits cost nothing.
- Starting audits is limited to 60 an hour per workspace, through the API and the workspace together.
- If the plan loses API access (downgrade, ended subscription), requests with keys answer 402 until it is back.
Account
- GET/api/v1/me
- The user, workspace, role, plan state and what the role may do.
- GET/api/v1/usage
- Credit balance and this period's usage by action.Role: Owner, admin, analyst or sales.
- DELETE/api/v1/token
- Revokes the token used for this request.
Prospects
- GET/api/v1/lookup?url=
- What the workspace knows about a website: prospect, stage, owner, latest scores, audits, reports and matching research results.
- GET/api/v1/prospects
- The pipeline, best opportunity first, at most 200. Filters: stage, q, archived=1.
- POST/api/v1/prospects
- Creates a prospect. Body: name (required), website, phone, email, address, city, countryCode, category, categoryKey, dealValue.Role: Owner, admin or analyst.
- GET/api/v1/prospects/:id
- One prospect.
Audits and reports
- POST/api/v1/prospects/:id/audits
- Starts an audit. Body: {"kind": "light"} (1 credit) or {"kind": "full"} (10 credits). Answers 202; the audit runs in the background.Role: Analyst and up, or sales on their own prospects.
- GET/api/v1/prospects/:id/audits
- The prospect's audits, newest first.
- GET/api/v1/audits/:id
- Status, scores and findings of one audit. Poll until status is completed or failed.
- GET/api/v1/prospects/:id/reports
- The prospect's PDF reports.
- GET/api/v1/reports/:id/pdf
- The stored PDF, byte for byte as generated.
Responses and errors
Lists come as { "data": [...] }, single records as { "data": {...} }. Errors always have the form { "error": { "code", "message", "details" } }:
- 401 unauthorized: Missing, unknown, expired or revoked token; or the member left the workspace.
- 402 plan_limit, plan_inactive: The plan does not include the feature, or the subscription is not active.
- 402 insufficient_credits: Not enough credits for the action. Nothing was charged.
- 403 forbidden: The member's role does not allow the action.
- 404 not_found: No such record in this workspace. Records of other workspaces are never revealed.
- 409 conflict: For example, a prospect with this domain already exists.
- 422 validation_error: The input is invalid; details lists the fields.
- 429 rate_limited: Too many requests. Wait for the seconds in the Retry-After header.
Example
curl -H "Authorization: Bearer $LIO_KEY" \
"https://your-workspace-address/api/v1/lookup?url=https://example-dental.ae"
curl -X POST -H "Authorization: Bearer $LIO_KEY" -H "Content-Type: application/json" \
-d '{"kind":"light"}' \
"https://your-workspace-address/api/v1/prospects/PROSPECT_ID/audits"Webhooks
The workspace owner can register up to 10 https endpoints under Settings, Integrations (Agency and Enterprise plans). Each endpoint subscribes to events and receives a POST with a JSON body like { "id": "evt_…", "type": "audit.completed", "createdAt": "…", "workspaceId": "…", "data": {...} }.
- Events:
prospect.created,prospect.stage_changed,audit.completed,audit.failed,report.created,research.completed,comparison.completed. The Send test button sendsping. - Headers:
x-lio-event(the type),x-lio-delivery(the delivery id) andx-lio-signature. - Answer with any 2xx within 10 seconds. Anything else is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours, then marked failed; you can send a failed delivery again from the settings page. Retries keep the same event id, so use it to ignore duplicates.
- After 30 failures in a row the endpoint is switched off and the owner sees why. Redirects are not followed, and only public addresses are called.
To verify a request, take t and v1 from the signature header, compute an HMAC-SHA256 of t + "." + raw body with your signing secret, compare it to v1 in constant time, and reject timestamps older than five minutes. Use the raw request body, before any JSON parsing.
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(secret, header, rawBody) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const a = Buffer.from(expected), b = Buffer.from(parts.v1 ?? "");
return a.length === b.length && timingSafeEqual(a, b);
}Chrome extension
The Chrome extension uses the same endpoints with its own token from a device sign-in (/api/v1/device/code and /api/v1/device/token, modelled on OAuth 2.0 device authorization). It is available on every plan and limited to 120 requests a minute per browser.
