# Prophet HTTP API

[简体中文](API_zh.md)

The Node server and Cloudflare Worker expose the same JSON endpoints. Responses use `Cache-Control: no-store`. Browser requests must have the same origin; cross-site requests receive 403 and no cross-origin permission is granted. A merchant backend can call the service without an `Origin` header.

Personal IP checks, ecommerce IP quality investigations and website abuse investigations use the same observations and evidence engine. Results describe exits, environment differences and missing evidence; they do not provide a calibrated IP reputation or fraud score.

## Browser SDK

Import `detect`, `analyze` and `verify` from `https://prophet.offfold.com/sdk/prophet.js` inside the browser being measured. Importing starts no collection. Bind collection to a disclosed user action; [privacy information](/privacy.html) describes the homepage's scope and external destinations.

```js
const controller = new AbortController();
const detections = await detect({
  publicNetwork: true,
  stun: true,
  behavior: { durationMs: 8_000 },
  signal: controller.signal,
});
```

| `detect()` option | Default | Effect |
| --- | --- | --- |
| `network` | `true` | Read `/api/network` on the embedding website's own origin; disable if unavailable |
| `integrity` | `true` | Compare declarations in temporary iframe and Worker contexts |
| `diagnostics` | `true` | Run temporary rendering, storage, scheduling and document experiments after the other collectors |
| `publicNetwork` | `false` | Contact the fixed public HTTP observation services |
| `stun` | `false` | Replace local ICE gathering with the fixed STUN observation |
| `behavior` | `false` | Collect foreground event aggregates; accepts `{ durationMs }` |

`signal` cancels collection; `onDetection` streams observations. Diagnostics follow passive and behavior collection so temporary storage writes do not alter passive usage readings. No permission prompts are requested by these new modules.

Default collection registers 283 checks, including 31 integrity observations and 30 diagnostics. The example matches the homepage's full flow: up to 302 registered observations, or 317 when all 15 optional edge fields are supplied. These are registered collection ceilings, not guaranteed readable values. `verify()` defaults to public HTTP and a 15-second behavior sample, and returns `{ detections, analysis, evidenceGraph, observedAt }`; STUN remains opt-in. Override the same options to change or disable modules.

| Endpoint | Purpose | Model credentials |
| --- | --- | --- |
| `GET /api/network` | Observe the connection reaching this service and an allowlist of request headers | None |
| `POST /api/detections/analyze` | Run deterministic correlations and construct an evidence graph | None; no model call |
| `GET /api/agent/status` | Read BYOK mode and explicit operator configuration state | None |
| `POST /api/agent/analyze` | Run the bounded model/tool investigation | Caller-owned model API key |

## Network observations

`GET /api/network` returns `peer`, `httpVersion`, `transport`, `headers`, and `observedAt`; the Worker can also return an `edge` array when Cloudflare supplies metadata. Node trusts the socket address. The Worker attributes `CF-Connecting-IP` only at the edge runtime boundary. Forwarded address claims and credentials are excluded from the observation schema. These values describe the connection to this service, not independently established identity.

## Detection analysis

Send `Content-Type: application/json`. No `Authorization` or model setting is required.

```http
POST /api/detections/analyze
Content-Type: application/json

{"detections":[{"id":"device.platform","group":"device","label":"Platform","value":"Windows","source":"navigator.platform","status":"info"}],"observedAt":"2026-11-02T00:00:00.000Z"}
```

The body accepts `detections`, optional `previous` using the same detection schema, and an ISO 8601 `observedAt`. Each snapshot supports at most 600 unique signal IDs; current detections must be nonempty. A detection contains `id`, `group`, `label`, `value`, `source`, `status`, and optional `note` and `sensitive`. Status is `ok`, `warn`, `info`, `unsupported`, or `error`. Groups match the [shared schema](../src/probes/types.ts). The HTTP body is limited to 512 KiB; schema field and combined-report limits also apply.

The response contains:

```ts
{
  schemaVersion: 1,
  observedAt: string,        // supplied collection time
  serverObservedAt: string,  // service processing time
  receivedSignals: number,
  trust: 'client-observations',
  analysis: AnalysisResult,
  evidenceGraph: EvidenceGraph
}
```

`analysis` contains 22 deterministic correlation rules and a coverage summary. `evidenceGraph` contains real signal nodes, sparse support/conflict/context/missing relations, hypotheses, missing evidence, and truncation counts. It retains up to 500 nodes and 32 hypotheses. See [analysis types](../src/lib/analysis.ts) and [graph types](../src/lib/evidence-graph.ts). Missing or failed observations remain insufficient evidence. IPv4 and IPv6 observations are not automatically contradictory. The endpoint performs no model request and ignores model/key/URL fields in the body.

The graph also returns `priorities` with `environment`, `ip-quality` and `anti-abuse` investigation profiles. `basis` is `configured-investigation-priority`, `calibrated` is `false`, and `aggregation` is `maximum-per-evidence-family`. Source type and available coverage adjust configured priorities; repeated fields and derived checks within a family are not added together. These values order investigation, not merchant acceptance or risk scoring.

Client observations can be fabricated. A user clicking a verification button starts collection; this response is not proof of a human, identity, device integrity, or a passed verification. Merchant applications must make and enforce their own server-side decisions using the returned evidence and its limitations.

## BYOK Agent analysis

Prophet ships with no selected or recommended provider or model. Browser SDK `analyze()` requires the caller's explicit `apiKey`, `baseUrl` and `model`; it does not inherit HTTP operator settings. For example, replace the placeholders `https://your-provider.example/v1` and `your-model-id` with the caller's choices before use.

`GET /api/agent/status` returns `{"mode":"byok","requiresApiKey":true,"model":null,"providerConfigured":false}` when the operator has not configured an upstream or model. `model` is the explicit `AGENT_MODEL` value or `null`; `providerConfigured` indicates whether a nonempty operator `AGENT_BASE_URL` is present. Do not send a model key to this status endpoint.

For `POST /api/agent/analyze`, use the detection body above plus a nonempty `question` of at most 2,000 characters. Optional `history` contains at most six `{role: 'user' | 'assistant', content: string}` entries, each at most 2,000 characters. Send only conversation content already visible to the user.

```http
Authorization: Bearer <caller-model-key>
X-Prophet-Model: your-model-id
Content-Type: application/json
```

Each request must supply its caller's own printable ASCII model key, at most 4,096 characters. There is no shared server-key fallback. The model must be supplied through `X-Prophet-Model` unless the operator explicitly sets `AGENT_MODEL`; accepted names contain 1–200 ASCII letters, digits, or `. / : @ _ -`. The provider base is controlled only by an explicit operator `AGENT_BASE_URL`. It has no default, and client body fields or headers cannot choose the provider. Example operator placeholders are `AGENT_BASE_URL=https://your-provider.example/v1` and `AGENT_MODEL=your-model-id`; they are not a working or recommended service configuration. Model usage belongs to the caller's provider account.

Authentication is checked first: a missing caller key returns 401 even if no provider is configured. For an authenticated request, a missing provider base returns 503 before any provider request. Once a provider base is configured, absence of both a model header and operator model returns 400. Detection analysis remains available without any of these settings.

The result is an `AgentAnalysisResponse` with `runId`, `model`, `summary`, `findings`, `nextChecks`, actual tool `steps`, `durationMs`, and optional `usage`; see the [DTO](../src/agent/types.ts). Tool evidence is bounded; an inference is not an identity or maliciousness verdict.

The homepage waits for its complete collection flow before optionally running a configured Agent. Its public model fields start empty. The session test key is not a product preset or shared credential.

## Limits and errors

Both POST analysis routes share a rolling limit of four accepted analysis attempts per minute per source IP. A BYOK caller key has one in-flight Agent request per IP; different caller keys can run independently within the IP budget. Rate state is bounded to 512 IP entries and stored per Node process or Worker isolate. A 429 includes `Retry-After: 60`.

Errors use `{"error":"..."}`: 400 invalid schema or missing/invalid model; 401 missing, malformed, or rejected caller model key (Agent only); 403 foreign browser origin; 405 wrong method; 413 oversized request; 415 non-JSON content; 429 rate limit; 499 cancellation; 502 invalid model output; 503 unconfigured provider base or unavailable model service; 504 model timeout. Detection processing errors return 500. Network and detection endpoints remain independent of model credentials.
