EchoScan API and Integration Guide

EchoScan provides device identity and access-risk results for sensitive actions such as sign-in, registration, and payment. After setup, the browser generates an imprint, and your server uses a secret API key to query the official Report.

Building an automated evaluator? The Agent API discovery guide covers the product catalog, OpenAPI contract, and Agent Trial flow. Human Workspace users can connect the production MCP Server through OAuth 2.1 without sharing a Secret API Key.

Follow this checklist from top to bottom. For the first integration, enter one website URL in Console. EchoScan creates the Web App and default server API key together. As you select or scroll through a step, the right panel adds the execution boundary, practical checks, and the expected result for that stage.

1. Add Your Website

Open first website setup in Console. Enter the website where Browser Verifier will run, such as https://shop.example.com. Console reduces paths and query parameters to the exact Origin, then creates the Web App and default server API key in one transaction. Copy the publishable env_... Environment ID after creation.

2. Save The Server API Key

After first website setup succeeds, Console shows the default API key secret once. Save it immediately in your backend secret manager or environment variables. Keep it out of browser code, logs, and source control. You can later create, rotate, or revoke keys independently in API Key management without changing the Web App.

3. Install Browser Verifier

Install the Browser SDK and pass the Environment ID from step 1 to createEchoScan({ environmentId }). Console owns Allowed Origins, so browser code only needs the public Environment ID.

4. Generate And Send The Imprint

Call run() when a protected action such as sign-in, registration, or payment occurs. Send the returned imprint to your own server with the business request.

5. Query The Report On Your Backend

Use the secret API key from step 2 to query the canonical Report endpoint by imprint. The official device identity and risk result come from the server Report.

6. Apply Your Business Decision

Read risk.status and, for Pro, risk.reasons. Combine the result with account, transaction, and business context to allow, challenge, review, or deny the action. Return only the minimum business result to the browser.

Browser code and server routes may share a repository. The security boundary is where code runs: environmentId is publishable, while the API key is server-only.

Generate Imprint In The Browser

Direct mode requires environmentId in the fixed env_<32 lowercase hex> format. Browser Verifier uses it only for fingerprint submission. It does not accept a Workspace ID and does not place an API key or authorization secret in the browser.

Install command
npm install @echoscan/browser-verifier
JavaScript
import { createEchoScan } from '@echoscan/browser-verifier'

const sdk = createEchoScan({
  environmentId: 'env_0123456789abcdef0123456789abcdef'
})
const { imprint } = await sdk.run()
HTML
<script type="module">
  import { createEchoScan } from 'https://cdn.echoscan.org/v1/echoscan.esm.js'

  const sdk = createEchoScan({
    environmentId: 'env_0123456789abcdef0123456789abcdef'
  })
  const { imprint } = await sdk.run()
</script>

<script src="https://cdn.echoscan.org/v1/echoscan.umd.js"></script>
<script>
  const sdk = window.EchoScan.createEchoScan({
    environmentId: 'env_0123456789abcdef0123456789abcdef'
  })
  sdk.run().then(({ imprint }) => {
    console.log(imprint)
  })
</script>

run() returns only { imprint }. New imprints use imp_<32 lowercase hex>.

Allowed Origins are exact browser deployment protection, not secret authentication. Register complete http or https origins such as https://staging.example.com:8443 or http://localhost:3000. Matching preserves scheme and port and lowercases the host. Paths, query strings, fragments, user info, wildcards (including *.example.com), bare domains, null, file://, and extension origins are rejected. An empty allowlist or a missing Origin header rejects every public Submit.

Browser Verifier Parameters

Parameter Meaning
environmentId Publishable Browser Environment ID. The server resolves it to the trusted Workspace and validates Allowed Origins
imprint Server-issued report identifier returned by a successful run() call; send it with the protected business action

Send Imprint To The Server

Send the imprint with the protected business action using the shape that fits your API.

Request example
await fetch('/api/your-action', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    ...yourActionPayload,
    echoscanImprint: imprint
  })
})
Request example
await fetch('/api/your-action', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-EchoScan-Imprint': imprint
  },
  body: JSON.stringify(yourActionPayload)
})
Request example
await fetch('/api/echoscan/report', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ imprint })
})

/api/echoscan/report above is an example path owned by the integrator, not an EchoScan endpoint.

Lite Integration

Lite uses the canonical report endpoint. The secret key must be read only by server-executed code.

Query Report

Request example
GET https://api.echoscan.org/api/v1/fingerprint/report/{imprint}
X-API-Key: <your_lite_key>
Accept: application/json

Node.js, Go, Python, and Rust SDKs wrap the same server HTTP API.

Backend language
SDK install / dependency
npm install @echoscan/echoscan
Server query example
import { createLiteClient } from '@echoscan/echoscan'

const echoscan = createLiteClient({
  apiKey: process.env.ECHOSCAN_LITE_KEY
})

const report = await echoscan.getReport(imprint)
console.log(report.risk.status)

Lite Report

Lite returns the minimum public device identity and risk contract. It does not include product reasons, recent activity, device timestamps, or Pro network details.

Normal example:

Response example
{
  "schema_version": "1.0",
  "imprint": "imp_0123456789abcdef0123456789abcdef",
  "created_at": "2026-07-28T01:30:00Z",
  "device": {
    "id": "did_A12B34C56D78",
    "seen_before": false,
    "access_count": 1
  },
  "risk": {
    "status": "PASS"
  },
  "browser": {
    "status": "PASS",
    "name": "Google Chrome",
    "version": "145"
  },
  "operating_system": {
    "status": "PASS",
    "name": "Windows",
    "version": "11"
  },
  "network": {
    "status": "PASS",
    "observed_ip": "203.0.113.10",
    "country_code": "JP"
  }
}

Suspicious example:

Response example
{
  "schema_version": "1.0",
  "imprint": "imp_11111111111111111111111111111111",
  "created_at": "2026-07-28T01:35:00Z",
  "device": {
    "id": "did_C98D76E54F32",
    "seen_before": false,
    "access_count": 1
  },
  "risk": {
    "status": "SUSPICIOUS"
  },
  "browser": {
    "status": "SUSPICIOUS",
    "name": "Google Chrome",
    "version": "145"
  },
  "operating_system": {
    "status": "PASS",
    "name": "Windows",
    "version": "11"
  },
  "network": {
    "status": "PASS",
    "observed_ip": "198.51.100.20",
    "country_code": "US"
  }
}

Returning-device example:

Response example
{
  "schema_version": "1.0",
  "imprint": "imp_22222222222222222222222222222222",
  "created_at": "2026-07-28T02:00:00Z",
  "device": {
    "id": "did_A12B34C56D78",
    "seen_before": true,
    "access_count": 8
  },
  "risk": {
    "status": "PASS"
  },
  "browser": {
    "status": "PASS",
    "name": "Google Chrome",
    "version": "145"
  },
  "operating_system": {
    "status": "PASS",
    "name": "Windows",
    "version": "11"
  },
  "network": {
    "status": "PASS",
    "observed_ip": "203.0.113.10",
    "country_code": "JP"
  }
}

Field Reference

Field Availability Meaning
schema_version Always returned. Public data-structure version used by this report.
imprint Always returned. Unique identifier for this detection report.
created_at Always returned in UTC. Time this report was generated.
device Always returned. Identification result and visit history for this device in the current Workspace.
device.id Always returned. Device ID within the current Workspace.
device.seen_before Always returned. Whether this device was seen before the current visit.
device.access_count Always returned and includes the current visit. Total visits for this device in the current Workspace, including this visit.
risk Always returned. Overall risk result for this visit.
risk.status Always returned. Overall risk level for this visit.
browser Always returned. Browser information identified by EchoScan and its risk status.
browser.status Always returned. Risk level for the browser identity dimension of this visit.
browser.name Returned when available. Browser name identified by EchoScan.
browser.version Returned when available. Browser version identified by EchoScan.
operating_system Always returned. Operating-system information identified by EchoScan and its risk status.
operating_system.status Always returned. Risk level for the operating-system environment dimension of this visit.
operating_system.name Returned when available. Operating-system name identified by EchoScan.
operating_system.version Returned when available. Operating-system version identified by EchoScan.
network Always returned. Network information and risk status for this visit.
network.status Always returned. Risk level for the network dimension of this visit.
network.observed_ip Returned when available. Public egress IP used when this request reached EchoScan.
network.country_code Returned when available. Country or region code associated with the request egress IP.

Finite Values

Field Value Title Meaning Example customer action
device.seen_before true Previously seen EchoScan had seen this device before the current visit.
device.seen_before false First record This visit is the first record for the device.
risk.status PASS No risk currently needs attention Continue processing this visit under the normal business rules. Continue the existing business flow.
risk.status SUSPICIOUS Risk needs attention Evaluate the result together with the account and business context. Add verification, rate limits, or manual review.
risk.status DECEPTIVE Clear high-risk indicators are present The visit shows clear signs of disguise, automation, or a high-risk network. Apply stricter verification, restrictions, or manual review.
browser.status PASS Browser information looks normal No browser-information issue currently needs attention.
browser.status SUSPICIOUS Browser information needs attention The browser information contains an issue that needs attention.
browser.status DECEPTIVE Browser information is high risk The browser information shows clear signs of disguise.
operating_system.status PASS Operating-system information looks normal No operating-system information issue currently needs attention.
operating_system.status SUSPICIOUS Operating-system information needs attention The operating-system information contains an issue that needs attention.
operating_system.status DECEPTIVE Operating-system information is high risk The operating-system information shows clear signs of disguise.
network.status PASS Network information looks normal No network-information issue currently needs attention.
network.status SUSPICIOUS Network information needs attention The network information contains a risk that needs attention.
network.status DECEPTIVE Network information is high risk The network information shows clear high-risk indicators.

Pro Integration

Pro uses the same browser imprint, server-only secret boundary, and canonical endpoint as Lite. The key plan selects Pro depth without requiring customers to change endpoints. Enterprise currently receives the Pro contract.

Query Report

Request example
GET https://api.echoscan.org/api/v1/fingerprint/report/{imprint}
X-API-Key: <your_pro_key>
Accept: application/json

History is available only to Pro and Enterprise:

Request example
GET https://api.echoscan.org/api/v1/fingerprint/imprint/{imprint}/history?days=7&recent=20
X-API-Key: <your_pro_key>
Accept: application/json
Request example
GET https://api.echoscan.org/api/v1/fingerprint/imprint/{imprint}/history?from=2026-03-01&to=2026-03-18&recent=20
X-API-Key: <your_pro_key>
Accept: application/json
Backend language
SDK install / dependency
npm install @echoscan/echoscan
Server query example
import { createProClient } from '@echoscan/echoscan'

const echoscan = createProClient({
  apiKey: process.env.ECHOSCAN_PRO_KEY
})

const report = await echoscan.getReport(imprint)
console.log(report.risk.status, report.risk.reasons)
const history = await echoscan.getHistory(imprint, { days: 7 })

Optional: Enable Account Map (Pro)

Use Account Map when you need to answer questions such as:

  • How many accounts are associated with this device?
  • How many devices has this account used?
  • Have this account and device appeared together before?
  • Is one device receiving many new account relationships in a short period?
  • Could the pattern indicate account sharing or account takeover?

Pass the stable internal user ID with the Pro server-side Report query.

Node.js

Request example
const report = await pro.getReport(imprint, {
  accountRef: currentUser.id
})

Go

Request example
report, err := pro.GetReportWithOptions(
	context.Background(),
	imprint,
	echoscan.ReportOptions{AccountRef: currentUser.ID},
)
if err != nil {
	return err
}

Python

Request example
report = pro.get_report(
    imprint,
    account_ref=current_user.id,
)

Rust

Request example
let report = pro
    .get_report_with_options(
        imprint,
        ReportOptions { account_ref: Some(current_user.id.clone()) },
    )
    .await?;

Account Map is completely optional. Use a stable, non-sensitive, immutable internal user primary key as accountRef. Do not send an email address, phone number, real name, nickname, or mutable username. Account relationships are isolated by Workspace.

Omitting accountRef preserves the existing Report request and response. Account Map v1 does not automatically change risk.status; combine these relationship signals with your own business rules.

Consistency for concurrent or delayed linking

Account Map statistics include only relationships that have completed and are visible when the response is generated.

Concurrent POST responses cannot anticipate other relationships that have not completed yet.

After all concurrent or delayed links complete, later GET responses recompute historical statistics in server event order (recorded_at, then event_id).

An earlier event keeps its historical boundary, while a later event includes visible relationships at or before its boundary.

Account Map response example:

Response example
{
  "account_map": {
    "account_seen_before": true,
    "relationship_seen_before": false,
    "accounts_on_device": 7,
    "devices_on_account": 2,
    "accounts_first_seen_on_device_1h": 4
  }
}
Field Meaning
account_seen_before Whether this account appeared on any device in the current Workspace before this visit.
relationship_seen_before Whether this exact account and current device appeared together before this visit.
accounts_on_device Number of distinct accounts associated with the current device, including the current relationship.
devices_on_account Number of distinct devices associated with the current account, including the current relationship.
accounts_first_seen_on_device_1h Number of accounts whose first relationship with the current device occurred during the previous hour. When accountRef is sent after registration, this can approximate new registrations. When sent only at sign-in, it counts accounts first observed by EchoScan and does not prove that the account was just created.

Pro Report

Pro is a strict superset of Lite. It adds device continuity timestamps, product-level reasons, optional network details, and recent activity.

Response example
{
  "schema_version": "1.0",
  "imprint": "imp_0123456789abcdef0123456789abcdef",
  "created_at": "2026-07-28T01:30:00Z",
  "device": {
    "id": "did_A12B34C56D78",
    "seen_before": true,
    "access_count": 42,
    "first_seen_at": "2026-06-10T03:20:00Z",
    "previous_seen_at": "2026-07-27T08:40:00Z"
  },
  "risk": {
    "status": "DECEPTIVE",
    "reasons": [
      "BROWSER_VERSION_MISMATCH",
      "PROXY_DETECTED",
      "NETWORK_INCONSISTENT"
    ]
  },
  "browser": {
    "status": "DECEPTIVE",
    "name": "Google Chrome",
    "version": "145"
  },
  "operating_system": {
    "status": "PASS",
    "name": "Windows",
    "version": "11"
  },
  "network": {
    "status": "DECEPTIVE",
    "observed_ip": "198.23.233.104",
    "country_code": "US",
    "alternate_ip": "126.234.173.23",
    "ip_consistency": "MISMATCH",
    "proxy_detected": true,
    "location": {
      "country_name": "United States",
      "region": "Illinois",
      "city": "Elk Grove Village",
      "timezone": "America/Chicago"
    },
    "provider": "Example Hosting Provider",
    "connection_type": "proxy",
    "asn": 36352
  },
  "activity": {
    "5m": {
      "events": 2,
      "distinct_ips": 1,
      "distinct_countries": 1
    },
    "1h": {
      "events": 8,
      "distinct_ips": 2,
      "distinct_countries": 2
    },
    "24h": {
      "events": 21,
      "distinct_ips": 4,
      "distinct_countries": 3
    }
  }
}

A passing Pro report still serializes an explicit empty reasons array:

Response example
{
  "risk": {
    "status": "PASS",
    "reasons": []
  }
}

The tables below provide the return rule for every field and the product meaning of each finite machine value.

Product Reasons

Reason Meaning
BROWSER_VERSION_MISMATCH Browser version information is inconsistent
BROWSER_IDENTITY_MISMATCH Browser identity information is inconsistent
OS_ENVIRONMENT_MISMATCH Operating-system environment information is inconsistent
AUTOMATION_DETECTED Automated access was detected
ENVIRONMENT_INCONSISTENT Current device environment information is inconsistent
PROXY_DETECTED Proxy or VPN risk was detected
HOSTING_NETWORK_DETECTED Access originated from a hosting service or datacenter network
NETWORK_INCONSISTENT Network identity information is inconsistent
LOCATION_INCONSISTENT Location-related information is inconsistent
SIGNAL_DATA_INCOMPLETE Critical data for this detection is incomplete

Reasons describe risk categories, not severity. Severity is expressed only by risk.status.

Pro-Only Fields

Field Availability Meaning
device.first_seen_at Returned in Pro when available, in UTC. Time this Workspace first saw the device.
device.previous_seen_at Always returned in Pro; null on the first visit. Most recent time this device was seen before the current visit.
risk.reasons Always returned in Pro. Returns an empty array [] for PASS. Product-level reason categories associated with this risk result.
network.alternate_ip Returned in Pro when available. Client public IP detected by EchoScan.
network.ip_consistency Always returned in Pro. Consistency result between the client public IP and request egress IP.
network.proxy_detected Returned in Pro when available. Whether proxy or VPN risk was detected.
network.location Returned in Pro when available. Network location information associated with the request egress IP.
network.location.country_name Returned when available with network.location. Country or region name associated with the request egress IP.
network.location.region Returned when available with network.location. Region or first-level administrative area associated with the request egress IP.
network.location.city Returned when available with network.location. City associated with the request egress IP.
network.location.timezone Returned when available with network.location. Time zone associated with the network location of the request egress IP.
network.provider Returned in Pro when available. Network organization or service provider associated with the request egress IP.
network.connection_type Returned in Pro when available. Network type associated with the request egress IP.
network.asn Returned in Pro when available. Autonomous system number associated with the request egress IP.
activity Returned in Pro when available. Summary of recent visit activity for the current device.
activity.5m Always returned with activity and includes this visit. Visit activity for the current device during the last 5 minutes.
activity.5m.events Always returned with its time window. Number of visits in this time window.
activity.5m.distinct_ips Always returned with its time window. Number of distinct IP addresses in this time window.
activity.5m.distinct_countries Always returned with its time window. Number of distinct countries or regions in this time window.
activity.1h Always returned with activity and includes this visit. Visit activity for the current device during the last hour.
activity.1h.events Always returned with its time window. Number of visits in this time window.
activity.1h.distinct_ips Always returned with its time window. Number of distinct IP addresses in this time window.
activity.1h.distinct_countries Always returned with its time window. Number of distinct countries or regions in this time window.
activity.24h Always returned with activity and includes this visit. Visit activity for the current device during the last 24 hours.
activity.24h.events Always returned with its time window. Number of visits in this time window.
activity.24h.distinct_ips Always returned with its time window. Number of distinct IP addresses in this time window.
activity.24h.distinct_countries Always returned with its time window. Number of distinct countries or regions in this time window.

Pro Finite Values

Field Value Title Meaning Example customer action
network.ip_consistency MATCH IP information matches Network IP information is consistent.
network.ip_consistency MISMATCH IP information differs Network IP information is inconsistent.
network.ip_consistency UNKNOWN No clear result No clear IP consistency result is available.
network.proxy_detected true Risk detected Proxy or VPN risk was detected.
network.proxy_detected false No risk detected No proxy or VPN risk was detected.
network.connection_type residential Residential Residential broadband network.
network.connection_type mobile Mobile Mobile carrier network.
network.connection_type corporate Corporate Business or institutional network.
network.connection_type hosting Hosting Cloud, server-hosting, or datacenter network.
network.connection_type proxy Proxy Proxy, VPN, or similar relay network.
network.connection_type unknown Unclassified The network has not been assigned a specific type.

Pro History

The History API is available to Pro and Enterprise only.

Response example
{
  "imprint": "imp_33333333333333333333333333333333",
  "range": {
    "from": "2026-03-01",
    "to": "2026-03-18",
    "days": 18
  },
  "summary": {
    "events": 12,
    "truncated": false,
    "firstSeenAt": "2026-03-01T08:10:00+09:00",
    "lastSeenAt": "2026-03-18T21:34:00+09:00"
  },
  "timeline": [
    {
      "date": "2026-03-01",
      "count": 2
    },
    {
      "date": "2026-03-18",
      "count": 3
    }
  ],
  "recent": [
    {
      "at": "2026-03-18T21:34:00+09:00",
      "surface": "login"
    }
  ]
}

days is mutually exclusive with from/to; from and to must be provided together in YYYY-MM-DD format.

History Field Reference

Field Meaning
imprint Detection report identifier used for this History query
range Time range applied to this History query
range.from First date in the query range
range.to Last date in the query range
range.days Number of calendar days covered by the range
summary Aggregate summary for the selected range
summary.events Total visit events in the selected range
summary.truncated Whether the response reached its return limit
summary.firstSeenAt Earliest event time inside the selected range
summary.lastSeenAt Most recent event time inside the selected range
timeline Daily visit-count series
timeline.date Calendar date for a timeline entry
timeline.count Visit events recorded on that date
recent Most recent visit-event list
recent.at Time of a recent event
recent.surface Customer-provided business surface identifier

Server Decision

Use risk.status as the primary report input and combine it with account, transaction, and business context. Pro may additionally use risk.reasons, network details, activity, and History.

JavaScript
const decision =
  report.risk.status === 'PASS'
    ? 'allow'
    : 'challenge'

return Response.json({ decision })

This allow-or-challenge branch is an example customer policy. EchoScan Report API v1 does not return recommended_action.

Keep the full report server-side and return only the business result needed by the browser.

Error Shape

Response example
{
  "error": {
    "code": "auth_failed",
    "message": "Authentication failed"
  }
}

Branch on error.code; treat error.message as display text.

Error Field Reference

Field Meaning
error Public error object
error.code Stable machine code for server-side branching
error.message Error description suitable for logs or interface text

Connect AI With EchoScan MCP

Use standard MCP to connect Codex, Claude Code, VS Code, or another compatible client directly to EchoScan. The client opens browser authorization the first time it needs access to a Workspace.

REMOTE MCP SERVER

Connect EchoScan to your AI

Add this remote endpoint once. Your client opens EchoScan authorization when access is required—no API key is pasted into the AI client.

Streamable HTTP OAuth 2.1 + PKCE
Server endpoint https://api.echoscan.org/mcp
View setup methods
Choose your client

Run this once in a terminal, then approve the EchoScan workspace and permissions in your browser.

Codex
codex mcp add echoscan --url https://api.echoscan.org/mcp

Current tools are echoscan_get_report, echoscan_get_history, and echoscan_get_usage. Current OAuth scopes are echoscan.report.lite, echoscan.report.pro, echoscan.history.read, and echoscan.usage.read.

If the AI will also change EchoScan integration code, give it these canonical sources first:

  • https://echoscan.org/llms.txt
  • https://echoscan.org/docs/ai-context.md

They are maintained against the same public API contract and validated for consistency. MCP authorization never requires you to paste an EchoScan API key into the AI client.