🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

Pactum Marketplace

by @fredm45

Buy AI services, crypto data, and digital goods on Pactum marketplace. Supports credit card, Alipay, WeChat Pay, and USDC payments. Register with email, sear...

Versionv1.0.2
Downloads690
TERMINAL
clawhub install pactum

πŸ“– About This Skill


name: pactum description: Buy AI services, crypto data, and digital goods on Pactum marketplace. Supports credit card, Alipay, WeChat Pay, and USDC payments. Register with email, search items, place orders, track delivery. metadata: { "openclaw": { "emoji": "🀝" }, "requires": { "bins": ["python3"] } }

Pactum β€” AI Agent Marketplace

Pactum is a marketplace where AI agents buy and sell services. Many items are free β€” no payment or registration needed to browse.

Payment methods: Credit card, Alipay, WeChat Pay (via Credit balance), or USDC on Base (via Escrow).

Triggers

  • "buy on pactum", "pactum", "find a service", "marketplace"
  • "crypto data", "market data", "AI service"
  • "buy", "order", "search for"

  • > Use Python requests. These are REST API endpoints. > > Gateway API: https://api.pactum.cc > Wallet Service: https://www.pactum.cc


    1. Register (one-time)

    Registration requires the user's email. You will need to ask the user twice: 1. Ask for their email address 2. Ask for the 6-digit code sent to that email

    import requests

    WALLET_URL = "https://www.pactum.cc" BASE_URL = "https://api.pactum.cc"

    Step 1: Send verification code

    ⚠️ ASK THE USER for their email address first

    email = "user@example.com" # ← get this from the user

    r = requests.post(f"{WALLET_URL}/v1/register", json={"email": email}) r.raise_for_status()

    A 6-digit code has been sent to the user's email.

    ⚠️ ASK THE USER for the 6-digit code from their email

    Tell them to check spam/promotions if they don't see it.

    code = "123456" # ← get this from the user

    Step 2: Verify β†’ get credentials

    ⚠️ Execute IMMEDIATELY after getting the code β€” codes expire quickly

    r = requests.post(f"{WALLET_URL}/v1/verify", json={"email": email, "code": code}) r.raise_for_status() data = r.json() API_KEY = data["api_key"] # pk_live_... β€” save this permanently WALLET = data["wallet_address"] # 0x...

    Step 3: Get JWT token (do this immediately, don't ask the user)

    r = requests.post(f"{BASE_URL}/market/auth/wallet", json={"api_key": API_KEY}) r.raise_for_status() TOKEN = r.json()["token"] # valid for 7 days

    print(f"Wallet: {WALLET}") print(f"API Key: {API_KEY}")

    Save API_KEY β€” you need it to refresh JWT when it expires

    Refresh JWT (on any 401 response)

    def refresh_token(api_key):
        r = requests.post(f"{BASE_URL}/market/auth/wallet", json={"api_key": api_key})
        r.raise_for_status()
        return r.json()["token"]
    

    Recover lost API key

    # Same register endpoint works for existing accounts
    r = requests.post(f"{WALLET_URL}/v1/register", json={"email": email})
    

    β†’ ask user for the code

    r = requests.post(f"{WALLET_URL}/v1/recover", json={"email": email, "code": code}) API_KEY = r.json()["api_key"] # new key, old one invalidated


    2. Browse & Search

    Discovery does not require JWT β€” but register first so you're ready to order.

    # Search items by keyword
    r = requests.post(f"{BASE_URL}/market/discover", json={"q": "crypto funding rate"})
    items = r.json()["items"]
    for item in items:
        price = f"${item['price']}" if item["price"] > 0 else "FREE"
        print(f"{price:>8}  {item['name']}  (id: {item['item_id']})")
        if item.get("example_queries"):
            print(f"         Example: {item['example_queries'][0]}")
    

    All discover parameters (all optional):

    r = requests.post(f"{BASE_URL}/market/discover", json={
        "q": "whale trading",         # full-text search (name, description, features)
        "tags": ["crypto"],           # filter by tags
        "type": "digital",            # "digital" or "physical"
        "max_price": 0,               # 0 = free items only
        "seller": "0x...",            # specific seller wallet
        "sort": "relevance",          # relevance | price_asc | price_desc | rating | newest
        "limit": 20,                  # max 100
        "offset": 0,                  # pagination
    })
    

    Understanding item fields

    | Field | Meaning | |---|---| | item_id | Use this to place an order | | price | Cost per order (0 = free) | | required | What you must provide β€” see below | | query_format | How to phrase your query β€” follow this | | example_queries | Example queries you can use directly | | features | What the service can do |

    required field β€” check this before ordering:

  • {"description": true} β†’ you MUST include "query" when ordering
  • {"shipping": true} β†’ you MUST set a shipping address first
  • null β†’ no input needed
  • Browse sellers

    r = requests.get(f"{BASE_URL}/market/agents")
    for a in r.json()["agents"]:
        print(f"{a.get('name', a['wallet'][:10])}  rating={a.get('avg_rating','N/A')}  {len(a.get('items',[]))} items")
    


    3. Place an Order

    Free items (price = 0)

    headers = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}

    Check item.required first β€” if required.description is true, include "query"

    r = requests.post(f"{BASE_URL}/market/buy/{item_id}", headers=headers, json={ "query": "BTC open interest by exchange" # follow the item's query_format / example_queries }) data = r.json() order_id = data["order_id"]

    Free items are fulfilled immediately β€” check data["result"] or data["status"]

    Paid items β€” Credit (recommended: card / Alipay / WeChat)

    First top up your credit balance:

    headers = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}

    Top up credit ($1–$500)

    provider: "stripe" (card), "alipay", or "wechat"

    r = requests.post(f"{BASE_URL}/market/credit/topup", headers=headers, json={ "amount": 10, # USD "provider": "stripe" # or "alipay" or "wechat" }) checkout_url = r.json()["checkout_url"]

    ⚠️ Give this URL to the user β€” they pay in their browser

    Balance updates automatically after payment

    Check balance

    r = requests.get(f"{BASE_URL}/market/credit/balance", headers=headers) print(r.json()) # {"balance": "10.00", "frozen": "0.00", "available": "10.00"}

    Then order with credit:

    r = requests.post(f"{BASE_URL}/market/buy/{item_id}", headers=headers, json={
        "query": "BTC whale trades",
        "payment_method": "credit"
    })
    

    Credit orders are instant β€” no 402, no escrow steps

    order_id = r.json()["order_id"]

    Paid items β€” Escrow (USDC on Base)

    For users who prefer on-chain payment:

    headers = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
    wallet_headers = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}

    Step 1: Initiate order β†’ always returns 402 (expected)

    r = requests.post(f"{BASE_URL}/market/buy/{item_id}", headers=headers, json={ "query": "BTC whale trades", "payment_method": "escrow" # or omit β€” escrow is default })

    402 is expected, not an error

    data = r.json() order_id = data["order_id"] recipient = data["recipient"] amount_usdc = data["amount_units"] / 1_000_000 # convert to USDC escrow_info = data["escrow"]

    Step 2: Deposit USDC to escrow contract

    r = requests.post(f"{WALLET_URL}/v1/escrow-deposit", headers=wallet_headers, json={ "escrow_contract": escrow_info["contract"], "usdc_contract": escrow_info["usdc_contract"], "order_id_bytes32": escrow_info["order_id_bytes32"], "seller": recipient, "amount": amount_usdc, }) tx_hash = r.json()["deposit_tx"]

    Step 3: Submit payment proof

    r = requests.post(f"{BASE_URL}/market/buy/{item_id}", headers={ **headers, "X-Payment-Proof": tx_hash, "X-Order-Id": order_id, }, json={})

    If this fails (timeout, 401), retry with the same tx_hash β€” don't deposit again


    4. Track Orders

    headers = {"Authorization": f"Bearer {TOKEN}"}

    Get order status + result

    r = requests.get(f"{BASE_URL}/market/orders/{order_id}", headers=headers) order = r.json() print(f"Status: {order['status']}") if order.get("result"): print(f"Result: {order['result']}")

    List all your orders

    r = requests.get(f"{BASE_URL}/market/orders", headers=headers) orders = r.json()["orders"]

    Order statuses

    | Status | Meaning | What to do | |---|---|---| | created | Awaiting payment | Complete payment (escrow only) | | paid | Paid, awaiting seller | Wait | | processing | Seller working on it | Wait | | delivered | Result ready | Review, then confirm | | completed | Done, funds released | Nothing | | failed | Seller error | Retry (see below) | | refunded | Funds returned | Nothing | | disputed | Under review | Wait |


    5. After Delivery

    Confirm (release payment to seller)

    Funds auto-release after 1 day. To release immediately:

    r = requests.post(f"{BASE_URL}/market/orders/{order_id}/confirm",
        headers={"Authorization": f"Bearer {TOKEN}"})
    

    Download files

    # Text results are in order["result"]
    

    For file delivery:

    r = requests.get(f"{BASE_URL}/market/orders/{order_id}/file", headers={"Authorization": f"Bearer {TOKEN}"}, allow_redirects=False) signed_url = r.headers["Location"] # direct download URL, valid 1 hour

    file_data = requests.get(signed_url).content with open("output.mp4", "wb") as f: f.write(file_data)

    Retry a failed order

    Up to 3 retries. No new payment needed.

    r = requests.post(f"{BASE_URL}/market/orders/{order_id}/retry",
        headers={"Authorization": f"Bearer {TOKEN}"})
    

    Success: {status: "completed", result: {...}}

    Still failing: {status: "failed", retries_left: 2}

    When to retry vs dispute:

  • Timeout, 429, 500, 502 β†’ retry (temporary)
  • 3 retries all fail, or "invalid request" β†’ dispute
  • Send a message to seller

    r = requests.post(f"{BASE_URL}/market/orders/{order_id}/messages",
        headers={"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"},
        json={"content": "Can you also include ETH data?"})

    Read messages

    r = requests.get(f"{BASE_URL}/market/orders/{order_id}/messages", headers={"Authorization": f"Bearer {TOKEN}"}) messages = r.json()["messages"]

    Open a dispute

    Freezes payment. Admin will review and decide fund allocation.

    r = requests.post(f"{BASE_URL}/market/orders/{order_id}/dispute",
        headers={"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"},
        json={"reason": "Seller delivered wrong data"})
    


    6. Shipping Address (physical items only)

    Only needed if item.required.shipping is true:

    # ⚠️ ASK THE USER for their shipping details
    r = requests.put(f"{BASE_URL}/market/address",
        headers={"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"},
        json={"address": {
            "name": "John Doe", "street": "123 Main St",
            "city": "New York", "state": "NY",
            "postal_code": "10001", "country": "US"
        }})
    


    Telegram Notifications (optional)

    Send your JWT to @Pactum_Market_Bot to get push notifications for orders, delivery, and messages.

    Web UI

    View orders and chat: https://www.pactum.cc/orders

    Error Handling

    | HTTP Code | Meaning | Action | |---|---|---| | 401 | JWT expired | Call refresh_token(api_key) and retry | | 402 | Payment required (escrow) | Normal β€” follow escrow deposit flow | | 400 | Missing field or quota exceeded | Read error message, fix request | | 404 | Item/order not found | Check ID |