Who this is for: Everyone -- developers integrating SDKs, DevOps engineers configuring pipelines, compliance officers using the dashboard, assessors reviewing evidence, and account administrators managing billing. If you have a question, start here.

1. Getting Started and Onboarding

How do I create an account?
  1. Go to sovereign.tenova.io/signup
  2. Enter your email, password, organization name, and select your primary framework (NIST 800-53, CMMC, AI RMF, EU AI Act, SR 11-7, or NIST 800-171).
  3. On success, you receive a tenant ID and API key. Copy the API key immediately -- it is shown once and cannot be recovered.
  4. You start on the OPEN (Free) tier with 10 requests/second and 100 anchors/month.
My API key is not working (401 error)
  • Check the format: Keys must start with axm_live_ or axm_trial_. If yours does not, it is not a valid SWT3 API key.
  • Check for whitespace: Copy-paste can add leading/trailing spaces. Trim the key.
  • Check the header format: It must be Authorization: Bearer axm_live_... (with the word "Bearer" and a space).
  • Key lost? Keys are stored as SHA-256 hashes -- the raw key cannot be recovered. Go to Settings > API Keys and create a new one.
  • Key revoked? Check the audit log at Settings > Audit Log to see if someone revoked it.

Diagnostic command:

curl -s -o /dev/null -w "%{http_code}" \
  -H "Authorization: Bearer $SWT3_API_KEY" \
  https://sovereign.tenova.io/api/v1/health

If this returns 200, your key is valid. If 401, the key is invalid or revoked.

My first anchor is not appearing in the dashboard

This is the most common onboarding issue. Work through these checks in order:

  1. Verify connectivity: curl https://sovereign.tenova.io/api/v1/health should return {"status":"healthy"}. If it does not, check your firewall allows outbound HTTPS (port 443).
  2. Verify your API key works: See the diagnostic command above.
  3. Check that the SDK flushed: The SDK buffers anchors and flushes asynchronously. Call witness.flush() (Python) or await witness.flush() (TypeScript) explicitly during testing.
  4. Check your tenant ID: The tenant ID in the SDK must match the one shown in Settings on the dashboard.
  5. Check the clearing level: At Clearing Level 3 (Classified), ai_model_id is hashed. The anchor still exists in the ledger but model names appear as hashes.
  6. Check the dashboard filter: The ledger page has filters for verdict type and time range. Make sure "All" is selected.
How do I install the SDK?
LanguageInstall CommandVerify
Pythonpip install swt3-aipython -c "import swt3_ai; print(swt3_ai.__version__)"
TypeScriptnpm install @tenova/swt3-ainode -e "console.log(require('@tenova/swt3-ai').version)"
Rustcargo add swt3-aicargo build
C#dotnet add package swt3-aidotnet build
Rubygem install swt3-airuby -e "require 'swt3_ai'; puts Swt3Ai::VERSION"
SwiftAdd https://github.com/tenova-labs/swt3-ai-swift via SPMBuild in Xcode
KotlinAdd Maven Central dependency io.tenova:swt3-ai./gradlew build
MCPnpm install @tenova/swt3-mcpnpx swt3-mcp --version

Minimum runtime versions: Python 3.9+, Node.js 18+, Rust stable, .NET 6+, Ruby 3.0+, Swift 5.9+, Kotlin 1.9+.

What environment variables does the SDK need?
VariableRequiredDescription
SWT3_API_KEYYes (cloud mode)Your API key starting with axm_
SWT3_ENDPOINTNoOverride the default endpoint. Defaults to https://sovereign.tenova.io
SWT3_TENANT_IDYesYour tenant identifier (shown in Settings)
SWT3_CLEARING_LEVELNoDefault clearing level (0-3). Defaults to 1.

Alternatively, pass these as constructor arguments: Witness(api_key="...", tenant_id="...", clearing_level=1)

What is the difference between local mode and cloud mode?
FeatureLocal ModeCloud Mode
Network requiredNoYes
API key requiredNoYes
Anchors storedLocal WAL fileCloud evidence ledger
Dashboard visibleNoYes
VerificationTerminal SHA-256 onlyPublic verifier + Merkle proofs
RetentionUnlimited (your disk)By tier (7d / 90d / 365d / unlimited)

Local mode is useful for evaluation, air-gapped environments, and development. To switch to cloud mode, add your API key and tenant ID. See the Self-Hosted Quick Start.

How do I run the SDK demo?

Both Python and TypeScript SDKs include a zero-dependency demo that works without an API key:

# Python
python -m swt3_ai.demo

# TypeScript
npx swt3-demo

The demo mints sample anchors locally and verifies them via SHA-256. No network connection or API key required.

2. SDK Integration

The Python class is Witness, not SWT3Witness

The correct import is:

from swt3_ai import Witness

Not from swt3_ai import SWT3Witness. There is no SWT3Witness class.

What is the difference between wrap() and individual witness methods?

wrap(client) is the recommended approach. It creates a transparent proxy around your OpenAI, Anthropic, Bedrock, or LiteLLM client. Every inference is witnessed automatically with zero code changes.

Individual methods (e.g., witness_drift(), witness_hardware(), witness_rag_context()) are for custom pipelines where you control the exact procedure and factors.

Use wrap() for standard inference witnessing. Use individual methods for specialized procedures like RAG provenance, model weight attestation, or hardware health.

My fingerprints do not match across languages

The fingerprint formula is:

SHA256("WITNESS:{tenant_id}:{procedure_id}:{factor_a}:{factor_b}:{factor_c}:{fingerprint_timestamp_ms}").hex()[:12]

Common causes of mismatch:

  • Timestamp precision: The formula uses milliseconds, not seconds. A timestamp of 1774800000 (seconds) is wrong -- it should be 1774800000000 (milliseconds).
  • Tenant ID casing: Tenant IDs are case-sensitive. DEMO_TENANT and demo_tenant produce different fingerprints.
  • Factor types: Factors must be integers, not floats. 1.0 and 1 may produce different string representations depending on the language.

Verify against the published test vectors:

echo -n "WITNESS:ENCLAVE_PROD:AI-INF.1:1:1:0:1774800000000" | sha256sum | cut -c1-12
# Expected: 2e16e2fe92dd

The test-vectors.json file contains 55 fingerprint vectors verified across all 9 SDKs.

flush_interval is in seconds, not milliseconds

A common mistake: flush_interval=5000 means 5,000 seconds (83 minutes), not 5 seconds.

# Correct: flush every 5 seconds
witness = Witness(flush_interval=5.0)

# Wrong: flush every 83 minutes
witness = Witness(flush_interval=5000)

The default is 30 seconds. For testing, use flush_interval=1.0 or call witness.flush() manually.

What is the dead-letter queue and why is it growing?

When the SDK cannot reach the API (network timeout, server error, rate limit), it stores failed payloads in a dead-letter queue. These are retried automatically on the next flush cycle.

If the queue keeps growing:

  • Check connectivity: curl https://sovereign.tenova.io/api/v1/health
  • Check your API key is valid (not revoked).
  • Check you are not hitting the monthly anchor limit (OPEN tier: 100/month).
  • The queue is bounded in memory. If your process restarts, undelivered payloads are lost. For mission-critical environments, use signing_key for tamper detection and monitor the queue size.
How does the SDK handle network timeouts?

The Python and TypeScript SDKs never block your application. Witnessing is asynchronous:

  • The SDK queues anchors in memory.
  • A background flush sends them to the API.
  • If the API is unreachable, the SDK stores payloads in the dead-letter queue and retries on the next flush cycle.
  • Your inference call returns immediately -- witnessing never adds latency to your AI pipeline.

For the Rust, C#, and Ruby SDKs (core primitives), you handle HTTP transport yourself. The SDK provides fingerprint minting and signing only.

How do I set up a signing key?

Signing keys add tamper detection. The SDK signs each anchor's fingerprint with HMAC-SHA256, and the server verifies the signature.

# Python
witness = Witness(
    api_key="axm_live_...",
    tenant_id="YOUR_TENANT",
    signing_key="your-secret-key-here"
)

The server validates signatures progressively -- if you provide a signature, it must be valid. If you do not provide one, the anchor is accepted as unsigned.

Key rotation: Create a new Witness instance with the new key. The server accepts signatures from any registered key. After all clients are updated, revoke the old key.

What clearing level should I use?
LevelNameWhat the Server SeesUse When
0AnalyticsFull prompt, response, and metadataInternal testing, non-sensitive workloads
1StandardHashes of prompt/response + factorsProduction (recommended default)
2SensitiveHashes + factors only, no metadata beyond model_idPII-heavy workloads, healthcare, finance
3ClassifiedFactors only, model_id hashed, zero metadataDefense, SCIF, classified environments

Level 1 is the recommended default. It provides full auditability with zero exposure of sensitive content.

How do I check my SDK version?
# Python
pip show swt3-ai

# TypeScript
npm list @tenova/swt3-ai

# MCP
npm list @tenova/swt3-mcp

# Rust
cargo tree -p swt3-ai

# Ruby
gem list swt3-ai

Current version: 0.6.4 (Python, TypeScript, Rust, C#, Ruby, Swift, MCP). Kotlin: 0.1.1.

3. API Integration

I am getting a 401 Unauthorized error

The three most common causes:

  1. Missing header: The header must be exactly Authorization: Bearer axm_live_.... Note the capital "B" in Bearer and the space before the key.
  2. Wrong key format: Keys must start with axm_. If your key starts with anything else, it is not a valid SWT3 key.
  3. Revoked key: If the key was revoked (by you or another admin), it returns 401. Generate a new key at Settings > API Keys.
I am getting a 403 Forbidden error

403 means your authentication succeeded, but you lack permission:

  • "Enclave tier required" -- You are on OPEN or PRO tier. Webhooks, VC export, and some endpoints require Enclave ($9,500/mo) or higher.
  • "Assessor mode is read-only" -- You are logged in as an assessor. Assessors cannot create, modify, or delete resources.
  • "Admin role required" -- This action requires the admin role. Check your role at Settings.
  • "Agent revoked" -- The agent_id in your payload was revoked via AI-REV.1. Re-register the agent or use a different agent_id.
I am getting a 400 Validation Error

The error message tells you exactly what is wrong. Common causes:

  • "Unknown AI procedure" -- The procedure_id is not recognized. Check the UCT Registry for valid IDs (113 total).
  • "clearing_level must be 0-3" -- You passed a value outside this range.
  • "Anchor timestamp out of bounds" -- The fingerprint_timestamp_ms is more than 24 hours old or more than 5 minutes in the future. Sync your system clock via NTP.
  • "Anchor fingerprint validation failed" -- The factors in the request body do not match the fingerprint. Something modified the request body between SDK and server (proxy, middleware, serialization).
  • "Invalid lifecycle_chain_id format" -- Must match LC-[0-9a-f]{16} (the prefix "LC-" followed by exactly 16 hex characters).
I am getting a 429 Rate Limit Exceeded error

Two types of rate limits:

  1. Per-second limit: OPEN=10/s, PRO=50/s, ENCLAVE=200/s, SOVEREIGN=500/s. Use the Retry-After header to determine wait time. Implement exponential backoff.
  2. Monthly anchor limit: OPEN tier is limited to 100 anchors per calendar month. Upgrade to Pro for unlimited.

Optimization tips:

  • Use the batch endpoint (POST /api/v1/witness/batch) to send up to 500 anchors in one request.
  • Increase flush_interval to batch more anchors per flush cycle.
  • If you are hitting limits regularly, upgrade your tier.
I am getting a 502 error with an AXM-XXXX reference code

502 errors are transient upstream failures (database write, external service timeout). They are always retryable.

  • Wait 5-10 seconds and retry.
  • If the error persists for more than 5 minutes, check the health endpoint: curl https://sovereign.tenova.io/api/v1/health
  • Include the AXM-XXXX reference code when contacting support@tenovaai.com. This code links to server-side logs for fast diagnosis.
What Content-Type header should I use?

Always: Content-Type: application/json. The API only accepts JSON request bodies. If you omit this header, you may receive a 400 error or unexpected behavior.

How do I handle partial success in batch requests?

The batch endpoint validates each payload independently. The response includes both accepted and rejected counts, and rejected items include an error field:

{
  "results": [
    { "procedure_id": "AI-INF.1", "verdict": "PASS", "swt3_anchor": "SWT3-..." },
    { "procedure_id": "AI-FOO.1", "error": "Unknown AI procedure: AI-FOO.1" }
  ],
  "accepted": 1,
  "rejected": 1
}

Check the rejected count and handle individual errors. Do not retry the entire batch -- only retry rejected items after fixing the validation issue.

4. Webhook Integration

My webhook is not firing

Work through this checklist:

  1. Tier check: Webhooks require Enclave tier ($9,500/mo) or above. OPEN and PRO tiers do not have webhook access.
  2. Subscription active? Go to Settings > Webhooks. Verify is_active is true.
  3. Event types match? If you subscribed to verdict.failed but all your anchors are PASS, no webhook fires. Add verdict.issued to capture PASS verdicts too.
  4. URL reachable? Your endpoint must be HTTPS and publicly reachable. Localhost URLs do not work.
  5. Test it: Use POST /api/v1/webhooks/:id/test to send a test ping event. Check the delivery status at GET /api/v1/webhooks/:id/deliveries.
HMAC signature verification is failing

The X-SWT3-Signature header contains an HMAC-SHA256 hex digest of the raw request body using your webhook secret.

Common causes of verification failure:

  • Parsing before verifying: You must verify the signature against the raw bytes, not a parsed/re-serialized JSON string. Parsing may reorder keys or change whitespace.
  • Wrong secret: The secret (whsec_...) is shown once when you create the subscription. If lost, delete the subscription and create a new one.
  • Encoding mismatch: The signature is hex-encoded (not base64). Use hexdigest() not b64encode().

See the API Reference -- Webhooks for Python and TypeScript verification code.

What event types are available?
EventDescription
verdict.issuedA PASS verdict was recorded
verdict.failedA FAIL verdict was recorded
drift.detectedA control changed from PASS to FAIL
attestation.lapsedA manual attestation expired
score.thresholdSovereign Score dropped below threshold
hw.attestation.staleHardware attestation exceeds staleness window
hw.drift.detectedHardware configuration drift detected
pingTest event from the test endpoint
How do I check delivery history and failures?

Use GET /api/v1/webhooks/:id/deliveries to see delivery attempts, HTTP status codes, response bodies, and retry status. This helps you debug why deliveries are failing on your end (e.g., 500 from your server, TLS error, timeout).

5. Verification and Anchors

How do I verify an anchor?

Three methods, from simplest to most rigorous:

  1. Web UI: Go to sovereign.tenova.io/verify, paste the anchor token, and click Verify. No login required.
  2. API: GET /api/v1/verify/public?token=SWT3-E-VULTR-AI-AI-INF.1-PASS-...
  3. Terminal (offline): Re-derive the fingerprint from the factors:
    echo -n "WITNESS:{tenant}:{proc}:{fa}:{fb}:{fc}:{ts_ms}" | sha256sum | cut -c1-12
    If the output matches the last 12 characters of the anchor token, the factors are authentic.
What is the difference between structural verification and ledger verification?

Structural verification (public endpoint) confirms the anchor token is well-formed and the fingerprint is mathematically valid. It does not query the ledger.

Ledger verification (dashboard or Merkle proof) confirms the anchor exists in the immutable compliance ledger and has a valid Merkle inclusion proof.

For audits, use both: structural verification for quick checks, Merkle proofs for cryptographic certainty.

Why does my anchor look different from older anchors?

Legacy anchors minted before v5.21.0 may have slightly different formatting. Specifically, older anchors may strip hyphens and periods from procedure IDs (e.g., AI-GRD.1 becomes AIGRD1). The public verifier checks both formats automatically.

How does revocation work?

Revocation (AI-REV.1) creates a new anchor that marks a previous anchor as revoked. Seven reason codes:

CodeReason
0Unspecified
1Model recall
2Policy violation
3Data contamination
4Consent withdrawal
5Regulatory order
6Error correction

SDK: witness.revoke(fingerprint, reason="policy_violation")

An anchor exists but is not showing in the dashboard
  • Tenant mismatch: You may be logged into a different tenant than the one used by the SDK.
  • Ledger filters: Clear all filters (verdict, type, time range) on the Ledger page.
  • Retention: OPEN tier anchors expire after 7 days. If the anchor is older, it has been purged.
  • Eventual consistency: After a witness ingestion, there may be a 1-2 second delay before the anchor appears in dashboard queries.

6. Dashboard and Audit Portal

I cannot log in to the dashboard
  • Email + password login: Use the email and password you registered with at signup. If you forgot your password, click "Forgot password" on the login page.
  • MFA enrolled? If you enabled TOTP MFA, you need your authenticator app code after entering your password.
  • Browser cookies: The dashboard uses httpOnly session cookies. Ensure cookies are enabled and not blocked by browser extensions.
  • Incognito/private mode: Try a regular browser window. Some privacy extensions block session cookies.
How do I set up MFA (multi-factor authentication)?
  1. Log in to the dashboard.
  2. Go to Settings.
  3. Click Enable MFA.
  4. Scan the QR code with your authenticator app (Google Authenticator, Authy, 1Password, etc.).
  5. Enter the 6-digit code to confirm enrollment.

MFA uses TOTP (Time-based One-Time Password). The codes rotate every 30 seconds. Make sure your device clock is accurate.

How do I share audit access with an external assessor?
  1. Go to Settings > Auditor Share Links (Pro+ tier).
  2. Click Create Share Link.
  3. Send the generated URL to your assessor.

The assessor can view evidence, submit findings, and export the conformity evidence package. They cannot modify verdicts, attestations, or settings. No account creation required.

What can an assessor see vs. what they cannot?
Can SeeCannot See / Do
Controls catalogGap analysis (ISSM-only)
Compliance ledger (all verdicts)Settings or configuration
Anchor verificationCreate/revoke API keys
Evidence exportsManage tenants or users
Submit findingsModify verdicts or attestations
Merkle proofsEvidence ingestion
What export formats are available from the dashboard?
  • OSCAL: SSP, POA&M, Assessment Results (JSON, validated against NIST oscal-cli)
  • Compliance Passport: HTML, HMAC-signed JSON (Pro+), W3C Verifiable Credential (Enclave+)
  • Executive Summary: Self-contained HTML, printable to PDF
  • Gap-to-Green: Self-contained HTML with score ring and remediation roadmap
  • Ledger: CSV or JSON from the Ledger page
  • CVE Report: Printable HTML with POA&M status and SWT3 anchor
  • Conformity Evidence Package: SWT3 Spec v1.0.1 format (from audit portal)

7. Compliance and Exports

Which compliance frameworks are supported?

36 frameworks including: NIST 800-53, CMMC, NIST AI RMF 100, EU AI Act, FedRAMP, SR 11-7, NIST 800-171, ISO 42001, GPAI Code of Practice, NIST CSF 2.0, MITRE ATLAS, OWASP Top 10 for LLMs, and 24 more. See the UCT Registry for the full list with crosswalk mappings.

How do Compliance Passport format options differ?
FormatMinimum TierMachine-ReadableSelf-Verifiable
HTMLProNoNo
JSON (HMAC)ProYesServer-side only
W3C VC (Ed25519)EnclaveYesYes (any verifier)

The W3C Verifiable Credential format is self-verifiable -- anyone with the public key (published in the DID document at did:web:sovereign.tenova.io) can verify the passport without contacting TeNova.

What is the data retention policy by tier?
TierAnchor RetentionExport Retention
OPEN (Free)7 daysOn-demand (no storage)
PRO90 days90 days
ENCLAVE365 days365 days
SOVEREIGNUnlimitedUnlimited

After retention expires, anchors are purged from the ledger. Exported artifacts (OSCAL, Passport, reports) remain on your infrastructure.

How do crosswalk mappings work?

Crosswalks map procedures from one framework to another. For example, NIST 800-53 AC-2 maps to CMMC AC.L2-3.1.1. There are 1,057 crosswalk rows across 36 frameworks.

Use the API: POST /api/v1/crosswalks/resolve with source and target frameworks. Or browse the full matrix at GET /api/v1/crosswalks/matrix.

OSCAL export is failing or contains validation errors

All OSCAL exports are validated against NIST oscal-cli v3.1.0 (JRE 21) before delivery. If validation fails:

  • The export will still be returned but will include a validation status section.
  • Common issue: custom control IDs that do not match the NIST catalog. This does not affect the evidence quality.
  • For eMASS compatibility, use GET /api/v1/emass-guide which documents the 2 pre-import adjustments needed (system-id and POA&M risk-level property mapping).

8. Operations and Scaling

How do I monitor the API health?
curl -s https://sovereign.tenova.io/api/v1/health | jq .
# Expected: {"status":"healthy","checks":{"supabase":true},...}

Set up automated monitoring (Datadog, UptimeRobot, Pingdom) against this endpoint. Alert on:

  • HTTP status != 200
  • checks.supabase == false
  • Response time > 2 seconds
How do I rotate API keys without downtime?
  1. Create a new key at Settings > API Keys. (You can have up to 5 active keys.)
  2. Update your SDK configuration and CI/CD pipelines to use the new key.
  3. Verify anchors flow with the new key (check the ledger for new entries).
  4. Revoke the old key once you confirm no systems still use it.

Both keys work simultaneously during the transition. There is zero downtime.

How do I upgrade my tier?
  • Pro or Enclave: Go to Settings and click the upgrade button. Payment is processed via Stripe. Tier activation is instant.
  • Sovereign: This is a sales-led engagement ($125,000 ATO sprint). Contact support@tenovaai.com.

Upgrading preserves all existing anchors, API keys, and configuration. No migration required.

How do I deploy in an air-gapped environment?

SWT3 is designed for air-gapped deployment. The SDK works in local mode with zero network dependency:

  1. Install the SDK package via your internal mirror or manual file transfer.
  2. Initialize without an API key: Witness(tenant_id="YOUR_ENCLAVE")
  3. Anchors are stored in a local Write-Ahead Log (WAL).
  4. Verify anchors using the terminal SHA-256 method (no network required).
  5. Optionally sync to the cloud ledger later using .pulse bundles.

See the Self-Hosted Quick Start for detailed deployment patterns.

How do I optimize for high throughput?
  • Use batch ingestion: POST /api/v1/witness/batch accepts up to 500 anchors per request, reducing HTTP overhead.
  • Increase flush_interval: Larger intervals batch more anchors per flush cycle (e.g., flush_interval=30 seconds).
  • Upgrade tier: OPEN=10 req/s, PRO=50, ENCLAVE=200, SOVEREIGN=500.
  • The API handles ~227 anchors/second on the current infrastructure (benchmarked). This supports 19.6 million anchors per day.

9. Billing and Account

How do I manage my subscription?

Go to Settings > Manage Billing (Pro+ tiers). This opens the Stripe billing portal where you can:

  • Update your payment method
  • View invoices and payment history
  • Change your plan
  • Cancel your subscription
What happens if I cancel my subscription?

Your account is automatically downgraded to the OPEN (Free) tier. All existing data is preserved -- no data is deleted. However:

  • Anchor retention drops to 7 days (anchors older than 7 days are purged on a rolling basis).
  • Rate limit drops to 10 req/s and 100 anchors/month.
  • Webhook subscriptions are deactivated (Enclave+ feature).
  • Compliance Passport export is disabled (Pro+ feature).
What are the subscription tiers and pricing?
TierMonthlyAnnualKey Features
OPENFreeFree10 req/s, 100 anchors/mo, 7-day retention, local mode
PRO$499$4,99050 req/s, unlimited anchors, 90-day retention, Passport export, Auditor Share
ENCLAVE$9,500$102,000200 req/s, 365-day retention, W3C VC export, webhooks, agent governance
SOVEREIGNContact usContact us500 req/s, unlimited retention, dedicated support, ATO sprint
How do I check how many anchors I have used this month?

For OPEN tier, the monthly limit (100 anchors) is enforced server-side. When you hit the limit, witness requests return 429 with the message "Monthly anchor limit reached (100)". The counter resets on the 1st of each calendar month (UTC).

Pro, Enclave, and Sovereign tiers have unlimited monthly anchors.

10. Still Need Help?

If you have worked through this guide and the API Reference and still cannot resolve your issue:

Tip for faster resolution: When emailing support, include: (1) your tenant ID, (2) the endpoint you are calling, (3) the full error response including any AXM-XXXX code, and (4) your SDK version (pip show swt3-ai or npm list @tenova/swt3-ai). This eliminates the first round of back-and-forth.