A FastAPI application that combines LlamaCloud for document storage and parsing with Auth0 / Okta FGA for fine-grained authorization on paycheck RAG queries, using the auth0-ai-llamaindex SDK and Anthropic Claude for answer synthesis.
Employees query their paycheck information through a REST API. Every request is authorized by Okta FGA — employees can only see their own paychecks, while department managers can see their whole team's:
| User | Role | Accessible paychecks |
|---|---|---|
| john | Software Engineer | Own paychecks only |
| jane | Software Engineer | Own paychecks only |
| mary | Eng. Manager | All department paychecks (via FGA manager relation) |
The caller's identity is taken from the sub claim of the bearer token — no separate login flow required.
POST /pay/upload/{user_id}
│
├─ Upload PDF(s) to LlamaCloud
├─ Parse with LlamaParse
└─ Write FGA tuple: user:{user_id} → owner → paycheck:{file_id}
POST /pay/insights (bearer token → sub → user_id)
│
▼
[retrieve step]
├─ FGA list_objects — get all paycheck IDs the user can view
├─ Fetch filenames + parsed content from LlamaCloud
└─ FGARetriever batch_check — explicit per-document authorization gate
│
▼
RetrievedEvent (authorized nodes only)
│
▼
[synthesize step]
├─ FGA list_objects — resolve department membership (for managers)
└─ Anthropic Claude answers using only authorized paycheck content
│
▼
{"answer": "..."}
Authorization is modelled in fga/model.fga.yaml. Mary's access to team paychecks is derived automatically: she is the manager of department:devrel, and can_view on each paycheck includes manager from owner.
- Python 3.11+
- An Anthropic API key
- A LlamaCloud API key
- A free Okta FGA store with a client ID and secret
# 1. Install dependencies
pip install -e .
# 2. Copy and fill in your credentials
cp .env.example .env
# 3. Initialise the FGA authorization model from fga/model.fga.yaml
python3 ./scripts/setup_fga.py
# 4. Start the API
run-serverThe API will be live at http://localhost:8000.
To interact with it, you can use the pre-made client:
run-clientWhich has an interactive (but minimal) TUI to send requests to the server.
Warning
Both endpoints require a Bearer token in the Authorization header. The token is
a standard JWT; the app decodes the payload (without verification — this is a
demo) and extracts the sub claim as the user identifier. In production you
would verify the token signature against your Auth0 JWKS endpoint.
Upload one or more paycheck PDFs for a given user. Requires a valid bearer token.
curl -X POST http://localhost:8000/pay/upload/john \
-H "Authorization: Bearer <token>" \
-F "files=@john_paycheck_01.pdf" \
-F "files=@john_paycheck_02.pdf"Files are uploaded and parsed by LlamaCloud. An FGA owner tuple is written for each file using the LlamaCloud file_id as the paycheck object identifier.
Ask a question about paycheck data. The user is identified from the sub claim of the bearer token.
curl -X POST http://localhost:8000/pay/insights \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"question": "What is my total net pay across all paychecks?"}'.
├── api.py # FastAPI app — upload and insights endpoints
├── workflow.py # LlamaIndex Workflow (retrieve → synthesize)
├── retriever.py # LlamaCloudRetriever + FGARetriever
├── fga_config.py # Shared Okta FGA client configuration
├── fga/
│ └── model.fga.yaml # FGA authorization model, tuples, and tests
├── paychecks/ # Sample paycheck PDFs
├── scripts/
│ ├── setup_fga.py # Initialize FGA model and tuples from YAML
│ ├── clear_fga_tuples.py # Delete all tuples from the FGA store
│ └── generate_paychecks.py # Generates sample paycheck PDFs
├── requirements.txt
└── .env.example
| Variable | Description |
|---|---|
ANTHROPIC_API_KEY |
Anthropic API key |
ANTHROPIC_BASE_URL |
Optional custom Anthropic endpoint |
LLAMA_CLOUD_API_KEY |
LlamaCloud API key |
FGA_STORE_ID |
Okta FGA store ID |
FGA_CLIENT_ID |
Okta FGA OAuth2 client ID |
FGA_CLIENT_SECRET |
Okta FGA OAuth2 client secret |
FGA_API_URL |
FGA API URL (defaults to US1 region) |
Copyright 2026 Okta, Inc.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0