# Reverse Api

> Use when reverse-engineering a web app's internal API from network requests, building an unofficial API client, or capturing and analyzing HTTP traffic to create programmatic access to a website without an official API

- **Type:** Skill
- **Install:** `agentstack add skill-metterian-reverse-api-skill-reverse-api-skill`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [metterian](https://agentstack.voostack.com/s/metterian)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [metterian](https://github.com/metterian)
- **Source:** https://github.com/metterian/reverse-api-skill

## Install

```sh
agentstack add skill-metterian-reverse-api-skill-reverse-api-skill
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Reverse-Engineer Web App Internal API

## Overview

Analyze captured network requests from any web app and generate a Python httpx client with API reference documentation. Works with REST, GraphQL, Google batchexecute, gRPC-web, and other protocols.

**Core principle:** Capture real requests, understand the protocol, generate code that mimics the browser exactly.

## When to Use

- Building programmatic access to a site with no official API
- Reverse-engineering internal API endpoints from browser network traffic
- Creating an unofficial Python client for a web app
- Analyzing captured HTTP requests to understand API patterns

## Invocation

```
/reverse-api                          # Init: scaffold project + analyze first endpoint
/reverse-api add "list applications"  # Add: new method to existing client
```

## Mode Detection

```dot
digraph mode {
    "Client file exists?" [shape=diamond];
    "Chrome DevTools MCP available?" [shape=diamond];
    "Init Mode\n(scaffold + first endpoint)" [shape=box];
    "Add Mode\n(append method to client)" [shape=box];
    "Auto Capture\n(navigate + get_network_request)" [shape=box];
    "Paste Mode\n(user provides request data)" [shape=box];

    "Client file exists?" -> "Init Mode\n(scaffold + first endpoint)" [label="no"];
    "Client file exists?" -> "Add Mode\n(append method to client)" [label="yes"];
    "Init Mode\n(scaffold + first endpoint)" -> "Chrome DevTools MCP available?";
    "Add Mode\n(append method to client)" -> "Chrome DevTools MCP available?";
    "Chrome DevTools MCP available?" -> "Auto Capture\n(navigate + get_network_request)" [label="yes"];
    "Chrome DevTools MCP available?" -> "Paste Mode\n(user provides request data)" [label="no"];
}
```

**How to detect Chrome DevTools MCP:** Check if `navigate_page` and `get_network_request` tools are available in the current session.

## Init Mode

When no client file exists in the project, run initialization:

### Step 1: Gather site info

Ask the user:
1. **Target URL** — the web app URL (e.g., `https://app.greenhouse.io`)
2. **Site name** — used for naming (e.g., `greenhouse`)

### Step 2: Scaffold project structure

Create:
```
src/_client/
├── __init__.py       # Package with version
├── client.py         # BaseClient with auth + HTTP infra
├── constants.py      # Endpoint URLs, code mappings
└── types.py          # Response dataclasses
docs/
└── api_reference.md  # Discovered API documentation
```

**BaseClient template** — generate `client.py` with:
```python
import httpx

class BaseClient:
    """Client for  internal API."""

    BASE_URL = ""

    def __init__(self, cookies: dict[str, str], **auth_kwargs):
        self.cookies = cookies
        self._client: httpx.Client | None = None
        # Store additional auth params (csrf_token, api_key, etc.)
        self._auth = auth_kwargs

    def _get_client(self) -> httpx.Client:
        if self._client is None:
            self._client = httpx.Client(
                cookies={k: v for k, v in self.cookies.items()},
                headers={
                    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
                    "Origin": self.BASE_URL,
                    "Referer": f"{self.BASE_URL}/",
                },
                timeout=30.0,
            )
        return self._client

    def close(self):
        if self._client:
            self._client.close()
            self._client = None

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.close()
```

**api_reference.md template:**
```markdown
#  Internal API Reference

Auto-generated by reverse-api skill. Each endpoint documented as discovered.

---
```

### Step 3: Proceed to Request Capture

After scaffolding, immediately move to capturing the first endpoint.

## Request Capture

### Auto Capture (Chrome DevTools MCP)

```
1. navigate_page(url="")
2. Tell user: "Now perform the action you want to capture (e.g., click 'List Applications')"
3. Wait for user confirmation
4. get_network_request() — capture the relevant XHR/fetch request
5. Filter: ignore static resources (.js, .css, images, fonts)
6. Extract: URL, method, headers, body, response
```

### Paste Mode (Manual)

Tell the user:

> Open Chrome DevTools (F12) → Network tab → perform the action → right-click the API request → **Copy as cURL**.
>
> Or copy these individually:
> 1. **Request URL**
> 2. **Request Method**
> 3. **Request Headers** (especially Cookie, Content-Type, Authorization, X-CSRF-Token)
> 4. **Request Body** (if POST/PUT/PATCH)
> 5. **Response Body** (first 2000 chars is enough)

Parse the pasted data. If user pastes a cURL command, extract all fields from it.

## Protocol Analysis

After capturing a request, analyze it to determine the protocol and patterns.

### Protocol Detection

| Signal | Protocol |
|--------|----------|
| `f.req=` in body, URL contains `batchexecute` | Google batchexecute |
| `/graphql` in URL, or body has `query` + `variables` fields | GraphQL |
| REST verbs on resource paths (`GET /api/v1/users/123`) | REST |
| `content-type: application/grpc-web+proto` | gRPC-web |
| `content-type: application/x-www-form-urlencoded` with custom structure | Form-encoded RPC |
| WebSocket upgrade in headers | WebSocket |

### Auth Detection

| Signal | Auth Type |
|--------|-----------|
| `Cookie:` header with session tokens | Cookie-based |
| `Authorization: Bearer ` | Bearer token |
| `X-CSRF-Token` or `X-CSRFToken` header | CSRF token (usually with cookies) |
| `at=` parameter in body | Google-style CSRF |
| `X-API-Key` or `api_key` param | API key |
| No auth headers, public endpoint | None / Public |

### Response Format Detection

| Signal | Format |
|--------|--------|
| Starts with `)]}'\n` | XSSI-prefixed JSON (Google) |
| Valid JSON directly | Standard JSON |
| Chunked with byte counts between JSON arrays | Google batchexecute response |
| `data:` prefixed lines | Server-Sent Events (SSE) |
| Binary content | Protobuf / gRPC |

### What to Extract

For each captured request, document:
1. **Endpoint** — URL pattern (replace IDs with `{id}` placeholders)
2. **Method** — HTTP method
3. **Auth** — which auth mechanism
4. **Request params** — body structure with field names and types
5. **Response structure** — field names, types, nesting
6. **Pagination** — if applicable (cursor, offset, page)
7. **Rate limiting** — response headers indicating limits

## Code Generation

### For Each Endpoint, Generate:

**1. Method in `client.py`:**

```python
def list_applications(self, page: int = 1, per_page: int = 50) -> list[Application]:
    """List all applications.

    Discovered: 2026-04-02
    Endpoint: GET /api/v1/applications
    """
    client = self._get_client()
    response = client.get(
        f"{self.BASE_URL}/api/v1/applications",
        params={"page": page, "per_page": per_page},
    )
    response.raise_for_status()
    data = response.json()
    return [Application(**item) for item in data["results"]]
```

**2. Dataclass in `types.py`:**

```python
from dataclasses import dataclass

@dataclass
class Application:
    id: str
    candidate_name: str
    status: str
    # ... fields discovered from response
```

**3. Constants in `constants.py`** (if applicable):

```python
# Endpoint paths
APPLICATIONS_LIST = "/api/v1/applications"

# Status code mappings
STATUS_ACTIVE = 1
STATUS_ARCHIVED = 2
```

**4. Entry in `docs/api_reference.md`:**

```markdown
## list_applications
- **URL:** `GET /api/v1/applications`
- **Auth:** Cookie + CSRF
- **Parameters:**
  | Param | Type | Required | Description |
  |-------|------|----------|-------------|
  | page | int | no | Page number (default: 1) |
  | per_page | int | no | Results per page (default: 50) |
- **Response:**
  ```json
  {
    "results": [{"id": "...", "candidate_name": "...", "status": "..."}],
    "total": 150,
    "page": 1
  }
  ```
- **Discovered:** 2026-04-02
```

### Protocol-Specific Code Patterns

**Google batchexecute:**
```python
def _call_rpc(self, rpc_id: str, params: Any) -> Any:
    """Execute a batchexecute RPC call."""
    params_json = json.dumps(params, separators=(',', ':'))
    f_req = [[[rpc_id, params_json, None, "generic"]]]
    body = f"f.req={urllib.parse.quote(json.dumps(f_req))}"
    if self._auth.get("csrf_token"):
        body += f"&at={urllib.parse.quote(self._auth['csrf_token'])}"
    response = self._get_client().post(self.BATCHEXECUTE_URL, content=body)
    # Parse XSSI-prefixed response
    text = response.text
    if text.startswith(")]}'"):
        text = text[4:]
    return json.loads(text.strip().split('\n')[1])
```

**GraphQL:**
```python
def _query(self, query: str, variables: dict | None = None) -> Any:
    """Execute a GraphQL query."""
    payload = {"query": query}
    if variables:
        payload["variables"] = variables
    response = self._get_client().post(
        f"{self.BASE_URL}/graphql",
        json=payload,
    )
    response.raise_for_status()
    data = response.json()
    if "errors" in data:
        raise APIError(data["errors"])
    return data["data"]
```

**REST:** Use standard httpx methods directly (get, post, put, delete).

## Add Mode

When client file already exists:

1. **Read existing code** — understand current patterns, naming conventions, auth setup
2. **Capture new request** — same as Request Capture section
3. **Analyze** — same as Protocol Analysis section
4. **Generate method** — follow existing code style exactly:
   - Same naming convention (snake_case, same prefix patterns)
   - Same error handling pattern
   - Same return type style (dataclass, dict, etc.)
   - Add to existing client class, not a new one
5. **Update docs** — append to existing api_reference.md
6. **Update types/constants** — add new dataclasses and constants as needed

**Do NOT:**
- Change existing methods
- Refactor the client structure
- Change auth patterns
- Add new dependencies unless absolutely necessary

## Common Mistakes

| Mistake | Fix |
|---------|-----|
| Hardcoding session-specific IDs in client code | Use parameters — extract IDs from URLs into method arguments |
| Ignoring response cookie updates | Some sites rotate cookies per request — update cookie jar |
| Missing CSRF token refresh | CSRF tokens expire — add refresh logic if site uses them |
| Not URL-encoding body params | Use `urllib.parse.quote()` for form-encoded bodies |
| Assuming JSON response | Check Content-Type and response prefix (XSSI, SSE, etc.) |
| Copying all headers from capture | Only include required headers — User-Agent, Origin, Referer, Auth |
| Not handling pagination | Always check if response indicates more pages |

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [metterian](https://github.com/metterian)
- **Source:** [metterian/reverse-api-skill](https://github.com/metterian/reverse-api-skill)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-metterian-reverse-api-skill-reverse-api-skill
- Seller: https://agentstack.voostack.com/s/metterian
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
