APTOGON
ManifestSolutionsDevelopersPricingBlogDonateVerify

For Developers

Add human verification to your app with a drop-in snippet. Create a key in the console, embed the verifier, and confirm each session server-side with your secret key.

POST https://homosapience.org/api/embed/verify
Get API key →API reference ↓

Quick Start

Minimal integration — 3 steps

1Get your API keys
// In the APTOGON console (/console): create a key, then verify your domain.
// pk_live_…  — publishable key, safe to ship in the browser
// sk_live_…  — secret key, server-side only (shown once at creation)
2Drop in the verifier (browser)
<!-- Declarative: renders a "Verify you're human" button -->
<script src="https://homosapience.org/embed/v1/aptogon.js"
        data-aptogon-key="pk_live_…"></script>
<div data-aptogon-verify data-on-success="onHuman"></div>

<script>
  // …or call it programmatically (opens the APTOGON signer popup):
  async function verify() {
    const { token, trust_band } = await window.Aptogon.verify({
      publishableKey: 'pk_live_…',
    })
    // send `token` to your backend to confirm it
    await fetch('/my/confirm', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ token }),
    })
  }
</script>
3Confirm server-side (secret key)
// On YOUR server — never expose sk_live_ to the browser
const res = await fetch('https://homosapience.org/api/embed/verify', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer sk_live_…',
  },
  body: JSON.stringify({ token }),
})
const { human, did_hash, trust_band } = await res.json()
if (human) {
  // verified, unique human — trust_band ∈ newcomer | community | trusted
  // did_hash is anonymous (no link to the user's real identity)
}

What your server receives — and what it never touches

Every successful /api/embed/verify call returns exactly three fields. Nothing personal is ever transmitted.

✅ You receive
  • human: true — a real person did this action, right now
  • did_hash — anonymous fingerprint to enforce one-human-one-action
  • trust_bandnewcomer | community | trusted
🚫 You never receive (or store)
  • No name, email, phone, or government document
  • No biometric template — unlike iris or palm systems
  • GDPR-native: no consent banner required for the check, no biometric data liability
Store did_hash in your database to enforce "one human per action" across sessions — it is a one-way hash with no link to real identity. Never store the raw DID.

API Reference

Base URL: https://homosapience.org/api

All requests require Authorization: Bearer YOUR_KEY. Use sk_live_ server-side, pk_live_ in the browser.

Get your keys in the developer console.

POST/api/embed/challengeCreate a new verification challenge
Request
application/json
{
  "domain": "yourplatform.com",
  "session_id": "unique-session-identifier",
  "locale": "en"          // optional — defaults to "en"
}
Response 200
application/json
{
  "challenge_id": "chal_7f4a2c9e1b...",
  "nonce": "7f4a2c9e1b3d4a5f...",
  "expires_at": 1732801234,
  "gesture_path": [3, 1, 4, 1, 5, 9],
  "widget_url": "https://homosapience.org/embed/verify?c=chal_..."
}
challenge_id expires in 60 seconds. gesture_path is the sequence of gesture segments the user must trace.
POST/api/embed/assertSubmit gesture proof and verify liveness
Request
application/json
{
  "challenge_id": "chal_7f4a2c9e1b...",
  "did": "did:aptogon:device:abc123...",
  "gesture_hash": "sha3_256_of_gesture_data",
  "signature": "ed25519_signature_of_nonce+gesture_hash",
  "canvas_fp": "webgl_canvas_fingerprint"
}
Response 200
application/json
{
  "token": "tok_9f3b1d7c...",
  "trust_band": "community",
  "did_hash": "sha256_anonymous_hash",
  "expires_at": 1732887634
}
Token is single-use. Pass it to POST /api/embed/verify on your server. The aptogon.js widget handles this endpoint automatically.
POST/api/embed/verifyServer-to-server: redeem token with your secret key
Request
application/json
// Authorization: Bearer sk_live_…  (never expose in browser)
{
  "token": "tok_9f3b1d7c..."
}
Response 200
application/json
{
  "human": true,
  "did_hash": "sha256_anonymous_hash",
  "trust_band": "community",   // newcomer | community | trusted
  "issued_at": 1732801234
}
This is the billable call. Use sk_live_ only on your server. Returns human: false if token is expired, tampered, or already redeemed.
GET/api/verify/statusLook up the verification status of a DID
Request
query params
GET /api/verify/status?did=did:aptogon:device:abc123...
Response 200
application/json
{
  "is_human": true,
  "trust_band": "community",
  "valid_until": 1732887634,
  "bond_count": 12
}
Use for long-lived sessions where you want to re-check without a new gesture. Returns is_human: false if credential has expired.

Embed widget (iframe)

Alternative to the JS snippet: embed the widget in an iframe. On success, a postMessage event fires with the token.

HTML + JS
<!-- 1. Embed iframe with widget_url from POST /embed/challenge -->
<iframe
  id="aptogon-widget"
  src="https://homosapience.org/embed/verify?c=CHALLENGE_ID"
  width="400" height="280"
  style="border:none;border-radius:12px"
></iframe>

<script>
  window.addEventListener('message', (event) => {
    if (event.origin !== 'https://homosapience.org') return
    if (event.data.type === 'aptogon:verified') {
      const { token } = event.data
      // Send token to your backend → POST /api/embed/verify
      fetch('/your-backend/confirm', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ token }),
      })
    }
  })
</script>

SDK examples

No official SDK yet — full flow using standard HTTP libraries.

cURL
# 1. Create challenge (server-side)
curl -X POST https://homosapience.org/api/embed/challenge \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"domain":"yourplatform.com","session_id":"sess_abc123"}'

# 2. After widget completes, redeem token (server-side)
curl -X POST https://homosapience.org/api/embed/verify \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"token":"tok_9f3b1d7c..."}'
Python
import requests

BASE = "https://homosapience.org/api"
SK = "sk_live_…"
HEADERS = {"Authorization": f"Bearer {SK}", "Content-Type": "application/json"}

def create_challenge(domain: str, session_id: str) -> dict:
    r = requests.post(f"{BASE}/embed/challenge", headers=HEADERS,
                      json={"domain": domain, "session_id": session_id})
    r.raise_for_status()
    return r.json()

def verify_token(token: str) -> dict:
    r = requests.post(f"{BASE}/embed/verify", headers=HEADERS,
                      json={"token": token})
    r.raise_for_status()
    return r.json()

# After widget completes and sends token to your backend:
result = verify_token("tok_9f3b1d7c...")
if result["human"] and result["trust_band"] in ("community", "trusted"):
    print("Access granted")
Node.js
const BASE = 'https://homosapience.org/api'
const SK = 'sk_live_…'
const h = { Authorization: `Bearer ${SK}`, 'Content-Type': 'application/json' }

async function createChallenge(domain, sessionId) {
  const r = await fetch(`${BASE}/embed/challenge`, {
    method: 'POST', headers: h,
    body: JSON.stringify({ domain, session_id: sessionId }),
  })
  if (!r.ok) throw new Error(await r.text())
  return r.json()
}

async function verifyToken(token) {
  const r = await fetch(`${BASE}/embed/verify`, {
    method: 'POST', headers: h,
    body: JSON.stringify({ token }),
  })
  if (!r.ok) throw new Error(await r.text())
  return r.json()
}

// In your route handler (e.g. Express):
app.post('/confirm', async (req, res) => {
  const { human, did_hash, trust_band } = await verifyToken(req.body.token)
  if (!human) return res.status(403).json({ error: 'not_human' })
  // trust_band ∈ newcomer | community | trusted
  res.json({ ok: true, did_hash, trust_band })
})

Trust bands

Every verification returns a trust_band. Use it for access control.

trusted
score 1.0
Hardware-attested DID, clean gesture, established bond graph. Safe to grant full access.
community
score 0.5–0.99
Verified DID, gesture passed, some social history. Suitable for most use cases.
newcomer
score 0.1–0.49
Gesture passed, new DID with no bond history yet. Consider step-up verification or restricted access.

Error codes

HTTPError codeDescription
400invalid_requestMissing required field or malformed JSON
401unauthorizedMissing or invalid API key in Authorization header
403domain_not_allowedRequest domain not registered for this API key
404not_foundchallenge_id or token does not exist
409challenge_expiredchallenge_id has exceeded its 60-second validity window
409token_already_usedToken was already redeemed — tokens are single-use
422gesture_rejectedGesture failed liveness check — synthetic input detected
422did_cluster_flaggedDID was flagged by cluster detection as likely sybil
429rate_limitedMonthly verification quota exceeded for your plan tier
500server_errorInternal error — retry with exponential backoff

Rate limits & quotas

Limits are per API key per calendar month. Burst: 100 req/min per key. Headers: X-RateLimit-Limit · X-RateLimit-Remaining · X-RateLimit-Reset

TierMonthly volumePrice / verification
Developer1,000 /mo$0
Starterup to 10K /mo$0.05
Growth10K–100K /mo$0.03
Scale100K–1M /mo$0.01
Enterprise1M+ /moNegotiated

Features

🔐
Zero PII

Raw gesture coordinates never leave the browser. Only statistics go to the server.

🌐
W3C DID

Compatible with the did:key standard. Works with any identity system.

⛓️
On-chain proof

ExpressionProof hash recorded on Aptos testnet. Move contract in progress — mainnet coming soon.

Accessibility

Accounts for motor impairments. People with tremors are never blocked.

Fast

Median 800ms. Gonka AI with fallback — works even when service is unavailable.

📋
Verifiable Credential

W3C VC format. Signed with Ed25519. Expires in 30 days, auto-renewed.

Ready to integrate?

Create a key in the console to get started. Contact us for higher quotas or enterprise integration.