APTOGON
ManifestSolutionsDevelopersPricingBlogDonateVerify

Für Entwickler

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 ↓

Schnellstart

Minimale Integration — 3 Schritte

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/captcha/siteverifyGesture CAPTCHA — validate a token on your server (S2S)
Request
application/json
// Authorization: Bearer sk_live_…  (never expose in browser)
{
  "token": "<aptogon-response from the submitted form>"
}
Response 200
application/json
{
  "success": true,
  "human": true,
  "band": "community",          // newcomer | community | trusted
  "hostname": "https://your-site.com",
  "issued_at": 1732801234
}
The billable call. Token is single-use, expires in 120s. Returns 409 if already redeemed, 403 on audience mismatch, 429 on quota cap. The aptogon.js v2 loader produces the token client-side.
POST/api/captcha/verifyGesture CAPTCHA — classify a gesture (called by the iframe)
Request
application/json
{
  "publishable_key": "pk_live_…",
  "origin": "https://your-site.com",
  "events": [ /* captured gesture points */ ],
  "session_id": "uuid"
}
Response 200
application/json
{
  "token": "<signed JWT — pass to /siteverify>",
  "human": true,
  "band": "community"
}
The embedded iframe calls this automatically; you normally do not call it directly. Fails closed (503) if the classifier is unavailable — never emits human:true when down.
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.

Gesture CAPTCHA (drop-in)

A privacy-first alternative to image CAPTCHAs — the visitor draws a gesture in an inline iframe. Add the loader + a data-aptogon-captcha element, then validate the token on your server. First: verify your domain and create a key in the Console.

1 · Client (HTML)
<script src="https://homosapience.org/embed/v2/aptogon.js"></script>

<form action="/signup" method="post">
  <!-- your fields … -->
  <div data-aptogon-captcha data-aptogon-key="pk_live_YOUR_KEY"></div>
  <button type="submit">Sign up</button>
</form>
<!-- On success the loader adds a hidden input[name="aptogon-response"]. -->
2 · Server (validate the token, S2S)
curl -s https://homosapience.org/api/captcha/siteverify \
  -H "Authorization: Bearer sk_live_YOUR_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"token": "<aptogon-response from the submitted form>"}'

# → { "success": true, "human": true, "band": "…",
#     "hostname": "https://your-site.com", "issued_at": 173… }

Programmatic API: window.AptogonCaptcha.render(el, { key, onVerified }). Tokens are single-use and expire in 120s. Live demo: /embed/captcha-demo.html.

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

Funktionen

🔐
Zero PII

Rohe Gestenkoordinaten verlassen den Browser nie. Nur Statistiken gehen zum Server.

🌐
W3C DID

Kompatibel mit dem did:key-Standard. Funktioniert mit jedem Identitätssystem.

⛓️
On-Chain-Beweis

Move-Vertrag auf Aptos. Verifizierung ohne unseren Server — vollständig dezentralisiert.

Barrierefreiheit

Berücksichtigt motorische Beeinträchtigungen. Menschen mit Tremor werden nie blockiert.

Schnell

Median 800ms. Gonka AI mit Fallback — funktioniert auch wenn der Dienst nicht verfügbar ist.

📋
Verifiable Credential

W3C VC-Format. Signiert mit Ed25519. Läuft in 30 Tagen ab, wird automatisch erneuert.

Bereit zur Integration?

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