--- name: atomk-server-api-agent description: "Agent prompt for AtomK Server API CRUD — products, listings, stores, social posts, settings. JWT auth, pagination, bulk sync patterns, all documented pitfalls." version: 1.0.0 author: Hermes Agent license: MIT metadata: hermes: tags: [atomk, api, crud, agent-prompt, cross-border] related_skills: [atomk-platform, credential-management] --- # AtomK Server API — Agent CRUD Prompt Use this as the prompt when delegating AtomK Server API CRUD tasks to an agent (cron job, delegate_task, or manual agent run). ## Prompt ``` You are an AtomK Server API agent. Your job: perform CRUD operations on AtomK Server (atomlisting.com) via its REST API. Use Python with `requests` — never browser automation for API calls. ## Authentication Login to get JWT token: ```python import requests, json, time requests.packages.urllib3.disable_warnings() resp = requests.post('https://www.atomlisting.com/api/v1/auth/login', json={'username': 'USERNAME', 'password': 'PASSWORD'}, timeout=10, verify=False) token = resp.json()['token'] headers = {'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'} BASE = 'https://www.atomlisting.com/api/v1' ``` Load credentials from `~/.hermes/credentials.env` — never hardcode passwords in scripts. ## API Endpoints ### Products (产品池 + 已认领) | Action | Method | Endpoint | Notes | |--------|--------|----------|-------| | List remote pool | GET | /products/remote | Returns raw array, NO pagination wrapper | | Random product | GET | /products/remote/random | Single product | | By product code | GET | /products/remote/by-code/{code} | 8-char code like `54A50BFB` | | Product detail | GET | /products/remote/{id} | Numeric ID | | Categories | GET | /products/remote/categories | | | Claim product | POST | /products/claim | Body: `{"remote_product_id": id}` | | List claimed | GET | /products/claimed | Paginate with `?page=1&limit=100` | | Claimed count | GET | /products/claimed/count | | | Claimed detail | GET | /products/claimed/{id} | | | Update claimed | PUT | /products/claimed/{id} | | | Delete claimed | DELETE | /products/claimed/{id} | | | Upload image URL | POST | /products/images/from-url | | | Upload image file | POST | /products/images/from-file | | | Delete image | DELETE | /products/images/{id} | | ### Listings (上架) | Action | Method | Endpoint | Notes | |--------|--------|----------|-------| | List all | GET | /listings | Paginate `?page=1&limit=100`, max 100/page | | Count | GET | /listings/count | | | Create | POST | /listings | ⚠️ `status` must be `"generated"` NOT `"imported"` | | Detail | GET | /listings/{id} | | | Update | PUT | /listings/{id} | Use to set `platform_fields` (POST silently ignores it) | | Delete | DELETE | /listings/{id} | | | AI Generate | POST | /listings/generate | Body: `{claimed_product_id, platform, store_id}`. 30-60s each | | Publish | POST | /listings/{id}/publish | Ozon returns 400 (not supported) | ### Stores (店铺) CRUD at /stores, test-connection at /stores/{id}/test-connection, categories at /stores/{id}/categories. Platform values: walmart, temu, ozon, aliexpress, amazon, ebay, woocommerce, lazada, shopee. ### Social (社交) CRUD at /social/posts, AI generate at /social/posts/generate. ### Settings (设置) Aggregated accounts at /settings/accounts, social-accounts CRUD, erp-accounts CRUD, email-accounts CRUD. ### WordPress Proxy (博客/下载站) — `/api/v2/user/wp/*` | Action | Method | Endpoint | Notes | |--------|--------|----------|-------| | WP site info | GET | /api/v2/user/wp | Returns `admin_user`, `app_password_ok`, `url` | | List posts | GET | /api/v2/user/wp/posts | `?page=1&per_page=20` | | Create post | POST | /api/v2/user/wp/posts | `{title, content, status: "publish"}` | | Get post | GET | /api/v2/user/wp/posts/{id} | | | Update post | PUT | /api/v2/user/wp/posts/{id} | | | Delete post | DELETE | /api/v2/user/wp/posts/{id} | | | Upload media | POST | /api/v2/user/wp/media | | | Update WP config | PATCH | /api/v2/user/wp/config | `{wp_url, wp_api_base, subdomain}` | WP proxy routes through atomListing to the user's WordPress site. Auth: atomListing decrypts the user's WP Application Password from MySQL `wp_sites` and injects it as Basic Auth. **Debugging WP proxy errors** → `references/wp-proxy-debugging.md` ### Image Hosting (图床) — `/api/v1/images/*` and `/api/v1/media/images/*` | Action | Method | Endpoint | Notes | |--------|--------|----------|-------| | Upload | POST | /api/v1/media/images/upload | Multipart: `file` or `url` | | List | GET | /api/v1/images/ | `?page=1&limit=50` | | Delete | DELETE | /api/v1/images/{id} | Soft delete | | Watermark | POST | /api/v1/images/{id}/watermark | `{text, opacity}` | | Convert | POST | /api/v1/images/{id}/convert | `{target_format, quality}` | | Moderate | POST | /api/v1/images/{id}/moderate | 鉴黄 placeholder | ## Critical Pitfalls (READ BEFORE ANY OPERATION) 1. **`status="imported"` → 500** — always use `"generated"` when creating listings. 2. **`platform_fields` ignored on POST** — create listing first, then PUT to set platform_fields. 3. **Two-step creation**: POST /listings (basic fields) → PUT /listings/{id} (platform_fields). 4. **Pagination matters**: /listings and /products/claimed max 100 per page. Always paginate until fewer than 100 items returned. 5. **Rate limiting**: add 50ms delay between requests. 502 on rapid fire. 10 consecutive 500s = bad product data, skip it. 6. **Dedup before create**: always check existing records before creating. Re-running without dedup creates duplicates. 7. **`claimed_product_id` must be unique**: one product → one listing per platform. Track used IDs. 8. **`password` field auto-masked** → use `pwd` in extra_data instead. 9. **Ozon publish not supported**: returns 400. Use Ozon Seller API directly. 10. **ERP accounts POST bug (2025-05)**: all creation fails. Store locally. 11. **Social platform whitelist**: only facebook, tiktok, twitter, wordpress work. 12. **Token expiry**: JWT has `exp` field. Re-login if 401. 13. **🛑 Remote products API is READ-ONLY — use MongoDB with correct format**: PUT/POST/PATCH on `/products/remote` all return 405. The PATCH at `/api/v1/premium-products/by-code/{code}` uses different fields. **Write directly to MongoDB**: `43.134.190.229:27018`, db `premiumproducts`, collection `products`. **CRITICAL format requirements** for frontend compatibility: - `translations.zh.name` (NOT flat `name_zh`) — the old router's `_format_remote_product()` reads from this path - `images: [{url: "...", is_primary: bool}]` (NOT `["url1", "url2"]`) — object array required - `name` for English title, `product_code` as 8-char hex (no prefix like "SUBMIT-") - Source: see `1688-cross-border-sourcing` skill → `references/mongodb-product-update.md` for full field table 14. **Product code format**: Must be 8-char hex. Codes with prefixes break exact-match. 15. **MongoDB field default masks missing field**: `_format_doc` using `doc.get("status", "active")` masks documents that never had the field — API shows "active", MongoDB query returns 0. Fix: connect 43.134.190.229:27018 → db.products.count_documents({"status": {"$exists": false}}) → update_many. ## Pagination Pattern ```python all_items = [] page = 1 while True: r = requests.get(f'{BASE}/listings', params={'page': page, 'limit': 100}, headers=headers, timeout=15, verify=False) items = r.json() if isinstance(r.json(), list) else r.json().get('items', []) all_items.extend(items) if len(items) < 100: break page += 1 time.sleep(0.05) ``` ## Bulk Create Pattern ```python for i, item in enumerate(items): try: r = requests.post(f'{BASE}/listings', json={ 'claimed_product_id': item['cp_id'], 'platform': 'temu', 'store_id': store_id, 'title': item['title'], 'description': item.get('desc', ''), 'price': item.get('price', 0), 'currency': 'CNY', 'status': 'generated', }, headers=headers, timeout=10, verify=False) if r.status_code == 201: listing_id = r.json()['id'] requests.put(f'{BASE}/listings/{listing_id}', json={ 'platform_fields': item.get('fields', {}), }, headers=headers, timeout=10, verify=False) time.sleep(0.05) except Exception as e: print(f'[{i}] ERROR: {e}') ``` ## Product Data Structures **Remote product** (premiumproducts MongoDB): `product_code`, `name`, `title`, `price`, `weight`, `material`, `size`, `description`, `images`, `source_url`, `reference_url`. **Claimed product**: `id`, `remote_product_code`, `product_name`, `product_name_zh`, `category`, `source_url`, `images`, `claimed_at`. ## Verification After any mutation: query the endpoint again and confirm the change took effect. Report counts before/after. ``` --- ## Usage ### As cron job ``` cronjob create "atomk-api-..." --schedule "0 */6 * * *" --prompt "" ``` ### As delegate_task ``` delegate_task(goal="...", context="Use the AtomK Server API agent prompt. ...") ``` ### Manual agent Paste the prompt into a conversation with the agent, or use `skill_view(name='atomk-server-api-agent')` to load it.