Identity layer for autonomous AI agents — Ed25519 verification, trust scoring, audit logging, and CAPTCHA escalation.
Base URL: http://38.49.210.10:3846
Version: 0.1.0
curl http://38.49.210.10:3846/.well-known/agentpass.json{
"name": "AgentPass",
"version": "0.1.0",
"description": "Identity layer for autonomous AI agents",
"endpoints": {
"passports": "/passports",
"verify": "/verify",
"audit": "/passports/:id/audit",
"webhook": "/webhook/email-received",
"telegram": "/telegram/link/:email"
},
"capabilities": ["ed25519-verification", "trust-scoring", "audit-logging"]
}AgentPass supports two authentication methods:
Obtained via /auth/login or /auth/register. Pass in the Authorization header:
Authorization: Bearer <jwt_token>
Created via /api-keys. Also passed in the Authorization header:
Authorization: Bearer apk_xxxxxxxxxxxxxxxx
Note: API keys cannot manage other API keys — only JWT auth can create/list/revoke keys.
For webhook endpoints, use the X-Webhook-Secret header:
X-Webhook-Secret: <secret>
All errors return JSON with error (message) and code (machine-readable) fields:
{
"error": "Passport not found",
"code": "NOT_FOUND"
}| HTTP Status | Code | Description |
|---|---|---|
| 400 | VALIDATION_ERROR |
Invalid request body / missing fields |
| 401 | AUTH_REQUIRED |
Missing or invalid authentication |
| 401 | AUTH_FAILED |
Wrong credentials or invalid signature |
| 403 | FORBIDDEN |
Access denied (not the owner) |
| 403 | PASSPORT_REVOKED |
Passport has been revoked |
| 404 | NOT_FOUND |
Resource not found |
| 409 | EMAIL_EXISTS |
Email already registered |
| 409 | CONFLICT |
Resource already exists |
| 409 | ALREADY_REVOKED |
Already revoked |
| 409 | ALREADY_RESPONDED |
Approval already responded to |
| 409 | ALREADY_RESOLVED |
Escalation already resolved |
| 409 | ALREADY_CLOSED |
Session already closed |
| 409 | SESSION_CLOSED |
Cannot interact with closed session |
| 500 | INTERNAL_ERROR |
Server error |
| 500 | CONFIG_ERROR |
Server misconfiguration |
POST /auth/register
curl -X POST http://38.49.210.10:3846/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "owner@example.com",
"password": "securepassword123",
"name": "Alice"
}'Response 201:
{
"owner_id": "550e8400-e29b-41d4-a716-446655440000",
"email": "owner@example.com",
"name": "Alice",
"token": "eyJhbGciOi..."
}POST /auth/login
curl -X POST http://38.49.210.10:3846/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "owner@example.com",
"password": "securepassword123"
}'Response 200:
{
"owner_id": "550e8400-e29b-41d4-a716-446655440000",
"email": "owner@example.com",
"name": "Alice",
"token": "eyJhbGciOi..."
}GET /auth/me
🔒 Requires JWT
curl http://38.49.210.10:3846/auth/me \
-H "Authorization: Bearer <token>"Response 200:
{
"owner_id": "550e8400-...",
"email": "owner@example.com",
"name": "Alice",
"verified": false,
"created_at": "2026-02-20T10:00:00.000Z"
}POST /auth/logout
Response 200:
{ "ok": true }GET /passports?limit=50&offset=0
🔒 Requires Auth — returns only passports owned by the authenticated user.
curl http://38.49.210.10:3846/passports \
-H "Authorization: Bearer <token>"Response 200:
{
"passports": [
{
"id": "ap_abc123def456",
"public_key": "base64-ed25519-pubkey",
"owner_email": "owner@example.com",
"name": "my-agent",
"description": "My AI assistant",
"trust_score": 35,
"trust_level": "basic",
"status": "active",
"metadata": { "owner_verified": false, "payment_method": false, "abuse_reports": 0 },
"created_at": "2026-02-20T10:00:00.000Z",
"updated_at": "2026-02-20T10:00:00.000Z"
}
],
"total": 1,
"limit": 50,
"offset": 0
}POST /passports
🔒 Requires Auth · Rate limited
curl -X POST http://38.49.210.10:3846/passports \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"public_key": "base64-ed25519-public-key",
"name": "my-agent",
"description": "My AI assistant"
}'| Field | Type | Required | Description |
|---|---|---|---|
passport_id |
string | No | Custom ID (format: ap_xxxxxxxxxxxx). Auto-generated if omitted. |
public_key |
string | Yes | Ed25519 public key (base64) |
name |
string | Yes | 1–64 chars, alphanumeric/hyphens/underscores |
description |
string | No | Up to 256 chars |
Response 201:
{
"passport_id": "ap_abc123def456",
"email": "my-agent@agent-mail.xyz",
"created_at": "2026-02-20T10:00:00.000Z"
}GET /passports/:id
🔒 Requires Auth (owner only)
curl http://38.49.210.10:3846/passports/ap_abc123def456 \
-H "Authorization: Bearer <token>"Response 200: Full passport object (same shape as list item).
DELETE /passports/:id
🔒 Requires Auth + Ed25519 Signature
The X-AgentPass-Signature header must contain a valid Ed25519 signature of the passport ID, signed with the passport's private key.
curl -X DELETE http://38.49.210.10:3846/passports/ap_abc123def456 \
-H "Authorization: Bearer <token>" \
-H "X-AgentPass-Signature: base64-signature-of-passport-id"Response 200:
{ "revoked": true }POST /verify
🔓 Public (rate limited)
Verifies an agent's identity using Ed25519 challenge-response. The agent signs a challenge string with its private key; the server verifies against the stored public key.
curl -X POST http://38.49.210.10:3846/verify \
-H "Content-Type: application/json" \
-d '{
"passport_id": "ap_abc123def456",
"challenge": "random-challenge-string",
"signature": "base64-ed25519-signature"
}'Response 200:
{
"valid": true,
"passport_id": "ap_abc123def456",
"trust_score": 35,
"trust_level": "basic",
"status": "active"
}On successful verification, the trust score is recalculated automatically.
All trust routes are scoped to /passports/:id/trust and require owner auth.
GET /passports/:id/trust
🔒 Requires Auth (owner only)
curl http://38.49.210.10:3846/passports/ap_abc123def456/trust \
-H "Authorization: Bearer <token>"Response 200:
{
"passport_id": "ap_abc123def456",
"trust_score": 35,
"trust_level": "basic",
"factors": {
"owner_verified": false,
"payment_method": false,
"age_days": 5,
"successful_auths": 12,
"abuse_reports": 0
}
}PATCH /passports/:id/trust/verify-owner
🔒 Requires Auth (owner only)
Sets the owner_verified flag and recalculates trust score.
curl -X PATCH http://38.49.210.10:3846/passports/ap_abc123def456/trust/verify-owner \
-H "Authorization: Bearer <token>"Response 200: Same shape as GET trust details.
PATCH /passports/:id/trust/payment-method
🔒 Requires Auth (owner only)
Sets the payment_method flag and recalculates trust score.
curl -X PATCH http://38.49.210.10:3846/passports/ap_abc123def456/trust/payment-method \
-H "Authorization: Bearer <token>"Response 200: Same shape as GET trust details.
POST /passports/:id/report-abuse
🔒 Requires Auth
curl -X POST http://38.49.210.10:3846/passports/ap_abc123def456/report-abuse \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "reason": "Spamming service X" }'Response 200:
{
"passport_id": "ap_abc123def456",
"trust_score": 15,
"trust_level": "untrusted",
"abuse_reports": 1
}GET /audit?limit=50&offset=0
🔒 Requires Auth — returns entries across all owner's passports.
curl http://38.49.210.10:3846/audit \
-H "Authorization: Bearer <token>"Response 200:
{
"entries": [
{
"id": "uuid",
"passport_id": "ap_abc123def456",
"action": "verify",
"service": "agentpass",
"method": "challenge-response",
"result": "success",
"duration_ms": 0,
"details": { "challenge": "..." },
"created_at": "2026-02-20T10:05:00.000Z"
}
],
"total": 1,
"limit": 50,
"offset": 0
}GET /passports/:id/audit?limit=50&offset=0
🔒 Requires Auth (owner only)
curl http://38.49.210.10:3846/passports/ap_abc123def456/audit \
-H "Authorization: Bearer <token>"Response 200: Same shape as global audit list.
POST /passports/:id/audit
🔒 Requires Auth (owner only)
curl -X POST http://38.49.210.10:3846/passports/ap_abc123def456/audit \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"action": "login",
"service": "github.com",
"method": "oauth",
"result": "success",
"duration_ms": 1200,
"details": { "scope": "repo" }
}'| Field | Type | Required | Values |
|---|---|---|---|
action |
string | Yes | Free text |
service |
string | No | Service name |
method |
string | No | Auth method used |
result |
enum | No | success, failure, pending_approval, resolved_by_owner |
duration_ms |
number | No | Duration in milliseconds |
details |
object | No | Arbitrary JSON |
Response 201:
{
"id": "uuid",
"created_at": "2026-02-20T10:05:00.000Z"
}All routes require JWT auth only (API keys cannot manage other API keys).
POST /api-keys
🔒 JWT only
curl -X POST http://38.49.210.10:3846/api-keys \
-H "Authorization: Bearer <jwt_token>" \
-H "Content-Type: application/json" \
-d '{ "name": "my-mcp-server" }'Response 201:
{
"id": "uuid",
"name": "my-mcp-server",
"key": "apk_xxxxxxxxxxxxxxxxxxxx",
"key_prefix": "apk_xxxx",
"created_at": "2026-02-20T10:00:00.000Z"
}
⚠️ The fullkeyis only returned once. Store it securely.
GET /api-keys
🔒 JWT only
curl http://38.49.210.10:3846/api-keys \
-H "Authorization: Bearer <jwt_token>"Response 200:
{
"api_keys": [
{
"id": "uuid",
"name": "my-mcp-server",
"key_prefix": "apk_xxxx",
"last_used": "2026-02-20T12:00:00.000Z",
"created_at": "2026-02-20T10:00:00.000Z",
"revoked_at": null
}
]
}DELETE /api-keys/:id
🔒 JWT only
curl -X DELETE http://38.49.210.10:3846/api-keys/<key-id> \
-H "Authorization: Bearer <jwt_token>"Response 200:
{ "revoked": true }Agent requests that need owner approval (e.g., sensitive actions).
GET /approvals?status=pending
🔒 Requires Auth
curl http://38.49.210.10:3846/approvals \
-H "Authorization: Bearer <token>"Response 200:
{
"approvals": [
{
"id": "uuid",
"passport_id": "ap_abc123def456",
"action": "delete_repository",
"service": "github.com",
"details": "Agent wants to delete repo foo/bar",
"status": "pending",
"responded_at": null,
"created_at": "2026-02-20T10:00:00.000Z"
}
]
}POST /approvals
🔒 Requires Auth
curl -X POST http://38.49.210.10:3846/approvals \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"passport_id": "ap_abc123def456",
"action": "delete_repository",
"service": "github.com",
"details": "Agent wants to delete repo foo/bar"
}'Response 201:
{
"id": "uuid",
"created_at": "2026-02-20T10:00:00.000Z"
}POST /approvals/:id/respond
🔒 Requires Auth (owner only)
curl -X POST http://38.49.210.10:3846/approvals/<approval-id>/respond \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "approved": true }'Response 200:
{ "status": "approved" }CAPTCHA escalation flow: Agent detects CAPTCHA → creates escalation → owner resolves → agent continues.
POST /escalations
🔒 Requires Auth
curl -X POST http://38.49.210.10:3846/escalations \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"passport_id": "ap_abc123def456",
"captcha_type": "recaptcha_v2",
"service": "example.com",
"screenshot": "data:image/png;base64,..."
}'Response 201:
{
"escalation_id": "uuid",
"status": "pending",
"created_at": "2026-02-20T10:00:00.000Z"
}GET /escalations?status=pending
🔒 Requires Auth
curl http://38.49.210.10:3846/escalations \
-H "Authorization: Bearer <token>"Response 200:
{
"escalations": [
{
"id": "uuid",
"passport_id": "ap_abc123def456",
"captcha_type": "recaptcha_v2",
"service": "example.com",
"screenshot": "data:image/png;base64,...",
"status": "pending",
"created_at": "2026-02-20T10:00:00.000Z",
"resolved_at": null
}
]
}GET /escalations/:id
🔒 Requires Auth (owner only)
POST /escalations/:id/resolve
🔒 Requires Auth (owner only)
curl -X POST http://38.49.210.10:3846/escalations/<escalation-id>/resolve \
-H "Authorization: Bearer <token>"Response 200:
{
"status": "resolved",
"resolved_at": "2026-02-20T10:05:00.000Z"
}Live browser session management for remote CAPTCHA solving. Supports both HTTP polling and WebSocket streaming.
GET /browser-sessions?escalation_id=<uuid>
🔒 Requires Auth
POST /browser-sessions
🔒 Requires Auth
curl -X POST http://38.49.210.10:3846/browser-sessions \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"escalation_id": "uuid",
"page_url": "https://example.com/captcha",
"viewport_w": 1280,
"viewport_h": 720
}'Response 201:
{
"session_id": "uuid",
"escalation_id": "uuid",
"created_at": "2026-02-20T10:00:00.000Z"
}GET /browser-sessions/:id
🔒 Requires Auth — returns session with latest screenshot.
PUT /browser-sessions/:id/screenshot
🔒 Requires Auth
curl -X PUT http://38.49.210.10:3846/browser-sessions/<session-id>/screenshot \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"screenshot": "data:image/jpeg;base64,...",
"page_url": "https://example.com/captcha"
}'POST /browser-sessions/:id/command
🔒 Requires Auth
curl -X POST http://38.49.210.10:3846/browser-sessions/<session-id>/command \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"type": "click",
"payload": { "x": 150, "y": 300 }
}'Command types: click, type, scroll, keypress
Response 201:
{ "command_id": "uuid", "status": "pending" }GET /browser-sessions/:id/commands?status=pending
🔒 Requires Auth
PATCH /browser-sessions/:id/commands/:cmdId
🔒 Requires Auth
curl -X PATCH http://38.49.210.10:3846/browser-sessions/<session-id>/commands/<cmd-id> \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "status": "executed" }'Status values: executed, failed
GET /browser-sessions/:id/stream?token=<jwt_or_api_key>
WebSocket endpoint for real-time screenshot streaming. After connecting, send an identify message:
{ "type": "identify", "role": "mcp" }or
{ "type": "identify", "role": "dashboard" }Binary frames (JPEG screenshots) are forwarded between MCP and Dashboard automatically.
POST /browser-sessions/:id/close
🔒 Requires Auth
Response 200:
{
"closed": true,
"closed_at": "2026-02-20T10:10:00.000Z"
}POST /webhook/email-received
🔐 Requires X-Webhook-Secret header
Called by Cloudflare Email Worker when a new email arrives for an agent.
curl -X POST http://38.49.210.10:3846/webhook/email-received \
-H "Content-Type: application/json" \
-H "X-Webhook-Secret: <secret>" \
-d '{
"email_id": "msg-123",
"to": "my-agent@agent-mail.xyz",
"from": "sender@example.com",
"subject": "Welcome",
"received_at": "2026-02-20T10:00:00.000Z"
}'Response 200:
{ "ok": true }GET /webhook/email-notifications/:address
🔓 Public
Returns unprocessed email notifications for the given address and marks them as retrieved.
curl http://38.49.210.10:3846/webhook/email-notifications/my-agent@agent-mail.xyzResponse 200:
{
"notifications": [
{
"email_id": "msg-123",
"recipient": "my-agent@agent-mail.xyz",
"sender": "sender@example.com",
"subject": "Welcome",
"received_at": "2026-02-20T10:00:00.000Z",
"notified_at": "2026-02-20T10:00:01.000Z"
}
]
}POST /webhook/sms-received
🔐 Requires X-Webhook-Secret header (+ optional Twilio signature validation)
Called by Twilio when a new SMS arrives. Returns TwiML.
GET /webhook/sms-notifications/:phoneNumber
🔓 Public
curl http://38.49.210.10:3846/webhook/sms-notifications/+15551234567Response 200:
{
"notifications": [
{
"id": "SMxxxxxxxx",
"to": "+15551234567",
"from": "+15559876543",
"body": "Your verification code is 123456",
"received_at": "2026-02-20T10:00:00.000Z"
}
]
}POST /telegram/webhook
Placeholder for Telegram bot webhook integration. Returns { "ok": true }.
GET /telegram/link/:email
🔓 Public
curl http://38.49.210.10:3846/telegram/link/owner@example.comResponse 200:
{
"email": "owner@example.com",
"link": "https://t.me/AgentPass_bot?start=link_owner%40example.com",
"instructions": "Click the link to open Telegram and link your account to receive notifications from your AI agents."
}GET /telegram/status
🔓 Public
curl http://38.49.210.10:3846/telegram/statusResponse 200:
{
"enabled": true,
"bot_username": "AgentPass_bot",
"message": "Telegram notifications are enabled"
}The API applies rate limiting to all endpoints. Stricter limits apply to:
POST /passports— passport creationPOST /verify— verification requests
Allowed origins are configured via ALLOWED_ORIGINS environment variable. Default development origins:
http://localhost:3847http://localhost:3848http://localhost:3849http://localhost:5173
| Header | Usage |
|---|---|
Authorization |
Bearer <jwt> or Bearer <api_key> |
X-Webhook-Secret |
Webhook authentication |
X-AgentPass-ID |
Optional passport ID header |
X-AgentPass-Signature |
Ed25519 signature (required for revocation) |
X-Request-ID |
Optional request tracing ID |