{
    "openapi": "3.1.0",
    "info": {
        "title": "PPX PHP Reference Provider",
        "version": "0.1.0",
        "description": "Independent PHP implementation of the PPX v0.1 HTTP binding.",
        "license": {
            "name": "Apache-2.0"
        }
    },
    "servers": [
        {
            "url": "https://ppx.dev/ppx"
        }
    ],
    "components": {
        "securitySchemes": {
            "bearer": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "JWT"
            }
        }
    },
    "paths": {
        "/.well-known/ppx-card.json": {
            "get": {
                "summary": "Discovery card",
                "responses": {
                    "200": {
                        "description": "PPX discovery card"
                    }
                }
            }
        },
        "/.well-known/jwks.json": {
            "get": {
                "summary": "Grant-signing public keys",
                "responses": {
                    "200": {
                        "description": "JWKS"
                    }
                }
            }
        },
        "/v1/profile/effective": {
            "post": {
                "summary": "Read the effective profile within grant scope",
                "security": [
                    {
                        "bearer": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Scoped profile"
                    },
                    "401": {
                        "description": "Missing/invalid/expired token",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "403": {
                        "description": "Revoked grant or cross-domain denial",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "error": {
                                            "type": "string"
                                        },
                                        "message": {
                                            "type": "string"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/v1/profile/propose-updates": {
            "post": {
                "summary": "Propose claim updates",
                "security": [
                    {
                        "bearer": []
                    }
                ],
                "responses": {
                    "202": {
                        "description": "Proposal created, pending review"
                    },
                    "403": {
                        "description": "Write-back forbidden by grant"
                    }
                }
            }
        },
        "/v1/consent/request": {
            "post": {
                "summary": "Create a grant request. Carries no authority until approved.",
                "responses": {
                    "201": {
                        "description": "pending grant request"
                    },
                    "404": {
                        "description": "unknown client_id or subject_id"
                    }
                }
            }
        },
        "/v1/consent/request/{grant_request_id}": {
            "get": {
                "summary": "Poll a grant request for the subject decision.",
                "responses": {
                    "200": {
                        "description": "request state"
                    }
                }
            }
        },
        "/v1/consent/approve": {
            "post": {
                "summary": "Subject approves a request, optionally narrowing it. User token required.",
                "responses": {
                    "200": {
                        "description": "approved"
                    },
                    "403": {
                        "description": "not the subject, or a grant token was used"
                    }
                }
            }
        },
        "/v1/consent/reject": {
            "post": {
                "summary": "Subject rejects a request. User token required.",
                "responses": {
                    "200": {
                        "description": "rejected"
                    }
                }
            }
        },
        "/v1/consent/token": {
            "post": {
                "summary": "Exchange an approved request for a grant-scoped access token.",
                "responses": {
                    "200": {
                        "description": "access token"
                    },
                    "425": {
                        "description": "not yet decided"
                    }
                }
            }
        },
        "/v1/consent/refresh": {
            "post": {
                "summary": "Re-mint for an existing grant, with no user interaction.",
                "responses": {
                    "200": {
                        "description": "access token"
                    },
                    "410": {
                        "description": "grant revoked or expired"
                    }
                }
            }
        },
        "/v1/consent/grants": {
            "get": {
                "summary": "List the subject's grants. User token required.",
                "responses": {
                    "200": {
                        "description": "grant list"
                    }
                }
            }
        },
        "/v1/auth/login": {
            "post": {
                "summary": "Sign in with email and password; returns a short-lived user token",
                "description": "Rate limited per client address. Failures return an identical message whether or not the account exists.",
                "responses": {
                    "200": {
                        "description": "user token"
                    },
                    "401": {
                        "description": "invalid credentials"
                    },
                    "429": {
                        "description": "too many failed attempts; see Retry-After"
                    }
                }
            }
        },
        "/v1/auth/demo-login": {
            "post": {
                "summary": "Alias of /v1/auth/login, retained for the published tutorial",
                "responses": {
                    "200": {
                        "description": "user token"
                    }
                }
            }
        },
        "/v1/consent/revoke": {
            "post": {
                "summary": "Revoke a grant",
                "security": [
                    {
                        "bearer": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Revoked"
                    }
                }
            }
        },
        "/v1/profile/export": {
            "get": {
                "summary": "Everything held about the authenticated subject",
                "description": "Requires a USER token. A grant token is refused: an application authorised to read a scoped view is not thereby authorised to take the whole record.",
                "security": [
                    {
                        "bearer": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Full export including claims, grants and audit trail"
                    },
                    "403": {
                        "description": "a grant token was supplied instead of a user token"
                    }
                }
            }
        },
        "/v1/profile": {
            "delete": {
                "summary": "Irreversibly erase the authenticated subject",
                "description": "Requires a USER token AND the body {\"confirm\":\"ERASE\"}. Every grant is revoked before deletion, so tokens already issued to applications stop being honoured immediately.",
                "security": [
                    {
                        "bearer": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Erased; body reports what was removed"
                    },
                    "400": {
                        "description": "confirmation missing"
                    },
                    "403": {
                        "description": "protected demonstration account"
                    }
                }
            }
        }
    }
}
