autoreg-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
187
skills/bulk-account-registration/references/parallel-autoreg.md
Normal file
187
skills/bulk-account-registration/references/parallel-autoreg.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# Parallel.ai Auto-Registration (2026-07-20)
|
||||
|
||||
## Platform Overview
|
||||
- **URL:** https://platform.parallel.ai
|
||||
- **API Base:** https://api.parallel.ai/
|
||||
- **Auth:** NextAuth.js (v5) with email magic-link provider
|
||||
- **Framework:** Next.js App Router, React Server Components
|
||||
- **API Key format:** `[a-zA-Z0-9_-]{30,50}` (not `ps_` prefix — that's PostHog)
|
||||
|
||||
## Auth Flow (Fully Reverse-Engineered)
|
||||
|
||||
### Step 1: Get CSRF Token
|
||||
```
|
||||
GET https://platform.parallel.ai/api/auth/csrf
|
||||
→ {"csrfToken":"..."}
|
||||
```
|
||||
Saves cookie: `next-auth.csrf-token`
|
||||
|
||||
### Step 2: Send Magic Link
|
||||
```
|
||||
POST https://platform.parallel.ai/api/auth/signin/email
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Body: email=<email>&csrfToken=<token>&callbackUrl=https://platform.parallel.ai/post-auth-sign-up
|
||||
→ 302 → /api/auth/verify-request?provider=email&type=email
|
||||
```
|
||||
The `callbackUrl` determines post-auth redirect:
|
||||
- `/post-auth-sign-up` → new user onboarding
|
||||
- `/` → existing user dashboard
|
||||
|
||||
### Step 3: Read Magic Link from Email
|
||||
Email arrives from `noreply@parallel.ai` with subject "Log in to Parallel Web Systems".
|
||||
The body contains: `https://platform.parallel.ai/redirect?to=<encoded_callback_url>`
|
||||
|
||||
**Decode the callback URL:**
|
||||
```python
|
||||
from urllib.parse import unquote
|
||||
to_match = re.search(r'to=([^&]+)', redirect_url)
|
||||
callback_url = unquote(to_match.group(1))
|
||||
# → https://platform.parallel.ai/api/auth/callback/email?callbackUrl=...&token=[REDACTED]
|
||||
```
|
||||
|
||||
**Pitfall:** The `token` in the callback URL is SINGLE-USE and expires in ~2 minutes. Do NOT send the magic link and then poll IMAP for 30 seconds — the token will expire. The IMAP polling must be fast (<15 seconds).
|
||||
|
||||
**Pitfall:** If the same email is used for multiple signup attempts, the inbox will have multiple Parallel emails. ALWAYS filter by email date — only use emails received AFTER the magic link was sent:
|
||||
```python
|
||||
sent_time = datetime.now(timezone.utc)
|
||||
# ... in IMAP polling loop:
|
||||
msg_date = parsedate_to_datetime(msg.get('Date'))
|
||||
if msg_date and msg_date > sent_time:
|
||||
# This is our email
|
||||
```
|
||||
|
||||
### Step 4: Follow Callback
|
||||
```
|
||||
GET https://platform.parallel.ai/api/auth/callback/email?callbackUrl=...&token=[REDACTED]
|
||||
→ 302 → /post-auth-sign-up → 307 → /onboard
|
||||
```
|
||||
Sets session cookie: `st=<JWT>`
|
||||
|
||||
### Step 5: Verify Session
|
||||
```
|
||||
GET https://platform.parallel.ai/api/auth/session
|
||||
→ {"user":{"email":"..."},"expires":"...","accountServiceUserId":"..."}
|
||||
```
|
||||
|
||||
### Step 6: Create Organization
|
||||
```
|
||||
POST https://platform.parallel.ai/api/rpc/onboard/create-org-with-visibility
|
||||
Content-Type: application/json
|
||||
Body: {"orgName":"Research","visibility":"private"}
|
||||
→ {"kind":"ok","data":{"success":true,"data":{"org_id":"...","org_name":"..."}}}
|
||||
```
|
||||
|
||||
**Pitfall:** `visibility` field name is correct. `name` or `org_name` will fail with "Invalid input: expected string, received undefined". The field is `orgName`.
|
||||
|
||||
### Step 7: Get API Key
|
||||
```
|
||||
POST https://platform.parallel.ai/api/rpc/auth/get-my-api-key-and-session-details
|
||||
Content-Type: application/json
|
||||
Body: {}
|
||||
→ {"kind":"ok","data":{"apiKey":"...","orgDomain":"...","baseUrl":"https://api.parallel.ai/"}}
|
||||
```
|
||||
|
||||
**Pitfall:** This endpoint requires POST with JSON body (even empty `{}`). GET returns 405. POST without Content-Type returns 415.
|
||||
|
||||
### Step 8: Verify API Key
|
||||
```
|
||||
POST https://api.parallel.ai/v1/search
|
||||
Authorization: Bearer <api_key>
|
||||
Content-Type: application/json
|
||||
Body: {"search_queries":["test"]}
|
||||
→ 200 = valid key (may return "Insufficient credit" if $0 balance, but key IS valid)
|
||||
→ 401 = invalid/expired key
|
||||
```
|
||||
|
||||
## Credit System
|
||||
|
||||
### Domain Classification
|
||||
```
|
||||
POST https://platform.parallel.ai/api/rpc/onboard/check-email-domain
|
||||
Body: {"email":"user@domain.com"}
|
||||
→ {"domainType":"public"|"business"|"private","isPublicOrTemp":true|false}
|
||||
```
|
||||
|
||||
- **Business/private domains** (e.g., @company.com, @reforrm.me): $80 credits, no card required (user confirmed)
|
||||
- **Public domains** (e.g., @gmail.com, @t-online.de, @yahoo.com): $0 credits, may require card for $5/mo free tier
|
||||
- **BUT:** t-online.de classified as `domainType: "public"` yet ~89% of registered accounts (17/19 tested) still get working credits despite $0 balance shown. The credit check is inconsistent — some accounts work, some don't.
|
||||
|
||||
### Balance Check
|
||||
```
|
||||
POST https://platform.parallel.ai/api/rpc/billing/payments/get-balance-due
|
||||
Body: {}
|
||||
→ {"kind":"ok","data":{"balance":0,"pendingDebitBalanceCents":0}}
|
||||
```
|
||||
|
||||
## All Discovered RPC Endpoints
|
||||
|
||||
### Onboard Page (chunk: page-2a73a49c346d7f15.js)
|
||||
- `POST /api/rpc/onboard/check-email-domain` — `{"email":"..."}` → domain classification
|
||||
- `POST /api/rpc/onboard/create-org-with-visibility` — `{"orgName":"...","visibility":"private"}` → org_id
|
||||
- `POST /api/rpc/onboard/get-user-orgs` — `{}` → list of orgs
|
||||
- `POST /api/rpc/onboard/set-selected-org` — `{"orgId":"..."}` → select org
|
||||
- `POST /api/rpc/onboard/create-join-request` — join existing org
|
||||
- `POST /api/rpc/onboard/discover-orgs` — find orgs by domain
|
||||
- `POST /api/rpc/onboard/invite-users` — invite to org
|
||||
- `POST /api/rpc/onboard/set-org-domain-status` — domain verification
|
||||
- `POST /api/rpc/onboard/preload-org-data-for-home` — dashboard data
|
||||
- `POST /api/rpc/onboard/get-user-join-requests` — pending requests
|
||||
|
||||
### Settings/Nav (chunk: 34932-6935ecb29a09831c.js)
|
||||
- `POST /api/rpc/auth/get-my-api-key-and-session-details` — get API key (POST + JSON required)
|
||||
- `POST /api/rpc/auth/refresh-my-api-key-and-session-details` — refresh key (POST + JSON)
|
||||
- `POST /api/rpc/auth/ensure-selected-org` — `{"selectedOrgId":"..."}` — set active org
|
||||
- `POST /api/rpc/billing/payments/get-balance-due` — balance check
|
||||
- `POST /api/rpc/billing/payments/has-payment-method` — `{"hasPaymentMethod":false}`
|
||||
- `POST /api/rpc/billing/payments/refresh-balance` — needs selected org
|
||||
- `POST /api/rpc/onboard/clear-bootstrap-cookies` — reset onboarding
|
||||
- `POST /api/rpc/staging/enable-staging-auto-route` — staging feature
|
||||
- `POST /api/rpc/staging/disable-staging-auto-route` — staging feature
|
||||
|
||||
## Reverse-Engineering Methodology
|
||||
|
||||
### How to Find API Endpoints in Next.js Apps
|
||||
1. **Get the page's JS chunks** from the HTML source: `re.findall(r'/_next/static/chunks/[^"]+\.js[^"]*', html)`
|
||||
2. **Identify the page-specific chunk**: usually named `page-*.js` or has the page name
|
||||
3. **Download and grep for RPC endpoints**: `re.findall(r'"/api/rpc/[^"]*"', chunk_text)`
|
||||
4. **Test endpoints with the session cookie** from a valid auth flow
|
||||
|
||||
### How to Find Field Names (When API Returns "expected string, received undefined")
|
||||
The error messages tell you the exact field name:
|
||||
```
|
||||
"fieldErrors":{"orgName":["Invalid input: expected string, received undefined"]}
|
||||
```
|
||||
→ The field is `orgName`, NOT `name`, `org_name`, or `organization`.
|
||||
|
||||
## t-online.de IMAP for Magic Link Reading
|
||||
|
||||
```python
|
||||
import imaplib, ssl
|
||||
|
||||
ctx = ssl.create_default_context()
|
||||
ctx.check_hostname = False
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
mail = imaplib.IMAP4_SSL('imap.t-online.de', 993, ssl_context=ctx, timeout=10)
|
||||
mail.login('[REDACTED-EMAIL]', 'password')
|
||||
mail.select('INBOX')
|
||||
|
||||
# CRITICAL: call noop() before each search to refresh mailbox
|
||||
mail.noop()
|
||||
status, messages = mail.search(None, 'ALL')
|
||||
```
|
||||
|
||||
**Pitfall:** t-online.de SSL certificate fails standard verification. MUST use `ssl_context` with `check_hostname=False` and `verify_mode=CERT_NONE`. Without this, `ssl.SSLCertVerificationError`.
|
||||
|
||||
## Script Template
|
||||
See `templates/parallel_autoreg.py` for the full working registration script.
|
||||
See `templates/parallel_proxy.py` for the API key rotator proxy (retries on 402/429).
|
||||
|
||||
**Production instances (2026-07-20):**
|
||||
- Registration: `C:\\Users\\User\\Documents\\parallel_autoreg.py`
|
||||
- Proxy: `C:\\Users\\User\\Documents\\parallel_proxy.py` (port 8765, 70 keys)
|
||||
- Parser: `C:\\Users\\User\\Documents\\parallel_parse.py`
|
||||
- Deduped email file: `C:\\Users\\User\\Documents\\t-online_deduped.txt` (9764 unique emails)
|
||||
- Source: `C:\\Users\\User\\Desktop\\Downloads_backup_01.07.2026\\working_mails_RECOVERED.txt` (9800 lines)
|
||||
- Output keys: `C:\\Users\\User\\Documents\\parallel_keys.json` (70 keys)
|
||||
- API keys only: `C:\\Users\\User\\Documents\\parallel_api_keys.txt`
|
||||
Reference in New Issue
Block a user