Registration and Authorization
Try Registration & Authorization
Scan the QR code below with PalmxApp to experience the full Agent registration and human authorization flow.
Step 1 · Agent Generates Keys Locally and Self-Signs to Initiate Registration
The Agent client generates an Ed25519 key pair locally. The private key never leaves the device. During registration, the agent self-signs a fixed-format message to prove it holds the private key.
Message to sign (fixed format):
message = "clawkey-register-" + timestamp_ms
Request:
POST /palm/openai/x/agent/register/init
Content-Type: application/json
{
"AppId": "your_app_id", // Public app_id from console
"DeviceId": "trader-001", // Agent external identity, ^[A-Za-z0-9_-]{1,64}$, immutable after registration
"PubkeyDer": "MCowBQYDK2VwAyEA...", // base64(Ed25519 SPKI DER)
"Message": "clawkey-register-1717488000000",
"Signature": "h3Js9k...", // base64(Ed25519(message)), 64-byte raw signature
"Timestamp": 1717488000000 // Unix milliseconds, must match message exactly, ±5min
}
Response:
{
"code": 0,
"data": {
"SessionId": "ars_xxxx",
"RegistrationUrl": "https://open.palmoa.youtu.qq.com/agent/register?sid=ars_xxxx",
"ExpiresAt": 1717488600
}
}
device_idvsagent_name:
device_id: Stable external identity for the Agent, used by machines. Unique within(app_id, device_id), immutable after registration.agent_name: Human-friendly name (e.g., "My trading bot"), UTF-8 ≤64 characters, allows Chinese/emoji/duplicate names. Filled in by the real person on the confirmation page and displayed in PalmxApp.
Client Signing (Go Example):
import (
"crypto/ed25519"
"crypto/rand"
"crypto/x509"
"encoding/base64"
"fmt"
"time"
)
// Generate once and persist (store private key securely, never expose)
pub, priv, _ := ed25519.GenerateKey(rand.Reader)
spkiDER, _ := x509.MarshalPKIXPublicKey(pub)
tsMs := time.Now().UnixMilli()
message := fmt.Sprintf("clawkey-register-%d", tsMs)
sig := ed25519.Sign(priv, []byte(message))
req := map[string]interface{}{
"AppId": APP_ID,
"DeviceId": DEVICE_ID,
"PubkeyDer": base64.StdEncoding.EncodeToString(spkiDER),
"Message": message,
"Signature": base64.StdEncoding.EncodeToString(sig),
"Timestamp": tsMs,
}
// POST to /palm/openai/x/agent/register/init
Step 2 · Real Person Scans Palm to Confirm
The Agent passes the RegistrationUrl to the real person (display a QR code / send a link). When the real person opens it in a browser, they see:
The agent "
{agent_name}" under app "{app_name}" wants to run as your identity. Please use PalmxApp to scan the QR code and scan your palm to confirm.
The real person scans the QR code with PalmxApp, selects an authorization duration (1d / 3d / 7d / 30d, default 7d), and scans their palm to confirm. The server completes the following in a single transaction:
t_agent.status→active- Writes to
t_agent_user_binding(binds the real person'sopen_id+ authorization expiration time) - Generates a one-time
code(bound to(app_id, device_id, user_id, open_id), Redis TTL 5min)
This step is handled by PalmxApp / the landing page — the business does not need to implement it.
Step 3 · Agent Polls for One-time Code
POST /palm/openai/x/agent/register/status
Content-Type: application/json
{ "SessionId": "ars_xxxx" }
Response:
{
"code": 0,
"data": {
"Status": "completed", // pending / completed / expired / failed
"DeviceId": "trader-001",
"Code": "ab12...cd34" // returned when completed, one-time, TTL 5min; open_id is NOT returned
}
}
Poll at 2-second intervals. Note: The Agent itself never obtains the open_id, it only gets a one-time code to pass to the business.
Step 4 · Business Exchanges Code for open_id (Authorization Completion Point)
The Agent passes the code to the business backend, which exchanges it using the api_key:
POST /palm/openai/x/agent/code/exchange
Authorization: Bearer <api_key>
Content-Type: application/json
{ "Code": "ab12...cd34" }
Response:
{
"code": 0,
"data": {
"DeviceId": "trader-001",
"OpenId": "u_a1b2c3...",
"Registered": true
}
}
After receiving the open_id, the business checks its system for the open_id ↔ local account binding:
- If found → record
(open_id, device_id) ↔ local user, authorization complete; - If not found → return failure (prerequisite: the user must have previously bound their account with Palm in your system).
codeis one-time use. Expired or duplicate redemption returns an error.code.app_idmust match theoc_appidcorresponding to your api_key, otherwiseErrAgentForbidden.
Business Backend (Go Example):
// POST /your-api/agent/complete — Agent sends code to your backend
func CompleteAgentAuth(w http.ResponseWriter, r *http.Request) {
var in struct{ Code string `json:"code"` }
json.NewDecoder(r.Body).Decode(&in)
body, _ := json.Marshal(map[string]string{"Code": in.Code})
req, _ := http.NewRequest("POST",
"https://open.palmoa.youtu.qq.com/palm/openai/x/agent/code/exchange",
bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+API_KEY)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var out struct {
Data struct {
DeviceId string `json:"DeviceId"`
OpenId string `json:"OpenId"`
} `json:"data"`
}
json.NewDecoder(resp.Body).Decode(&out)
// Look up local account mapping
localUser, ok := db.FindByPalmOpenId(out.Data.OpenId)
if !ok {
http.Error(w, "open_id not bound to any local account", http.StatusForbidden)
return
}
// Record (open_id, device_id) ↔ localUser, authorization complete
db.BindAgent(localUser.ID, out.Data.OpenId, out.Data.DeviceId)
json.NewEncoder(w).Encode(map[string]bool{"success": true})
}