Proof of Me
Proof of Me is anti-impersonation reverse verification. Instead of proving your own identity, you let trusted contacts confirm that a person reaching out to them is really you.
An account holder enrolls named members (trusted contacts — colleagues, family, clients) on a Circle. When someone suspicious contacts a member claiming to be you, the member taps a button and you get a CONFIRM request on a different channel than the impersonator used. You approve or deny; the member is told the result. Drills exercise the flow on a schedule so members stay trained.
Proof of Me is built on
Circle, the people primitive. It was originally attached to a HITL config and enrolled "challengers"; that surface was removed and the member-management endpoints now live under/api/v1/circles.
How it works
- Enroll — add a member to a Circle, then mint a single-use invite. The member taps the Telegram/WhatsApp deep link, binding their messenger identity to your Circle.
- Challenge — when a member is contacted by a suspicious party, they trigger an identity challenge. You receive a cross-channel CONFIRM and approve/deny.
- Drill — automated daily drills and monthly reinforcement messages keep members sharp. You can also fire an on-demand drill.
The Circle endpoints are dual-auth (JWT dashboard session or API key). An API key needs circles:create to create a Circle, circles:read to read one, and circles:write for every member mutation. The identity-challenge endpoints are also dual-auth and use hitl:write (create) and hitl:read (poll).
Endpoints
| Operation | Method | Path |
|---|---|---|
| Add member | POST | /api/v1/circles/:id/members |
| List members | GET | /api/v1/circles/:id/members |
| Invite member | POST | /api/v1/circles/:id/members/invite |
| Trigger drill | POST | /api/v1/circles/:id/members/:memberId/drill |
| Remove member | DELETE | /api/v1/circles/:id/members/:memberId |
| Create identity challenge | POST | /api/v1/identity-challenges |
| Get identity challenge | GET | /api/v1/identity-challenges/:id |
A member can also carry more than one messenger. The per-member channel endpoints are POST/GET /api/v1/circles/:id/members/:memberId/channels and DELETE /api/v1/circles/:id/members/:memberId/channels/:channelId.
SDK usage
Member management lives on the circles / Circles resource in every official SDK.
JavaScript / TypeScript
import { Proof } from '@proof-holdings/sdk';
const proof = new Proof('pk_live_...');
// Enroll a member and invite them
const member = await proof.circles.addMember(circleId, { name: 'Alex' });
const invite = await proof.circles.inviteMember(circleId, member.id);
console.log(invite.telegram_deep_link); // send this to Alex
// Keep them trained
await proof.circles.triggerDrill(circleId, member.id);Python
from proof_sdk import Proof
async with Proof("pk_live_...") as proof:
member = await proof.circles.add_member(circle_id, name="Alex")
invite = await proof.circles.invite_member(circle_id, member["id"])
await proof.circles.trigger_drill(circle_id, member["id"])Go
client, _ := proof.NewClient("pk_live_...")
member, _ := client.Circles.AddMember(ctx, circleID, map[string]any{
"name": "Alex",
})
invite, _ := client.Circles.InviteMember(ctx, circleID, member["id"].(string))
_, _ = client.Circles.TriggerDrill(ctx, circleID, member["id"].(string))PHP
use ProofHoldings\Proof;
$proof = new Proof('pk_live_...');
$member = $proof->circles->addMember($circleId, ['name' => 'Alex']);
$invite = $proof->circles->inviteMember($circleId, $member['id']);
$proof->circles->triggerDrill($circleId, $member['id']);Raising an identity challenge
The challenge itself is one POST naming the Circle and the member, then a poll until it reaches a terminal state:
curl -X POST https://api.proof.holdings/api/v1/identity-challenges \
-H "Authorization: Bearer pk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"circle_id": "CIRCLE_ID",
"member_id": "MEMBER_ID"
}'curl https://api.proof.holdings/api/v1/identity-challenges/CHALLENGE_ID \
-H "Authorization: Bearer pk_live_YOUR_API_KEY"suspicious_channel is an optional third field naming the channel the impersonator used, so the CONFIRM is routed somewhere else.
MCP tools
The MCP server exposes the same surface for AI agents:
add_circle_memberlist_circle_membersinvite_circle_membercreate_circle_member_drillremove_circle_membercreate_identity_challengeget_identity_challenge
After create_identity_challenge, poll get_identity_challenge until it reaches a terminal state (active = confirmed, denied, or expired).