# PropRaven authentication guide for agents

PropRaven (https://propraven.com) is US parcel and property intelligence for AI agents: owners, deeds, permits, hazards, valuations and deal screens, served by a hosted MCP server and a REST API. This page describes how an agent authenticates with PropRaven today. Nothing here is a plan: every method listed is live.

## Summary

| You have | Use | Account needed |
|---|---|---|
| An email address a human can open | Agent-assisted sign-up, then an API key (Method 0) | Created for you after one human confirmation |
| A wallet that can pay USDC on Base | x402 pay-per-call on the REST API | No (parcel data only) |
| An MCP client with OAuth | The MCP connector at https://propraven.com/mcp | Yes (a human signs in once) |
| A key a human created | `Authorization: Bearer pz_...` on REST and MCP | Yes |

Fully autonomous registration is NOT available: a human must confirm the email, sign in and accept the Terms once (Method 0). There is no auth.md claim code flow and no identity-assertion exchange.

## Method 0: agent-assisted sign-up (one human step)

You need a person's email address. The person does one thing: open an emailed link, sign in or create their account, accept the Terms, and press Confirm. Everything else is yours.

```bash
# 1. Start. The human receives the confirmation email, you do not.
curl -s -X POST https://propraven.com/api/v1/agent/signup -H 'content-type: application/json' \
  -d '{"email":"person@example.com","agent_name":"My Agent"}'
# -> 202 {"signup_id":"...","poll_token":"pzsu_...","status_url":"https://propraven.com/api/v1/agent/signup/<id>","expires_in":1800,"interval":5}

# 2. Tell the person (they get an email from notifications@propraven.com, subject "Confirm your PropRaven account for an AI agent"; check spam):
#    "Open the link in that email, sign in or create your account with this address, tick the Terms box, press Confirm."

# 3. Poll every 5 seconds (not faster) until status is complete. The link expires in 30 minutes.
curl -s -H "Authorization: Bearer $POLL_TOKEN" "https://propraven.com/api/v1/agent/signup/$SIGNUP_ID"
# -> {"status":"pending","interval":5}  ...  then {"status":"complete","api_key":"pz_...","tier":"free","key_id":"...","key_name":"agent:My Agent"}
```

Poll results: HTTP 200 `pending` (keep polling); HTTP 200 `complete` (the key, once); 404 `unknown_signup` (wrong token, or the sign-up expired: start again); 410 `already_delivered` (the key was already returned once); 409 `key_limit_reached` (the account has no room for another key: the person revokes one at https://propraven.com/settings/api-keys, then keep polling); 429 `slow_down` (poll less often); 503 `unavailable` (poll again).

The api_key (it starts with `pz_`) is returned once, on the poll that completes the sign-up. Store it. If that response is lost, start again with a new POST; the person confirms the new email. It is a normal key (Method 2) with the account's plan limits, named `agent:<agent_name>`. An account may hold 2 active agent keys.

Python without curl:

```python
import json, time, urllib.request as u
def call(url, token=None, data=None):
    r = u.Request(url, data=json.dumps(data).encode() if data else None, method="POST" if data else "GET",
                  headers={"content-type": "application/json", **({"Authorization": "Bearer " + token} if token else {})})
    try: return json.load(u.urlopen(r))
    except u.HTTPError as e: return json.load(e)
s = call("https://propraven.com/api/v1/agent/signup", data={"email": "person@example.com", "agent_name": "My Agent"})
while True:  # ask the person to confirm the email first
    time.sleep(s["interval"]); p = call(s["status_url"], s["poll_token"])
    if p.get("status") == "complete": key = p["api_key"]; break
    if p.get("status") != "pending": print(p)  # an error or a limit: read its code
```

Rotate or revoke the key you hold, with that key:

```bash
curl -s -X POST -H "Authorization: Bearer $PROPRAVEN_API_KEY" https://propraven.com/api/v1/agent/keys/rotate   # new key shown once, old key stops
curl -s -X POST -H "Authorization: Bearer $PROPRAVEN_API_KEY" https://propraven.com/api/v1/agent/keys/revoke
```

A revoked key answers HTTP 403 `API key is inactive`. Usage: `GET https://propraven.com/api/v1/account/usage`. To move to a paid plan, give the person https://propraven.com/subscribe: they sign in and add a payment method. Payment cannot be done by an agent.

Limits and refusals: 5 sign-up starts per hour per IP and 3 per day per email address; disposable email domains are refused (HTTP 422, `code: "disposable_email"`); the email must belong to a person who confirms. Errors are `application/problem+json` with a `code`.

## Method 1: x402 pay-per-call (no account, no signup)

Parcel data can be bought per call with no account. Request the resource, receive HTTP 402 with the exact price, sign the payment, and retry.

```bash
# Free: what exists, and how complete it is
curl -s "https://propraven.com/api/v1/storefront/catalog" | head -c 400
curl -s "https://propraven.com/api/v1/storefront/availability?state=NC"

# Priced call: the 402 body carries the price in accepts[0].maxAmountRequired (USDC atomic units, Base)
curl -s -i "https://propraven.com/api/v1/parcels/37:119:11906402/report"

# Pay: sign an EIP-3009 payment for that amount, base64 it, retry with it
curl -s "https://propraven.com/api/v1/parcels/37:119:11906402/report" -H "X-PAYMENT: <base64 payment payload>"
```

A prepaid balance is also available: `GET https://propraven.com/api/v1/storefront/credits/topup?amount=10` is paid the same way and returns a credit token to send as `X-CREDIT-TOKEN`. Details: https://propraven.com/docs/agents

People data (owner names, owner mailing addresses, owner-name lookups, lead lists) is never served to an anonymous caller. A wallet alone gets HTTP 401 with `code: "account_required"` on those paths. Send an account credential (Method 2 or 3) with the payment.

## Method 2: API key (REST and MCP)

A human signs in at https://propraven.com/sign-in (or creates an account at https://propraven.com/sign-up) and creates a key at https://propraven.com/settings/api-keys. An account created with email and password confirms its email by link before first sign-in; if sign-in says "email not confirmed", the sign-in page offers a resend. The key starts with `pz_` and is shown once. A user may hold 5 active keys.

```bash
export PROPRAVEN_API_KEY=pz_...

# REST: one parcel
curl -s -H "Authorization: Bearer $PROPRAVEN_API_KEY" "https://propraven.com/api/v1/parcels/37:119:11906402"

# Find a parcel id by address, then read it. Ids: the UUID in results[].parcel_id, or state_fips:county_fips:parcel_id.
curl -s -H "Authorization: Bearer $PROPRAVEN_API_KEY" "https://propraven.com/api/v1/search/full?q=2232+Wilmore+Dr&state=NC&limit=1"

# Your tier, usage and limits
curl -s -H "Authorization: Bearer $PROPRAVEN_API_KEY" "https://propraven.com/api/v1/account/usage"
```

The same key works on the MCP endpoint https://propraven.com/mcp as `Authorization: Bearer pz_...` (streamable HTTP). Spec: https://propraven.com/openapi.json

A missing or invalid credential returns HTTP 401. An authenticated call that exceeds a cap returns 429 (rate) or a documented cap error with the `code` field set.

## Method 3: MCP connector with OAuth 2.1

For MCP clients that support OAuth (Claude, Grok, Cursor, Codex and others). A call to https://propraven.com/mcp with no credential returns 401 with `WWW-Authenticate: Bearer resource_metadata="https://propraven.com/.well-known/oauth-protected-resource/mcp"`. The client follows it:

1. Protected resource metadata (RFC 9728): https://propraven.com/.well-known/oauth-protected-resource/mcp (resource: https://propraven.com/mcp).
2. Authorization server metadata (RFC 8414): https://propraven.com/.well-known/oauth-authorization-server
3. Register the client with RFC 7591 dynamic registration, `POST https://propraven.com/api/oauth/register`, or publish an OAuth client ID metadata document and use its https URL as `client_id`.
4. Authorization code flow with PKCE (S256 only): `https://propraven.com/api/oauth/authorize`. A human signs in and approves on the consent screen. This step cannot be done by an agent.
5. `POST https://propraven.com/api/oauth/token` exchanges the code (and later the refresh token).

```bash
curl -s -i -X POST https://propraven.com/mcp -H 'content-type: application/json' -d '{}'   # 401 + the challenge above
curl -s https://propraven.com/.well-known/oauth-authorization-server
```

Clients are public (no client secret). Scope: `read` is the only scope. Access tokens (`pzoauth_...`, sent as Bearer) last 60 days; refresh tokens rotate and expire after 90 days of disuse or 180 days in total. Not supported: client_credentials, implicit, password. Reference: https://propraven.com/docs/mcp#oauth

## Tiers, scopes and limits

PropRaven has no per-endpoint scopes. What a credential may do depends on the account's plan and on whether the account is signed in. Limits are enforced per account across all its keys.

| Plan | Price | Lookups and API calls included per month | API calls per day | API calls per minute |
|---|---|---|---|---|
| Free | $0 | 1,000 (hard cap) | 1,000 | 60 |
| Developer | $99/mo | 100,000 (hard cap) | 100,000 | 3,000 |
| Team | $1,000/mo | 1,000,000 (hard cap) | 1,000,000 | 12,000 |

Developer and Team stop at their included lookups; there is no overage billing. Lookups and API calls are separate counters. On these plans the included API calls per month equal the lookup cap. Per day and per minute count API calls. Whole-state and national tables are a Data License (custom, invoiced annually). Accounts on a legacy API plan keep their existing limits. Anonymous callers share a small per-IP budget (100 per day, 60 per minute) on free discovery routes only. Prices and plan features: https://propraven.com/pricing. Read your own numbers at `GET https://propraven.com/api/v1/account/usage`.

Three MCP tools (get_catalog, check_availability, get_account) are free and unmetered.

## Data policy

People data (owner names, mailing addresses, deed parties, owner-name lookups, lead lists) is served only to a signed-in or paid account, never to anonymous use. Free discovery and parcel facts do not need an account.

## Revoking access

- API key: a signed-in human revokes it at https://propraven.com/settings/api-keys. A revoked key stops working within about a minute.
- OAuth connector: `POST https://propraven.com/api/oauth/revoke` with `token=<access or refresh token>` (RFC 7009; always answers 200). The connection also appears as a key named `oauth:...` in https://propraven.com/settings/api-keys, where it can be revoked.
- A leaked key: revoke it and write to support@propraven.com.

## Not available

- Agent self-registration, account claim codes, and `/agent/identity`.
- Identity assertions (ID-JAG) and signed security event receivers.
- A published `agent_auth` metadata block. The discovery documents above are standard OAuth only.
- Creating keys with an API key or OAuth token, or any key change beyond rotating or revoking the agent key you hold. Everything else needs a human session.

## Contact, terms, pricing

- Questions, key problems, abuse reports: support@propraven.com
- Terms: https://propraven.com/terms
- Privacy: https://propraven.com/privacy
- Pricing: https://propraven.com/pricing
- Agent quickstart: https://propraven.com/docs/agents
- MCP docs: https://propraven.com/docs/mcp
- Machine-readable index: https://propraven.com/llms.txt and https://propraven.com/.well-known/mcp/server-card.json
