# MapXPet API for AI agents

> Status: Membership is open: C$3.99/month plus tax via Stripe checkout at https://buy.stripe.com/00w14mdJHetJ5yhd1Y9R600 (a human completes payment; agents never enter card details).

Status: NOT LIVE YET. These endpoints return 404 until launch. Membership: C$3.99/month + applicable tax (preview runs Stripe in test mode).

This page is for AI agents (Grok, Claude, ChatGPT, Gemini and others) acting for a person. Principle: **agent-first, humans stay in the loop.** Agents can browse, express interest and draft listings; a human approves payments, posting and any sharing of contact details.

Markdown version: https://mapxpet.com/for-agents/index.md · OpenAPI: [community boards](https://mapxpet.com/for-agents/openapi-community.json), [membership](https://mapxpet.com/for-agents/openapi-membership.json) (both not live yet) · Directory API (live, keyless): https://mapxpet.com/api/openapi.json

## 1. Base URL and headers
- Base URL: `https://mapxpet.com/api/v1`
- JSON in and out (`Content-Type: application/json`), UTF-8, CORS open on GET.
- `MapXPet-Agent: <YourAgent>/<version>`: please send it. It is informational only and never used for auth.

## 2. Authentication
- **Directory and board browsing:** no key.
- **Breeding and pets for sale (respond, post, see seller details):** the member's bearer token from the membership agent signup flow: `Authorization: Bearer <member_token>`. Without an active membership you get `403 not_member`.
- **Lost & found (free, no account):** send your human's email. MapXPet emails them a one-tap confirm link; nothing reaches the owner until they tap it. A member token, if your human has one, skips the email step.

## 3. Browse listings
| Board | List | One listing | Filters |
|---|---|---|---|
| Lost & found | `GET /lost-pets.json` | `GET /lost-pets/{id}.json` | `type` (lost, found), `city`, `species`, `since` |
| Breeding | `GET /breeding.json` | `GET /breeding/{id}.json` | `species`, `breed`, `city` |
| Pets for sale | `GET /pets-for-sale.json` | `GET /pets-for-sale/{id}` | `species`, `breed`, `city` |

- Pagination: `limit` (max 100) and `cursor`; follow `next_cursor` until it is null.
- Public fields: photos, species, breed, description, city and area, dates, price (sale).
- Member-only fields (sale, with token): health records, registration.
- Contact phone/email appear only when the poster set `contact_public: true`. Otherwise use "I'm interested" or the message relay.

## 4. Express interest ("I'm interested")
An anonymous ping: the owner sees only "Someone in {city}", the time, an optional note and a neutral "via AI assistant" tag (`source: ai_assistant`); it never says which assistant or who sent it. The owner decides whether to share contact details; if they do, details go privately to your human (API response and email), never onto the listing.

```http
POST /api/v1/listings/{type}/{id}/interest
MapXPet-Agent: ExampleAgent/1.0
Authorization: Bearer <member_token>   (breeding and sale: required)

{"note":"Is she still available?","human":{"email":"person@example.com"},"callback_url":"https://agent.example/hooks/mapxpet"}
```
`type` is `lost-found`, `breeding` or `sale`. Response `202`:
```json
{"interest_id":"int_8f2c","status":"pending_confirmation","status_url":"https://mapxpet.com/api/v1/interests/int_8f2c","status_token":"st_..."}
```
Poll `GET /interests/{interest_id}` (at most every 5 minutes) or wait for `callback_url`. States: `pending_confirmation`, `pinged`, `shared`, `declined`, `expired` (30 days). When `shared`, the response includes `owner_contact`; give it to your human privately.

## 5. Create a listing (draft, human approves)
Agents can draft listings on all three boards with the same fields as the human forms. Nothing goes to moderation until your human approves.
1. Ask your human what to post, and collect real photos of the actual animal.
2. Upload photos: `POST /photos` (returns photo ids).
3. Create the draft: `POST /listings/{type}/drafts`.
4. MapXPet emails your human a review link (member email for breeding and sale; the given email for lost & found) with **Approve**, **Edit** and **Discard**. Your human, never the agent, confirms `contact_public` and the disclaimer: "I understand that once I share my contact details, MapXPet is not responsible for how others use them."
5. After approval the normal checks run: photo check, scam scan and an instant decision (members: the bot's decision stands; anyone, member or not, gets one appeal per rejected listing; held listings alert an admin; on every board, 3 reports from 3 different members hide a listing until an admin reviews it).
6. Poll `GET /drafts/{draft_id}` or use `callback_url`. Drafts expire after 7 days.

```http
POST /api/v1/listings/lost-found/drafts
MapXPet-Agent: ExampleAgent/1.0

{"type":"found","species":"Dog","description":"Small tan and white dog, no collar.","photos":["ph_12a"],"found_location":{"city":"Ottawa","area":"Bank St & Sunnyside Ave"},"date_found":"2026-10-30","human":{"email":"person@example.com"},"contact_public":false,"callback_url":"https://agent.example/hooks/mapxpet"}
```
Response `202`:
```json
{"draft_id":"drf_51d0","status":"draft_pending_owner","review_sent_to":"p***@example.com","expires_at":"2026-11-08T16:00:00Z","status_url":"https://mapxpet.com/api/v1/drafts/drf_51d0"}
```
Draft states: `draft_pending_owner` → `pending_scan` → `active` | `held` | `rejected` (one appeal: member bearer token, or the signed appeal link emailed to non-members), or `discarded` / `expired`.
- Found pets require at least one photo and `found_location` (city + nearest intersection or landmark, never a home address), else `422 missing_required`.
- `contact_public` defaults to `false`. An agent may suggest `true`, but it only takes effect if the human ticks it on the review page.
- Sale drafts need `price_cad` (or `null` for "price on request").

## 6. Membership
Breeding and pets-for-sale actions need a membership. Follow the [membership signup recipe](https://mapxpet.com/membership/agents.md): ask your human first; they review and pay on Stripe's page. Agents never enter card details.

### Preview backend: paid signup via Stripe Checkout (implemented, Stripe TEST mode, not live)
Price: C$3.99/month + applicable tax (GST/HST/QST calculated by Stripe Tax at checkout). Renews monthly until cancelled.
1. Ask your human first. Then `POST /api/membership/checkout` with JSON `{"email":"<human's email>","city":"<optional>","agent_name":"<optional, e.g. YourAgent/1.0>","source":"agent"}` (or send header `MapXPet-Agent: <YourAgent>/<version>`).
2. The `201` response has `checkout_url` (Stripe-hosted, expires in 24 h), `checkout_session_id`, `status` (`"pending"`), `status_token` and `status_url`. Give `checkout_url` to your human. **A bot cannot pay on its own:** the human opens the link, enters their billing address and card on Stripe's page and confirms. You never see card details.
3. Check activation with `GET /api/membership/status?token=<status_token>`. It returns only that signup's `status` (`pending` → `active`, later `past_due` or `cancelled`) and `activated_at`, never anyone else's data. Poll no more than every 30 s.
4. After payment, the human lands on `/membership/success/` (verified server-side with Stripe) and gets a confirmation email with a Manage membership link. Account access stays with the human.
5. Manage or cancel: `POST /api/membership/portal` with `{"token":"<status_token>"}` (or `Authorization: Bearer <member_token>`) returns `portal_url`, a short-lived Stripe Customer Portal link for that member only. You may request it, but **the human opens it** to cancel (at the end of the paid month), change card, billing address or email, or see invoices. After a cancel, status shows `cancel_at_period_end: true`, then `cancelled` when the period ends. Limit: 20/hour/IP.
- Agent-started signups are tagged neutrally as "via AI assistant". Same price and terms as web signups.
- Limits: 10 checkout starts per IP per hour, 120 status checks per IP per hour (HTTP 429 with retry_after). One active membership per email (HTTP 409 already_member).
- If payments are not configured on a server, checkout returns `503 payments_unavailable` with `fallback: /api/membership/signup` (free preview signup, nothing charged).

## 7. Rate limits and rules
- Placeholders: 10 interest pings per day and 5 drafts per day per token, email or IP; 1 ping per listing per person; 3 unconfirmed lost & found requests per email per day. On `429`, wait `retry_after`.
- Don't scrape or store contact details; don't send spam pings; owners can block you and report pings, and reports count toward moderation.
- Respect robots.txt and llms.txt. Use the JSON endpoints instead of crawling pages.
- Humans stay in the loop for payments, posting approval and contact sharing.
- Moderation policy: https://mapxpet.com/moderation/

## 8. Errors
Errors look like `{"error":{"code":"...","message":"...","retry_after":60}}`.

| HTTP | code | Meaning |
|---|---|---|
| 401 | invalid_token | Missing or wrong bearer token |
| 403 | not_member / blocked | No active membership for this board, or the owner blocked this person |
| 404 | not_found | Unknown id (or the API isn't live yet) |
| 409 | already_interested / already_resolved | Duplicate ping or action |
| 422 | missing_required / email_required | e.g. found pet without photo or found_location |
| 429 | rate_limited | Wait `retry_after` seconds |
