# Kamatera Free Trial — Registration & Billing API ## Overview Kamatera offers a 30-day free trial ($100 cloud credit) with payment card required. The registration and billing flow is entirely API-driven through a WordPress-based backend. ## Registration ### Endpoint (WordPress AJAX) ``` POST https://www.kamatera.com/wp-admin/admin-ajax.php Content-Type: application/x-www-form-urlencoded ``` ### jQuery Serialization Format The form uses jQuery's `$.ajax` with nested objects. Key format: `data_options[field]=value` ``` action=cwm_api_action data_options[cases]=cwml data_options[email]=...@domain.com data_options[password]=... data_options[orgUrl]=https://www.kamatera.com/free-trial/ data_options[formButtonText]=Create free account data_options[submitText]=Create free account data_options[headerText]= ``` ### Email Validation - Client-side regex: `/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,})+$/` - **`+` aliases NOT allowed** (Gmail `+suffix` fails `\w` check) - Use plain emails or disposable email domains ### Password Requirements - At least 1 lowercase - At least 1 uppercase - At least 1 number - At least 8 characters - Less than 28 characters ### Response ```json { "status": 0, // 0 = success, 13 = blocked/captcha fail "token": "...", "existing": false, // true if account already exists "url": "https://console.kamatera.com/al.php?t=...", "validationToken": "...", "impactid": "kt-..." } ``` Status 13 with `url: null` = blocked (likely reCAPTCHA v3 score too low, or IP flagged). ## Email Verification Verification email comes from `support@kamatera.com` with subject "Confirm your Kamatera cloud account". Contains a link: ``` https://console.kamatera.com/rd.php?t=...&e=...&p=emailValidation&h=... ``` Click the link → redirects to `validateSuccessful?email=...` → account verified. ## Disposable Email **mail.tm** works reliably: ```python # 1. Get domain r = requests.get("https://api.mail.tm/domains") domain = r.json()["hydra:member"][0]["domain"] # 2. Create account r = requests.post("https://api.mail.tm/accounts", json={ "address": f"{username}@{domain}", "password": "password" }) # 3. Get token r = requests.post("https://api.mail.tm/token", json={ "address": email, "password": "password" }) token = [REDACTED])["token"] # 4. Read messages r = requests.get("https://api.mail.tm/messages", headers={"Authorization": f"Bearer {token}"}) ``` **GuerrillaMail** works but Kamatera emails may not arrive to `.guerrillamailblock.com` — use mail.tm instead. ## Console API ### Base URL & Auth ``` Base: https://console.kamatera.com/svc/ Headers: Content-Type: application/json Accept: application/json api-version: latest token: ``` ### CSRF Token Get from `/csrf` endpoint: ```python r = requests.get("https://console.kamatera.com/csrf") csrf = re.search(r'CSRF_TOKEN",\s*"([^"]+)"', r.text).group(1) ``` The `/csrf` page returns HTML that sets `sessionStorage.setItem("CSRF_TOKEN", "...")`. The token is embedded in the HTML as a script tag. ### Login Flow (with 2FA) ``` 1. POST /svc/login {"email", "password"} → {"email": "...", "mediateToken": "..."} 2. POST /svc/sendtfa {"email", "mediateToken"} (header: token=) → triggers email with 6-digit code 3. Check mail.tm for code in subject "Kamatera Cloud Platform: 2-Step Verification" 4. POST /svc/logintfa {"email", "code", "rememberBrowser": true, "mediateToken"} → 200 = logged in, session cookie set → 400 + code 51 = "Invalid code" (code expired or wrong) ``` ### TFA Code Pitfalls - Codes expire quickly (~2-5 minutes) - `sendtfa` may return 200 but NOT send a new email if one was already sent recently - Delete old emails from mail.tm before calling `sendtfa` to avoid picking up stale codes - The 2FA email body contains the code in format: `Your access code ... is: XXXXXX` - Filter out false positives: `1999` and `2026` are in the email footer, not the code ### Billing Profile API ``` PUT /svc/billing/profile (header: token=) ``` ### Billing Profile Fields (Discovered via Progressive 400 Errors) The API validates fields in sequence. Each fix reveals the next missing field: | Error Code | Message | Required Fix | |-----------|---------|-------------| | 122 | invalid invoice name | Add `invoiceName` | | 105 | invalid first name | Add `firstName` | | 107 | invalid country | Fix `country` format | | 110 | invalid city | Add `city` | | 112 | invalid zip code | Use `postalCode` (NOT `zipCode` or `zip`) | | 109 | invalid phone | Add `phone` | | 21 | generic error | Card rejected by payment processor | ### Working Billing Payload (Passes All Validation) ```json { "firstName": "Alex", "lastName": "Morgan", "country": "US", "city": "New York", "address": "123 Broadway", "state": "NY", "postalCode": "10001", "phone": "2125550100", "invoiceName": "Alex Morgan", "paymentType": 0, "cardNumber": "4874100085748500", "cardExpiry": "05/2030", "cardCvv": "617" } ``` ### Error Code 21 (Card Rejected) After all field validation passes, error 21 means the payment processor rejected the card. This is NOT a field format issue. Possible causes: - Card blocked/stolen - Insufficient funds for $1 authorization hold - 3D Secure required but not supported - Region mismatch (card issuing country ≠ billing country) - Card expired (check BIN + expiry date format) ### Card Expiry Formats Tried - `cardExpiry: "05/2030"` — works (passes to processor) - `cardExpiry: "05/30"` — works (passes to processor) - Separate `expMonth`/`expYear` fields — also pass validation All formats pass field validation — the rejection is at the payment processor level. ## Browser-Based API Calls When the React SPA is unresponsive (clicks don't navigate), use `fetch()` from browser console with the session's CSRF token: [REDACTED] fetch('/svc/billing/profile', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'api-version': 'latest', 'token': sessionStorage.getItem('CSRF_TOKEN') }, body: JSON.stringify({...}) }).then(r => r.json()).then(d => console.log(JSON.stringify(d))) ``` This bypasses the broken React navigation and uses the browser's authenticated session directly. ## Key Pitfalls 1. **Email `+` aliases fail client-side validation** — use plain email or disposable domain 2. **`sendtfa` doesn't always send** — delete old emails before calling, codes may have cooldown 3. **React SPA navigation broken in headless** — use direct `fetch()` from console 4. **Field names are specific**: `postalCode` not `zipCode`, `invoiceName` required, `firstName`/`lastName` not `name` 5. **Country format**: `"US"` (2-letter ISO) works, `"USA"` fails 6. **Card validation is sequential** — fix errors one at a time, each reveals the next