# LitigationWatch for AI agents

Everything an AI agent needs to watch Australian companies for litigation on someone's behalf. It's plain HTTPS and JSON on this website: no SDK, plug-in or MCP server.

## How it works

1. **Get access.** Ask the account owner to approve you (OAuth device flow). If they have no account yet, approving creates one.
2. **Check the account.** `GET /api/v1/account` returns the plan, usage and `next_steps`.
3. **Make sure a plan is active.** If not, create a payment link and give it to the owner. _The owner always pays; agents never pay or change what is charged._
4. **Add companies** by full legal name or ACN. Look names up in our directory of 15,340 companies first.
5. **Get alerts:** poll `GET /api/v1/alerts?since=<last id>`, or create an alert rule that POSTs matching alerts to your URL. The owner keeps getting alert emails either way.

Base URL: `https://litigationwatch.dev`. All API responses are JSON. Every page on this site is also available as Markdown: add `.md` to the path (`/pricing.md`, `/index.md`) or send `Accept: text/markdown`. Machine-readable: [/llms.txt](https://litigationwatch.dev/llms.txt), [/openapi.json](https://litigationwatch.dev/openapi.json), [OAuth metadata](https://litigationwatch.dev/.well-known/oauth-authorization-server), [resource metadata](https://litigationwatch.dev/.well-known/oauth-protected-resource), [API catalog](https://litigationwatch.dev/.well-known/api-catalog).

## 1. Get access (device flow)

Standard OAuth 2.0 device authorization grant (RFC 8628). No client registration: choose a short `client_id` for your agent and a `client_name` the owner will recognise. Add `login_hint` (the owner's email) and we email them a link to approve; otherwise show them `verification_uri_complete`.

```
curl -s https://litigationwatch.dev/oauth/device_authorization \
  -d client_id=my-agent \
  -d client_name="My Agent" \
  -d login_hint=owner@example.com
```

```
{
  "device_code": "kV3...",
  "user_code": "BCDF-GHJK",
  "verification_uri": "https://litigationwatch.dev/activate",
  "verification_uri_complete": "https://litigationwatch.dev/activate?code=BCDF-GHJK",
  "expires_in": 900,
  "interval": 5,
  "email_sent": true,
  "instructions": "We emailed owner@example.com a link to approve this request. ..."
}
```

The owner signs in, checks the code matches and approves. Poll every `interval` seconds until you get tokens:

```
curl -s https://litigationwatch.dev/oauth/token \
  -d grant_type=urn:ietf:params:oauth:grant-type:device_code \
  -d device_code=kV3... \
  -d client_id=my-agent
```

While waiting you get `{"error": "authorization_pending"}` (keep polling), `slow_down` (add 5 seconds to your interval), `access_denied` or `expired_token` (stop). On approval:

```
{
  "access_token": "lwat_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "lwrt_...",
  "scope": "account:read companies:read companies:write ..."
}
```

Send `Authorization: Bearer <access_token>` on API calls. Access tokens last an hour. Refresh them before or after they expire; each refresh token works once and returns a new one. Reusing an old refresh token revokes your access, so always store the newest.

```
curl -s https://litigationwatch.dev/oauth/token \
  -d grant_type=refresh_token \
  -d refresh_token=lwrt_...
```

**Optional parameters:** `scope` (space-separated; omit it to ask for everything below). **API keys:** owners can also create a key at [/app/agents](https://litigationwatch.dev/app/agents) and give it to an agent; send it the same way. It doesn't expire until they remove it.

| Scope | Lets the agent |
| --- | --- |
| `account:read` | See your account email, plan and usage |
| `companies:read` | See your watchlist |
| `companies:write` | Add, edit and remove companies on your watchlist |
| `alerts:read` | Read your litigation alerts |
| `alerts:write` | Mark alerts as read or unread |
| `rules:read` | See alert rules (where alerts are sent) |
| `rules:write` | Create, change and delete alert rules that send your alerts to web addresses |
| `billing:read` | See your plan and billing status |
| `billing:write` | Create payment links for you to open (you always confirm and pay yourself) |

## 2. Check the account

```
curl -s https://litigationwatch.dev/api/v1/account \
  -H "Authorization: Bearer lwat_..."
```

Returns `account`, `plan` (`plan`, `monitoring_active`, `company_limit`, `renews_on`, `ends_on`, `payment_overdue`), `usage`, alert counts and `next_steps`: plain sentences telling you what to do next.

## 3. Make sure a plan is active

Plans: Standard, US$9 a month for 20 companies; Pro, US$29 a month for 100. Larger watchlists: [Corporate](https://litigationwatch.dev/contact-sales). Create a Stripe Checkout link for the owner (it expires in 24 hours). With `"email_owner": true` we also email it to them.

```
curl -s https://litigationwatch.dev/api/v1/billing/checkout \
  -H "Authorization: Bearer lwat_..." \
  -H "Content-Type: application/json" \
  -d '{"plan": "standard", "email_owner": true}'
```

Then poll `GET /api/v1/account` until `plan.monitoring_active` is `true`. Plan changes and cancellations are confirmed by the owner in the app; `GET /api/v1/billing` gives you the links to send them.

## 4. Add companies

Courts record parties by their full legal name, so get it exactly. Search our directory (no token needed):

```
curl -s "https://litigationwatch.dev/api/v1/directory?q=woolworths"
```

Then add companies: by `acn` alone if it's in the directory, otherwise by `legal_name` (with `acn` or `abn` if you know it).

```
curl -s https://litigationwatch.dev/api/v1/companies \
  -H "Authorization: Bearer lwat_..." \
  -H "Content-Type: application/json" \
  -d '{"companies": [
        {"acn": "000014675"},
        {"legal_name": "Example Holdings Pty Ltd", "note": "Parent company"}
      ]}'
```

```
{
  "added": 2,
  "results": [
    { "index": 0, "status": "added", "legal_name": "WOOLWORTHS GROUP LIMITED", "company": { "id": "ent_...", ... } },
    { "index": 1, "status": "added", "legal_name": "Example Holdings Pty Ltd", "company": { ... } }
  ],
  "companies_count": 2,
  "company_limit": 20
}
```

Each result has a `status`: `added`, `duplicate`, `invalid`, `limit_reached` (with a `hint` to upgrade), or `needs_confirmation` when the name doesn't end like a company name (for example no "Pty Ltd"); resend with `"allow_unusual_names": true` if it's right. Litigation is often against a parent, subsidiary or trustee company, so add related entities too. New companies are included in the next daily check.

Each company's `status` is `monitored`, `paused` (over the plan limit after a downgrade) or `not_monitored` (no active plan).

## 5. Get alerts

### Poll

Alert ids only increase. Keep the highest id you've seen and ask for newer ones:

```
curl -s "https://litigationwatch.dev/api/v1/alerts?since=0&limit=100" \
  -H "Authorization: Bearer lwat_..."
```

Filters: `company_id`, `jurisdiction` (comma-separated: CTH, NSW, VIC, QLD, WA, SA, TAS, ACT, NT), `min_confidence` (low, medium, high, exact), `unread` (true/false). Paging: `since` (ascending) or `before` with `order=desc`; `limit` up to 200; follow `next` while `has_more` is true. Mark alerts read with `POST /api/v1/alerts/read {"ids": [1042]}` or `{"all": true}`.

```
{
  "id": 1042,
  "company_id": "ent_...",
  "company_name": "Example Holdings Pty Ltd",
  "court": "Federal Court of Australia",
  "jurisdiction": "CTH",
  "case_number": "NSD 1482/2026",
  "case_title": "Northbridge Capital Pty Ltd v Example Holdings Pty Ltd",
  "party_name": "EXAMPLE HOLDINGS PTY LTD",
  "party_role": "Respondent",
  "event_type": "Originating application filed",
  "event_date": "2026-11-04",
  "summary": "Claim for breach of a supply agreement.",
  "source_url": "https://www.fedcourt.gov.au/...",
  "confidence": "exact",
  "detected_at": "2026-11-05T21:14:03.000Z",
  "read": false,
  "app_url": "https://litigationwatch.dev/app/alerts/1042"
}
```

`confidence` says how sure the match is: `exact` (the legal name as recorded), `high`, `medium` (possible match) or `low`.

### Alert rules (push)

A rule sends matching alerts to your HTTPS endpoint, as they happen (checked every 5 minutes) or once a day as a digest. Only alerts found after the rule is created are sent. Up to 20 rules per account.

```
curl -s https://litigationwatch.dev/api/v1/alert-rules \
  -H "Authorization: Bearer lwat_..." \
  -H "Content-Type: application/json" \
  -d '{
        "name": "Federal and NSW, high confidence",
        "url": "https://agent.example.com/hooks/litigation",
        "filter": { "jurisdictions": ["CTH", "NSW"], "min_confidence": "high" },
        "schedule": "instant"
      }'
```

- `filter` (all optional, combined with AND): `company_ids` (from `GET /api/v1/companies`), `jurisdictions`, `min_confidence`, `keywords` (any of them, matched in the court, case title, parties, event or summary).
- `schedule`: `instant` (default) or `daily`, with `daily_hour_utc` 0–23 (default 22, which is morning on the Australian east coast).
- `url`: a public `https://` address on the default port. Redirects are not followed.

The response includes a `secret`, shown once. Test the endpoint with `POST /api/v1/alert-rules/{id}/test`. Each delivery looks like this:

```
POST https://agent.example.com/hooks/litigation
Content-Type: application/json
LitigationWatch-Signature: t=1794022334,v1=5f2c...
LitigationWatch-Rule: rule_...
LitigationWatch-Delivery: rule_...-881-2

{
  "type": "alerts",
  "rule": { "id": "rule_...", "name": "Federal and NSW, high confidence" },
  "sent_at": "2026-11-05T21:15:00.000Z",
  "alerts": [ { "id": 1042, "court": "Federal Court of Australia", ... } ]
}
```

Verify the signature: HMAC-SHA256 with the secret over `<t>.<raw body>`, compared with `v1`. Reject requests more than 5 minutes old.

```
import crypto from "node:crypto";

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
  const expected = crypto.createHmac("sha256", secret).update(parts.t + "." + rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
```

```
import hashlib, hmac, time

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    if abs(time.time() - int(parts["t"])) > 300:
        return False
    expected = hmac.new(secret.encode(), parts["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])
```

Answer with any 2xx status within 10 seconds. Failed deliveries are retried after 1 minute, 5 minutes, 30 minutes, 2, 6 and 12 hours. After 20 failed deliveries in a row the rule is paused; resume it with `POST /api/v1/alert-rules/{id} {"enabled": true}`. The owner can see and delete every rule.

## All endpoints

| Method | Path | Scope | Purpose |
| --- | --- | --- | --- |
| POST | `/oauth/device_authorization` | — | Ask the owner for access (device flow) |
| POST | `/oauth/token` | — | Poll for tokens; refresh tokens |
| POST | `/oauth/revoke` | — | Revoke a token or API key |
| GET | `/api/v1/account` | `account:read` | Plan, usage, alert counts and next steps |
| GET | `/api/v1/directory?q=` | — | Find a company's exact legal name and ACN |
| GET | `/api/v1/companies` | `companies:read` | List the watchlist |
| POST | `/api/v1/companies` | `companies:write` | Add up to 200 companies |
| GET | `/api/v1/companies/{id}` | `companies:read` | One company |
| POST | `/api/v1/companies/{id}` | `companies:write` | Change legal name, ACN/ABN or note |
| POST | `/api/v1/companies/{id}/delete` | `companies:write` | Remove from the watchlist |
| GET | `/api/v1/alerts` | `alerts:read` | Alerts, filtered and paged by id |
| GET | `/api/v1/alerts/{id}` | `alerts:read` | One alert |
| POST | `/api/v1/alerts/read` | `alerts:write` | Mark alerts read or unread |
| GET | `/api/v1/alert-rules` | `rules:read` | List alert rules |
| POST | `/api/v1/alert-rules` | `rules:write` | Create a rule (webhook) |
| GET | `/api/v1/alert-rules/{id}` | `rules:read` | A rule and its recent deliveries |
| POST | `/api/v1/alert-rules/{id}` | `rules:write` | Change a rule (any field, or enabled) |
| POST | `/api/v1/alert-rules/{id}/test` | `rules:write` | Send a sample alert to the URL now |
| POST | `/api/v1/alert-rules/{id}/rotate-secret` | `rules:write` | New signing secret |
| POST | `/api/v1/alert-rules/{id}/delete` | `rules:write` | Delete a rule |
| GET | `/api/v1/billing` | `billing:read` | Plan, prices and links for the owner |
| POST | `/api/v1/billing/checkout` | `billing:write` | Payment link for the owner to open |

Only GET and POST are used. The full schema is at [/openapi.json](https://litigationwatch.dev/openapi.json).

## Errors and limits

Errors use RFC 9457 problem details with a `hint` saying what to do next. OAuth endpoints use the standard `{"error", "error_description"}` form.

```
HTTP/1.1 402 Payment Required
Content-Type: application/problem+json

{
  "type": "https://litigationwatch.dev/agents#errors",
  "title": "plan_required",
  "status": 402,
  "detail": "This account has no active plan, so companies can't be added yet.",
  "hint": "Create a payment link with POST /api/v1/billing/checkout {\"plan\":\"standard\"} and give it to owner@example.com. Once they pay, retry."
}
```

Common `title` values: `invalid_token`, `token_expired` (refresh it), `insufficient_scope`, `plan_required`, `invalid_field` (with `field`), `not_found`, `rate_limited` (with `retry_after` and a `Retry-After` header). Limits: 120 API requests a minute per agent; 60 directory searches a minute per IP; 10 access requests per 10 minutes per IP.

## Acting for the owner

- Act only on the owner's instructions. They approve your access, see everything you set up at [/app/agents](https://litigationwatch.dev/app/agents), and can remove it at any time.
- Never pay or try to get around payment. Send the owner a link; they decide.
- Company pages in our directory don't list court cases and don't suggest a company is involved in any. Don't present them as evidence of litigation.
- We can only see what courts and tribunals publish. Suppressed or confidential matters aren't visible to anyone outside the court. Alerts aren't legal advice.

Questions: [contact support](https://litigationwatch.dev/contact).

---
Source: https://litigationwatch.dev/agents
For AI agents: https://litigationwatch.dev/llms.txt
