# APIClient - Agent > **AI-first API testing desktop client** - built with Python + PyQt6. > Specialised for the [EKIKA Odoo API Framework](https://apps.odoo.com/apps/modules/19.0/api_framework), but works with any REST, GraphQL, or WebSocket API. --- ## Features ### Core API Testing - **Multi-tab request editor** - work on multiple requests simultaneously, drag to reorder - **All HTTP methods** - GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS - **Smart params & headers table** - per-row enable/disable checkboxes, 36 px comfortable rows, auto-expanding blank row - **Body editor** - raw JSON/XML/text with syntax highlighting, **Format JSON** button, `application/vnd.api+json` support - **Auth panel** - Bearer Token, Basic Auth, API Key (header or query) - **Pre-request scripts** - Python executed before each request; access `pm.environment.get/set` - **Test scripts** - assertions auto-run after every response; `pm.test(...)` / `expect(...)` DSL - **Response viewer** - syntax-highlighted body, headers table, test results, search, copy, save - **WebSocket client** - connect, send, receive, log messages - **Mock server** - local HTTP mock with configurable routes ### Collections & Environments - **Collections sidebar** - import/export Postman Collection v2.1 JSON, cURL - **Environment variables** - `{{base_url}}`, `{{api_key}}`, etc. resolved at send time; per-environment values - **Collection runner** - run all requests in a collection, view pass/fail results - **History** - every sent request automatically saved ### AI Co-pilot (Claude-powered) - **Persistent AI chat sidebar** - toggle with the `✦ AI` button or `Ctrl+Shift+A` - **Full context awareness** - AI sees your current request (method, URL, headers, body, params, test scripts) and the last response (status, body, errors); secrets are automatically redacted - **Streaming responses** - tokens stream in real time - **One-click Apply** - AI suggestions come with **Apply Body**, **Apply Params**, **Apply Headers**, **Apply Test Script** buttons that set the values directly in the request editor - **Multi-turn conversation** - full history maintained per session; Clear to reset - **Quick actions** - Analyze, Fix Error, Gen Body, Write Tests, Auth Help, Explain Response - **EKIKA Odoo collection generator** - generate complete collections for JSON-API, REST JSON, GraphQL, and Custom REST JSON without spending AI tokens; supports all auth types ### EKIKA Odoo API Framework specialisation - Generates full CRUD + Execute / Export / Report / Fields / Access-Rights endpoints per model - Auth types: API Key (`x-api-key`), Basic Auth, User Credentials, OAuth2, JWT, Public - Correct JSON-API body format out of the box: `{"data": {"type": "sale.order", "attributes": {...}}}` - Automatic environment creation with `base_url`, `api_key`, tokens --- ## Installation ### Requirements - Python 3.11+ - Linux, macOS, or Windows ```bash git clone https://git.ekika.co/EKIKA.co/APIClient-Agent.git cd APIClient-Agent python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate pip install -r requirements.txt python main.py ``` ### requirements.txt ``` PyQt6>=6.6.0 httpx>=0.27.0 websockets>=12.0 anthropic>=0.25.0 pyyaml>=6.0 pyinstaller>=6.0.0 ``` --- ## Quick Start ### 1 - Send your first request 1. Launch the app: `python main.py` 2. Type a URL in the bar, e.g. `https://jsonplaceholder.typicode.com/todos/1` 3. Press **Send** (or `Ctrl+Enter`) 4. See the JSON response with syntax highlighting in the bottom panel ### 2 - Use environment variables 1. Click **Manage** → **New Environment** → name it `My API` 2. Add variables: ``` base_url = https://api.example.com api_key = your-secret-key ``` 3. Select the environment in the top bar 4. In the URL bar, type `{{base_url}}/v1/users` 5. In Headers, add `Authorization: Bearer {{api_key}}` 6. Variables are resolved automatically at send time ### 3 - Import a collection **From Postman export:** 1. `File → Import…` 2. Paste Postman Collection v2.1 JSON or drop the file **From cURL:** ``` File → Import… → paste: curl -X POST https://api.example.com/v1/orders \ -H "Content-Type: application/json" \ -d '{"product_id": 42, "qty": 1}' ``` **From OpenAPI spec:** 1. `Tools → AI Assistant → Import from Docs` 2. Paste the OpenAPI JSON/YAML URL - parsed instantly, no AI tokens used --- ## EKIKA Odoo API Framework - Complete Example ### Generate a collection in 30 seconds 1. Open **Tools → AI Assistant** (or click `✦ AI` → `AI Assistant` in the menu) 2. Select the **EKIKA Odoo API** tab 3. Fill in: | Field | Example | |---|---| | Instance URL | `https://mycompany.odoo.com` | | API Endpoint | `/user-jsonapi-apikey` | | API Kind | `JSON-API` | | Auth Type | `API Key` | | API Key | `EwKCljvZoHXsaGlxxvCHt1h4SvWLpuWW` | | Models | `sale.order, res.partner, account.move` | | Operations | ✓ List, Get, Create, Update, Delete | 4. Click **Generate Collection** - preview appears instantly 5. Click **Import Both** - collection + environment are saved This generates the following requests for each model with zero AI tokens: ``` GET {{base_url}}/user-jsonapi-apikey/sale.order GET {{base_url}}/user-jsonapi-apikey/sale.order/{{id}} POST {{base_url}}/user-jsonapi-apikey/sale.order PATCH {{base_url}}/user-jsonapi-apikey/sale.order/{{id}} DELETE {{base_url}}/user-jsonapi-apikey/sale.order/{{id}} ``` ### Sending a JSON-API request The generated **Create sale.order** request body: ```json { "data": { "type": "sale.order", "attributes": { "name": "New Order" } } } ``` Headers sent automatically: ``` x-api-key: EwKCljvZoHXsaGlxxvCHt1h4SvWLpuWW Content-Type: application/vnd.api+json Accept: application/vnd.api+json ``` ### Filtering and pagination (List endpoint) The **List sale.order** request includes query params: ``` page[number] = 1 page[size] = 10 fields[sale.order] = id,name,display_name,partner_id sort = -id ``` To filter by customer, open the AI chat sidebar and ask: > *"Add a filter param to get only sale orders for partner ID 7"* AI responds with an apply block you can apply in one click. ### GraphQL example Select **API Kind: GraphQL**. The generator creates: **List sale.order (GraphQL)** ```json { "query": "query {\n sale_order(\n filter: \"\"\n pageSize: 10\n pageNumber: 1\n ) {\n id\n name\n display_name\n }\n}" } ``` **Create sale.order (GraphQL)** ```json { "query": "mutation {\n create_sale_order(\n attributes: {\n name: \"New Record\"\n }\n ) {\n id\n name\n }\n}" } ``` --- ## AI Chat Co-pilot - Example Session Click **✦ AI** in the top bar to open the sidebar. The AI automatically knows what request you have open and the last response. ### Debugging a 401 error ``` You: I'm getting 401 Unauthorized on all my Odoo API requests. AI: A 401 on the EKIKA JSON-API endpoint means the x-api-key header is missing, empty, or incorrect. Checklist: 1. Confirm your environment has api_key set (Tools → Environments) 2. Confirm the request Headers tab includes x-api-key: {{api_key}} 3. In Odoo, verify the API key is still active under your user's API Framework settings Here are the correct headers to apply: ┌─ apply:headers ──────────────────────────────────┐ │ x-api-key: {{api_key}} │ │ Accept: application/vnd.api+json │ └──────────────────────────────────────────────────┘ [ Apply Headers to Request ] ``` Click **Apply Headers to Request** - headers are set immediately and the Headers tab opens. ### Generating a body for a complex model ``` You: Generate a body to create an invoice (account.move) for partner ID 14, with one line for product ID 7, quantity 3, price 150. AI: Here is a JSON-API compliant body for creating an account.move: ┌─ apply:body ─────────────────────────────────────┐ │ { │ │ "data": { │ │ "type": "account.move", │ │ "attributes": { │ │ "move_type": "out_invoice", │ │ "partner_id": 14, │ │ "invoice_line_ids": [[0, 0, { │ │ "product_id": 7, │ │ "quantity": 3, │ │ "price_unit": 150.0 │ │ }]] │ │ } │ │ } │ │ } │ └──────────────────────────────────────────────────┘ [ Apply Body to Request ] ``` ### Writing test scripts ``` You: Write tests to verify a successful JSON-API list response. AI: ┌─ apply:test ─────────────────────────────────────┐ │ pm.test('Status 200', lambda: │ │ pm.response.to_have_status(200)) │ │ pm.test('Has data array', lambda: │ │ expect(pm.response.json()).to_have_key('data'))│ │ pm.test('Data is list', lambda: │ │ expect(pm.response.json()['data']).to_be_list())│ │ pm.test('Response time < 2s', lambda: │ │ expect(pm.response.response_time).to_be_below(2000))│ └──────────────────────────────────────────────────┘ [ Apply Test Script to Request ] ``` --- ## Keyboard Shortcuts | Shortcut | Action | |---|---| | `Ctrl+Enter` | Send request | | `Ctrl+T` | New tab | | `Ctrl+W` | Close tab | | `Ctrl+S` | Save to collection | | `Ctrl+F` | Search requests | | `Ctrl+E` | Manage environments | | `Ctrl+Shift+A` | Toggle AI chat sidebar | | `Ctrl+Shift+F` | Format JSON body | | `Escape` | Cancel in-flight request | | `Ctrl+Q` | Quit | --- ## Project Structure ``` APIClient-Agent/ ├── main.py # Entry point ├── requirements.txt ├── LICENSE ├── app/ │ ├── models.py # HttpRequest, HttpResponse, Environment │ ├── core/ │ │ ├── storage.py # SQLite persistence (collections, environments, history) │ │ ├── http_client.py # httpx-based request engine, variable resolution │ │ ├── ai_client.py # Claude API - collection generation from docs │ │ ├── ai_chat.py # Claude API - multi-turn conversational co-pilot │ │ ├── openapi_parser.py # OpenAPI 3.x / Swagger 2.0 local parser │ │ ├── ekika_odoo_generator.py# EKIKA Odoo framework collection generator │ │ ├── test_runner.py # pm.test / expect assertion engine │ │ ├── mock_server.py # Local HTTP mock server │ │ ├── code_gen.py # Code generation (curl, Python, JS, etc.) │ │ ├── exporter.py # Postman Collection v2.1 export │ │ └── importer.py # Postman Collection / cURL import │ └── ui/ │ ├── main_window.py # Main window, splitter layout, env bar │ ├── theme.py # Central QSS stylesheet engine (dark/light) │ ├── request_panel.py # URL bar, params/headers/body/auth/tests editor │ ├── response_panel.py # Response viewer with status badge │ ├── tabs_manager.py # Multi-tab request manager │ ├── sidebar.py # Collections tree sidebar │ ├── ai_panel.py # AI Assistant dialog (collection generator) │ ├── ai_chat_panel.py # AI chat sidebar (co-pilot) │ ├── environment_dialog.py # Environment manager │ ├── collection_runner.py # Collection runner │ ├── websocket_panel.py # WebSocket client │ ├── mock_server_panel.py # Mock server UI │ ├── import_dialog.py # Import dialog │ ├── code_gen_dialog.py # Code generation dialog │ ├── search_dialog.py # Request search │ └── highlighter.py # JSON syntax highlighter ``` --- ## Configuration Settings are stored in an SQLite database at `~/.apiclient_agent/data.db` (created automatically). ### Anthropic API Key (for AI features) 1. Get a key at [console.anthropic.com](https://console.anthropic.com) 2. In the app: `Tools → AI Assistant → Settings tab` 3. Paste the key and click **Save API Key** The key is stored locally in the SQLite database only - never transmitted except to the Anthropic API. --- ## SSL / TLS Notes Some servers (especially demo/development instances) use self-signed certificates or wildcard certificates that don't match the exact hostname. If you see: ``` SSL certificate error - could not connect to https://... Tip: disable SSL verification in the request Settings tab. ``` Open the **Settings** tab in the request editor and uncheck **Verify SSL certificate**. --- ## Building a Standalone Executable ```bash pyinstaller --onefile --windowed \ --name "APIClient-Agent" \ --add-data "app:app" \ main.py ``` The executable is produced in `dist/APIClient-Agent`. --- ## Contributing 1. Fork the repository 2. Create a feature branch: `git checkout -b feature/my-feature` 3. Commit your changes: `git commit -m "Add my feature"` 4. Push and open a pull request Please keep UI styling in `theme.py` using `setObjectName()` selectors - never inline `setStyleSheet()` for static colors. --- ## License [MIT License](LICENSE) - Copyright (c) 2026 EKIKA.co