EchoScan API and Integration Guide

EchoScan helps evaluate browser-environment risk around sensitive visits and business actions such as signup, login, checkout, payment, withdrawal, coupon redemption, voting, form submission, and password reset.

The core flow is short: browser-executed code generates an imprint, sends it to server-executed code with the protected request, and the server uses an EchoScan secret key to query the report and make the final risk decision.

  1. Choose a visit or business action you want to protect.
  2. Run Browser Verifier in browser-executed code to generate an imprint.
  3. Send the imprint to your server with the current business request. It can live in the body, a request header, or a separate risk endpoint that you own.
  4. Read the EchoScan secret key only in server-executed code and query a Lite or Pro report.
  5. Return a minimal server decision, such as allow, challenge, review, or deny.

If you use Next.js, Nuxt, Remix, SvelteKit, Laravel, Rails, Django, Vercel Functions, or Cloudflare Workers, browser code and server routes may live in the same repository. The important boundary is runtime location, not folder layout: the secret key belongs only in the server runtime.

Generate Imprint In The Browser

Browser Verifier only collects browser-environment signals and returns an imprint. It does not return the final report and it never holds an EchoScan secret key.

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

const { imprint } = await createEchoScan().run()

CDN loading is useful for no-build pages and quick demos. Production applications should prefer package-manager installation and the normal build pipeline.

HTML
<script type="module">
  import { createEchoScan } from 'https://cdn.echoscan.org/v1/echoscan.esm.js'

  const { imprint } = await createEchoScan().run()
</script>

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

Send Imprint To The Server

The imprint only needs to reach server-executed code with the protected action. Choose the transport shape that fits the current API.

Body transport fits JSON business requests.

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

Header transport fits projects that do not want to change the business body.

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

You can also keep the risk flow separate by creating your own server endpoint.

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

/api/echoscan/report is only an example path that must be implemented by your server. It is not an EchoScan official endpoint.

Lite Integration

Lite is the fastest way to run public and lightweight risk checks. A Lite key is still a secret key, so keep it in server environment variables, a secret manager, or an equivalent server-side secret store.

Node.js

Install command
npm install @echoscan/echoscan
Request example
import { createLiteClient } from '@echoscan/echoscan'

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

export async function getEchoScanReport(imprint) {
  return echoscan.getReport(imprint)
}

Go

Install command
go get github.com/echoscan/echoscan-go@latest
Request example
package main

import (
	"context"
	"log"
	"os"

	echoscan "github.com/echoscan/echoscan-go"
)

func main() {
	imprint := "fp_session_xxx"

	echoscanClient, err := echoscan.NewLiteClient(os.Getenv("ECHOSCAN_LITE_KEY"))
	if err != nil {
		log.Fatal(err)
	}

	report, err := echoscanClient.GetReport(context.Background(), imprint)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("lyingCount=%v", report["lyingCount"])
}

Python

Install command
pip install echoscan
Request example
import os
from echoscan import EchoScanLiteClient

imprint = "fp_session_xxx"
echoscan_client = EchoScanLiteClient(os.environ["ECHOSCAN_LITE_KEY"])
report = echoscan_client.get_report(imprint)

print(report.get("lyingCount"))

Rust

Install command
[dependencies]
echoscan = "0.2.1"
Request example
use std::env;
use echoscan::LiteClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let imprint = "fp_session_xxx";
    let api_key = env::var("ECHOSCAN_LITE_KEY")?;
    let echoscan = LiteClient::new(&api_key)?;

    let report = echoscan.get_report(imprint).await?;
    println!("{}", report["lyingCount"]);

    Ok(())
}

Lite Report

Lite report is the minimal public result returned after the server queries an imprint. It keeps the summary fields needed for risk decisions while hiding raw collection values, internal evidence, and detection implementation details.

You can read Lite report as a summary plus details: lyingCount is the strong-risk summary, and projection is the module-level result map. The server uses these results to produce its own allow, challenge, review, or deny decision.

Normal example:

Response example
{
  "analysis": {
    "browser": {
      "brand": "Google Chrome",
      "browser_version": "126.0.x",
      "rendering_engine": "Chromium",
      "browser_version_consistency_status": "PASS"
    },
    "os": {
      "os": "Windows",
      "version": "11",
      "os_consistency_status": "PASS"
    },
    "hash": {
      "accessCount": 1,
      "stableHash": "stb_8f4d2c1a",
      "uniqueHash": "unq_3a9b7e42"
    }
  },
  "lyingCount": 0,
  "projection": {
    "navigator": {
      "status": "PASS"
    },
    "screen": {
      "status": "PASS"
    },
    "webgl": {
      "status": "PASS"
    }
  }
}

Suspicious example:

Response example
{
  "analysis": {
    "browser": {
      "brand": "Google Chrome",
      "browser_version": "126.0.x",
      "rendering_engine": "Chromium",
      "browser_version_consistency_status": "FAIL"
    },
    "os": {
      "os": "Windows",
      "version": "11",
      "os_consistency_status": "PASS"
    },
    "hash": {
      "accessCount": 1,
      "stableHash": "stb_8f4d2c1a",
      "uniqueHash": "unq_9c0f2d31"
    }
  },
  "lyingCount": 1,
  "projection": {
    "navigator": {
      "status": "DECEPTIVE"
    },
    "screen": {
      "status": "PASS"
    },
    "webgl": {
      "status": "SUSPICIOUS"
    },
    "webrtc": {
      "status": "SUSPICIOUS"
    }
  }
}

Complete Lite surface example:

Response example
{
  "analysis": {
    "browser": {
      "brand": "Google Chrome",
      "browser_version": "126.0.x",
      "rendering_engine": "Chromium",
      "browser_version_consistency_status": "PASS"
    },
    "os": {
      "os": "Windows",
      "version": "11",
      "os_consistency_status": "PASS"
    },
    "hash": {
      "accessCount": 7,
      "stableHash": "stb_8f4d2c1a",
      "uniqueHash": "unq_3a9b7e42"
    }
  },
  "lyingCount": 0,
  "projection": {
    "audio": {
      "status": "PASS"
    },
    "canvas": {
      "status": "PASS"
    },
    "font": {
      "status": "PASS"
    },
    "ip-geo-timezone": {
      "status": "PASS"
    },
    "navigator": {
      "status": "PASS"
    },
    "screen": {
      "status": "PASS",
      "resolution": "1920x1080"
    },
    "speech": {
      "status": "PASS",
      "default_voice_name": "Microsoft Jenny Online"
    },
    "webgl": {
      "status": "PASS",
      "unmasked_vendor": "Google Inc. (NVIDIA)",
      "unmasked_renderer": "ANGLE (NVIDIA GeForce RTX 4060)"
    },
    "webrtc": {
      "status": "PASS"
    }
  }
}

Field reference

Field Example Meaning
analysis { ... } Normalized access-environment summary for server logs, admin review, and risk investigation
analysis.browser { ... } Browser summary, including brand, version, rendering engine, and version consistency
analysis.browser.brand Google Chrome Detected browser brand, such as Chrome, Firefox, or Safari
analysis.browser.browser_version 126.0.x Detected browser version
analysis.browser.rendering_engine Chromium Browser rendering engine, such as Chromium or WebKit
analysis.browser.browser_version_consistency_status PASS Browser-version consistency result; FAIL means version spoofing or strong inconsistency was found
analysis.os { ... } Operating-system summary, including OS name, version, and consistency
analysis.os.os Windows Detected operating system
analysis.os.version 11 Detected OS version
analysis.os.os_consistency_status PASS Operating-system consistency result
analysis.hash { ... } Access-environment identifiers and repeat-visit summary
analysis.hash.accessCount 7 Observed count associated with the stable access environment; useful for continuity, not an account request count
analysis.hash.stableHash stb_8f4d2c1a Stable environment identifier for continuity, repeat visits, and risk correlation
analysis.hash.uniqueHash unq_3a9b7e42 More granular identifier for the current access environment
lyingCount 0 Strong-risk summary that can be used for quick risk segmentation
projection { ... } Module-level result map, such as navigator, screen, webgl, and webrtc
projection.<module>.status SUSPICIOUS Public status for that module; servers usually use it as part of the final decision
projection.screen.resolution 1920x1080 Screen-resolution summary
projection.speech.default_voice_name Microsoft Aria Browser default-voice name summary
projection.webgl.unmasked_vendor Google Inc. WebGL graphics vendor summary
projection.webgl.unmasked_renderer ANGLE WebGL renderer summary

Status reference

status Meaning Common handling
PASS No obvious abnormality was found in the module Usually allow
SUSPICIOUS Suspicious or low-trust signals were found Challenge, observe, or raise risk score
DECEPTIVE Strong spoofing or inconsistency signals were found Challenge, review, or deny

Policy code can keep compatibility with legacy WARN and FAIL.

Server Decision

After the server receives a report, the common pattern is to turn it into a minimal decision result, such as allow, challenge, review, or deny, and return that result to the browser.

The server implements these decisions according to business risk, false-positive cost, and user experience. Common handling looks like this:

decision Common handling
allow Allow the request and continue the signup, login, checkout, or other business flow
challenge Require extra verification, such as CAPTCHA, SMS, email verification, or a second confirmation
review Send the request to manual review, a back-office queue, or delayed processing
deny Reject the request and stop the protected business action

The snippet below is a minimal decision example. Production policy can be tuned for the application’s risk model and user experience. Keep the complete report in server-side logs, admin systems, or risk platforms when needed.

JavaScript
const riskStatuses = new Set([
  'SUSPICIOUS',
  'DECEPTIVE',
  'WARN',
  'FAIL'
])

const hasStrongRisk = report.lyingCount > 0
const hasRiskStatus = Object.values(report.projection ?? {}).some((module) =>
  riskStatuses.has(module?.status)
)

const decision = hasStrongRisk || hasRiskStatus
  ? 'challenge'
  : 'allow'

return Response.json({ decision })

Pro Integration

Pro uses the same browser imprint and server-side secret boundary, but is intended for richer risk policy and operations analytics.

  • Compared with Lite, Pro adds stronger bot, IP/network risk, and historical access analysis signals
  • Pro exposes richer module observations for rule composition, attribution, and internal dashboards
  • Pro history queries support days or from/to windows and return summary plus timeline

Node.js

Install command
npm install @echoscan/echoscan
Request example
import { createProClient } from '@echoscan/echoscan'

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

const report = await echoscan.getReport(imprint)
const historyByDays = await echoscan.getHistory(imprint, { days: 7 })
const historyByRange = await echoscan.getHistory(imprint, {
  from: '2026-03-01',
  to: '2026-03-18'
})

Go

Install command
go get github.com/echoscan/echoscan-go@latest
Request example
package main

import (
	"context"
	"log"
	"os"

	echoscan "github.com/echoscan/echoscan-go"
)

func main() {
	imprint := "fp_session_xxx"
	days := 7

	echoscanClient, err := echoscan.NewProClient(os.Getenv("ECHOSCAN_PRO_KEY"))
	if err != nil {
		log.Fatal(err)
	}

	report, err := echoscanClient.GetReport(context.Background(), imprint)
	if err != nil {
		log.Fatal(err)
	}

	history, err := echoscanClient.GetHistory(context.Background(), imprint, echoscan.HistoryQuery{
		Days: &days,
	})
	if err != nil {
		log.Fatal(err)
	}

	_, _ = report, history
}

Python

Install command
pip install echoscan
Request example
import os
from echoscan import EchoScanProClient

imprint = "fp_session_xxx"
echoscan_client = EchoScanProClient(os.environ["ECHOSCAN_PRO_KEY"])

report = echoscan_client.get_report(imprint)
history = echoscan_client.get_history(imprint, days=7)

print(report.get("lyingCount"), history.get("summary"))

Rust

Install command
[dependencies]
echoscan = "0.2.1"
Request example
use std::env;
use echoscan::{HistoryQuery, ProClient};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let imprint = "fp_session_xxx";
    let api_key = env::var("ECHOSCAN_PRO_KEY")?;
    let echoscan = ProClient::new(&api_key)?;

    let report = echoscan.get_report(imprint).await?;
    let history = echoscan
        .get_history(imprint, HistoryQuery::Days { days: 7, recent: None })
        .await?;

    println!("{} {}", report["lyingCount"], history["summary"]);
    Ok(())
}

Pro Report

Response example
{
  "analysis": {
    "browser": {
      "brand": "Google Chrome",
      "browser_version": "146.0.x",
      "browser_version_consistency_status": "PASS",
      "rendering_engine": "Blink"
    },
    "os": {
      "os": "Windows",
      "os_consistency_status": "PASS",
      "version": "11"
    },
    "ip": {
      "status": "PASS"
    },
    "hash": {
      "accessCount": 31,
      "calculatedAt": "2026-03-19T10:27:25+09:00",
      "stableHash": "stb_8f4d2c1a",
      "uniqueHash": "unq_3a9b7e42"
    },
    "proxy": {
      "status": "PASS"
    }
  },
  "lyingCount": 0,
  "projection": {
    "bot_detection": {
      "status": "PASS",
      "data": {
        "is_bot": "No"
      }
    },
    "screen": {
      "status": "PASS",
      "data": {
        "resolution": "1920 x 1080"
      }
    },
    "speech": {
      "status": "PASS",
      "data": {
        "default_voice_lang": "en-US",
        "default_voice_name": "Microsoft David - English (United States)"
      }
    },
    "webgl": {
      "status": "PASS",
      "data": {
        "unmasked_vendor": "Google Inc. (NVIDIA)",
        "unmasked_renderer": "NVIDIA GeForce RTX 3060"
      }
    }
  }
}

Pro History

Response example
{
  "imprint": "fp_session_...",
  "range": {
    "from": "2026-03-01",
    "to": "2026-03-18"
  },
  "recent": 20,
  "summary": {},
  "timeline": []
}
  • days is mutually exclusive with from/to
  • from and to must be provided together
  • Date format is fixed to YYYY-MM-DD

Error Shape

Response example
{
  "code": "auth_failed",
  "httpStatus": 401,
  "message": "Authentication failed",
  "requestId": "req_...",
  "retryable": false
}

Branch on code; treat message as display text.

API Keys And Security Boundary

  • Sign in to EchoScan Console and create a Lite or Pro key from the API Keys page.
  • The plaintext key is shown only once at creation time. Store it immediately in a server environment variable or secret manager.
  • Do not put EchoScan secret keys in browser code, frontend environment variables, localStorage, logs, git, or publicly accessible files.
  • The browser generates and submits imprint; report lookup and final risk decision should run on the server.
  • Integration and technical support: support@echoscan.org
  • Privacy and security issues: security@echoscan.org

AI Coding Tools

If you want Claude, Cursor, Codex, or another coding agent to integrate EchoScan, ask it to read:

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

These files are generated from the same documentation facts as this page. If the agent cannot read them, it should stop and say that it cannot confirm the EchoScan API context instead of inventing package names, fields, or integration paths.