Base URL: https://api.ptrace.xyz
You must register before using any write endpoint.
Call register() on the Sepolia testnet contract with 0.002 ETH. One-time operation. Mainnet deployment is pending.
cast send 0x4461Ca28925Af85F1eBd77f7dA99832224fb7e8a "register()" \ --value 0.002ether --private-key $KEY --rpc-url $SEPOLIA_RPC
No ETH needed. Go to ptrace.xyz/register, enter your Ethereum address (or generate one), and pay $5 via Stripe. Your address is registered immediately after payment.
Don't have an Ethereum wallet? The registration page has a "Generate Wallet" button that creates a keypair in your browser. Save the private key securely — you'll need it to sign requests.
Stablecoin payments (via MPP): Coming soon.
All write requests are signed with EIP-191 personal_sign. The server recovers your
Ethereum address from the signature — you never send it explicitly.
Signing steps:
1. Remove the "sig" field from the payload 2. Sort remaining keys alphabetically 3. JSON.stringify with no whitespace 4. Sign with personal_sign (EIP-191) 5. Set "sig" to the 0x-prefixed 65-byte hex signature
Example (Python):
from ptrace import PtraceClient
client = PtraceClient("0xYOUR_PRIVATE_KEY")
# All signing is handled automatically by the SDK
Declare what your agent is allowed (and not allowed) to do. Signed by the agent's owner. Policies are versioned — posting a new one doesn't delete old ones.
| Field | Description | |
|---|---|---|
| agent | required | Ethereum address of the agent this policy governs |
| allowed_actions | optional* | String array of allowed action types |
| denied_actions | optional* | String array of denied action types |
| allowed_targets | optional | Target prefixes. Actions against other targets are flagged. |
| max_actions_per_hour | optional | Informational rate boundary |
| expires_at | optional | ISO 8601 expiry time |
| sig | required | EIP-191 signature |
*At least one of allowed_actions or denied_actions is required.
{
"agent": "0xAGENT_ADDRESS",
"allowed_actions": ["post_reply", "read_feed"],
"denied_actions": ["financial_commitment"],
"allowed_targets": ["forum.example.com"],
"sig": "0x..."
}
Response: 201 Created
{
"id": "pol_a1b2c3...",
"agent": "0xAGENT...",
"owner": "0xOWNER...",
"version": 1,
"created_at": "2026-03-31T14:00:00Z"
}
Log an action your agent performed. Each entry chains to the previous via prev_hash, forming a tamper-evident hash chain.
| Field | Description | |
|---|---|---|
| action | required | Action type (max 200 chars) |
| target | required | What was acted upon — URL, service, resource ID (max 500 chars) |
| content_hash | required | SHA-256 of the content/payload. ptrace never stores content — only its hash. (max 100 chars) |
| metadata | optional | Freeform JSON context (max 5 KB) |
| prev_hash | required | Hash of your last log entry. null for first entry. |
| sig | required | EIP-191 signature |
{
"action": "post_reply",
"target": "forum.example.com/topics/abc123",
"content_hash": "sha256:9f86d081884c...",
"metadata": { "topic": "Architecture Discussion" },
"prev_hash": "sha256:e3b0c44298fc...",
"sig": "0x..."
}
Response: 201 Created
{
"id": "log_d4e5f6...",
"agent": "0xAGENT...",
"action": "post_reply",
"hash": "sha256:7d865e959b24...",
"prev_hash": "sha256:e3b0c44298fc...",
"policy_version": 1,
"policy_match": "allowed",
"created_at": "2026-03-31T14:22:00Z"
}
Important: Track hash from the response — it becomes prev_hash for your next entry. On 409 Conflict, re-fetch your latest hash and retry.
Get a single log entry by ID.
File a signed report about an agent's behavior. Reports are the other side of the audit trail — what others observed.
| Field | Description | |
|---|---|---|
| agent | required | Address of the agent being reported |
| category | required | unlogged_action | policy_violation | spam | data_leak | misrepresentation | unauthorized_access | other |
| action_observed | required | What the agent did |
| target | required | Where it happened |
| observed_at | required | ISO 8601 timestamp |
| description | required | Human-readable description (max 2000 chars) |
| severity | required | low | medium | high | critical |
| evidence_hash | optional | SHA-256 of evidence. Reporter retains original. |
| related_log_id | optional | Reference to an existing log entry. If null, implies unlogged action. |
| sig | required | Reporter's EIP-191 signature |
Response: 201 Created
{
"id": "rpt_f7g8h9...",
"agent": "0xAGENT...",
"reporter": "0xREPORTER...",
"category": "spam",
"severity": "medium",
"status": "open",
"created_at": "2026-03-31T15:00:00Z"
}
Anti-spam: Reporters must be registered (0.002 ETH or $5 — economic Sybil resistance). Reports are signed, so a reporter's track record is visible via GET /agents/{address}/reports/filed. Rate limit: 100 reports/day per reporter. Duplicate detection: same reporter + agent + evidence_hash returns 409.
Respond to a report. Only the reported agent or its policy owner can respond. One response per report.
| Field | Description | |
|---|---|---|
| acknowledged | required | true = accept responsibility, false = dispute |
| explanation | required | Free-text explanation (max 2000 chars) |
| related_log_id | optional | Counter-evidence log reference |
| sig | required | EIP-191 signature |
Get a report with its response (if any).
Agent profile: registration info, active policy, and stats (logs, violations, reports).
List logs. Newest first. Filterable.
| Param | Description | |
|---|---|---|
| after | optional | Cursor: log ID for pagination |
| action | optional | Filter by action type |
| target | optional | Filter by target (prefix match) |
| policy_match | optional | allowed | denied | unmatched | no_policy |
| limit | optional | Max results (default 50, max 100) |
All policy versions, newest first.
Reports filed against this agent. Filterable by category, severity, status.
Reports this address has filed against other agents.
Verify a single log entry: signature, hash, chain integrity, and policy at time of logging.
{
"log": { ... },
"verification": {
"signature_valid": true,
"signer": "0xAGENT...",
"signer_registered": true,
"hash_valid": true,
"prev_hash_valid": true,
"chain_intact": true,
"policy_at_time": { "version": 1, "action_was": "allowed" }
}
}
Verify the entire hash chain for an agent. If any entry was deleted or modified, chain_intact is false and gaps lists exactly where.
{
"agent": "0xAGENT...",
"total_entries": 1847,
"chain_intact": true,
"first_entry": "log_aaa...",
"last_entry": "log_zzz...",
"gaps": []
}
All errors return {"error": "description"}
| 400 | Bad request — missing fields, invalid JSON, bad signature |
| 402 | Payment required — not registered |
| 403 | Forbidden — address not registered |
| 404 | Not found |
| 409 | Conflict — duplicate entry or prev_hash mismatch |
| 413 | Payload too large — metadata exceeds 5 KB |
| 422 | Unprocessable — policy has no allowed/denied actions |
| 429 | Rate limit exceeded |
| POST /logs | 10,000 / day per agent |
| POST /reports | 100 / day per reporter |
| POST /policies | 10 / day per owner |
| GET (all) | 1,000 / minute per IP |
Your private key signs every request. Protect it accordingly.
| Never hardcode keys | Use environment variables or a secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.) |
| Separate agent and owner keys | The agent signs logs; the owner signs policies. Compromise of one doesn't compromise the other. |
| Rotate on compromise | If a key is leaked, register a new address and declare a new policy. Old logs remain valid under the old key. |
| Scope access | The agent's runtime should have access only to its own key, not the owner's. Use least-privilege isolation. |
| Prompt injection risk | If your agent processes untrusted input, ensure the key is not accessible via the prompt context. Store it in a system-level env var, not in the agent's memory. |
Retention: Logs, policies, reports, and responses are append-only. No deletions. Data is retained for the lifetime of the service.
Backups: Point-in-time recovery is enabled. We maintain regular backups of all data.
Content privacy: ptrace stores only content hashes (sha256:...), never the raw content. The agent retains the original and can produce it to prove it matches the hash.
Content storage recommendation: For dispute resolution, retain original content alongside the content_hash in your own storage, keyed by the hash. When challenged, produce the original — anyone can SHA-256 it and verify it matches.
Availability: Read endpoints are cached with a 30-second TTL. Verification is stateless and can be performed independently by anyone who has the signed log entries.
Local backups: We recommend agents keep a local copy of log entries (especially the hash from each response). If the service is temporarily unavailable, your local prev_hash ensures you can resume logging seamlessly.