Developer Documentation

Integrate Proof of Humanity (PoH) verification into your website or application.

1. Overview

The PoH Service provides a robust and user-friendly alternative to traditional CAPTCHAs. By leveraging WebAuthn biometric and passkey technology, you can verify that users interacting with your platform are genuine humans, significantly reducing bot activity, spam, and fraudulent sign-ups.

Integration involves a client-side JavaScript widget for the end-user verification flow, followed by a mandatory server-to-server API call from your backend to ours to authoritatively confirm the user's humanity status.

2. Getting Started

Follow these steps to start using the PoH Service:

  1. Sign Up for a Business Account:Visit our Business Dashboard Registration to create your account. (In production, this link would be: https://poh-dashboard-gqns.vercel.app/dashboard/register)
  2. Generate Your API Key:After signing up and logging into your dashboard, navigate to the "API Keys" section. Click "Generate New API Key."
    • You will be provided with a full_key. This is your secret API key.
    • The full_key is used in two places:
      • Passed to the data-poh-client-api-key attribute when embedding the widget.
      • Used in the X-API-Key header for server-to-server calls to our /poh/check API.
    • Important: The full_key is displayed only once upon creation. Copy it immediately and store it securely in your backend environment variables. Do not expose it in your client-side frontend code (other than in the widget's data attribute, which our script reads).

3. Widget Integration Guide

Our JavaScript widget handles the end-user PoH verification flow.

3.1. Embed the Widget Script

Include the following script tag on the pages where you want to offer PoH verification. It's best placed just before the closing </body> tag.

<script src="https://poh-dashboard-gqns.vercel.app/widget.js" async defer></script>

3.2. Configure the Widget

Place an empty <div> element where you want the PoH verification button to appear. Configure it with these data- attributes:

<div
    class="poh-widget-button-container" id="myUniquePoHWidget" data-poh-client-api-key="YOUR_FULL_BUSINESS_API_KEY_HERE" data-poh-button-text="Verify I'm Human" data-poh-verify-url="https://poh-dashboard-gqns.vercel.app/widget-verify" >
    <p>Loading PoH Verification...</p> </div>

Attribute Details:

  • class="poh-widget-button-container": (Required) Our widget script uses this class to find where to render the button.
  • id (Optional): If you have multiple PoH widgets on a single page, providing a unique ID helps in managing events or specific instances. The widget will include this ID in the detail of dispatched events if provided.
  • data-poh-client-api-key (Required): Your **full** Business API Key. This identifies your business to our service.
  • data-poh-button-text (Optional): Custom text for the verification button (e.g., "Prove Humanity", "Start Verification"). Defaults to "Verify I'm Human".
  • data-poh-verify-url (Required): Must be set exactly to https://poh-dashboard-gqns.vercel.app/widget-verify. This is the URL of our dedicated verification page that opens in the popup.

3.3. Handling Widget Events

Once the user interacts with the widget and completes (or fails) the PoH verification in the popup, our widget script will dispatch custom events on your page's window object.

  • pohVerificationSuccess: Fired when the end-user successfully completes the biometric verification. The event.detail object will contain:
    • pohUsername: (String) The username the end-user verified with in the PoH system. You will send this to your backend.
    • message: (String) A success message from our service.
    • widgetInstanceId: (String, optional) The ID of the widget container that triggered this event, if you provided an ID.
  • pohVerificationFailure: Fired if the verification process fails, is canceled by the user, or an error occurs. The event.detail object will contain:
    • error: (String) A message describing the reason for failure.
    • widgetInstanceId: (String, optional) The ID of the widget container.

Example JavaScript to listen for these events:

window.addEventListener('pohVerificationSuccess', function(event) {
    console.log('PoH Verification Succeeded on Frontend!', event.detail);
    const pohUsername = event.detail.pohUsername;

    if (pohUsername) {
        // TODO: Send 'pohUsername' to YOUR backend server.
        // Your backend will then call our /poh/check API for final confirmation.
        // Example:
        // fetch('/your-server-endpoint/confirm-humanity', {
        //   method: 'POST',
        //   headers: { 'Content-Type': 'application/json' },
        //   body: JSON.stringify({ usernameToVerify: pohUsername })
        // })
        // .then(response => response.json())
        // .then(data => {
        //   if (data.isVerifiedByYourBackend) {
        //     // Unlock content, proceed with action, etc.
        //   } else {
        //     // Handle case where your backend couldn't confirm
        //   }
        // });
        alert('PoH Frontend: User ' + pohUsername + ' verified. Your backend should now call /poh/check.');
    }
});

window.addEventListener('pohVerificationFailure', function(event) {
    console.error('PoH Verification Failed on Frontend:', event.detail);
    alert('PoH Frontend: Verification failed. Reason: ' + event.detail.error);
    // Handle the failure in your UI (e.g., show an error message).
});

4. Server-Side Verification (/poh/check API)

4.1. Why is Server-Side Confirmation Mandatory?

Client-side signals can be spoofed. For true security, after receiving a success event from the widget on your frontend, your backend server must make a direct, authenticated API call to our /poh/check endpoint using your secret Business API Key.

4.2. Calling the /poh/check API

  • Method: GET
  • Endpoint: https://poh-service-api.fly.dev/poh/check
  • Query Parameters:
    • username: (String, Required) The pohUsername you received from the pohVerificationSuccess widget event.
  • Required HTTP Headers:
    • X-API-Key: Your full Business API Key (secret).

4.3. API Response Structure

On Success (HTTP 200 OK):

The API will return a JSON object:

{
  "username": "string",
  "isHuman": true,
  "checked_at": "YYYY-MM-DDTHH:mm:ssZ"
}

Your application should primarily check the isHuman field.

On Error:

  • 400 Bad Request: Usually indicates a missing or malformed username query parameter. The response body might be plain text or JSON like {"error": "Descriptive message"}.
  • 401 Unauthorized: Invalid, missing, or inactive X-API-Key header. Response body details similar to 400.
  • 404 Not Found: While the endpoint itself exists, this could be returned if, for example, an invalid path was somehow constructed. A user not found in the PoH system will result in a 200 OK with {"isHuman": false}.
  • 500 Internal Server Error: Indicates an issue on our PoH service side.

4.4. Backend Code Examples for /poh/check

Node.js (using `axios`):

const axios = require('axios');

async function confirmPohUser(pohUsername, yourBusinessApiKey) {
  const POH_API_URL = "https://poh-service-api.fly.dev/poh/check";
  try {
    const response = await axios.get(POH_API_URL, {
      params: { username: pohUsername },
      headers: { 'X-API-Key': yourBusinessApiKey }
    });
    if (response.data && response.data.isHuman === true) {
      console.log('User "' + pohUsername + '" is verified as human by PoH service.');
      return { verified: true, details: response.data };
    } else {
      console.log('User "' + pohUsername + '" is NOT verified or not found. Response:', response.data);
      return { verified: false, details: response.data };
    }
  } catch (error) {
    let errorDetails = error.message;
    if (error.response && error.response.data) {
        errorDetails = typeof error.response.data === 'string' ? error.response.data : JSON.stringify(error.response.data);
    }
    console.error('Error calling PoH /check API:', errorDetails);
    return { verified: false, error: errorDetails };
  }
}

// Example usage:
// const result = await confirmPohUser('userFromWidgetEvent', 'YOUR_BUSINESS_FULL_API_KEY');
// if (result.verified) { /* Allow action */ }

Python (using `requests`):

import requests
import json

POH_API_URL = "https://poh-service-api.fly.dev/poh/check"

def confirm_poh_user(poh_username, business_api_key):
    headers = {'X-API-Key': business_api_key}
    params = {'username': poh_username}
    try:
        response = requests.get(POH_API_URL, headers=headers, params=params)
        response.raise_for_status()
        data = response.json()
        if data.get('isHuman') is True:
            print(f"User '{poh_username}' is verified as human.")
            return {"verified": True, "details": data}
        else:
            print(f"User '{poh_username}' is NOT verified or not found. Response: {data}")
            return {"verified": False, "details": data}
    except requests.exceptions.RequestException as e:
        error_details = str(e)
        if e.response is not None:
            try:
                error_details = e.response.json()
            except json.JSONDecodeError:
                error_details = e.response.text
        print(f"Error calling PoH /check API: {error_details}")
        return {"verified": False, "error": error_details}

# Example usage:
# result = confirm_poh_user('userFromWidgetEvent', 'YOUR_BUSINESS_FULL_API_KEY')
# if result["verified"]: # Allow action

5. Security Best Practices

  • Always keep your full Business API Key SECRET and store it securely on your backend server environment (e.g., as an environment variable).
  • The data-poh-client-api-key attribute in the widget is read by our client-side script to initiate the flow with your account; while visible in HTML source, its primary secure use is for the server-to-server /poh/check call.
  • The server-to-server call to /poh/check is the authoritative step for verification. Do not grant access or trust based solely on the client-side widget success event.
  • Monitor API key usage (feature coming to your dashboard).
  • Revoke and regenerate API keys immediately if you suspect they have been compromised.
  • Ensure the parentOrigin passed from your widget to our verification page (this is handled automatically by widget.js using window.location.origin) is your website's correct origin for secure postMessage communication. Our verification page will only post messages back to this specified origin.

6. Support & FAQ

Q: What if the user doesn't have biometrics?

A: WebAuthn supports various authenticators, including hardware security keys (like YubiKey) and, on some platforms, device PINs or patterns if biometrics aren't available or enabled. Our flow leverages what the user's browser and OS support.

Q: How "bot-proof" is this?

A: WebAuthn with biometrics/passkeys significantly raises the bar for automated attacks compared to CAPTCHAs. It requires interaction with a genuine authenticator. While no system is 100% infallible against highly sophisticated targeted attacks, this provides a very strong layer of defense against common botnets and automated scripts.

For further questions or support, please reach out to us at emumo211@gmail.com.