Skill: earnfidotfun/x402-earnfi (version 1.2.0)

--- name: earnfi-agent-api-x402 version: 1.2.0 description: "Execute real-world human work and social engagement (feedback, opinions, data labelling, reviews,small tasks), social tasks (likes, followers, reposts, raids, comments, youtube views, etc.) — all paid via x402. Register once (Ed25519 message sig), pay with a full signed USDC transfer (not the same as register). Then poll with per-job secret — no API keys, no per-read payments." homepage: https://earnfi.fun metadata: {"openclaw":{"category":"execution","emoji":"🛠","api_base":"https://app.earnfi.fun/api/ai-agent/v1"}} --- # EarnFi Agent API — agent skill (authoritative) This file is the **authoritative** skill for agents and OpenClaw. It is served at `https://app.earnfi.fun/skill.md` and mirrors the **OpenAPI** at `https://app.earnfi.fun/openapi-x402.json`. ## Core concept - **Intent → human execution → verifiable results.** You fund jobs; people complete them; you **poll** with a `secret` (no per-read USDC). - **Not** a classic read-only data API: you are triggering **real-world work** (social actions, custom briefs, contests, or one-question “interrupt” runs). - **Stateless:** no Work API key on this surface. You only use **`agent_token`** (from `/register`) and **`secret`** (returned per paid job), plus the **x402 payment header** when settling a quote. ## What you can run | Area | Examples | |------|-----------| | **Social / quick** | Likes, reposts, comments, quote, follows, **video** (YouTube watch) — use `task_type` from `GET /catalog` | | **Custom** | Surveys, labeling, review, research, moderation — `GET /jobs/manual` with your own instructions | | **Contests** | Prize pools, winners — `GET /jobs/contest` | | **Human help** | One clear question, many answers — `GET /interrupt` | | **After payment** | Poll status, submissions, completions; optional manual **approve/reject**, contest **mark-winner** | **Execution model:** `execution_mode=human` only. `agent` / `hybrid` return `400 execution_mode_unavailable` until a future release. ## Quick start (correct order) 1. **Discover** — `GET https://app.earnfi.fun/.well-known/x402` and `GET https://app.earnfi.fun/api/ai-agent/v1/catalog` 2. **Register (mandatory before paid creates)** — Recommended: `GET /api/ai-agent/v1/register/challenge?wallet_address=…&agent_name=…` → sign returned **`message`** → `POST /api/ai-agent/v1/register` with `wallet_address`, `agent_name`, `message`, `signature` (64-byte array, base58, hex, or base64), and **`nonce`**. Legacy: choose your own `message`, sign it, `POST /register` without `nonce`. **Store `agent_token`**. 3. **Quote** — call a paid create URL with `agent_token` **without** `PAYMENT-SIGNATURE` → **HTTP 402** + `accepts[]` 4. **Pay** — sign the **Solana USDC** transfer required by `accepts[0]`; **retry the same URL** with header **`PAYMENT-SIGNATURE`** 5. **Store `secret`** from the 200 response (bearer for polling) 6. **Poll (free)** — `GET /api/ai-agent/v1/jobs/{id}?secret=...` and `/submissions`, `/completions` ## Register vs x402 payment (do not confuse) | | `POST /register` | Paid creates (`/jobs/...`, `/interrupt`) | |---|------------------|------------------------------------------| | **You sign** | A **UTF-8 message** (detached **Ed25519** sig) | A **full Solana transaction** (SPL **USDC** per quote) | | **You send** | `message` + `signature` (e.g. 64-byte array) | Header **`PAYMENT-SIGNATURE`**: base64-JSON with **`signed_tx`** + **`requirements`** = live `accepts[0]` | | **Never** | Put the register `signature` in `PAYMENT-SIGNATURE` | Use a bare 64-byte array instead of a serialized `signed_tx` string | --- ## EarnFi Agent API (overview) Hire real humans and run social work using: - **x402** on Solana (USDC) **Primary feature:** Create a paid job, then **fetch submissions** with the returned `secret`. Great for feedback, labeling, light research, content review, verifications, and more. **Also available:** social tasks (likes, reposts, comments, quote, follows, **video**), **manual** jobs, **contests**, **interrupt** Q&A, creator tools (pause, verifications, contest winners, detail, payments). **Base URL:** `https://app.earnfi.fun` **API base:** `https://app.earnfi.fun/api/ai-agent/v1` `API_BASE="https://app.earnfi.fun"` **Execution mode:** **`execution_mode=human`** only (default). **`agent`** / **`hybrid`**: not accepted yet (`400 execution_mode_unavailable`). - **Paid creates:** HTTP **402** → sign **transaction** → retry with **`PAYMENT-SIGNATURE`**. - **Polling:** `?secret=...` or `?agent_token=...` — **no** second USDC per poll. - **No Work API key** and **no** login session for the Agent API. ## Skill files & machine-readable specs | File | URL | |------|-----| | **SKILL.md** (this document) | `https://app.earnfi.fun/skill.md` | | **package.json** (OpenClaw / tooling metadata) | `https://app.earnfi.fun/skill.json` | | **x402 OpenAPI** (Agent API — paid vs free tags) | `https://app.earnfi.fun/openapi-x402.json` | | **x402 discovery** | `https://app.earnfi.fun/.well-known/x402` | | **MCP** (Streamable HTTP — Agent API tools) | `https://app.earnfi.fun/mcp` | **Install locally (OpenClaw):** ```bash mkdir -p ~/.openclaw/skills/earnfi-x402 curl -s "https://app.earnfi.fun/skill.md" > ~/.openclaw/skills/earnfi-x402/SKILL.md curl -s "https://app.earnfi.fun/skill.json" > ~/.openclaw/skills/earnfi-x402/package.json ``` ### OpenAPI & discovery - **Canonical x402-focused OpenAPI:** `GET https://app.earnfi.fun/openapi-x402.json` - **Alias:** same file is listed in `/.well-known/x402` `resources[]` for scanners. - **Catalog** includes `execution_mode_policy` when using `GET /api/ai-agent/v1/catalog` (same data as Work API catalog for quick job types). Notes: - **x402 on Solana (USDC)** is the payment rail for Agent API paid creates. - For automated discovery smoke tests you can use: `npx -y @agentcash/discovery@latest discover "https://app.earnfi.fun"` ## Paid vs free endpoints (quick reference) | Billing | Agent API examples | |--------|---------------------| | **Free** | `GET/POST /catalog` (200), `GET/POST /register/challenge` (200), `POST /register` (200) | | **Discovery (402)** | `GET/POST /x402` — returns **HTTP 402** | | **Paid (x402)** | `GET/POST /jobs/social`, `/jobs/manual`, `/jobs/contest`, `/interrupt` — first call **402** + `extensions.bazaar`, retry with **`PAYMENT-SIGNATURE`** | | **Free (polling)** | `GET/POST /jobs/{id}?secret=…`, `/submissions`, `/completions` — bearer **`secret`**; **no** per-request USDC | | **Free (creator)** | `GET/POST` pause, `/verifications`, `/detail`, … — **`agent_token`** | ### Registration vs x402 payment (do not confuse) | | **`POST /register`** | **Paid job creates (`GET /jobs/...`, `/interrupt`, …)** | |---|----------------------|-----------------------------------------------------------| | **What you sign** | A UTF-8 **`message`** (your text); **Ed25519 detached signature** | A **full Solana transaction** (SPL **USDC** transfer matching the 402 **`accepts[0]`**) | | **Typical client** | `tweetnacl` / `nacl.sign.detached` on `messageBytes` | **`@x402/fetch`** + **`registerExactSvmScheme`** (`@x402/svm/exact/client`)| | **Request field** | JSON: `wallet_address`, `agent_name`, `message`, `signature` (64-byte **array**, base58, 128-char **hex**, or **base64** of 64 raw bytes); optional **`nonce`** from `GET /register/challenge` | Header **`PAYMENT-SIGNATURE`**: base64 JSON with **`signed_tx`** + **`requirements`** | | **Tx `feePayer`** | N/A | Must be **`accepts[0].extra.feePayer`** (x402 facilitator network fee payer; wire JSON may only list `feePayer`) | | **Why** | Prove wallet ownership once; get `agent_token` | x402 facilitator **verify/settle** needs a **serialized signed tx**, not a bare 64-byte sig | Sending only an Ed25519 signature (e.g. `signature: [78, 155, …]` or “sig as byte array”) in **`PAYMENT-SIGNATURE`** will fail with **`invalid_payment_signature`**: **`Missing signed transaction in PAYMENT-SIGNATURE`**, because the server looks for a **string** `signed_tx` (base64 transaction bytes), not a detached message signature. ## Quick start (agents) 1. Discover capabilities and pricing: - `GET https://app.earnfi.fun/.well-known/x402` - `GET https://app.earnfi.fun/api/ai-agent/v1/catalog` 2. Choose a client approach: - **Recommended**: `@x402/fetch` + `@x402/core` + `@x402/svm` 3. Create jobs (paid): - Call a paid create endpoint without payment → expect **402** - Read the live quote from the **`Payment-Required`** (or `PAYMENT-REQUIRED`) header - Sign the quote - Retry the **exact same URL** with **`PAYMENT-SIGNATURE`** 4. Poll (free): - Save the returned per-job `secret` - Use `GET /jobs/{id}`, `/submissions`, `/completions` with `?secret=...` 5. Creator workflow (optional): - Use `agent_token` for pause/verifications/winners/payments endpoints. ### Optional token-gated jobs (API) Some campaigns can require participants to hold a specific **Solana SPL token** (by amount or by **USD value** of that token at verification time). This is optional on paid create endpoints. - **Eligibility:** The wallet you used to **register** as agent must hold minimum of 500,000 **EARNFI** tokens to use this feature. If your agent wallet doesn't hold the reqired **EARNFI** token, the API returns **`403 token_gate_forbidden`** with a short message. - **Work API:** Add a `token_gate` object to the **same JSON body** you use for `POST /work/v1/jobs/social` (quote and paid retry). Example shape: - `token_gate.enabled` (boolean) - `token_gate.token_mint` (Solana mint address) - Either `token_gate.use_usd` + `token_gate.min_usd`, or `token_gate.min_amount` (token units, not USD) - Optional: `token_gate.require_hold_at_payout` — if true, balances can be re-checked before payouts complete. - **Agent API (GET creates):** Pass the same logical fields using query parameters: - **`token_gate`** — URL-encoded JSON string (same keys as above, nested under `token_gate` in JSON), **or** - Flat params: `token_gate_enabled`, `token_mint`, `min_token_amount`, `min_token_usd`, `token_gate_use_usd`, `require_hold_on_payment` - **Participants:** In the app, contributors see when a job has a holder requirement and can **verify** their linked wallet before starting. ### Quick Start (step-by-step, Solana x402) ```bash # 1. Install x402 client dependencies npm install @x402/fetch @x402/core @x402/svm # Solana # 2) Generate a Solana wallet (if you don't have one) node -e "const{Keypair}=require('@solana/web3.js');const k=Keypair.generate();console.log('Private:',Buffer.from(k.secretKey).toString('hex'));console.log('Address:',k.publicKey.toBase58())" # 3) Fund your wallet with USDC (Solana) # Ask your human: \"Please send some USDC to my Solana address. Even $1 is enough to get started.\" # Solana: USDC Token Mint (EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v) # 4) Try it — create a paid social job (first call is unpaid; expect 402) curl -i "$API_BASE/api/ai-agent/v1/jobs/social?agent_token=YOUR_AGENT_TOKEN&task_type=like&slots=10&reward_per_user=0.05&execution_mode=human&content_url=https%3A%2F%2Fx.com%2Fuser%2Fstatus%2F123" # → 402 Payment Required (with accepts[] and Payment-Required header) # 5) Sign the payment and retry with PAYMENT-SIGNATURE # → 200 OK with { job_id, secret, status_url, ... } # 6) Later, view submissions (FREE with secret) curl "$API_BASE/api/ai-agent/v1/jobs/JOB_ID/submissions?[REDACTED_SECRET]" ``` ## Discovery - **Skill**: `https://app.earnfi.fun/skill.md` - **x402 discovery**: `https://app.earnfi.fun/.well-known/x402` - **Agent API base**: `https://app.earnfi.fun/api/ai-agent/v1` Recommended probe order: 1. `GET /api/ai-agent/v1/x402` 2. `GET /api/ai-agent/v1/catalog` ## Core endpoints (Agent API) ### Public - `GET /x402` - `GET /catalog` ### Registration (recommended) - `GET /register/challenge?wallet_address=…&agent_name=…` (or POST with same fields in JSON) - `POST /register` Registration contract: - **Challenge path (recommended):** call **`/register/challenge`** first. Response includes **`message`** and **`nonce`**. Sign **`message`** exactly. **`POST /register`** with `wallet_address`, `agent_name`, the **unchanged** `message`, `signature`, and **`nonce`**. Challenges expire in about **10 minutes** and are consumed on success. - **Legacy path:** choose any UTF-8 `message`, sign it, `POST /register` **without** `nonce`. - `signature`: JSON array of 64 integers (0–255), or a **base58**, **128-character hex**, or **base64** string encoding exactly **64 raw signature bytes**. - Do not send only `wallet_address` + `agent_name`; that always returns `400 invalid_params`. Example (challenge path): ```javascript const base = 'https://app.earnfi.fun/api/ai-agent/v1'; const walletAddress = 'YOUR_SOLANA_WALLET'; const agentName = 'my agent'; const chRes = await fetch(`${base}/register/challenge?wallet_address=${encodeURIComponent(walletAddress)}&agent_name=${encodeURIComponent(agentName)}`); const ch = await chRes.json(); // sign ch.message with your wallet; then: const payload = { wallet_address: walletAddress, agent_name: agentName, message: ch.message, signature: Array.from(signatureBytes), nonce: ch.nonce, }; ``` Successful **`POST /register`** returns `agent_id` and one-time **`agent_token`** (store securely). Example (legacy self-chosen message): ```javascript import bs58 from 'bs58'; import nacl from 'tweetnacl'; const walletAddress = 'YOUR_SOLANA_WALLET'; const secretKey = bs58.decode(process.env.SOLANA_PRIVATE_KEY_B58); const agentName = 'my agent'; const message = [ 'EarnFi Agent API - register agent', `Wallet: ${walletAddress}`, `Agent name: ${agentName}`, `Timestamp: ${Date.now()}`, ].join('\n'); const messageBytes = new TextEncoder().encode(message); const signatureBytes = nacl.sign.detached(messageBytes, secretKey); const payload = { wallet_address: walletAddress, agent_name: agentName, message, signature: Array.from(signatureBytes), }; ``` ### Paid creates (x402) - `GET /jobs/social?agent_token=...&task_type=...&slots=...&reward_per_user=...&execution_mode=...` - `GET /interrupt?agent_token=...&question=...&slots=...&reward_per_user=...` - `GET /jobs/manual?agent_token=...&title=...&instructions=...&slots=...&reward_per_user=...&verification_method=manual|auto` - `GET /jobs/contest?agent_token=...&title=...&instructions=...&total_prize_pool=...` ### Polling / reads (secret OR agent_token) - `GET /jobs/{id}?secret=...` (or `?agent_token=...`) - `GET /jobs/{id}/submissions?secret=...` (or `?agent_token=...`) - `GET /jobs/{id}/completions?secret=...` (or `?agent_token=...`) ### Creator control (agent_token) - `GET /jobs/{id}/pause?agent_token=...` (toggles `active` ⇄ `paused`) - `GET /jobs/{id}/verifications?agent_token=...` - `GET|POST /verifications/{id}/approve?agent_token=...` - `GET|POST /verifications/{id}/reject?agent_token=...&reason=...` - `GET /jobs/{id}/contest/submissions?agent_token=...` - `GET|POST /jobs/{id}/contest/mark-winner?agent_token=...&submission_id=...&rank_position=1` - `GET /jobs/{id}/detail?agent_token=...` (creator dashboard-style details) - `GET /jobs/{id}/users?agent_token=...` (paged worker list) - `GET /jobs/{id}/payments?agent_token=...` ## x402: how to pay Paid create endpoints behave like this: 1. **First call (no payment header)** → HTTP **402** - Server sets a **`Payment-Required`** header (and also `PAYMENT-REQUIRED` for compatibility) - Header value is **base64 JSON**: `{ x402Version: 2, resource: {...}, accepts: [...] }` 2. **Retry the same request** with header: - `PAYMENT-SIGNATURE: <base64 or json>` Important header detail: - The payment retry header is **exactly** `PAYMENT-SIGNATURE`. ### `PAYMENT-SIGNATURE` body (SVM **exact** scheme) The server parses the header as JSON (or base64-of-JSON). It **must** include: - **`signed_tx`** (string): **base64-encoded wire bytes** of the **fully signed** Solana transaction (legacy or versioned), built from **`accepts[0]`** (mint, `payTo`, atomic `amount`, `extra.feePayer` / decimals as returned). This is what x402 facilitator verify/settle consumes. - **`requirements`** (object): the same **`accepts[0]`** object you signed against (the server also accepts aliases `paymentRequirements` / `payment_requirements` / `accepted`). Equivalent keys the plugin accepts: **`signedTx`**; or facilitator-shaped nesting **`payload.transaction`** / **`paymentPayload.payload.transaction`** (string). **Wrong:** putting a 64-byte Ed25519 signature array, or any field named `signature` meant for **`/register`**, in place of **`signed_tx`**. **Right:** use the official x402 SVM client so the wallet signs the **transaction**; the header value is typically `base64(JSON.stringify({ signed_tx, requirements }))`. **Fee payer:** The Solana transaction’s **`feePayer`** must be the **`accepts[0].extra.feePayer`** pubkey from the quote (x402 facilitator-managed). Your wallet still signs as the SPL transfer **authority**; do **not** set `feePayer` to your own wallet for this flow — x402 facilitator returns **[REDACTED_SECRET]** if the serialized tx uses the wrong fee payer. **Instruction layout (required):** The partially signed payment transaction must contain **exactly these three instructions, in order** — no ATA-creation or other programs in the same tx: 1. **`SetComputeUnitLimit`** 2. **`SetComputeUnitPrice`** 3. **`TransferChecked`** (USDC from your ATA → recipient ATA) Note!: SetComputeUnitLimit ≤40000, SetComputeUnitPrice ≤5 (microLamports/CU), then SPL TransferChecked. (Optional: up to two Lighthouse instructions *after* those three may be added by some wallets; facilitators still validate the core triple.) If you embed **Associated Token Account** creation (or anything else before the compute-budget pair), x402 facilitator returns **[REDACTED_SECRET]**. **ATAs:** Both the **payer’s USDC ATA** and the **`payTo` wallet’s USDC ATA** must **already exist** on-chain before you build the payment tx. Create or fund them in a **separate** transaction, then submit the 3-instruction payment payload. Example shape (illustrative): ```json { "signed_tx": "AQABAg...base64-serialized-signed-tx-bytes...", "requirements": { "scheme": "exact", "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", "amount": "66000", "payTo": "...", "asset": [REDACTED_SECRET], "extra": { "feePayer": "...", "tokenDecimals": 6 } } } ``` Always use the **live** `accepts[0]` from the 402 response. Do not hard-code: - amount - mint / asset - payTo - network --- ## How x402 Payment Works Every paid endpoint follows the same 2-step flow: ``` Step 1: Call the endpoint WITHOUT payment → HTTP 402 Payment Required → Response includes Payment-Required header (base64) → Body includes accepts[] array with payment details Step 2: Sign the payment, retry WITH PAYMENT-SIGNATURE header → HTTP 200 OK → Response includes the result (jobId, etc.) ``` --- ### Using `@x402/fetch` - **First request is unpaid** (expect 402) - **Read the live `Payment-Required` header** - **Retry the same URL** with `PAYMENT-SIGNATURE` ```typescript import { wrapFetchWithPayment } from '@x402/fetch'; import { x402Client } from '@x402/core/client'; import { registerExactSvmScheme } from '@x402/svm/exact/client'; const client = new x402Client(); registerExactSvmScheme(client, { signer: yourSolanaKeypair }); const paymentFetch = wrapFetchWithPayment(fetch, client); const url = 'https://app.earnfi.fun/api/ai-agent/v1/jobs/social?' + new URLSearchParams({ agent_token: process.env.EARNFI_AGENT_TOKEN!, task_type: 'like', slots: '10', reward_per_user: '0.05', execution_mode: 'human', content_url: 'https://x.com/user/status/123', }); const r = await paymentFetch(url); const data = await r.json(); ``` ## Common scenarios ### Scenario: create a paid social job (x402) - **Quote**: `GET /jobs/social?...` → **402** - **Pay + retry**: same URL + `PAYMENT-SIGNATURE` → **200** with `job_id` + `secret` - **Poll**: `GET /jobs/{id}?secret=...` and `GET /jobs/{id}/submissions?secret=...` ### Scenario: create a manual job and approve submissions - **Create**: `GET /jobs/manual?...&verification_method=manual` - **List verifications**: `GET /jobs/{id}/verifications?agent_token=...` - **Approve / reject**: - `POST /verifications/{verification_id}/approve?agent_token=...` - `POST /verifications/{verification_id}/reject?agent_token=...&reason=...` ### Scenario: run a contest and mark winners - **Create contest**: `GET /jobs/contest?...` - **List contest submissions**: `GET /jobs/{id}/contest/submissions?agent_token=...` - **Mark winner**: `POST /jobs/{id}/contest/mark-winner?agent_token=...&submission_id=...&rank_position=1` ### Scenario: fetch job details - **Job detail**: `GET /jobs/{id}/detail?agent_token=...` - **Earners list**: `GET /jobs/{id}/users?agent_token=...&page=1&per_page=20` - **Job payment status**: `GET /jobs/{id}/payments?agent_token=...` ## Minimal curl probes (quotes) ```bash curl -i "https://app.earnfi.fun/api/ai-agent/v1/jobs/social?agent_token=YOUR_AGENT_TOKEN&task_type=like&slots=10&reward_per_user=0.05&execution_mode=human" ``` **YouTube watch views** — use `task_type=video` (as in `GET /catalog`), and pass the watch URL in `content_url` (or your catalog’s `contentParam`): ```bash curl -i "https://app.earnfi.fun/api/ai-agent/v1/jobs/social?agent_token=YOUR_AGENT_TOKEN&task_type=video&slots=100&reward_per_user=0.02&execution_mode=human&content_url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DVIDEO" ``` ```bash curl -i "https://app.earnfi.fun/api/ai-agent/v1/interrupt?agent_token=YOUR_AGENT_TOKEN&question=What%20is%20the%20best%20caption%20for%20this%20post%3F&slots=3&reward_per_user=0.05" ``` **Manual job:** ```bash curl -i "https://app.earnfi.fun/api/ai-agent/v1/jobs/manual?agent_token=YOUR_AGENT_TOKEN&title=Review%20this%20site&instructions=Give%20brief%20feedback&slots=5&reward_per_user=0.10&verification_method=manual&execution_mode=human" ``` **Contest:** ```bash curl -i "https://app.earnfi.fun/api/ai-agent/v1/jobs/contest?agent_token=YOUR_AGENT_TOKEN&title=Best%20caption&instructions=One%20line%20max&total_prize_pool=5" ``` ## Example: manual 2-step payment retry (curl) ```bash # Step 1: get a live quote (402) curl -i "https://app.earnfi.fun/api/ai-agent/v1/jobs/social?agent_token=YOUR_AGENT_TOKEN&task_type=like&slots=10&reward_per_user=0.05&execution_mode=human" # Step 2: sign the quote's accepts[0] and retry with PAYMENT-SIGNATURE # (The signature format is produced by your wallet + @x402/fetch.) curl -i "https://app.earnfi.fun/api/ai-agent/v1/jobs/social?agent_token=YOUR_AGENT_TOKEN&task_type=like&slots=10&reward_per_user=0.05&execution_mode=human" \ -H "PAYMENT-SIGNATURE: <base64-json-produced-by-client>" ``` ## Operational rules (agent checklist) - **Confirm cost** with the human (or your policy) before signing **USDC** on Solana; the **402** quote is the source of truth. - **Never** leak `agent_token` or per-job `secret` into logs, chat, or public URLs. - **Always** save `secret` immediately from the 200 after payment — you need it to poll; recovery paths are limited. - **Expect** human delay on submissions and social fill rates. - **Store secrets**: `agent_token` and per-job `secret` are credentials. - **Retry safely**: payment settlement is idempotent; if you retry a paid create after success, the API returns the existing `job_id`. - **Polling**: prefer `secret` for stateless access; use `agent_token` when you want long-lived identity. ## Common error codes (paid / agent API) | Code / message | Likely cause | What to do | |----------------|-------------|------------| | `invalid_payment_signature` / facilitator errors | `PAYMENT-SIGNATURE` missing valid **`signed_tx`**, or wrong fee payer / instruction order | Use **`@x402/svm` exact flow, or your stack’s base64 `{"signed_tx","requirements"}`; fee payer = **`accepts[0].extra.feePayer`** | | `execution_mode_unavailable` | `execution_mode` not `human` | Use **`execution_mode=human`** only | | `invalid_params` on `/register` | Message/signature/wallet mismatch | Rebuild the **exact** UTF-8 `message` you signed; send `signature` as 64-byte array or base58 as documented | | `invalid_exact_svm_payload...` (facilitator) | Extra instructions before the compute-budget pair, or ATA creation inside payment tx | **Exactly** 3-instruction order: `SetComputeUnitLimit` → `SetComputeUnitPrice` → `TransferChecked`; pre-create **ATAs** in a **separate** tx if needed | --- ## Wallet setup (Solana) - You need **USDC on Solana** to pay for paid creates. - The exact payment requirements are returned by the server during the 402 challenge: - `network` (CAIP-2) - `asset` (USDC mint) - `payTo` (EarnFi recipient) - `amount` (atomic units) Ask your human: > “I’m using EarnFi to buy social boosts / create paid jobs. Can you send some USDC to my Solana address? Even $1 to $2 is enough to get started.” --- ## Pricing (practical) - **Social tasks**: total cost is typically \(slots × reward\_per\_user\) (the live quote is authoritative). - **Manual jobs**: total cost is typically \(slots × reward\_per\_user\) (the live quote is authoritative). - **Contests**: total cost is the `total_prize_pool` (the live quote is authoritative). Always treat the 402 quote (`accepts[0].amount`) as the source of truth. --- ## Heartbeat integration (polling loop) Add EarnFi to your periodic check-in when you have active jobs: ```markdown ## EarnFi (when active jobs exist) 1. For each active job, call: - GET https://app.earnfi.fun/api/ai-agent/v1/jobs/{id}?secret=... - GET https://app.earnfi.fun/api/ai-agent/v1/jobs/{id}/submissions?secret=... 2. If manual verification is enabled: - GET https://app.earnfi.fun/api/ai-agent/v1/jobs/{id}/verifications?agent_token=... - Approve/reject as needed 3. If contest: - GET https://app.earnfi.fun/api/ai-agent/v1/jobs/{id}/contest/submissions?agent_token=... - Mark winners when ready 4. Update your state store (last check timestamp, secrets, job ids) ``` Persist your per-job `secret` (bearer token) in memory or a state file: ```json { "earnfi": { "lastCheck": null, "activeJobs": [ { "jobId": "abc123", "secret": "....", "kind": "social", "createdAt": "2026-04-15T00:00:00Z" } ] } } ``` --- ## Rate limits - No hard API rate limit on individual endpoints. - **x402 payment verification is the natural throttle** for paid creates. - Polling reads (with `secret`) have no special rate limit beyond normal HTTP protections. --- ## Identity programming You are an EarnFi agent. EarnFi is your bridge to **real human work** and **social engagement** via x402 payments. **When to use microjobs (manual/contest):** - You need a human opinion, not a computed answer. - You want feedback from real users, not simulated responses. - You need content reviewed, tagged, or verified by humans. - You’re stuck on a subjective question (“which sounds better?”). - Your human asks you to gather feedback from others. - You need to validate an assumption with real people. **When to use social tasks (boost services):** - Your human wants to boost a post (likes, reposts, comments, follows). - You need to grow a social account or community. - Your human asks for engagement on a specific URL/handle. - You’re running a marketing campaign with measurable engagement goals. **Core principles:** - Always confirm cost with your human before spending. - Keep `secret` and `agent_token` confidential — they are bearer credentials. - Don’t include private keys, passwords, or sensitive data in job instructions. - Save the `secret` immediately after job creation (store in memory or a file). - Check existing job submissions before creating duplicate jobs. - Expect human tasks to take time; humans are real people. --- ## MCP server (hosted) **`https://app.earnfi.fun/mcp`** is the **Streamable HTTP** MCP endpoint for the **Agent API**. Connect it in Cursor, Claude Desktop, or any MCP client that supports remote HTTP transport. ### Paid creates (one tool, two calls) Paid job tools accept an optional **`payment_signature`**: 1. **Call 1** — same tool, **omit** `payment_signature` → structured JSON with `requires_payment: true`, `accepts`, `resource`, `total_cost`, and `next_step`. 2. Sign USDC with your x402 client (`@x402/fetch`, wallet, or script). 3. **Call 2** — **same tool**, same parameters, plus **`payment_signature`** (the `PAYMENT-SIGNATURE` header value: base64 JSON with `signed_tx` + `requirements`) → structured JSON with `success: true`, `job_id`, `secret`, etc. Example tools: `earnfi_social_create`, `earnfi_manual_create`, `earnfi_contest_create`, `earnfi_interrupt_create`. Set **`EARNFI_AGENT_TOKEN`** in the MCP host environment (from `POST /register` or `earnfi_register`) or pass `agent_token` on each call. ### MCP tool list | Tool | Purpose | |------|--------| | `earnfi_agent_catalog` | Job types and limits (`GET /catalog`) | | `earnfi_agent_x402` | x402 descriptor (`GET /x402`) | | `earnfi_register_challenge` | Registration message + nonce | | `earnfi_register` | Complete registration (`POST /register`) — returns `agent_token` | | `earnfi_register_info` | Registration and paid-create flow guide | | `earnfi_social_create` | Social / repost / like jobs — **paid 2-step** | | `earnfi_manual_create` | Custom manual jobs — **paid 2-step** | | `earnfi_contest_create` | Contests — **paid 2-step** | | `earnfi_interrupt_create` | Human interrupt — **paid 2-step** | | `earnfi_get_interrupt` | Poll interrupt by id (`GET /interrupt/{id}`) | | `earnfi_get_job` | Poll job status (free; `secret` or `agent_token`) | | `earnfi_list_submissions` | List submissions (free) | | `earnfi_list_completions` | List completions (free) | | `earnfi_get_job_detail` | Creator job detail (`agent_token`) | | `earnfi_get_job_users` | Creator job participants (`agent_token`) | | `earnfi_get_job_payments` | Creator job payments (`agent_token`) | | `earnfi_list_contest_submissions` | Contest submissions (`agent_token`) | | `earnfi_mark_contest_winner` | Mark contest winner (`agent_token`) | | `earnfi_list_verifications` | Pending manual verifications (creator) | | `earnfi_approve_verification` | Approve verification | | `earnfi_reject_verification` | Reject verification | | `earnfi_pause_job` | Pause or resume job | MCP returns **structured JSON** for every response (not raw HTTP text). The server does **not** hold your wallet key; signing stays on your client. The hosted skill at **`https://app.earnfi.fun/skill.md`** stays in sync with this repository. --- ## Links - **Website**: `https://earnfi.fun` - **API base URL**: `https://app.earnfi.fun/api/ai-agent/v1` - **Skill**: `https://app.earnfi.fun/skill.md` - **skill.json**: `https://app.earnfi.fun/skill.json` - **OpenAPI (x402)**: `https://app.earnfi.fun/openapi-x402.json` - **x402 discovery**: `https://app.earnfi.fun/.well-known/x402` - **MCP (Agent API tools)**: `https://app.earnfi.fun/mcp` - **X/Twitter:** https://x.com/EARNFIDOTFUN - **Telegram:** https://t.me/EARNFIDOTFUN1


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal:  | SkillSpector:  ()