···7788## Which endpoints require what?
991010-| Endpoint type | Client identification | User authentication |
1111-| ---------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------ |
1212-| Queries (`GET /xrpc/{method}`) | `X-Client-Key` required | Optional — DPoP auth if the query needs to know who the user is |
1313-| Procedures (`POST /xrpc/{method}`) | `X-Client-Key` required | Required — DPoP auth so HappyView can proxy writes to the user's PDS |
1010+| Endpoint type | Client identification | User authentication |
1111+| ---------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------- |
1212+| Queries (`GET /xrpc/{method}`) | `X-Client-Key` required | Optional — DPoP auth if the query needs to know who the user is |
1313+| Procedures (`POST /xrpc/{method}`) | `X-Client-Key` required | Required — DPoP auth so HappyView can proxy writes to the user's PDS |
1414| Admin API (`/admin/*`) | — | Required — admin API key or service auth JWT with the right [permissions](../guides/permissions.md) |
1515-| Health check (`GET /health`) | — | — |
1515+| Health check (`GET /health`) | — | — |
16161717## XRPC: API client identification
1818···131131Third-party apps that want HappyView to make PDS writes on behalf of their users use the **DPoP key provisioning** flow. This avoids browser-based redirects through HappyView's domain, which can be blocked by Firefox's Bounce Tracker Protection.
132132133133The idea: the app gets a DPoP keypair from HappyView, uses that keypair during its own OAuth flow with the user's PDS, then registers the resulting tokens back with HappyView. From that point on, XRPC requests authenticated with `Authorization: DPoP <access_token>` plus a `DPoP` proof header and `X-Client-Key` will have HappyView proxy writes using the stored session.
134134+135135+The client app and HappyView share the same DPoP keypair, so both can generate valid proofs that the PDS will accept. The PDS binds tokens to a key's thumbprint but it doesn't care who signs the proof, only that it was signed by the right key.
136136+137137+### Flow overview
138138+139139+```mermaid
140140+sequenceDiagram
141141+ participant Client as Client App
142142+ participant HV as HappyView
143143+ participant PDS as User's PDS
144144+145145+ note over Client,PDS: Phase 1 — DPoP Key Provisioning
146146+ Client->>HV: POST /oauth/dpop-keys<br/>X-Client-Key + secret or PKCE
147147+ HV->>HV: Authenticate client<br/>Generate ES256 keypair<br/>Encrypt & store private key
148148+ HV-->>Client: provision_id + DPoP private JWK
149149+150150+ note over Client,PDS: Phase 2 — OAuth with the User's PDS
151151+ Client->>PDS: Redirect user to authorize<br/>client_id embeds DPoP public key
152152+ PDS-->>Client: Auth code (redirect back)
153153+ Client->>PDS: Exchange code for tokens<br/>DPoP proof signed with provisioned key
154154+ PDS-->>Client: Access + refresh tokens<br/>(bound to DPoP key thumbprint)
155155+156156+ note over Client,PDS: Phase 3 — Session Registration
157157+ Client->>HV: POST /oauth/sessions<br/>provision_id + tokens + PKCE verifier
158158+ HV->>HV: Verify PKCE, validate scopes<br/>Encrypt & store tokens
159159+ HV-->>Client: session_id + DID
160160+161161+ note over Client,PDS: Phase 4 — Authenticated XRPC Request
162162+ Client->>HV: POST /xrpc/{method}<br/>Authorization: DPoP + proof + X-Client-Key
163163+ HV->>HV: Validate proof (sig, method,<br/>URL, token hash, thumbprint)
164164+ HV->>HV: Generate fresh DPoP proof<br/>with same shared keypair
165165+ HV->>PDS: POST /xrpc/com.atproto.repo.*<br/>Authorization: DPoP + proof
166166+ PDS-->>HV: Success
167167+ HV-->>Client: Response
168168+```
134169135170:::tip
136171The [JavaScript SDK](../sdk/overview.md) handles this entire flow for you. The raw HTTP flow below is useful for understanding the protocol or building a non-JavaScript client.