cURL
curl --fail-with-body --max-time 30 'https://app.reflections.ai/.well-known/jwks.json'import { exit } from "node:process";
const response = await fetch("https://app.reflections.ai/.well-known/jwks.json", {
method: "GET",
headers: {
},
signal: AbortSignal.timeout(30_000),
});
const responseBody = await response.text();
if (!response.ok) {
console.error(`HTTP ${response.status}: ${responseBody}`);
exit(1);
}
console.log(responseBody);
from urllib.error import HTTPError
from urllib.request import Request, urlopen
headers = {
}
request = Request("https://app.reflections.ai/.well-known/jwks.json",
method="GET", headers=headers)
try:
with urlopen(request, timeout=30) as response:
print(response.read().decode("utf-8"))
except HTTPError as error:
error_body = error.read().decode("utf-8")
raise RuntimeError(f"HTTP {error.code}: {error_body}") from error
package main
import (
"fmt"
"io"
"net/http"
"time"
)
func main() {
request, err := http.NewRequest("GET", "https://app.reflections.ai/.well-known/jwks.json", nil)
if err != nil {
panic(err)
}
client := &http.Client{Timeout: 30 * time.Second}
response, err := client.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
responseBody, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode < 200 || response.StatusCode >= 300 {
panic(fmt.Sprintf("HTTP %d: %s", response.StatusCode, responseBody))
}
fmt.Print(string(responseBody))
}
{
"keys": [
{
"kty": "EC",
"crv": "P-256",
"kid": "rfl-2026-10-docs3",
"use": "sig",
"alg": "ES256",
"x": "ZoYofSbzIa2end2Uk8RWL2lqZA8h1yhqDUPTdAeguns",
"y": "ufMrUfKD5j5lbepL80bq9gxm81nQ0Zy0n8ABI5ZorZ0"
},
{
"kty": "EC",
"crv": "P-256",
"kid": "rfl-2026-04-docs3",
"use": "sig",
"alg": "ES256",
"x": "smCKFBms8rHn_zcLkGN7leNXC0Kw0tMUos5Ptq3vTP8",
"y": "aXhqxlAT_XFcwIvpRfcQ3_F-osigj44ekOnIjTBcoRw"
},
{
"kty": "EC",
"crv": "P-256",
"kid": "rfl-test-2026-10-docs3",
"use": "sig",
"alg": "ES256",
"x": "olA2YiuvUovqtC-h3GQzdrJtqrG0cyh1YPgnzrBjVy8",
"y": "D7LpiphUIzpegpDaxDAj9NDmXi4ht4ADhSlphr8JCBc"
}
]
}{
"type": "https://docs.reflections.ai/errors/rate_limited",
"title": "Too many requests",
"status": 429,
"code": "rate_limited",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "invalid_request",
"request_id": "<string>",
"detail": "<string>",
"param": "<string>",
"doc_url": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "invalid_request",
"request_id": "<string>",
"detail": "<string>",
"param": "<string>",
"doc_url": "<string>"
}Discovery
Retrieve the signing keys
Test mode available. Live mode available. Public, with no key. The set holds the rfl-test- keys that sign sandbox tokens and receipts and the live keys published before live signing is activated. A published live key does not mean live signing is active.
The JWKS that holds the public keys for every authorization token, session token and
receipt. Public, and served with Cache-Control: public, max-age=3600. Cache it for that
hour, and fetch it again when a token names a kid you do not have, at most once per hour.
Retired keys stay published so old receipts keep verifying.
GET
/
.well-known
/
jwks.json
cURL
curl --fail-with-body --max-time 30 'https://app.reflections.ai/.well-known/jwks.json'import { exit } from "node:process";
const response = await fetch("https://app.reflections.ai/.well-known/jwks.json", {
method: "GET",
headers: {
},
signal: AbortSignal.timeout(30_000),
});
const responseBody = await response.text();
if (!response.ok) {
console.error(`HTTP ${response.status}: ${responseBody}`);
exit(1);
}
console.log(responseBody);
from urllib.error import HTTPError
from urllib.request import Request, urlopen
headers = {
}
request = Request("https://app.reflections.ai/.well-known/jwks.json",
method="GET", headers=headers)
try:
with urlopen(request, timeout=30) as response:
print(response.read().decode("utf-8"))
except HTTPError as error:
error_body = error.read().decode("utf-8")
raise RuntimeError(f"HTTP {error.code}: {error_body}") from error
package main
import (
"fmt"
"io"
"net/http"
"time"
)
func main() {
request, err := http.NewRequest("GET", "https://app.reflections.ai/.well-known/jwks.json", nil)
if err != nil {
panic(err)
}
client := &http.Client{Timeout: 30 * time.Second}
response, err := client.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
responseBody, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode < 200 || response.StatusCode >= 300 {
panic(fmt.Sprintf("HTTP %d: %s", response.StatusCode, responseBody))
}
fmt.Print(string(responseBody))
}
{
"keys": [
{
"kty": "EC",
"crv": "P-256",
"kid": "rfl-2026-10-docs3",
"use": "sig",
"alg": "ES256",
"x": "ZoYofSbzIa2end2Uk8RWL2lqZA8h1yhqDUPTdAeguns",
"y": "ufMrUfKD5j5lbepL80bq9gxm81nQ0Zy0n8ABI5ZorZ0"
},
{
"kty": "EC",
"crv": "P-256",
"kid": "rfl-2026-04-docs3",
"use": "sig",
"alg": "ES256",
"x": "smCKFBms8rHn_zcLkGN7leNXC0Kw0tMUos5Ptq3vTP8",
"y": "aXhqxlAT_XFcwIvpRfcQ3_F-osigj44ekOnIjTBcoRw"
},
{
"kty": "EC",
"crv": "P-256",
"kid": "rfl-test-2026-10-docs3",
"use": "sig",
"alg": "ES256",
"x": "olA2YiuvUovqtC-h3GQzdrJtqrG0cyh1YPgnzrBjVy8",
"y": "D7LpiphUIzpegpDaxDAj9NDmXi4ht4ADhSlphr8JCBc"
}
]
}{
"type": "https://docs.reflections.ai/errors/rate_limited",
"title": "Too many requests",
"status": 429,
"code": "rate_limited",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "invalid_request",
"request_id": "<string>",
"detail": "<string>",
"param": "<string>",
"doc_url": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "invalid_request",
"request_id": "<string>",
"detail": "<string>",
"param": "<string>",
"doc_url": "<string>"
}Response
The key set.
Every key that signs, or once signed, tokens and receipts. Retired keys stay published so old receipts keep verifying.
Show child attributes
Show child attributes