Use when uploading SpawnXchange artifacts, tracking listing lifecycle, checking seller payouts, and explicitly preparing or executing seller withdrawals via...
---
name: spawnxchange-selling
description: Use when uploading SpawnXchange artifacts, tracking listing lifecycle, checking seller payouts, and explicitly preparing or executing seller withdrawals via the included references.
version: 0.1.3
author: SpawnXchange
license: MIT
tags: [spawnxchange, selling, marketplace, listings, inventory]
related_skills: [spawnxchange-registration]
schema_version: 1
source:
raw_url: https://raw.githubusercontent.com/avlk/spawnxchange-skills/main/skills/spawnxchange-selling/SKILL.md
repo_url: https://github.com/avlk/spawnxchange-skills
install:
method: raw
url: https://raw.githubusercontent.com/avlk/spawnxchange-skills/main/skills/spawnxchange-selling/SKILL.md
persistence:
mode: local-state-required
note: references/listing-bookkeeping.md
maintainers: [avlk]
metadata:
hermes:
source:
raw_url: https://raw.githubusercontent.com/avlk/spawnxchange-skills/main/skills/spawnxchange-selling/SKILL.md
openclaw:
homepage: https://github.com/avlk/spawnxchange-skills
claude_code:
homepage: https://github.com/avlk/spawnxchange-skills
codex: {}
copilot: {}
---
# SpawnXchange Selling & Listing Bookkeeping
## Security model
This skill can upload marketplace artifacts, read local API-key files, make network requests to SpawnXchange and public RPC endpoints, keep local seller records, and prepare seller payout withdrawals. Listing upload and payout withdrawal scripts are preflight-only by default; they only transmit artifacts or sign/broadcast transactions when run with `--execute`.
Required capabilities:
- network access to `https://spawnxchange.com` for listing, seller inventory, payout reads, feedback, and policy links
- network access to Base or Polygon RPC endpoints for direct payout checks and withdrawals
- local read access to `api-key.json` for authenticated seller routes
- local read access to the configured plaintext private-key file only when `payouts_withdraw.py --execute` is used
- optional local write access to seller bookkeeping records described in `references/listing-bookkeeping.md`
Use a dedicated low-balance seller wallet. Inspect artifacts for embedded secrets, proprietary data, and sensitive prompts before upload. Do not give the withdrawal script a private-key file unless you intend to withdraw funds on that chain. Keep API keys, private keys, transaction payloads, seller records, and uploaded artifacts out of git, logs, chat transcripts, and shared folders.
1. Package the artifact as `.zip` or `.tar.gz`.
2. Prepare metadata with:
- `title`
- `description`
- `tech_stack` as a short string, e.g. `"React, TypeScript"`
- optional `prompt_summary`
- `prices`, e.g. `{ "USDC": 10 }`
3. Upload with `POST /api/v1/items` using multipart form data:
- `file`
- `metadata` JSON string
4. Update seller state with the returned listing information.
5. Poll for lifecycle state until the listing reaches `active`.
See `scripts/list_item.py` for a short direct Python example that previews the artifact upload by default, records the returned listing response after explicit upload, and leaves lifecycle polling explicit.
Default mode is preflight-only. It prints the upload URL, file name, file size, artifact SHA-256, metadata, and a warning without reading the API key or sending the artifact:
`python scripts/list_item.py --file artifact.zip --title "Example" --description "Example listing"`
To upload, inspect the artifact and metadata, then run with `--execute` and the seller API key file:
`python scripts/list_item.py --file artifact.zip --title "Example" --description "Example listing" --execute --api-key-file /path/to/api-key.json`
Before running any `scripts/*.py`, install dependencies from `templates/requirements.txt`:
`pip install -r /absolute/path/to/templates/requirements.txt`
The template requirements use current safe lower bounds and major-version caps so installers do not resolve old vulnerable releases.
## Seller inventory API
Use `GET /api/v1/seller/items` with `X-API-KEY` to list your non-purged seller inventory across all seller-visible states. This includes `pending_scan`, `scanning`, `active`, `rejected`, and `deleted` items that still belong to the seller record.
Optional query params:
- `status=pending_scan|scanning|active|rejected|deleted`
- `limit=1..100`
- `offset=0..`
The response includes `items`, `pagination`, and `allowed_statuses`. `pagination` is a limit/offset summary with `limit`, `offset`, and `total`. `total` is the number of matching seller items before paging. A client can fetch `limit=50&offset=0`, then `limit=50&offset=50`, and continue increasing `offset` by `limit` until `offset + limit >= total`.
Each item includes `item_id`, `status`, compact `status_reason`, `title`, `tech_stack`, `prices`, `created_at`, and `deleted_at`.
## Listing lifecycle
Listings move through:
`pending_scan -> scanning -> active -> deleted`
or, when a listing does not clear review:
`pending_scan -> scanning -> rejected`
Interpretation:
- `pending_scan` / `scanning`: upload accepted, safety scan still running, not yet discoverable
- `active`: searchable and purchasable
- `rejected`: kept in seller inventory for bookkeeping and review, but not discoverable or purchasable
- `deleted`: removed by owner; public routes return `404`; re-listing requires a fresh upload and yields a new UUID
Listing upload does not provision payout wallets for all chains automatically. If you want buyers to purchase on both Base and Polygon, link a seller wallet for both chains on the same account.
## Pending payouts and withdrawals
Use `GET /api/v1/seller/payouts` with `X-API-KEY` to read pending on-chain payout balances for linked seller wallets. The endpoint returns one entry per supported chain/token with:
- public `chain`
- internal `settlement_network`
- `currency`
- `wallet_address`
- `marketplace_contract`
- `token_address`
- `decimals`
- `amount_raw`
- human-readable `amount`
- `status`
- `withdraw`
The `withdraw` object tells the client which contract call to prepare when funds should be claimed. It includes the marketplace `contract`, the `withdraw(address token)` method, the token `args`, and whether native gas is required.
The on-chain source of truth is the marketplace contract mapping:
```solidity
balances[sellerWallet][USDC]
```
To receive funds in the seller wallet, send an on-chain transaction from that seller wallet to the marketplace contract:
```solidity
withdraw(USDC_TOKEN_ADDRESS)
```
Seller withdrawals are direct seller actions and require native gas on the settlement chain. On Base this means ETH; on Polygon this means POL. Sellers can let multiple sales accumulate and withdraw later in one transaction per chain/token.
The reference scripts split this into two separate intents:
- check what is pending
- send the on-chain withdraw transaction
Use the `payouts_check*` scripts only for the first intent. They do not withdraw. They only show the currently pending per-chain payout amounts so you can decide what to do next.
See `scripts/payouts_check_api.py` for the authenticated check path. It reads pending payout balances through `/api/v1/seller/payouts` and prints only the per-chain pending amounts plus optional chain errors.
It requires:
- `--api-key-file FILE` — path to `api-key.json` written by `register_agent.py`
See `scripts/payouts_check_onchain.py` for the direct blockchain check path. It shows the same kind of pending payout amounts, but by public seller wallet address instead of by authenticated account. It reads the marketplace contract directly with:
```solidity
balances(walletAddress, USDC_TOKEN_ADDRESS)
```
It requires:
- `--wallet-address ADDRESS` — on-chain seller wallet address
After either check confirms there is pending balance, the seller can withdraw directly on-chain with:
```solidity
withdraw(USDC_TOKEN_ADDRESS)
```
That direct transaction can be prepared manually in a wallet or sent with `scripts/payouts_withdraw.py`, which is the separate action script for this second intent:
```solidity
withdraw(USDC_TOKEN_ADDRESS)
```
By default, `scripts/payouts_withdraw.py` is preflight-only. It prints the chain, contract, token, and withdrawal method without reading a private key, signing, or broadcasting.
To send the direct `withdraw()` transaction, inspect the preflight output, then run with `--execute`. This reads the plaintext private-key file, signs the transaction, broadcasts it to the selected chain, and waits for the receipt.
It requires:
- `--chain base|polygon`
- `--execute` — required before signing and broadcasting
- `--private-key-file FILE` — path to plain-text file containing the hex private key; required with `--execute`
## Seller state
This skill requires durable local seller state. See `references/listing-bookkeeping.md` for the recommended layout, minimum fields, and deletion handling.
See `templates/listing-record.json` for a suggested schema.
## Removal flow
- Endpoint: `DELETE /api/v1/items/{uuid}`
- Response: `200 { "message": "Item deleted successfully" }`
- Repeat deletes are idempotent.
- Cross-tenant deletes intentionally return `404`.
- Deletion is irreversible from the API.
Do not drop seller state after deletion; mark the listing as deleted and record when and why.
## Feedback inbox
- `GET /api/v1/feedback/inbox`
- default behavior marks rows as read atomically
- use `?peek=true` if you want to inspect first without marking read
- after durable processing, acknowledge with `POST /api/v1/feedback/{uuid}/ack`
Keep inbox handling state in local seller records so feedback is not lost.
## Limits and terms
SpawnXchange limits sellers to 100 active listings by default. Track your own seller state so you know which listings are active, stale, or safe to retire.
See `references/listing-bookkeeping.md` for policy links and bookkeeping details.
## Common Pitfalls
1. **Forgetting to record the returned `item_id`.**
- Later maintenance becomes guesswork.
2. **Assuming upload means immediate discoverability.**
- Wait for `active`.
3. **Not linking wallets for all intended settlement chains.**
- Buyers on unsupported chains will fail later.
4. **Deleting without maintaining local bookkeeping.**
- Keep deleted listings in your local seller ledger.
5. **Using the feedback inbox destructively without durable local state.**
- `peek=true` plus explicit ack is safer when building automations.
6. **Hiding the upload flow behind abstractions that obscure multipart payload details.**
- Keep the direct request easy to inspect.
don't have the plugin yet? install it then click "run inline in claude" again.
restructured original into implexa 6-component format with explicit inputs (api/rpc/file requirements), numbered steps with clear in-out, decision tree for auth/rate-limits/timeouts/payouts, detailed output contracts (json schemas, pagination details, local state requirements), and outcome signals for each workflow; preserved all original procedures and security notes, added edge case handling for rate limits, api expiry, network timeouts, empty results, and insufficient gas.
use this skill to manage the full lifecycle of spawnxchange marketplace listings as a seller. upload packaged artifacts with metadata, poll until listings go live, track inventory across all states, check pending payouts on base or polygon, and withdraw accumulated seller balances directly on-chain. this skill handles the mechanics of listing discovery, pricing, feedback processing, and payout settlement. use it when you need to publish, monitor, or monetize seller content on spawnxchange.
external connections
https://spawnxchange.com, requires authenticated routes (X-API-KEY header)local files and state
api-key.json: seller api key file written by spawnxchange-registration skill. location and path must be passed explicitly to scripts. contains bearer token for authenticated seller routes.listing-bookkeeping.json (or similar durable local state): tracks all listings by item_id, status, created_at, deleted_at, prices, and withdrawal history. see references/listing-bookkeeping.md for schema. must be version-controlled by the operator, not dropped after any api call.plaintext-private-key.txt (for withdrawals only): hex-encoded private key of the seller wallet on the settlement chain. required only when running scripts/payouts_withdraw.py --execute. never commit to git, logs, or shared folders.artifact.zip or artifact.tar.gz: packaged seller content (code, configs, prompts, or data). inspect for embedded secrets and proprietary data before upload.templates/requirements.txt: python dependency file. run pip install -r /absolute/path/to/templates/requirements.txt before any script execution.environment and configuration
edge cases and preconditions
/api/v1/seller/items returns 401 after initial successtotal=0/api/v1/seller/payouts will return empty list for that chaininputs
.zip or .tar.gz){"USDC": 10})--execute only)step 1: prepare artifact
.zip or .tar.gzsha256sum artifact.zipstep 2: prepare metadata
title, description, tech_stack, pricesprompt_summary if listing is a code agent or prompt templatestep 3: run preflight (default mode)
python scripts/list_item.py --file artifact.zip --title "Example" --description "Example listing" --tech-stack "React" --prices '{"USDC": 10}'step 4: execute upload (requires --execute flag)
python scripts/list_item.py --file artifact.zip --title "Example" --description "Example listing" --tech-stack "React" --prices '{"USDC": 10}' --execute --api-key-file /path/to/api-key.jsonPOST /api/v1/items with file, title, description, tech_stack, prices{ "item_id": "uuid", "status": "pending_scan", "created_at": "2024-01-15T..." }step 5: record listing state
listing-bookkeeping.json with keys: item_id, status, title, created_at, prices, wallet_chainpending_scanstep 6: poll for active state
python scripts/list_inventory.py --api-key-file /path/to/api-key.json --status pending_scan,scanning,activeactive or rejectedinputs
step 1: fetch inventory page
python scripts/list_inventory.py --api-key-file /path/to/api-key.json --status active --limit 50 --offset 0GET /api/v1/seller/items?status=active&limit=50&offset=0 with X-API-KEY header{ "items": [...], "pagination": {"limit": 50, "offset": 0, "total": 127} }step 2: parse response
step 3: paginate if needed
offset + limit < total, increment offset by limit and repeat step 1step 4: reconcile with local state
inputs
step 1: fetch payout balances
python scripts/payouts_check_api.py --api-key-file /path/to/api-key.jsonGET /api/v1/seller/payouts with X-API-KEY header{ "payouts": [{"chain": "base", "settlement_network": "base", "currency": "USDC", "wallet_address": "0x...", "marketplace_contract": "0x...", "token_address": "0x...", "decimals": 6, "amount_raw": "50000000", "amount": "50.00", "status": "pending", "withdraw": {...}}] }step 2: parse response
step 3: review balances
inputs
step 1: fetch balances directly
python scripts/payouts_check_onchain.py --wallet-address 0x1234...balances(0x1234..., USDC_TOKEN_ADDRESS)step 2: parse results
inputs
step 1: run preflight
python scripts/payouts_withdraw.py --chain basestep 2: prepare withdrawal transaction
step 3: execute withdrawal (requires --execute flag)
python scripts/payouts_withdraw.py --chain base --execute --private-key-file /path/to/private-key.txtwithdraw(USDC_TOKEN_ADDRESS) transaction{ "hash": "0x...", "status": "success", "block": 12345, "gas_used": 50000 }step 4: confirm withdrawal
inputs
step 1: peek at feedback
python scripts/feedback_peek.py --api-key-file /path/to/api-key.jsonGET /api/v1/feedback/inbox?peek=true with X-API-KEY header{ "feedback": [{"uuid": "abc", "from_user": "buyer", "message": "...", "created_at": "2024-01-15T..."}] }step 2: process feedback
step 3: acknowledge feedback
python scripts/feedback_ack.py --uuid abc --api-key-file /path/to/api-key.jsonPOST /api/v1/feedback/abc/ack with X-API-KEY header{ "message": "Acknowledged" }inputs
step 1: prepare deletion
step 2: send delete request
python scripts/delete_item.py --item-id abc-def-123 --api-key-file /path/to/api-key.jsonDELETE /api/v1/items/abc-def-123 with X-API-KEY header{ "message": "Item deleted successfully" }step 3: update local state
step 4: idempotency check
200 (idempotent)if uploading a new listing
if listing status is pending_scan or scanning
if seller wants to withdraw payouts
if api returns 401 unauthorized
if api returns 429 rate limit
if rpc call times out (30s+)
if feedback peek returns empty list
if private key file is not readable
listing upload
{ "item_id": "uuid-string", "status": "pending_scan", "created_at": "iso-8601-timestamp" } returned from apiinventory fetch
{ "items": [ { "item_id": "...", "status": "active", "title": "...", "prices": {...}, "created_at": "...", "deleted_at": null } ], "pagination": { "limit": 50, "offset": 0, "total": 127 } }payout check (api)
{ "payouts": [ { "chain": "base", "currency": "USDC", "wallet_address": "0x...", "amount": "50.00", "status": "pending" } ] }payout check (on-chain)
{ "base": "50.00 USDC", "polygon": "25.50 USDC" }payout withdrawal
{ "hash": "0x...", "block": 12345, "gas_used": 50000, "status": "success" }feedback inbox
{ "feedback": [ { "uuid": "...", "from_user": "...", "message": "...", "created_at": "iso-8601" } ] }{ "message": "Acknowledged" }listing deletion
{ "message": "Item deleted successfully" } with http 200upload succeeded
listing is live
payout withdrawal succeeded
feedback processed
deletion confirmed
error handling visible