# JevShield server reference clients

Download and keep the file alongside your server code:

- [JavaScript client](https://jevshield.com/sdk/jevshield.mjs) — Node.js 18+ or a server Fetch runtime.
- [Python client](https://jevshield.com/sdk/jevshield.py) — Python 3.9+, standard library only.
- [API documentation](https://jevshield.com/docs/api.md).

These are downloadable reference clients, not published npm or PyPI packages. No package install is required. Both use the fixed API origin `https://jevshield.com`. Keep your API key in a server environment variable; do not place these clients or your key in browser code, public repositories, logs, or analytics.

## JavaScript

Save `jevshield.mjs` locally, then use it in a server `.mjs` file:

```js
import { JevShieldClient, JevShieldError } from './jevshield.mjs';

const client = new JevShieldClient(process.env.JEVSHIELD_API_KEY, { timeoutMs: 10_000 });
try {
  const status = await client.status(); // GET: checks validity/allowance; no detection quota.
  if (!status.valid) throw new Error('Check your server API key.');
  const result = await client.check({ text: 'Can you quote this project?', form_id: 'contact' });
  console.log({ verdict: result.verdict, remaining: result.quota_remaining });
  // Your application decides and performs its own submission/delivery policy.
} catch (error) {
  if (error instanceof JevShieldError) console.error(error.status, error.code, error.message);
  else throw error;
}
```

## Python

Save `jevshield.py` beside your server script:

```python
import os
from jevshield import JevShieldClient, JevShieldError

client = JevShieldClient(os.environ["JEVSHIELD_API_KEY"], timeout=10)
try:
    status = client.status()  # GET: does not consume detection quota.
    result = client.check({"text": "Can you quote this project?", "form_id": "contact"})
    print({"verdict": result["verdict"], "remaining": result.get("quota_remaining")})
    # Apply your own submission/delivery policy; the client takes no such action.
except JevShieldError as error:
    print(error.status, error.code, error.error)
```

## Methods and errors

- `check(payload)` sends one `POST /api/v1/check` and returns the JSON object.
- `status()` sends one `GET /api/v1/check`. It does not consume detection quota.
- `report(payload)` sends one `POST /api/v1/report`. After your integration actually applies its policy, pass the returned `check_id` and private `report_token`, plus the action and reason you applied. Reporting uses no detection quota. See the API documentation for valid values and token expiry.

The clients do not send form messages, block submissions, hold a review inbox, or follow booking links. `review` means uncertain, and `block` is a recommendation that your integration must interpret. An `allow` result does not prove delivery or sender identity.

HTTP errors raise `JevShieldError` with their HTTP `status`; network/timeout errors use `status=0`. Errors expose `code` and a sanitized `error` message (`message` also exists in JavaScript). Redirects are refused. Timeouts default to 10 seconds; JavaScript accepts 1–120,000 milliseconds and Python accepts greater than 0 through 120 seconds. Python bounds caller waiting with a daemon transport thread; after a timeout that request may still finish.

**No automatic retries are performed.** A detection request may consume quota before an error or timeout reaches your server. Do not assume failed checks are free or blindly retry them. Public-demo limits and subscription quotas are separate from these authenticated clients.

For repository tests only, JavaScript accepts an explicit `options.fetch` transport and Python's `_open(request, timeout)` helper can be overridden with a local fixture transport. These seams never configure a different production API origin. The accompanying tests use synthetic keys and local fixtures, not live accounts.
