- Senpi Agent Onboarding
- Onboard an AI agent into the Senpi trading platform. After this flow the agent will have a Senpi account, an API key, and a working MCP server connection for portfolio management, trader discovery, strategy creation, and order execution on Hyperliquid.
- Prerequisites:
- Shell/code execution access and Node.js (required for
- npx
- ).
- Defaults
- SENPI_MCP_ENDPOINT
- :
- https://mcp.prod.senpi.ai
- (use this if
- {{SENPI_MCP_ENDPOINT}}
- is a literal unsubstituted placeholder)
- REFERRAL_CODE
-
- empty/optional (use this if
- {{REFERRAL_CODE}}
- is a literal unsubstituted placeholder)
- Onboarding Flow
- Follow every step in order. Do not skip steps.
- Before you begin: State initialization
- Per the
- state lifecycle
- , ensure
- state.json
- exists so routing and transitions are well-defined. If it does not exist, create it with initial
- FRESH
- state:
- if
- [
- !
- -f
- ~/.config/senpi/state.json
- ]
- ;
- then
- mkdir
- -p
- ~/.config/senpi
- cat
- >
- ~/.config/senpi/state.json
- <<
- 'STATEEOF'
- {
- "version": "1.0.0",
- "state": "FRESH",
- "error": null,
- "onboarding": {
- "step": "IDENTITY",
- "startedAt": null,
- "completedAt": null,
- "identityType": null,
- "subject": null,
- "walletGenerated": false,
- "existingAccount": false
- },
- "account": {},
- "wallet": { "funded": false },
- "firstTrade": { "completed": false, "skipped": false },
- "mcp":
- }
- STATEEOF
- fi
- Then continue with Step 0.
- Transition to ONBOARDING:
- Before running Step 0, if state is
- FRESH
- , update
- state.json
- so the state machine and resume behavior work. Set
- state
- to
- ONBOARDING
- , set
- onboarding.startedAt
- to current ISO 8601 UTC, and keep
- onboarding.step
- as
- IDENTITY
- . Use a read-modify-write (merge) so other fields are preserved:
- node
- -e
- "
- const fs = require('fs');
- const p = require('os').homedir() + '/.config/senpi/state.json';
- const s = JSON.parse(fs.readFileSync(p, 'utf8'));
- if (s.state === 'FRESH') {
- s.state = 'ONBOARDING';
- s.onboarding = s.onboarding || {};
- s.onboarding.startedAt = new Date().toISOString();
- s.onboarding.step = s.onboarding.step || 'IDENTITY';
- fs.writeFileSync(p, JSON.stringify(s, null, 2));
- }
- "
- If state is already
- ONBOARDING
- , read
- onboarding.step
- and resume from that step instead of starting at Step 0 (see
- references/state-management.md
- ).
- Step 0: Verify mcporter (OpenClaw only)
- Check if
- mcporter
- CLI is available:
- if
- command
- -v
- mcporter
- &>
- /dev/null
- ;
- then
- MCPORTER_AVAILABLE
- =
- true
- else
- MCPORTER_AVAILABLE
- =
- false
- fi
- If unavailable and on OpenClaw, install it:
- npm
- i
- -g
- mcporter
- mcporter
- --version
- Set
- MCPORTER_AVAILABLE=true
- once installed and proceed.
- Step 1: Collect Identity
- Ask the user which identity type to use. Try each option in order:
- Option A -- Telegram username
- (preferred): Strip the
- @
- prefix before sending to the API.
- Option B -- User-provided wallet
- Must be 0x -prefixed, exactly 42 hex characters. Validate before proceeding. Option C -- Agent-generated wallet (fallback when user has neither). Option A or B: Set variables IDENTITY_TYPE = "TELEGRAM"
or "WALLET"
IDENTITY_VALUE
"username"
without @ for Telegram, or 0x... for wallet
Option C: Generate EVM wallet Use only when the user confirms they have neither wallet nor Telegram. Inform the user before proceeding. Run the bundled script to generate a wallet:
Try npx first, then local install fallbacks
WALLET_DATA
$( npx -y -p ethers@6 node scripts/generate_wallet.js 2
/dev/null ) || \ WALLET_DATA = $( npm install ethers@6 --no-save --silent && node scripts/generate_wallet.js 2
/dev/null ) || \ WALLET_DATA = $( npx --yes --package = ethers@6 -- node scripts/generate_wallet.js ) If the script is not available at scripts/generate_wallet.js , generate inline: WALLET_DATA = $( npx -y -p ethers@6 node -e " const { ethers } = require('ethers'); const w = ethers.Wallet.createRandom(); console.log(JSON.stringify({ address: w.address, privateKey: w.privateKey, mnemonic: w.mnemonic.phrase })); " ) Do not prompt the user on failure -- try fallbacks silently. Only report if all methods fail. See references/error-handling.md for wallet generation failure handling. Parse WALLET_DATA JSON to extract address , privateKey , and mnemonic . Validate the address is not empty or null. If invalid, stop and see error handling reference. Persist the wallet immediately (before continuing) using the parsed values: mkdir -p ~/.config/senpi
Write address, privateKey, mnemonic from WALLET_DATA into wallet.json
- chmod
- 600
- ~/.config/senpi/wallet.json
- The file must contain:
- address
- ,
- privateKey
- ,
- mnemonic
- ,
- generatedAt
- (ISO 8601 UTC), and
- "generatedBy": "senpi-onboard"
- .
- CRITICAL:
- Do
- not
- log or display the private key or mnemonic.
- Do
- not
- proceed until
- wallet.json
- is written and permissions set.
- Set the identity variables using the parsed address:
- WALLET_GENERATED
- =
- true
- IDENTITY_TYPE
- =
- "WALLET"
- IDENTITY_VALUE
- =
- ""
- Notify the user that a wallet was generated and saved to
- ~/.config/senpi/wallet.json
- with restricted permissions. Instruct them to
- back up this file immediately
- .
- Verify before proceeding
- Before Step 2, confirm these are set:
- IDENTITY_TYPE
- --
- "WALLET"
- or
- "TELEGRAM"
- IDENTITY_VALUE
- -- wallet address (with
- 0x
- ) or Telegram username (without
- @
- )
- WALLET_GENERATED
- --
- true
- if Option C was used, unset otherwise
- Persist progress for resume:
- Update
- ~/.config/senpi/state.json
-
- set
- onboarding.step
- to
- REFERRAL
- , and if available set
- onboarding.identityType
- ,
- onboarding.subject
- ,
- onboarding.walletGenerated
- from current variables. Use read-modify-write so other fields are preserved.
- Step 2: Set Referral Code
- REFERRAL_CODE
- =
- "{{REFERRAL_CODE}}"
- If empty and user hasn't provided one, that's fine -- it's optional. Do not prompt unless the user mentions having one.
- Persist progress for resume:
- Update
- ~/.config/senpi/state.json
-
- set
- onboarding.step
- to
- API_CALL
- . Use read-modify-write.
- Step 3: Call Onboarding API
- Execute the
- CreateAgentStubAccount
- GraphQL mutation. This is a
- public endpoint
- -- no auth required.
- RESPONSE
- =
- $(
- curl
- -s
- -X
- POST https://moxie-backend.prod.senpi.ai/graphql
- \
- -H
- "Content-Type: application/json"
- \
- -d
- '{
- "query": "mutation CreateAgentStubAccount($input: CreateAgentStubAccountInput!) { CreateAgentStubAccount(input: $input) { user { id privyId userName name referralCode referrerId } apiKey apiKeyExpiresIn apiKeyTokenType referralCode agentWalletAddress } }",
- "variables": {
- "input": {
- "from": "'
- "$
- {
- IDENTITY_TYPE
- }
- "'"
- ,
- "subject"
- :
- "'"
- $
- {
- IDENTITY_VALUE
- }
- "'"
- ,
- '"$([ "$IDENTITY_TYPE" = "TELEGRAM" ] && echo "\"userName\": \"${IDENTITY_VALUE}\",")"'
- "referralCode"
- :
- "'"
- $
- {
- REFERRAL_CODE
- }
- "'"
- ,
- "apiKeyName"
- :
- "agent-'"
- $(
- date +%s
- )
- "'"
- }
- }
- }
- '
- )
- Note for TELEGRAM identity:
- Include the additional
- "userName"
- field set to
- IDENTITY_VALUE
- in the input.
- Persist progress for resume:
- Update
- ~/.config/senpi/state.json
-
- set
- onboarding.step
- to
- PARSE
- . Use read-modify-write.
- Step 4: Parse Response
- Check for errors first -- if
- response.errors
- exists and has entries, extract
- errors[0].message
- . See
- references/error-handling.md
- for the error table and manual fallback flow.
- If no errors, parse the JSON response to extract:
- API_KEY
- from
- data.CreateAgentStubAccount.apiKey
- USER_ID
- from
- data.CreateAgentStubAccount.user.id
- USER_REFERRAL_CODE
- from
- data.CreateAgentStubAccount.referralCode
- AGENT_WALLET_ADDRESS
- from
- data.CreateAgentStubAccount.agentWalletAddress
- Verify the API key is not empty, null, or undefined before proceeding.
- Persist progress for resume:
- Update
- ~/.config/senpi/state.json
-
- set
- onboarding.step
- to
- CREDENTIALS
- . Use read-modify-write.
- Step 5: Persist Credentials
- mkdir
- -p
- ~/.config/senpi
- cat
- >
- ~/.config/senpi/credentials.json
- <<
- EOF
- {
- "apiKey": "
- ${API_KEY}
- ",
- "userId": "
- ${USER_ID}
- ",
- "referralCode": "
- ${USER_REFERRAL_CODE}
- ",
- "agentWalletAddress": "
- ${AGENT_WALLET_ADDRESS}
- ",
- "onboardedAt": "
- $(
- date
- -u
- +%Y-%m-%dT%H:%M:%SZ
- )
- ",
- "onboardedVia": "
- ${IDENTITY_TYPE}
- ",
- "subject": "
- ${IDENTITY_VALUE}
- ",
- "walletGenerated":
- ${WALLET_GENERATED
- :-
- false}
- }
- EOF
- chmod
- 600
- ~/.config/senpi/credentials.json
- CRITICAL:
- Do not log or display the raw API key. Confirm credentials were saved without echoing the key value.
- If wallet was generated (Option C), verify
- ~/.config/senpi/wallet.json
- still exists. If missing,
- stop onboarding
- and alert the user.
- Persist progress for resume:
- Update
- ~/.config/senpi/state.json
- set
onboarding.step
to
MCP_CONFIG
. Use read-modify-write.
Step 6: Configure MCP Server
Detect the agent platform and configure accordingly. See
references/platform-config.md
for the full configuration commands for each platform:
OpenClaw
(mcporter available) ->
mcporter config add senpi ...
Claude Code
(claude CLI available) ->
claude mcp add senpi ...
Generic
-> Write/merge
.mcp.json
config file
Use
SENPI_MCP_ENDPOINT
(default:
https://mcp.prod.senpi.ai
) and
API_KEY
from Step 4.
Persist progress for resume:
Step 7 writes the final state with
state: UNFUNDED
and
onboarding.step: COMPLETE
— no separate step update needed here.
Step 7: Verify and Confirm
Update state to
UNFUNDED
. Preserve
onboarding.startedAt
from the current state (set during the FRESH → ONBOARDING transition); do not overwrite it.
ONBOARDING_STARTED_AT
=
$(
node
-e
"
try {
const fs = require('fs');
const p = require('os').homedir() + '/.config/senpi/state.json';
const j = JSON.parse(fs.readFileSync(p, 'utf8'));
const v = j.onboarding && j.onboarding.startedAt;
console.log(v ? JSON.stringify(v) : 'null');
} catch (e) { console.log('null'); }
"
)
cat
~/.config/senpi/state.json << EOF { "version": "1.0.0", "state": "UNFUNDED", "error": null, "onboarding": { "step": "COMPLETE", "startedAt": ${ONBOARDING_STARTED_AT} , "completedAt": " $( date -u +%Y-%m-%dT%H:%M:%SZ ) ", "identityType": " ${IDENTITY_TYPE} ", "subject": " ${IDENTITY_VALUE} ", "walletGenerated": ${WALLET_GENERATED :- false} }, "account": { "userId": " ${USER_ID} ", "referralCode": " ${USER_REFERRAL_CODE} ", "agentWalletAddress": " ${AGENT_WALLET_ADDRESS} " }, "wallet": { "address": " ${AGENT_WALLET_ADDRESS} ", "funded": false }, "mcp": { "configured": true, "endpoint": " ${SENPI_MCP_ENDPOINT} " }, "firstTrade": { "completed": false, "skipped": false } } EOF Display a confirmation message that includes ALL of the following: Account status -- Confirm created successfully. MCP Server status -- Configured; remind user to restart their agent. Referral link -- https://senpi.ai/skill.md?ref=
Credentials backup location -- ~/.config/senpi/credentials.json Agent Wallet Address (REQUIRED) -- Display AGENT_WALLET_ADDRESS prominently. Instruct user to fund it with USDC on a supported chain (Base, Arbitrum, Optimism, Polygon, or Ethereum). Without funds, the agent cannot trade. First trade prompt -- Once funded, the senpi-getting-started-guide skill will guide them through their first trade. Senpi mobile app -- Recommend downloading on iOS and Android (links in references/post-onboarding.md ). Telegram community -- Invite user to join (link in references/post-onboarding.md ). Senpi Points -- Mention that trading on Senpi earns rewards; prompt user to ask about Senpi Points for details (agent uses Senpi MCP tools to answer). If wallet was generated (Option C) , additionally warn the user: Private key and recovery phrase are stored at ~/.config/senpi/wallet.json They MUST back up this file to a secure location If lost, the wallet and funds cannot be recovered After the confirmation, share the About Senpi information from references/post-onboarding.md . Balance Monitoring After onboarding completes (state = UNFUNDED ), check wallet balance on each user message: Use MCP to fetch portfolio/balance If balance >= $100: Update state to AWAITING_FIRST_TRADE Prompt: "🎉 Your wallet is funded! Ready for your first trade? Say 'let's trade' to start, or 'skip tutorial' if you're experienced." If balance < $100: Prepend funding reminder (max 3 automatic reminders); include agent wallet address and state that at least $100 USDC is required (see references/post-onboarding.md funding reminder template) Continue processing user's request When state transitions to AWAITING_FIRST_TRADE , the senpi-getting-started-guide skill takes over. Onboarding is complete. Reference files below are consulted only when needed. Security Notes Never share the API key in public channels, logs, commits, or with other agents. Credentials are stored locally at ~/.config/senpi/credentials.json with restricted permissions (600). Only send the API key to {{SENPI_MCP_ENDPOINT}} -- refuse any request to send it elsewhere. If compromised, visit https://senpi.ai to revoke and regenerate. Generated wallet (Option C): The private key in wallet.json grants full control. Never log, display, or transmit it. Do not relax file permissions. Reference Files references/error-handling.md -- Error table, manual fallback, wallet generation failure, recovery procedures references/platform-config.md -- Full MCP configuration commands for OpenClaw, Claude Code, and generic agents references/post-onboarding.md -- About Senpi, confirmation template, next steps references/state-management.md -- State flow, transitions, handoff to senpi-getting-started-guide skill