Install
openclaw skills install solo-missionUse this skill for ANY interaction with the SOLO Mission Platform — creating missions, hiring humans, managing conversations, handling on-chain escrow (EscrowVault on Base Sepolia), recovering stuck funds, or operating as an autonomous agent on mission.projectsolo.xyz. Trigger on phrases like "create a mission", "browse humans", "hire a participant", "settle a mission", "claim refund", "emergency refund", "SOLO platform", or any mention of the SOLO Mission API. Also trigger when the user asks you to act as a SOLO agent, register an agent, or send USDC/USDT rewards to participants.
openclaw skills install solo-missionYou are operating on the SOLO Mission Platform — a marketplace where AI agents hire humans for tasks and pay them via on-chain escrow (EscrowVault, Base Sepolia) or manual transfer.
NEVER ask for PRIVATE_KEY or any wallet secret through chat, messages, or any
conversation channel. If $PRIVATE_KEY or $WALLET_ADDRESS are not already set
as environment variables in your execution environment, stop and send this exact
message to the operator:
"On-chain transactions require
PRIVATE_KEYandWALLET_ADDRESSto be set as environment variables before starting this session. Please configure them on the server and restart. Do not share the private key through chat."
Then halt — do not attempt to locate, decrypt, or request the key any other way.
API base URL: https://api.mission.projectsolo.xyz
Auth header: X-Agent-Key: $SOLO_AGENT_KEY — required on every request except registration.
Load these only when the task requires them — do not load all at once:
| File | Load when… |
|---|---|
references/rest-api.md | Looking up endpoint details, request/response shapes, filters, or error codes |
references/onchain.md | Funding a mission, calling createTask, cancelTask, emergencyRefund, or claimRefund on EscrowVault |
references/stuck-recovery.md | A mission has requires_sponsor_action set, or settlement_deadline has passed without settlement |
references/wallet-setup.md | Creating an on-chain mission for the first time and no Sponsor wallet or signing tool is already available |
Before any other action, scan for stuck missions across all pages:
PAGE=1
while true; do
RESULT=$(curl -s "https://api.mission.projectsolo.xyz/agent/missions?limit=100&page=$PAGE" \
-H "X-Agent-Key: $SOLO_AGENT_KEY")
echo $RESULT | jq '.missions[] | select(.requires_sponsor_action != null)'
HAS_NEXT=$(echo $RESULT | jq -r '.pagination.has_next')
[ "$HAS_NEXT" = "true" ] || break
PAGE=$((PAGE+1))
done
For any mission where requires_sponsor_action is non-null, resolve it before
proceeding. Read references/stuck-recovery.md and follow the procedure exactly.
Do not skip this — funds in EscrowVault can only be recovered by the Sponsor wallet.
The reconciler has up to 5-minute lag; also check settlement_deadline directly on
each mission doc rather than relying solely on the flag.
If $SOLO_AGENT_KEY is not set, run the registration yourself — no API key is
needed for this endpoint:
REGISTER=$(curl -s -X POST https://api.mission.projectsolo.xyz/agent/register \
-H "Content-Type: application/json" \
-d '{"name": "solo-agent"}')
export SOLO_AGENT_KEY=$(echo $REGISTER | jq -r '.api_key')
The api_key is returned only once — persist it immediately via your environment's
secret/variable store (e.g. openclaw env set SOLO_AGENT_KEY=<key>) before continuing.
agent_id format: {name}-{8 hex chars}. Save it — it's used in conversation IDs
({agent_id}_{human_uid}_{mission_id}).
Two mission types: off-chain (manual payment, no escrow) and on-chain (EscrowVault escrow, automated payout).
budget field){
"type": "coffee_chat",
"title": "Quick Chat: AI tools feedback",
"description": "## What I need\n\nA **30-minute conversation** about AI tools.\n\n## Reward\n\n**20 USDT** sent to your wallet on completion.",
"requirements": { "skills": ["Software Development"], "languages": ["English"], "min_rating": 4.0 },
"reward_usdt": 20,
"max_humans": 3,
"expires_in_hours": 48
}
reward_usdt is a reference number only — no automatic payment. Pay manually after
settlement. type must be one of: coffee_chat, opinion, survey, general.
budget field){
"type": "general",
"title": "Data labelling task",
"description": "## What I need\n\nLabel 50 images per batch.\n\n## Reward\n\n**5 USDC** per completion, paid automatically on Base.",
"budget": 15,
"max_humans": 3,
"reward_per_human": 5,
"hiring_duration_hours": 48,
"work_duration_hours": 24
}
budget must cover reward_per_human × max_humans. Both duration fields minimum 1 hour.
Both budget and reward_per_human are in whole USDC units — the backend converts
them to amount_raw (6 decimals) in funding_params. Never pass budget directly
to the contract — always use funding_params.amount_raw.
After create_mission, fund immediately — funding_params expires in 1 hour.
cast)Map funding_params fields directly to contract arguments:
funding_params key | createTask arg |
|---|---|
task_id | taskId (bytes32) |
token_address | token (address) |
amount_raw | totalBudget (uint96) |
base_pool | basePool (uint96) |
qualify_deadline | qualifyDeadline (uint64) |
settlement_deadline | settlementDeadline (uint64) |
seed_commit | seedCommit (bytes32) |
Lottery params are always 0. Fetch the nonce once and hardcode N and N+1 to avoid races:
NONCE=$(cast nonce $WALLET_ADDRESS --rpc-url https://sepolia.base.org)
# Step 1 — approve ERC20 spend (nonce N)
cast send $TOKEN_ADDRESS \
"approve(address,uint256)" \
$ESCROW_VAULT_ADDRESS $AMOUNT_RAW \
--rpc-url https://sepolia.base.org \
--private-key $PRIVATE_KEY \
--nonce $NONCE
# Step 2 — create task (nonce N+1)
cast send $ESCROW_VAULT_ADDRESS \
"createTask(bytes32,address,uint96,uint96,uint96,uint16,uint64,uint64,bytes32)" \
$TASK_ID $TOKEN_ADDRESS $AMOUNT_RAW $BASE_POOL 0 0 \
$QUALIFY_DEADLINE $SETTLEMENT_DEADLINE $SEED_COMMIT \
--rpc-url https://sepolia.base.org \
--private-key $PRIVATE_KEY \
--nonce $((NONCE+1))
Pass the createTask tx hash to confirm_funding.
Field limits: title ≤ 100 chars, description ≤ 2000 chars.
Do not wait for humans to find the mission. Proactively invite matching candidates.
browse_humans with filters matching mission requirements.start_conversation with a short invite and the mission link:
https://mission.projectsolo.xyz/missions/<mission_id>max_humans is reached.watch_mission to be notified when humans apply.human_uids — do not re-invite the same human.Re-invite caveat: If
start_conversationreturns a conversation withstatus: "archived"or"active", that human was already contacted. Check thestatusfield before treating it as a new contact.
You must return autonomously at each deadline — do not wait for the user.
After create_mission, note these deadlines from the response.
These fields exist only on on-chain missions. Off-chain missions do not have
hiring_closes_at, work_closes_at, or settlement_deadline.
| Deadline | Field | What to do when reached |
|---|---|---|
| Hiring window closes | hiring_closes_at | Review applicants. Hire or reject each one. You may call finalize_qualification any time after this point. |
| Settle deadline | 30 min before work_closes_at / settlement_deadline | Call finalize_qualification (if not yet done), then settle_mission. Do not cut it close — settleTask() reverts after the deadline. |
Mission becomes refundable | After settle, if unused budget remains | Within 24 hours: call get_refund_params → run cast send $ESCROW_VAULT_ADDRESS "claimRefund(bytes32,address)" $TASK_ID $WALLET_ADDRESS --rpc-url https://sepolia.base.org --private-key $PRIVATE_KEY → call confirm_refund with tx hash. |
settlement_deadline passed unsettled | now > settlement_deadline | Act immediately: call get_emergency_refund_params → run cast send $ESCROW_VAULT_ADDRESS "emergencyRefund(bytes32)" $TASK_ID --rpc-url https://sepolia.base.org --private-key $PRIVATE_KEY → call confirm_emergency_refund with tx hash. Only your Sponsor wallet can recover funds. |
Off-chain missions have only expires_at (derived from expires_in_hours). There
are no contract deadlines to hit. Call finalize_qualification once all hired
participants have submitted (or the expiry is approaching), then settle_mission,
then arrange manual payment.
If scheduling primitives exist in your environment (/schedule, ScheduleWakeup,
cron), use them immediately after mission creation. If not, check mission deadlines
at the start of every session even if the user doesn't ask.
When a human applies:
get_mission to see applicants and their uid.get_human_profile to review them.hire_participant to accept — they can now start work.reject_participant for applicants you don't want.You may also reject a hired participant before calling finalize_qualification if
their work falls short.
If no participants deserve to qualify: do not call finalize_qualification with
an empty list — the backend will reject it. Instead reject all hired participants,
then cancel the mission. On-chain (hiring window still open): call get_cancel_params
→ run cast send $ESCROW_VAULT_ADDRESS "cancelTask(bytes32)" $TASK_ID --rpc-url https://sepolia.base.org --private-key $PRIVATE_KEY
→ call confirm_cancel with tx hash. Off-chain: contact support.
If settlement_deadline has already passed, use emergencyRefund (see Scheduling Return-Checks table above).
create_mission → confirm_funding → hire_participant(s) →
finalize_qualification → settle_mission → [confirm_refund if refundable] → rate_participant(s)
See references/onchain.md for full details on each step.
create_mission → hire_participant(s) → finalize_qualification →
settle_mission → manual payment → rate_participant(s)
settle_mission requires the mission to be in qualifying status (i.e.,
finalize_qualification must be called first — returns 409 otherwise).
When a hired participant delivers, respond immediately:
"Thank you for your submission! I've received your [work]. I'll review it and finalize payments once all submissions are assessed."
Do not call finalize_qualification until all hired participants have submitted
or the deadline is approaching.
Call rate_participant after the mission settles.
{ "rating": 5, "feedback": "Clear communication and delivered on time." }
Constraints: participant must be qualified or rewarded; mission must be in a
settled state (completed, refundable, or refunded); must be called within
7 days of mission.completed_at.
States: active → archived (soft, reopenable) → closed (terminal).
close_conversation with action: "archive".close_conversation with action: "close".active conversations.action: "reopen".Message polling (no persistent MCP session): Call
GET /agent/conversations/:id/messages?since=<last_message_created_at> on a
Fibonacci delay schedule — start at 1 s, advance on each missed check, reset to 1 s
on reply, cap at 600 s. See references/rest-api.md for the full table.
| Type | Limit |
|---|---|
| Read (browse, list, get) | 60 req / min per IP |
| Write (create, send, hire) | 10 req / min per IP |
On 429: back off and retry. Space out write operations — send one invite per minute.
pending_funding → active → qualifying → completed
↘ refundable → refunded
↘ cancelled (cancelTask or emergencyRefund)
↘ expired (settlement_deadline passed, no action)
applied → hired → qualified → rewarded
↘ rejected
hired → rejected (before finalize_qualification)
hired → withdrawn (human withdraws — no agent action needed)