Documentation

Guides for protecting production JavaScript

Reference guides for release workflows, command-line usage, cross-file protections, and the desktop app.

Inside The Docs

Practical guides, not placeholder pages.

How-to guides Start with release sequencing and command-line usage, then move into feature-specific references.
Advanced protection Browse cross-file controls like Replace Globals and Protect Members when a build spans multiple scripts.

JSO AI API reference

  • https://www.javascriptobfuscator.com/v1/ai/
  • application/json
  • Same APIKey + APIPwd shape as /HttpApi.ashx
  • Today: deterministic rule-based responses. Phase 1 GA (2026-Q3) swaps in Claude / GPT-class. Wire format unchanged.

The JSO AI endpoints are POST-only handlers in the /v1/ai/ namespace. They share an envelope: every successful response carries ok: true, a previewMode bool, a per-endpoint result object, and provider / token-cost metadata. Every error response is HTTP 200 with ok: false, a stable error code, and a human-readable message. This page documents all three Phase 1 endpoints with curl examples you can run today against preview mode.

Common envelope

Every successful response from any AI endpoint has this shape:

{
  "ok": true,
  "previewMode": true | false,
  "<result-field>": { ... },          // "suggestion", "report", or "explanation"
  "provider": "rule-based" | "rule-based-fallback" | "claude" | "openai" | "preview",
  "tokensIn": 0,
  "tokensOut": 0
}

The provider field names the actual source of the response. In preview mode it's "rule-based"; once the LLM is wired it becomes "claude" or "openai"; on a provider failure it falls back to "rule-based-fallback" rather than failing the request. Tokens are zero in any rule-based path.

Error responses:

{
  "ok": false,
  "error": "input_invalid" | "method_not_allowed" | "provider_error" | "quota_exhausted" | ...,
  "message": "human-readable explanation"
}

POST /v1/ai/preset-suggest.ashx

Natural-language → jso.config.json. Describe your app; get a starter config with preset, options, and a "detected signals" list explaining what the assistant picked up.

Request:

{
  "APIKey": "<base64-from-dashboard>",
  "APIPwd": "<base64-from-dashboard>",
  "description": "React SaaS frontend with balanced performance. Lock to example.com. Protect license-check strongly."
}

Response:

{
  "ok": true,
  "previewMode": true,
  "suggestion": {
    "previewMode": true,
    "source": "rule-based",
    "config": {
      "$schema": "./node_modules/jso-protector/jso.config.schema.json",
      "endpoint": "https://www.javascriptobfuscator.com/HttpApi.ashx",
      "apiKey": "$JSO_API_KEY",
      "apiPassword": "$JSO_API_PASSWORD",
      "projectName": "release",
      "input": "dist",
      "output": "dist-protected",
      "preset": "balanced",
      "extensions": [".js", ".jsx"],
      "exclude": ["**/*.map", "**/vendor/**", "**/*-obfuscated.js"],
      "options": {
        "LockDomain": true,
        "LockDomainList": "example.com"
      }
    },
    "signals": [
      "preset = balanced (default)",
      "LockDomain → example.com",
      "React detected (LLM version: exclude framework lifecycle names by default)",
      "Consider marking license / payment / anti-tamper functions with // @virtualize for VM bytecode protection (Corporate+)."
    ]
  },
  "provider": "rule-based",
  "tokensIn": 0,
  "tokensOut": 0
}

curl:

curl -X POST https://www.javascriptobfuscator.com/v1/ai/preset-suggest.ashx \
    -H "Content-Type: application/json" \
    -d '{
      "APIKey": "'"$JSO_API_KEY"'",
      "APIPwd": "'"$JSO_API_PASSWORD"'",
      "description": "React SaaS, balanced performance, lock to example.com"
    }'

POST /v1/ai/compat-check.ashx

Scan a JavaScript snippet for patterns that historically break under Maximum-mode obfuscation. Returns a structured findings report with severity, line / column, snippet, message, suggested fix.

Request:

{
  "APIKey": "<base64-from-dashboard>",
  "APIPwd": "<base64-from-dashboard>",
  "source": "function pay(amt) { eval('api.charge(' + amt + ')'); debugger; }",
  "framework": "react"        // optional hint; null OK
}

Response:

{
  "ok": true,
  "previewMode": true,
  "report": {
    "previewMode": true,
    "source": "rule-based",
    "summary": { "errors": 2, "warnings": 0, "infos": 0 },
    "findings": [
      {
        "category": "dynamic-eval",
        "severity": "error",
        "line": 1,
        "column": 21,
        "snippet": "eval('api.charge(' + amt + ')')",
        "message": "eval() will not survive identifier renaming...",
        "suggestedFix": "Replace with a static dispatch table, or VariableExclude..."
      },
      {
        "category": "debug-leak",
        "severity": "error",
        "line": 1,
        "column": 57,
        "snippet": "debugger;",
        "message": "debugger statement. Always halts protected code under DevTools.",
        "suggestedFix": "Remove before protection."
      }
    ]
  },
  "provider": "rule-based",
  "tokensIn": 0,
  "tokensOut": 0
}

Hard cap: 256KB per snippet (split larger files into chunks). The eight finding categories (dynamic-eval, reflective, framework-rt, explicit-keep, inline-html, source-map, debug-leak) are stable; new categories are additive.

curl:

curl -X POST https://www.javascriptobfuscator.com/v1/ai/compat-check.ashx \
    -H "Content-Type: application/json" \
    -d "{
      \"APIKey\": \"$JSO_API_KEY\",
      \"APIPwd\": \"$JSO_API_PASSWORD\",
      \"source\": $(jq -Rs . < dist/app.js),
      \"framework\": \"react\"
    }"

Folded into the obfuscation CLI: if you already use jso-protector, you don't need to wire this endpoint by hand. Add --ai-precheck to your existing command and the CLI will call /v1/ai/compat-check on every input file before the obfuscation API. The build aborts on findings per --ai-precheck-fail-on (error / warning / never) and the obfuscation quota is not touched on failure. To run the gate stand-alone over your whole config, use jso ai compat-scan --config jso.config.json.

POST /v1/ai/explain-error.ashx

Paste a protected-output runtime error; get the diagnosed JSO transform and a concrete fix. Optionally include the jso.config snippet for context.

Request:

{
  "APIKey": "<base64-from-dashboard>",
  "APIPwd": "<base64-from-dashboard>",
  "error": "Uncaught TypeError: x.someMethod is not a function at app.js:1:42",
  "config": "..."             // optional, your jso.config.json snippet
}

Response:

{
  "ok": true,
  "previewMode": true,
  "explanation": {
    "previewMode": true,
    "source": "rule-based",
    "cause": "name-mangling",
    "transform": "Name Mangling",
    "confidence": "high",
    "explanation": "A renamed identifier was called as a function by its original name...",
    "fix": "Add the function name to VariableExclusion in your jso.config...",
    "docsUrl": "/Docs/VariableExclusionList.aspx"
  },
  "provider": "rule-based",
  "tokensIn": 0,
  "tokensOut": 0
}

confidence is one of high / medium / low. Hard caps: 64KB on the error message, 32KB on the config snippet.

Try without auth: the browser preview runs the same rule matchers locally on whatever error you paste — no API key, nothing leaves your browser.

curl:

curl -X POST https://www.javascriptobfuscator.com/v1/ai/explain-error.ashx \
    -H "Content-Type: application/json" \
    -d '{
      "APIKey": "'"$JSO_API_KEY"'",
      "APIPwd": "'"$JSO_API_PASSWORD"'",
      "error": "Uncaught TypeError: someMethod is not a function"
    }'

POST /v1/ai/usage.ashx

Returns your account's current-month JSO AI usage as JSON — the same numbers /dashboard/AIUsage.aspx shows, exposed as a structured endpoint so you can wire quota into your own observability stack (Datadog, Grafana, PagerDuty). This is the right way to poll quota; do not scrape the HTML widget.

Request:

{
  "APIKey": "<base64-from-dashboard>",
  "APIPwd": "<base64-from-dashboard>"
}

Response:

{
  "ok": true,
  "previewMode": true,
  "tier": "FreeTrial",
  "billingMonth": "2026-05-01",
  "actionsUsed": 0,
  "actionsCap": 10,
  "actionsRemaining": 10,
  "tokensUsed": 0,
  "tokensCap": 0,
  "tokensRemaining": 0,
  "approxCostCents": 0,
  "costCapCents": 200,
  "costRemainingCents": 200,
  "quotaRejections": 0,
  "asOfUtc": "2026-05-21T14:22:11Z"
}

tier is one of FreeTrial / Basic / Corporate / Enterprise. previewMode: true means either the AI schema is not yet provisioned, or the auth wiring has not landed and you are seeing FreeTrial default caps — the wire format is identical in both modes. billingMonth is the first day of the UTC month the counters apply to.

curl:

curl -X POST https://www.javascriptobfuscator.com/v1/ai/usage.ashx \
    -H "Content-Type: application/json" \
    -d '{
      "APIKey": "'"$JSO_API_KEY"'",
      "APIPwd": "'"$JSO_API_PASSWORD"'"
    }'

This endpoint does not itself count against the actionsCap — polling for quota is free.

Prometheus exporter: drop-in Node script that turns this endpoint into Prometheus textfile-collector metrics — /download/jso-ai-quota-exporter.js. ~100 lines, MIT-licensed, zero npm dependencies. Cookbook recipe 13 has the cron-line setup.

POST /v1/ai/checkout-create.ashx

Creates a Stripe Checkout Session for a paid AI tier and returns the URL the customer's browser should redirect to. Once Stripe fires checkout.session.completed, the stripe-webhook.ashx handler inserts the AISubscription row and the next quota check sees the new tier.

Request:

{
  "APIKey":    "<base64-from-dashboard>",
  "APIPwd":    "<base64-from-dashboard>",
  "tier":      "Basic" | "Corporate" | "Enterprise",
  "returnUrl": "https://www.javascriptobfuscator.com/dashboard/AIUsage.aspx"   // optional
}

Response:

{
  "ok":        true,
  "url":       "https://checkout.stripe.com/c/pay/cs_test_...",
  "sessionId": "cs_test_..."
}

Error codes: method_not_allowed, input_invalid, auth_failed, rate_limited, checkout_not_configured (Stripe pricing not provisioned yet --- dashboard should fall back to manual signup), upstream_error (Stripe 4xx/5xx), internal_error. Rate-limited 5/min/IP since this is a write path that costs Stripe API calls.

POST /v1/ai/portal-create.ashx

Creates a Stripe Customer Portal session and returns the URL the dashboard can redirect to. The portal is where the customer manages their AI subscription end-to-end: update payment method, view invoices, cancel. Self-service; no support ticket required.

Request:

{
  "APIKey":    "<base64-from-dashboard>",   // OR omit and authenticate via dashboard session cookie
  "APIPwd":    "<base64-from-dashboard>",
  "returnUrl": "https://www.javascriptobfuscator.com/dashboard/AIUsage.aspx"   // optional
}

Response:

{ "ok": true, "url": "https://billing.stripe.com/p/session/..." }

Error codes: method_not_allowed, rate_limited (5/min/IP), input_invalid, auth_failed, portal_not_configured (Stripe key missing — dashboard should fall back to "contact support"), stripe_customer_missing (subscription was provisioned manually rather than via Stripe Checkout, so no Customer ID exists yet — same fallback), upstream_error (Stripe 4xx/5xx), internal_error.

The Stripe Customer ID is captured by the webhook on the first event with a customer attached, so the portal becomes available within a minute of the first paid checkout.

POST /v1/ai/stripe-webhook.ashx

Stripe webhook receiver. Verifies the HMAC-SHA256 signature, then routes lifecycle events onto AISubscription:

  • checkout.session.completed / customer.subscription.created / customer.subscription.updated — upsert.
  • customer.subscription.deleted — set CanceledUtc.
  • Everything else — 200 + ignore.

Idempotent on Stripe event ID via an internal ledger table. AccountID + tier read from session/subscription metadata.account_id + metadata.ai_tier, populated by checkout-create.ashx.

Customers don't call this directly — Stripe does. Documented here so integrators understand the round-trip.

Quota and pricing

Every successful call counts 1 AI action against your monthly tier cap (see JSO AI pricing). Tokens consumed against the protection token pool are reported in the response (tokensIn + tokensOut); rule-based / preview-mode calls cost zero tokens. The dashboard at /dashboard/AIUsage.aspx shows your live monthly counters with green / amber / red gradient at 70% and 90% of cap.

Hard quota by default. When you hit either cap the endpoints return ok: false with error: "quota_exhausted" and a message that includes the upgrade path. No surprise overages.

Preview-mode vs LLM-backed behavior

Today (preview mode) the endpoints serve deterministic rule-based responses. The wire format above is the final wire format — Phase 1 GA (2026-Q3) swaps Claude / GPT-class in behind the same envelope. Differences customers will see:

  • provider changes from "rule-based" to "claude" (or "openai") on real calls. The rule-based-fallback value appears when the LLM provider rate-limits / 503s and the endpoint serves the rule-based version instead of failing.
  • tokensIn / tokensOut get populated. Today both are 0.
  • Result quality goes up for novel inputs the rules don't cover. Common cases stay similar — the LLM and the rules largely agree on the canonical patterns.

If you're integrating against the API today, write your client to read these fields as authoritative regardless of mode. The CASCADE post explains why Phase 1's LLM is a single-purpose assistant (config + compat + diagnosis), not the deobfuscator-facing surface — that's Phase 2.

JSON Schema (machine-readable wire format)

The four endpoint envelopes are formally specified at /Docs/ai-wire-format.schema.json (JSON Schema draft 2020-12). Feed it to ajv, jsonschema (Python), or any other validator to assert parsed responses in your client — if validation breaks, the API changed and your code fails loudly rather than silently misinterprets. Same approach as the obfuscation API's wire-format.schema.json.

The smoke harness (packages/polyglot-smoke/ai-smoke.js) cross-checks every smoke fixture against the schema's required field lists, so the two sources of truth can't drift independently.

Copy-paste starting points in 10 languages (curl, Python, Node, Go, .NET, Java, Ruby, PHP, Rust, Kotlin): AIClients.aspx. Operator hand-off for flipping Phase 1 GA on: AIPhase1Deploy.aspx.

Test it now: three browser previews exercise the same rule logic as the server endpoints — PresetAssistantPreview, CompatCheckPreview, ExplainErrorPreview. The dashboard at /dashboard/AIUsage.aspx shows the metering view in its three states (no schema / no subscription / active).