diff --git a/.github/workflows/deploy_doc.yml b/.github/workflows/deploy_doc.yml index aadf72c55..7b6cdbb65 100644 --- a/.github/workflows/deploy_doc.yml +++ b/.github/workflows/deploy_doc.yml @@ -68,17 +68,22 @@ jobs: set -euo pipefail git fetch origin gh-pages git worktree add ghpages gh-pages + # Static root files. Copy each published artifact explicitly so repo-only + # files in site_root/ (e.g. README.md) never leak onto the live site. cp site_root/robots.txt ghpages/robots.txt cp site_root/llms.txt ghpages/llms.txt cp site_root/index.html ghpages/index.html + cp -R site_root/developers ghpages/developers if [ -f ghpages/latest/llms-full.txt ]; then cp ghpages/latest/llms-full.txt ghpages/llms-full.txt fi + # Public OpenAPI document, served at the predictable /openapi.json. + python scripts/build_public_openapi.py ghpages/openapi.json cd ghpages - git add robots.txt llms.txt index.html llms-full.txt + git add robots.txt llms.txt index.html llms-full.txt openapi.json developers if git diff --cached --quiet; then echo "Site-root files already up to date." else - git commit -m "chore(docs): publish root llms.txt, robots.txt and landing page" + git commit -m "chore(docs): publish root llms.txt, robots.txt, openapi.json, developer portal" git push origin gh-pages fi diff --git a/docs/authentication.md b/docs/authentication.md new file mode 100644 index 000000000..c58ceed1c --- /dev/null +++ b/docs/authentication.md @@ -0,0 +1,63 @@ +# Authentication + +The Rapidata API uses **OAuth 2.0 / OpenID Connect**. The authorization server is +`https://auth.rapidata.ai`, and its discovery document is published at +[`/.well-known/openid-configuration`](https://auth.rapidata.ai/.well-known/openid-configuration). + +For programmatic access (the SDK, scripts, agents) you authenticate with the +**client credentials** grant using a client ID and secret. + +## Get credentials + +Create a client ID and secret under [Rapidata Settings → Tokens](https://app.rapidata.ai/settings/tokens). + +## With the SDK + +The SDK performs the token exchange for you. Pass the credentials directly: + +```python +from rapidata import RapidataClient + +client = RapidataClient( + client_id="YOUR_CLIENT_ID", + client_secret="YOUR_CLIENT_SECRET", +) +``` + +or set `RAPIDATA_CLIENT_ID` and `RAPIDATA_CLIENT_SECRET` in the environment +(useful for headless or containerised runs) and construct `RapidataClient()` +with no arguments. On a workstation, calling `RapidataClient()` with no +credentials instead opens a browser login once and caches the token in +`~/.config/rapidata/credentials.json`. + +## Direct token request + +To call the API without the SDK, request a token from the token endpoint and +send it as a bearer token: + +```bash +curl -X POST https://auth.rapidata.ai/connect/token \ + -d grant_type=client_credentials \ + -d client_id=YOUR_CLIENT_ID \ + -d client_secret=YOUR_CLIENT_SECRET \ + -d scope="openid email roles" + +# → {"access_token": "...", "token_type": "Bearer", "expires_in": 3600} + +curl https://api.rapidata.ai/order/openapi/v1.json \ + -H "Authorization: Bearer ACCESS_TOKEN" +``` + +## Scopes + +Tokens are scoped. The SDK requests `openid roles email` by default, which is +sufficient for all SDK operations. Request only the scopes you need. Every +endpoint in the [OpenAPI specification](https://docs.rapidata.ai/openapi.json) +declares the scopes it requires under its `OpenIdConnect` security scheme. + +| Scope | Grants | +|-------|--------| +| `openid` | Required for OIDC; identifies the token subject. | +| `email` | Access to the account email claim. | +| `roles` | The account's role claims, which gate API operations. | +| `offline_access` | A refresh token for long-lived sessions. | diff --git a/mkdocs.yml b/mkdocs.yml index 9a3e2117d..92b3ef387 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -58,6 +58,7 @@ plugins: Guides: - starting_page.md - quickstart.md + - authentication.md - audiences.md - signals.md - job_definition_parameters.md @@ -120,6 +121,7 @@ nav: - Guides: - Overview: starting_page.md - Quick Start: quickstart.md + - Authentication: authentication.md - Custom Audiences: audiences.md - Signals: signals.md - Parameter Reference: job_definition_parameters.md diff --git a/scripts/build_public_openapi.py b/scripts/build_public_openapi.py new file mode 100644 index 000000000..d6758cc32 --- /dev/null +++ b/scripts/build_public_openapi.py @@ -0,0 +1,58 @@ +"""Produce the public OpenAPI document served at docs.rapidata.ai/openapi.json. + +The combined spec under ``openapi/schemas/`` is the contract the SDK is generated +from, but it carries the internal ``rabbitdata.ch`` host and an inaccurate title. +Rewrite those to the public ``rapidata.ai`` host (which already serves the same +per-service specs and OIDC discovery document) so agents get an accurate surface. + +Usage: python scripts/build_public_openapi.py +""" + +from __future__ import annotations + +import json +import sys +from pathlib import Path + +SOURCE = ( + Path(__file__).resolve().parent.parent + / "openapi" + / "schemas" + / "rapidata.filtered.openapi.json" +) + + +def build() -> dict: + spec = json.loads(SOURCE.read_text(encoding="utf-8")) + + # The internal build host is the only thing standing between this and the + # live public spec at api.rapidata.ai — rewrite both the server and the + # OIDC discovery URL in the security scheme. + serialized = json.dumps(spec).replace("rabbitdata.ch", "rapidata.ai") + spec = json.loads(serialized) + + spec["servers"] = [{"url": "https://api.rapidata.ai/"}] + info = spec.setdefault("info", {}) + info["title"] = "Rapidata API" + info.setdefault( + "description", + "Public Rapidata API. Authentication uses OAuth 2.0 (OpenID Connect) — " + "see https://docs.rapidata.ai/latest/authentication/.", + ) + return spec + + +def main() -> None: + if len(sys.argv) != 2: + print(__doc__) + raise SystemExit(2) + out = Path(sys.argv[1]) + spec = build() + out.write_text(json.dumps(spec), encoding="utf-8") + print( + f"Wrote {out} ({out.stat().st_size} bytes, {len(spec.get('paths', {}))} paths)" + ) + + +if __name__ == "__main__": + main() diff --git a/site_root/developers/index.html b/site_root/developers/index.html new file mode 100644 index 000000000..ce3b651c0 --- /dev/null +++ b/site_root/developers/index.html @@ -0,0 +1,80 @@ + + + + + + Rapidata Developer Portal + + + + + + + + + + + + +
+

Rapidata Developer Portal

+

+ Rapidata provides human feedback at scale — crowd-sourced labeling, model + evaluation, ranking, and preference data. Integrate with the Python SDK or + directly against the OAuth 2.0 secured REST API. +

+ +

Get started

+ + +

Reference

+ + +

Guides & examples

+ + +

For AI agents

+ + +

Source

+ +
+ + diff --git a/site_root/index.html b/site_root/index.html index 53c82696c..0bb2b9271 100644 --- a/site_root/index.html +++ b/site_root/index.html @@ -49,6 +49,18 @@ "softwareHelp": "https://docs.rapidata.ai/latest/quickstart/", "publisher": { "@id": "https://www.rapidata.ai/#organization" }, "offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" } + }, + { + "@type": "WebAPI", + "name": "Rapidata API", + "description": "OAuth 2.0 secured REST API for requesting human feedback at scale.", + "documentation": "https://docs.rapidata.ai/developers/", + "provider": { "@id": "https://www.rapidata.ai/#organization" }, + "EntryPoint": { + "@type": "EntryPoint", + "urlTemplate": "https://api.rapidata.ai/", + "contentType": "application/json" + } } ] } @@ -73,9 +85,11 @@

Rapidata Python SDK

Install with pip install rapidata, then authenticate with a token from app.rapidata.ai/settings/tokens.