High-risk Operation Verification
When a user initiates a sensitive operation in your system (transfer, account deletion, password change, etc.), you can use the Palm verification feature to require the user to confirm via palm scan on the PalmxApp.
Flow Overview
┌─ Your Frontend ───────────────────────────────────────────────┐
│ │
│ User initiates dangerous operation → request your backend │
│ Wait for verification result from backend → display to user │
│ │
└───────────────────────────────────────────────────────────────┘
│ ↑
↓ │
┌─ Your Backend ────────────────────────────────────────────────┐
│ │
│ 1. Detect dangerous operation → call VerifyCreate to create │
│ a verification challenge │
│ 2. Poll VerifyQuery to check verification status │
│ 3. Status is approved → release the operation, return result │
│ to frontend │
│ │
└───────────────────────────────────────────────────────────────┘
(Palm push notification)
↓
┌─ PalmxApp ───────────────────────────────────────────────────┐
│ │
│ User sees the verification request on their phone → │
│ palm scan to confirm / deny │
│ │
└───────────────────────────────────────────────────────────────┘
Step 1: Backend Creates a Verification Challenge
POST /palm/openai/x/oauth/verify/challenges/create
Authorization: Bearer <API_KEY>
Content-Type: application/json
{
"ChallengeId": "550e8400-e29b-41d4-a716-446655440000", // UUID you generated (idempotency key)
"OpenId": "a1b2c3d4e5f6...", // User's open_id (obtained during binding)
"Action": "withdraw", // Business action identifier
"ActionDetail": "{\"amount\":\"1000 USD\"}" // Business detail JSON (≤512 bytes, displayed to user)
}
Response:
{
"code": 0,
"data": {
"ExpiresAt": 1700000120, // Verification expiration timestamp (seconds)
"Status": "pending" // Initial status
}
}
Idempotency: Re-submitting the same
ChallengeIdwill not create a new challenge, but will return the existing status.
Step 2: Backend Polls for Verification Results
Your frontend polls your own backend API, and your backend calls the Palm VerifyQuery API to check the status:
POST /palm/openai/x/oauth/verify/challenges/query
Authorization: Bearer <API_KEY>
Content-Type: application/json
{
"ChallengeId": "550e8400-e29b-41d4-a716-446655440000"
}
Response:
{
"code": 0,
"data": {
"Status": "approved" // pending / approved / denied / expired
}
}
Status Reference
| Status | Meaning | Frontend Action |
|---|---|---|
pending | Waiting for user confirmation | Continue polling (recommended 2s interval) |
approved | User confirmed | Stop polling, notify backend to proceed |
denied | User rejected | Stop polling, notify user the operation was denied |
expired | Timed out without response | Stop polling, notify timeout, allow user to retry |
Complete Backend Example (Go)
// POST /api/withdraw — Initiate withdrawal, create verification challenge
func WithdrawHandler(w http.ResponseWriter, r *http.Request) {
bizId := GetCurrentBizId(r) // Your system's business identifier (order number, user ID, etc.)
openId := db.GetPalmOpenId(bizId)
// Create verification challenge
challengeId := uuid.New().String()
body, _ := json.Marshal(map[string]string{
"ChallengeId": challengeId,
"OpenId": openId,
"Action": "withdraw",
"ActionDetail": fmt.Sprintf(`{"amount":"%s","to":"%s"}`, req.Amount, req.To),
})
httpReq, _ := http.NewRequest("POST",
"https://open.palmoa.youtu.qq.com/palm/openai/x/oauth/verify/challenges/create",
bytes.NewReader(body))
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Authorization", "Bearer "+API_KEY)
http.DefaultClient.Do(httpReq)
// Save challenge-business association, return info needed by frontend
db.SavePendingWithdraw(bizId, challengeId, req.Amount, req.To)
json.NewEncoder(w).Encode(map[string]interface{}{"success": true})
}
// POST /api/withdraw/poll — Frontend polls verification status
func WithdrawPollHandler(w http.ResponseWriter, r *http.Request) {
bizId := GetCurrentBizId(r)
challengeId := db.GetPendingChallenge(bizId)
// Call Palm VerifyQuery
body, _ := json.Marshal(map[string]string{"ChallengeId": challengeId})
httpReq, _ := http.NewRequest("POST",
"https://open.palmoa.youtu.qq.com/palm/openai/x/oauth/verify/challenges/query",
bytes.NewReader(body))
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Authorization", "Bearer "+API_KEY)
resp, _ := http.DefaultClient.Do(httpReq)
defer resp.Body.Close()
var result struct {
Data struct {
Status string `json:"Status"`
} `json:"data"`
}
json.NewDecoder(resp.Body).Decode(&result)
// approved → execute business logic
if result.Data.Status == "approved" {
executeWithdraw(bizId, challengeId)
}
// Return your own business status to frontend
json.NewEncoder(w).Encode(map[string]string{"status": result.Data.Status})
}