curl --request POST \
--url https://api.pid.peridotvault.com/v1/session-keys/grants/validate \
--header 'Content-Type: application/json' \
--cookie pid_access= \
--data '
{
"vault": "<string>",
"accountId": "<string>",
"sessionPubkey": "<string>",
"allowedProgram": "<string>",
"expiresAt": 123,
"recordedHasAuthority": true,
"recordedSlot": "<string>",
"deadline": 123,
"nonce": "<string>",
"recordedAuthority": "<string>"
}
'const options = {
method: 'POST',
headers: {cookie: 'pid_access=', 'Content-Type': 'application/json'},
body: JSON.stringify({
vault: '<string>',
accountId: '<string>',
sessionPubkey: '<string>',
allowedProgram: '<string>',
expiresAt: 123,
recordedHasAuthority: true,
recordedSlot: '<string>',
deadline: 123,
nonce: '<string>',
recordedAuthority: '<string>'
})
};
fetch('https://api.pid.peridotvault.com/v1/session-keys/grants/validate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.pid.peridotvault.com/v1/session-keys/grants/validate"
payload = {
"vault": "<string>",
"accountId": "<string>",
"sessionPubkey": "<string>",
"allowedProgram": "<string>",
"expiresAt": 123,
"recordedHasAuthority": True,
"recordedSlot": "<string>",
"deadline": 123,
"nonce": "<string>",
"recordedAuthority": "<string>"
}
headers = {
"cookie": "pid_access=",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.pid.peridotvault.com/v1/session-keys/grants/validate"
payload := strings.NewReader("{\n \"vault\": \"<string>\",\n \"accountId\": \"<string>\",\n \"sessionPubkey\": \"<string>\",\n \"allowedProgram\": \"<string>\",\n \"expiresAt\": 123,\n \"recordedHasAuthority\": true,\n \"recordedSlot\": \"<string>\",\n \"deadline\": 123,\n \"nonce\": \"<string>\",\n \"recordedAuthority\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("cookie", "pid_access=")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"sessionAddress": "<string>",
"registerPayload": "<string>"
}{
"statusCode": 400,
"message": [
"handle must be 3-20 chars: lowercase letters, numbers, underscore"
]
}{
"statusCode": 401,
"message": "Unauthorized"
}{
"statusCode": 429,
"message": "ThrottlerException: Too Many Requests"
}Validate a session grant and return its canonical address + owner challenge
Pure scope-hygiene check mirroring register_session (24h TTL, game binding,
upgrade snapshot shape) plus the canonical session PDA and the owner
challenge (what the passkey signs). Authenticated; submission-time
pid→vault binding is enforced by the relay path.
curl --request POST \
--url https://api.pid.peridotvault.com/v1/session-keys/grants/validate \
--header 'Content-Type: application/json' \
--cookie pid_access= \
--data '
{
"vault": "<string>",
"accountId": "<string>",
"sessionPubkey": "<string>",
"allowedProgram": "<string>",
"expiresAt": 123,
"recordedHasAuthority": true,
"recordedSlot": "<string>",
"deadline": 123,
"nonce": "<string>",
"recordedAuthority": "<string>"
}
'const options = {
method: 'POST',
headers: {cookie: 'pid_access=', 'Content-Type': 'application/json'},
body: JSON.stringify({
vault: '<string>',
accountId: '<string>',
sessionPubkey: '<string>',
allowedProgram: '<string>',
expiresAt: 123,
recordedHasAuthority: true,
recordedSlot: '<string>',
deadline: 123,
nonce: '<string>',
recordedAuthority: '<string>'
})
};
fetch('https://api.pid.peridotvault.com/v1/session-keys/grants/validate', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.pid.peridotvault.com/v1/session-keys/grants/validate"
payload = {
"vault": "<string>",
"accountId": "<string>",
"sessionPubkey": "<string>",
"allowedProgram": "<string>",
"expiresAt": 123,
"recordedHasAuthority": True,
"recordedSlot": "<string>",
"deadline": 123,
"nonce": "<string>",
"recordedAuthority": "<string>"
}
headers = {
"cookie": "pid_access=",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.pid.peridotvault.com/v1/session-keys/grants/validate"
payload := strings.NewReader("{\n \"vault\": \"<string>\",\n \"accountId\": \"<string>\",\n \"sessionPubkey\": \"<string>\",\n \"allowedProgram\": \"<string>\",\n \"expiresAt\": 123,\n \"recordedHasAuthority\": true,\n \"recordedSlot\": \"<string>\",\n \"deadline\": 123,\n \"nonce\": \"<string>\",\n \"recordedAuthority\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("cookie", "pid_access=")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"sessionAddress": "<string>",
"registerPayload": "<string>"
}{
"statusCode": 400,
"message": [
"handle must be 3-20 chars: lowercase letters, numbers, underscore"
]
}{
"statusCode": 401,
"message": "Unauthorized"
}{
"statusCode": 429,
"message": "ThrottlerException: Too Many Requests"
}Authorizations
JWT access token set by /auth/google/callback or /auth/refresh
Body
Vault PDA the session attaches to (base58)
sha256(pid) account id (0x + 64 hex chars)
Ed25519 session keypair pubkey (base58)
The one allowlisted game program (base58)
Session hard expiry (unix seconds, ≤ 24h out)
Whether the game program currently has an upgrade authority
ProgramData slot at registration
Owner-authorization deadline (unix seconds, TTL ≤ 600s)
Owner nonce the registration consumes
Upgrade authority at registration (base58, required when recordedHasAuthority)