<!--
hoody-curl Subskill (http)
Auto-generated by Hoody Skills Generator
Generated: 2026-06-20T00:38:45.034Z
Model: mimo-v2.5-pro
Mode: http


Tokens: 8993

DO NOT EDIT MANUALLY - Changes will be overwritten on next generation
-->

# hoody-curl Subskill

## Overview

**hoody-curl** is a universal HTTP request proxy service that "GET-ifies" any REST API for universal access. It transforms complex API calls into simple, accessible URLs through a hoody-curl instance.

### What This Service Does

hoody-curl provides:
- **HTTP Request Proxying**: Execute arbitrary HTTP requests (GET, POST, PUT, DELETE, etc.) through a simple REST API
- **Async Job Execution**: Submit long-running requests as background jobs with status tracking
- **Scheduled Requests**: Create cron-based recurring HTTP requests
- **Session Management**: Maintain cookie-based sessions across multiple requests for stateful interactions
- **File Storage**: Save HTTP response bodies to persistent storage organized by job, domain, or date
- **Real-time Monitoring**: Stream job lifecycle events via WebSocket or Server-Sent Events

### When to Use hoody-curl

| Scenario | Use Case |
|----------|----------|
| API Integration | Proxy requests to third-party APIs that require complex headers, auth, or payloads |
| Scheduled Tasks | Execute recurring HTTP requests (health checks, data syncs, cleanup jobs) |
| Stateful Scraping | Maintain login sessions across multiple page requests with cookie preservation |
| File Downloads | Download and persist files from HTTP endpoints to hoody-curl storage |
| Long-running Requests | Execute async requests that may take minutes or hours to complete |
| Metrics Export | Monitor hoody-curl instance health and performance via Prometheus metrics |

### How It Fits Into Hoody Philosophy

hoody-curl embodies the Hoody principle of **universal access through simplification**:

- **GET-ify Everything**: Convert any REST API interaction into a simple GET/POST to your hoody-curl instance
- **Zero Configuration**: Access through automatic Hoody Proxy routing—no DNS setup required
- **Service Isolation**: Each container gets its own hoody-curl instance with isolated state
- **Transparent Proxying**: Requests are forwarded with original headers, cookies, and payloads preserved

### Base URL Construction

All hoody-curl endpoints use this base URL pattern:

```
https://{projectId}-{containerId}-curl-{serviceId}.{node}.containers.hoody.com
```

For documentation purposes, `${BASE_URL}` represents this full URL. Replace with your actual instance URL.

---

## Common Workflows

### Workflow 1: Execute a Simple GET Request

Use the simplified GET interface for quick requests to public APIs.

**Step 1: Execute the request**

```
curl -s "${BASE_URL}/api/v1/curl/request?url=https://httpbin.org/get"
```

**Step 2: Verify the response**

A successful response returns the proxied HTTP response body:

```
{
  "args": {},
  "headers": {
    "Host": "httpbin.org"
  },
  "origin": "1.2.3.4",
  "url": "https://httpbin.org/get"
}
```

**Notes:**
- The `url` query parameter is required
- Best suited for simple GET requests and quick testing
- For advanced features, use the POST endpoint

---

### Workflow 2: Execute a Full HTTP Request (POST)

Use the POST interface for requests with headers, body, or non-GET methods.

**Step 1: Execute a POST request with JSON body**

```
curl -s -X POST "${BASE_URL}/api/v1/curl/request" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://httpbin.org/post",
  "method": "POST",
  "body": "{\"key\": \"value\"}",
  "headers": {
    "X-Custom-Header": "test-value"
  }
}'
```

**Step 2: Verify the proxied request executed correctly**

```
{
  "args": {},
  "data": "{\"key\": \"value\"}",
  "files": {},
  "form": {},
  "headers": {
    "Content-Type": "application/json",
    "X-Custom-Header": "test-value"
  },
  "json": {
    "key": "value"
  },
  "origin": "1.2.3.4",
  "url": "https://httpbin.org/post"
}
```

**Notes:**
- `url` is the only required field
- Supports all HTTP methods: GET, POST, PUT, DELETE, PATCH, etc.
- Set `mode: "async"` to submit as a background job instead of synchronous execution

---

### Workflow 3: Async Job Execution

For requests that may take a long time, submit as an async job and poll for results.

**Step 1: Submit an async job**

```
curl -s -X POST "${BASE_URL}/api/v1/curl/request" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://httpbin.org/delay/10",
  "mode": "async"
}'
```

Response returns the job ID:

```
{
  "job_id": "job_abc123def456",
  "status": "pending"
}
```

**Step 2: Check job status**

```
curl -s "${BASE_URL}/api/v1/curl/jobs/job_abc123def456"
```

Response includes current state and progress:

```
{
  "id": "job_abc123def456",
  "name": "async-request",
  "status": "running",
  "progress": 0.5,
  "created_at": "2025-01-15T10:30:00Z"
}
```

**Step 3: Retrieve the completed result**

```
curl -s "${BASE_URL}/api/v1/curl/jobs/job_abc123def456/result"
```

**Job Lifecycle States:**
- `pending` → `running` → `completed` (success)
- `pending` → `running` → `failed` (error)
- `pending` → `cancelled` (manual cancel)
- `running` → `cancelled` (manual cancel)

---

### Workflow 4: List and Cancel Jobs

**Step 1: List all jobs**

```
curl -s "${BASE_URL}/api/v1/curl/jobs"
```

Response returns jobs sorted by creation time (newest first):

```
{
  "jobs": [
    {
      "id": "job_abc123",
      "status": "running",
      "created_at": "2025-01-15T11:00:00Z"
    },
    {
      "id": "job_def456",
      "status": "completed",
      "created_at": "2025-01-15T10:00:00Z"
    }
  ]
}
```

**Step 2: Cancel a pending or running job**

```
curl -s -X DELETE "${BASE_URL}/api/v1/curl/jobs/job_abc123"
```

**Notes:**
- Only `pending` or `running` jobs can be cancelled
- Once cancelled, a job cannot be restarted

---

### Workflow 5: Create a Scheduled Request

Set up recurring HTTP requests using cron expressions.

**Step 1: Create a schedule**

```
curl -s -X POST "${BASE_URL}/api/v1/curl/schedule" \
  -H "Content-Type: application/json" \
  -d '{
  "cron": "0 */5 * * * *",
  "request": {
    "url": "https://httpbin.org/get"
  }
}'
```

Response returns the schedule ID:

```
{
  "id": "sched_xyz789",
  "cron": "0 */5 * * * *",
  "enabled": true,
  "next_run": "2025-01-15T11:05:00Z"
}
```

**Step 2: Verify the schedule was created**

```
curl -s "${BASE_URL}/api/v1/curl/schedule/sched_xyz789"
```

**Step 3: Toggle schedule on/off without deleting**

```
curl -s -X PATCH "${BASE_URL}/api/v1/curl/schedule/sched_xyz789/toggle" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": false
}'
```

**Step 4: Delete a schedule permanently**

```
curl -s -X DELETE "${BASE_URL}/api/v1/curl/schedule/sched_xyz789"
```

**Cron Expression Format (6 fields):**

```
second minute hour day month weekday
```

Example: `0 */5 * * * *` = every 5 minutes at second 0

---

### Workflow 6: Cookie-Based Sessions

Maintain stateful HTTP sessions for authentication flows or multi-step interactions.

**Step 1: List existing sessions**

```
curl -s "${BASE_URL}/api/v1/curl/sessions"
```

**Step 2: Execute requests within a session context**

When executing requests via POST `/api/v1/curl/request`, include session configuration to preserve cookies across calls. Cookies are automatically stored and replayed within the session.

**Step 3: Retrieve session details and cookies**

```
curl -s "${BASE_URL}/api/v1/curl/sessions/sess_abc123/cookies"
```

Response returns cookie snapshot:

```
{
  "session_id": "sess_abc123",
  "cookies": {
    "auth_token": "eyJhbGciOiJIUzI1NiJ9",
    "csrf_token": "abc123def456"
  }
}
```

**Step 4: Clean up a session when done**

```
curl -s -X DELETE "${BASE_URL}/api/v1/curl/sessions/sess_abc123"
```

**Notes:**
- Sessions preserve cookies across multiple requests
- Essential for authentication flows and stateful scraping
- Session deletion is permanent and cannot be undone

---

### Workflow 7: File Storage

Save HTTP response bodies to persistent storage and retrieve them later.

**Step 1: List stored files**

```
curl -s "${BASE_URL}/api/v1/curl/storage"
```

Files are organized in three directory structures:
- `by-job/` — organized by the job that downloaded them
- `by-domain/` — organized by the target domain
- `by-date/` — organized by download date

All three paths point to the same physical files.

**Step 2: Download a stored file**

```
curl -s "${BASE_URL}/api/v1/curl/storage/by-domain/httpbin.org/response.json" \
  -o downloaded-file.json
```

**Step 3: Delete a stored file**

```
curl -s -X DELETE "${BASE_URL}/api/v1/curl/storage/by-domain/httpbin.org/response.json"
```

**Notes:**
- Files are returned with `Content-Type: application/octet-stream`
- Storage deletion is permanent

---

## Advanced Operations

### Real-time Event Monitoring

hoody-curl provides two mechanisms for receiving real-time job lifecycle events.

#### Option A: Server-Sent Events (SSE)

```
curl -s "${BASE_URL}/api/v1/curl/sse"
```

Each event is delivered as a standard SSE frame:

```
event: jobstarted
data: {"job_id": "job_abc123", "name": "my-request"}

event: jobprogress
data: {"job_id": "job_abc123", "progress": 0.5}

event: jobcompleted
data: {"job_id": "job_abc123", "status": "completed"}
```

#### Option B: WebSocket

```
curl -s "${BASE_URL}/api/v1/curl/ws"
```

Messages are delivered as JSON:

```
{"event": "jobstarted", "job_id": "job_abc123", "name": "my-request"}
```

#### Option C: Multiplexed Channel

For executing validated CurlRequests through a persistent connection:

```
curl -s "${BASE_URL}/api/v1/curl/channel"
```

This is distinct from `/api/v1/curl/ws` — the channel supports multiplexed request execution, not just event streaming.

---

### Multi-Step Workflow: Authenticated API Scraping

Combine sessions, requests, and storage for complex scraping tasks.

**Step 1: Create a session for authentication**

Execute a login request via POST `/api/v1/curl/request` with session configuration. The response cookies are preserved automatically.

**Step 2: Execute authenticated requests within the same session**

```
curl -s -X POST "${BASE_URL}/api/v1/curl/request" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://api.example.com/user/profile",
  "headers": {
    "Accept": "application/json"
  }
}'
```

**Step 3: Verify session cookies are maintained**

```
curl -s "${BASE_URL}/api/v1/curl/sessions/sess_abc123/cookies"
```

**Step 4: Clean up when complete**

```
curl -s -X DELETE "${BASE_URL}/api/v1/curl/sessions/sess_abc123"
```

---

### Scheduled Monitoring Pattern

Combine schedules with health checks for automated monitoring.

**Step 1: Create a health check schedule**

```
curl -s -X POST "${BASE_URL}/api/v1/curl/schedule" \
  -H "Content-Type: application/json" \
  -d '{
  "cron": "0 */10 * * * *",
  "request": {
    "url": "https://status.example.com/health"
  }
}'
```

**Step 2: Monitor schedule execution via SSE**

```
curl -s "${BASE_URL}/api/v1/curl/sse"
```

**Step 3: Check schedule execution history**

```
curl -s "${BASE_URL}/api/v1/curl/schedule/sched_xyz789"
```

---

### Error Recovery Patterns

**Scenario: Job stuck in running state**

1. Check job status: `GET /api/v1/curl/jobs/{id}`
2. If unresponsive, attempt cancellation: `DELETE /api/v1/curl/jobs/{id}`
3. Resubmit the request as a new job

**Scenario: Schedule not executing**

1. Verify schedule is enabled: `GET /api/v1/curl/schedule/{id}`
2. If disabled, re-enable: `PATCH /api/v1/curl/schedule/{id}/toggle`
3. Check next_run time for expected execution window

**Scenario: Session authentication expired**

1. Retrieve current cookies: `GET /api/v1/curl/sessions/{id}/cookies`
2. Execute a fresh login request in a new session
3. Delete the stale session: `DELETE /api/v1/curl/sessions/{id}`

---

### Health and Monitoring

**Check service health (unauthenticated)**

```
curl -s "${BASE_URL}/api/v1/curl/health"
```

Returns a standardized 9-field health response.

**Export Prometheus metrics**

```
curl -s "${BASE_URL}/metrics"
```

Exports a minimal Prometheus metrics set suitable for dashboards and alerting.

---

### Performance Considerations

| Factor | Recommendation |
|--------|----------------|
| Request timeout | Use `` (10 min) for standard requests; increase for large downloads |
| Async mode | Use `mode: "async"` for requests expected to exceed 30 seconds |
| Concurrent jobs | Monitor active job count via `GET /api/v1/curl/jobs` to avoid resource exhaustion |
| Session cleanup | Delete sessions when no longer needed to free cookie storage |
| File storage | Periodically audit `GET /api/v1/curl/storage` and delete unneeded files |
| Schedule frequency | Avoid sub-minute schedules unless necessary; each execution creates a job |

---

## Quick Reference

### Most Common Endpoints

| Method | Path | Purpose |
|--------|------|---------|
| `GET` | `/api/v1/curl/request?url=...` | Execute simple GET request |
| `POST` | `/api/v1/curl/request` | Execute full HTTP request |
| `GET` | `/api/v1/curl/jobs` | List all async jobs |
| `GET` | `/api/v1/curl/jobs/{id}` | Get job details and status |
| `GET` | `/api/v1/curl/jobs/{id}/result` | Get raw job response body |
| `DELETE` | `/api/v1/curl/jobs/{id}` | Cancel a job |
| `POST` | `/api/v1/curl/schedule` | Create recurring schedule |
| `GET` | `/api/v1/curl/schedule` | List all schedules |
| `PATCH` | `/api/v1/curl/schedule/{id}/toggle` | Enable/disable schedule |
| `GET` | `/api/v1/curl/health` | Check service health |

### Essential Parameters

**POST /api/v1/curl/request (required):**
- `url` (string) — Target URL to request

**POST /api/v1/curl/request (optional):**
- `method` (string) — HTTP method (GET, POST, PUT, DELETE, PATCH)
- `headers` (object) — Request headers
- `body` (string) — Request body
- `mode` (string) — `"sync"` (default) or `"async"`
- `session_id` (string) — Cookie session to use

**POST /api/v1/curl/schedule (required):**
- `cron` (string) — 6-field cron expression
- `request` (object) — cURL request parameters
- `request.url` (string) — Target URL

**GET /api/v1/curl/request (required):**
- `url` (query parameter) — Target URL to request

### Typical Response Formats

**Job object:**
```
{
  "id": "job_abc123",
  "name": "request-name",
  "status": "completed",
  "progress": 1.0,
  "created_at": "2025-01-15T10:00:00Z"
}
```

**Schedule object:**
```
{
  "id": "sched_xyz789",
  "cron": "0 */5 * * * *",
  "enabled": true,
  "next_run": "2025-01-15T11:05:00Z"
}
```

**Session list:**
```
{
  "sessions": [
    {
      "id": "sess_abc123",
      "last_used": "2025-01-15T10:30:00Z"
    }
  ]
}
```

### All Endpoints (23 total)

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/v1/curl/health` | Health check (unauthenticated) |
| `GET` | `/api/v1/curl/request` | Simple GET-based request proxy |
| `POST` | `/api/v1/curl/request` | Full HTTP request proxy |
| `GET` | `/api/v1/curl/jobs` | List all async jobs |
| `GET` | `/api/v1/curl/jobs/{id}` | Get job details |
| `DELETE` | `/api/v1/curl/jobs/{id}` | Cancel job |
| `GET` | `/api/v1/curl/jobs/{id}/result` | Get raw job result |
| `GET` | `/api/v1/curl/schedule` | List all schedules |
| `POST` | `/api/v1/curl/schedule` | Create schedule |
| `GET` | `/api/v1/curl/schedule/{id}` | Get schedule details |
| `DELETE` | `/api/v1/curl/schedule/{id}` | Delete schedule |
| `PATCH` | `/api/v1/curl/schedule/{id}/toggle` | Toggle schedule enabled/disabled |
| `GET` | `/api/v1/curl/sessions` | List all sessions |
| `GET` | `/api/v1/curl/sessions/{id}` | Get session details |
| `DELETE` | `/api/v1/curl/sessions/{id}` | Delete session |
| `GET` | `/api/v1/curl/sessions/{id}/cookies` | Get session cookies |
| `GET` | `/api/v1/curl/storage` | List stored files |
| `GET` | `/api/v1/curl/storage/{path}` | Download stored file |
| `DELETE` | `/api/v1/curl/storage/{path}` | Delete stored file |
| `GET` | `/api/v1/curl/sse` | SSE event stream |
| `GET` | `/api/v1/curl/ws` | WebSocket event stream |
| `GET` | `/api/v1/curl/channel` | Multiplexed request channel |
| `GET` | `/metrics` | Prometheus metrics |