# Wash Bodega auth.md

You are an agent. Wash Bodega, a neighborhood laundromat at 6604 W Bellfort Ave, Houston, TX 77035 (https://washbodega.com), supports **agentic registration** so you can ask Wash Bodega to pick up a customer's laundry and check on that request — only after the customer, signed in to their own Wash Bodega account, approves you. Follow the steps in order.

The approved ChatGPT plugin is named Wash Bodega. Search ChatGPT Plugins for "Wash Bodega". After you connect a Wash Bodega account, it files Wash & Fold pickup requests through https://washbodega.com/mcp/customer. A request is not an order and is not a paid order. Staff review it and enter the order in Cents. Nothing is paid in ChatGPT. The plugin never asks for a phone number or payment details. Package name: `washbodega-pickups`.

Public facts need no credential: see "Public reads" below. Everything in this file is about the authenticated customer MCP server.

## Step 1 — Discover

The customer MCP server is `https://washbodega.com/mcp/customer` (Streamable HTTP, JSON-RPC over POST). Without a token it answers 401 with:

```http
WWW-Authenticate: Bearer resource_metadata="https://washbodega.com/.well-known/oauth-protected-resource/mcp/customer", scope="pickup.read pickup.request"
```

- Protected resource metadata (RFC 9728): `https://washbodega.com/.well-known/oauth-protected-resource/mcp/customer` (the origin's is at `/.well-known/oauth-protected-resource`).
- Authorization server metadata (RFC 8414): `https://washbodega.com/.well-known/oauth-authorization-server`. Its `agent_auth` block is the registration surface; `issuer` is `https://washbodega.com`.
- Signing keys: `https://washbodega.com/.well-known/jwks.json`.

Scopes:

- `pickup.request` — file a pickup request for the customer and cancel it before staff enter it in Cents.
- `pickup.read` — read the status of the requests you filed.

Neither scope reads the customer's name, phone, email, LaundryCard, rewards, offers, or payments.

## Step 2 — Pick a method

1. **You are an MCP client that can open a browser** (Claude, ChatGPT, an IDE): use standard OAuth — register at `POST /oauth2/register` (RFC 7591, public client, `token_endpoint_auth_method: "none"`), then authorization code with PKCE S256 at `/oauth2/authorize`. The customer approves on a Wash Bodega page while signed in.
2. **You know the customer's email or phone**: `service_auth`. The claim ceremony starts immediately.
3. **You know neither yet**: `anonymous`. You get an assertion with no customer scopes; claim later.

`identity_assertion` (ID-JAG) is not accepted yet: `/agent/identity` answers `issuer_not_enabled`.

## Step 3 — Register

```http
POST https://washbodega.com/agent/identity
Content-Type: application/json

{ "type": "anonymous" }
```

Returns `registration_id`, `identity_assertion` (ES256 JWT, no scopes), `assertion_expires`, `pre_claim_scopes: []`, `claim_url`, `claim_token` (returned once; keep it in memory only), `claim_token_expires` (24 hours), and `post_claim_scopes`.

```http
POST https://washbodega.com/agent/identity
Content-Type: application/json

{ "type": "service_auth", "login_hint": "customer@example.com" }
```

Returns the same registration fields plus a `claim` block (Step 4b). The answer is the same whether or not the contact has an account.

## Step 4 — Claim ceremony

### 4a. Start the claim (anonymous registrations)

```http
POST https://washbodega.com/agent/identity/claim
Content-Type: application/json

{ "claim_token": "clm_…", "email": "customer@example.com" }
```

Send exactly one of `email` or `phone` for the customer you act for. Only a signed-in customer whose **verified** email or phone matches can finish. Wash Bodega never contacts that address or number because of this request.

The response carries `claim_attempt`: `user_code` (6 digits), `verification_uri`, `expires_in` (up to 600 seconds), and `interval` (5 seconds). Calling again with the same `claim_token` replaces the attempt with a fresh code.

### 4b. Hand off to the customer

> Open this link, sign in to your Wash Bodega account, and enter this 6-digit code: **123456**
> https://washbodega.com/agent/connect#claim=…

The code goes into the Wash Bodega page, not back to you. Five wrong codes (or a customer whose verified contact does not match) lock the attempt.

### 4c. Poll

```http
POST https://washbodega.com/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:workos:agent-auth:grant-type:claim&claim_token=clm_…
```

Answers `authorization_pending`, `slow_down` (add 5 seconds), `expired_token` (start a new attempt), or `invalid_grant` (already delivered, revoked, or unknown). Success returns `access_token`, `expires_in` (3600), `scope`, and a new `identity_assertion` (valid 30 days). Claiming revokes any pre-claim access token and supersedes the pre-claim assertion. Tokens are delivered once: if that response is lost, ask the customer to disconnect the agent at https://washbodega.com/agent/connect and register again.

## Step 5 — Exchange the assertion

```http
POST https://washbodega.com/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=<identity_assertion>
```

Returns a 1-hour bearer token with the assertion's scopes. Repeat with the same assertion when it expires; `invalid_grant` means start again at Step 3.

## Step 6 — Use the token

Call `https://washbodega.com/mcp/customer` with `Authorization: Bearer <access_token>`. Call `tools/list` for the current tools. Before filing a pickup request, confirm the details with the customer. A request is not an order: Wash Bodega staff review it and enter it in Cents, which handles weighing and payment. New requests are for tomorrow only in America/Chicago; omit pickup_date to default. An existing idempotency key keeps its original request/date across midnight. A request and staff Cents-entry attestation do not by themselves confirm a route or time; actual staff or provider confirmation is needed.

## Revocation

- `POST https://washbodega.com/oauth2/revoke` with `token=<token>` (RFC 7009; always 200). Revoking a refresh token ends the customer's approval of your app.
- The customer can disconnect you at any time at https://washbodega.com/agent/connect (Spanish: https://washbodega.com/agente/conectar). Your tokens then stop working; restart at Step 1 only if the customer asks.
- A replayed authorization code, or a refresh token replayed more than 60 seconds after it was used, revokes the whole approval. A quick retry inside that window is refused without revoking.

## Public reads (no credential)

- Business facts: https://washbodega.com/business-info.json
- Agent index: https://washbodega.com/llms.txt
- API catalog: https://washbodega.com/.well-known/api-catalog
- OpenAPI: https://washbodega.com/openapi.json
- Agent service guide: https://washbodega.com/agents
- A2A Agent Card: https://washbodega.com/.well-known/agent-card.json
- Read-only MCP endpoint: https://washbodega.com/mcp
- MCP Server Card: https://washbodega.com/mcp/server-card
- AI Catalog: https://washbodega.com/.well-known/ai-catalog.json

Store customer access is open 24 hours a day, 7 days a week. That statement applies only to store customer access. Staff are on site 24/7 (owner-confirmed 2026-10-09). Neither statement means 24/7 phone coverage, Wash & Fold or Wash & Go intake, pickup and delivery, or commercial service. Wash Bodega does not offer dry cleaning.

## Boundaries

- Do not create Wash Bodega accounts, send email or SMS on anyone's behalf, or enter payment details anywhere. Customers sign in at https://washbodega.com/account (Spanish: https://washbodega.com/cuenta).
- Do not call `/api/customer-intake`, `/api/reach-event`, `/api/apply`, `/api/account*`, `/api/agent-access` (the customer's own page uses it), or webhook paths.
- A customer's Wash Bodega session is for that person in a browser. It is not an agent token and must not be copied into agent software.
- Approval to file pickup requests is not permission to market to the customer.
