<!--
hoody-curl Subskill (sdk)
Auto-generated by Hoody Skills Generator
Generated: 2026-06-20T00:07:18.843Z
Model: mimo-v2.5-pro + fixer:mimo-v2.5-pro
Mode: sdk


Tokens: 15290

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

```
# hoody-curl Subskill

## Overview

hoody-curl is Hoody's universal HTTP proxy service. It **GET-ifies any REST API** by converting complex HTTP calls into simple, accessible URLs through a managed proxy layer. This enables AI agents and automation workflows to interact with external APIs without managing authentication, cookies, sessions, or complex request configurations directly.

### Capabilities & When to Use

- **Request Proxying**: Execute any HTTP method (GET, POST, PUT, DELETE, PATCH) through managed endpoints — proxy API calls through simple GET URLs for universal accessibility
- **Async Execution**: Submit long-running requests as background jobs — use for long-running requests that would block your workflow
- **Scheduled Requests**: Create cron-based recurring HTTP requests that survive server restarts — ideal for periodic data collection or API polling
- **Cookie Sessions**: Maintain stateful interactions across multiple requests — essential for APIs requiring complex authentication or cookie management
- **File Storage**: Save HTTP response bodies to organized storage directories — download and store files from external sources
- **Real-time Events**: Monitor job lifecycle via WebSocket or SSE connections
- **Health & Metrics**: Prometheus-compatible metrics and standardized health checks

### Service Architecture

Requests flow through a managed proxy layer:

```
Agent → hoody-curl Proxy → External API
```

The service handles connection management, cookie persistence, redirect following, and response storage transparently.

### Base URL Pattern

All hoody-curl endpoints use:

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

All API endpoints are prefixed with `/api/v1/curl/` except `/metrics` which is at the root path. Use the SDK client to abstract away URL construction.

---

## Common Workflows

### Workflow 1: Simple GET Request

**⚠️ Prefer `execute({...})` (Workflow 2) for new code.** Execute a quick HTTP request using query parameters. Best for simple GET requests and testing.

```
import { HoodyClient } from '@hoody-ai/hoody-sdk'

const client = new HoodyClient({
  baseURL: 'https://{projectId}-{containerId}-curl-{serviceId}.{node}.containers.hoody.com',
  token: 'YOUR_TOKEN'
})

const response = await client.curl.executeCurlRequestGet(
  'https://api.example.com/data'
)
```

Additional positional parameters (in order): `method`, `response`, `mode`, `session_id`, `follow_redirects`, `timeout`, `user_agent`, `referer`, `bearer_token`, `save`, `save_path`, `insecure`, `compressed`, `job_name`, `data`, `json`, `header`, `data_base64`.

### Workflow 2: Full-Featured Request (POST)

For comprehensive control, use the POST-based `execute` method with a request object.

```
const response = await client.curl.execute({
  url: 'https://api.example.com/data',
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer sk-xxxx'
  },
  json: {
    query: 'search term',
    limit: 10
  },
  follow_redirects: true,
  timeout: 30,
  mode: 'sync'
})
```

Common `mode` values:
- `sync` (default): Return response immediately
- `async`: Submit as background job, return job ID

### Workflow 3: Async Job Lifecycle

Submit a long-running request as a background job and manage its lifecycle.

**Step 1 — Submit async request:**

```
const submission = await client.curl.execute({
  url: 'https://api.example.com/large-export',
  method: 'GET',
  mode: 'async',
  job_name: 'daily-export',
  timeout: 300
})
```

**Step 2 — Check job status:**

```
const job = await client.curl.jobs.get(submission.job_id)
```

**Step 3 — Get result on completion:**

```
const result = await client.curl.jobs.getResult(submission.job_id)
```

**Step 4 — Cancel if needed:**

```
await client.curl.jobs.cancel(submission.job_id)
```

**List all jobs:**

```
const jobs = await client.curl.jobs.list({ page: 1, limit: 20 })

// Or collect all pages automatically
const allJobs = await client.curl.jobs.listAll()
```

### Workflow 4: Scheduled Recurring Requests

Create cron-based schedules that execute HTTP requests at specified intervals.

**Step 1 — Create a schedule:**

```
const schedule = await client.curl.schedules.create({
  cron: '0 */5 * * * *',
  request: {
    url: 'https://api.example.com/health-check',
    method: 'GET'
  }
})
```

Cron format (6 fields): `second minute hour day month weekday`

Common patterns:
- `0 */5 * * * *` — Every 5 minutes
- `0 0 9 * * 1-5` — Weekdays at 9:00 AM
- `0 0 0 1 * *` — First day of every month

**Step 2 — List and inspect:**

```
const schedules = await client.curl.schedules.list()
const detail = await client.curl.schedules.get({ id: 'schedule-id' })
```

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

```
await client.curl.schedules.toggle('schedule-id', { enabled: false })
```

**Step 4 — Permanently delete:**

```
await client.curl.schedules.delete({ id: 'schedule-id' })
```

### Workflow 5: Cookie Sessions for Stateful Requests

Maintain cookies across multiple requests for authentication flows and stateful interactions.

**Step 1 — Execute login with session:**

```
await client.curl.execute({
  url: 'https://example.com/login',
  method: 'POST',
  json: { username: 'user', password: 'pass' },
  session_id: 'my-auth-session'
})
```

**Step 2 — Reuse session cookies automatically:**

```
const profile = await client.curl.execute({
  url: 'https://example.com/profile',
  method: 'GET',
  session_id: 'my-auth-session'
})
```

**Step 3 — Inspect stored cookies:**

```
const cookies = await client.curl.sessions.getCookies({ id: 'my-auth-session' })
```

**Step 4 — List all sessions:**

```
const sessions = await client.curl.sessions.list()
```

**Step 5 — Delete session when done:**

```
await client.curl.sessions.delete({ id: 'my-auth-session' })
```

### Workflow 6: File Storage and Retrieval

Save HTTP response bodies to organized storage directories.

**Step 1 — Download with storage enabled:**

```
await client.curl.execute({
  url: 'https://example.com/report.pdf',
  method: 'GET',
  save: true,
  save_path: 'reports/daily-report.pdf'
})
```

**Step 2 — List stored files:**

```
const files = await client.curl.storage.list()
```

Files are organized in three directory structures: `by-job/`, `by-domain/`, and `by-date/`.

**Step 3 — Retrieve a stored file:**

```
const file = await client.curl.storage.getFile({ path: 'by-domain/example.com/report.pdf' })
```

**Step 4 — Delete a stored file:**

```
await client.curl.storage.deleteFile({ path: 'by-domain/example.com/report.pdf' })
```

---

## Advanced Operations

### Real-time Job Monitoring

Subscribe to job lifecycle events for real-time progress tracking without polling.

**Server-Sent Events (SSE):**

```
const events = await client.curl.events.sseJobEvents()
```

Events delivered as standard SSE frames:
- `jobstarted` — Job execution began
- `jobprogress` — Progress update (0.0 to 1.0 fraction)
- `jobcompleted` — Job finished successfully
- `jobfailed` — Job encountered an error

**WebSocket events (JSON messages):**

```
const ws = await client.curl.events.streamWs()
```

**Multiplexed request channel:**

For executing multiple requests over a single WebSocket connection:

```
const channel = await client.curl.events.wsRequestChannel()
```

This establishes a persistent WebSocket connection for multiplexed, validated CurlRequest execution — separate from `streamWs` which only streams job lifecycle events.

### Multi-step Workflow: Authenticated Data Pipeline

Combine sessions, async execution, storage, and monitoring for complex automation.

```
// Step 1: Authenticate with cookie session
await client.curl.execute({
  url: 'https://protected-site.com/login',
  method: 'POST',
  json: { username: 'user', password: 'pass' },
  session_id: 'pipeline-session'
})

// Step 2: Submit async download with session cookies and storage
const submission = await client.curl.execute({
  url: 'https://protected-site.com/large-dataset',
  method: 'GET',
  mode: 'async',
  session_id: 'pipeline-session',
  save: true,
  save_path: 'datasets/fetch-1.json',
  job_name: 'dataset-download',
  timeout: 600
})

// Step 3: Monitor progress (polling approach)
let job = await client.curl.jobs.get(submission.job_id)
while (job.status === 'running' || job.status === 'pending') {
  await new Promise(r => setTimeout(r, 2000))
  job = await client.curl.jobs.get(submission.job_id)
}

// Step 4: Retrieve result if completed
if (job.status === 'completed') {
  const result = await client.curl.jobs.getResult(submission.job_id)
}

// Step 5: Clean up
await client.curl.sessions.delete({ id: 'pipeline-session' })
```

### Error Recovery Patterns

**Job failure and retry:**

```
const job = await client.curl.jobs.get({ id: 'failed-job-id' })

if (job.status === 'failed') {
  const retry = await client.curl.execute({
    url: 'https://api.example.com/data',
    method: 'GET',
    mode: 'async',
    timeout: 600,
    job_name: 'retry-attempt'
  })
}
```

**Schedule health audit:**

```
const allSchedules = await client.curl.schedules.listAll()

for (const schedule of allSchedules) {
  const detail = await client.curl.schedules.get(schedule.id)
  // Inspect execution history for recurring failures
}
```

**Session cleanup on error:**

```
try {
  const result = await client.curl.execute({
    url: 'https://example.com/action',
    method: 'POST',
    session_id: 'temp-session'
  })
} finally {
  await client.curl.sessions.delete({ id: 'temp-session' })
}
```

### Performance Considerations

- **Async mode for long requests**: Use `mode: 'async'` for any request exceeding 30 seconds to avoid proxy timeouts
- **Pagination control**: Use `list()` with `page`/`limit` for memory-efficient iteration; use `listAll()` when you need complete datasets
- **Session reuse**: Reuse session IDs across related requests to maintain cookies without creating redundant sessions
- **Storage cleanup**: Periodically delete stored files via `client.curl.storage.deleteFile()` to manage disk usage
- **Connection reuse**: Use `wsRequestChannel()` for high-frequency request patterns to reduce connection overhead

### Health and Metrics

**Health check (unauthenticated):**

```
const health = await client.curl.health.check()
```

Returns the standardized health response.

**Prometheus metrics:**

```
const metrics = await client.curl.ops.metrics()
```

Exports Prometheus-compatible metrics for dashboards and alerting.

---

## Quick Reference

### Core Request Endpoints

| Method | Endpoint | SDK Call | Purpose |
|--------|----------|----------|---------|
| GET | `/api/v1/curl/request` | `client.curl.executeCurlRequestGet(url, ...)` | Simple request via query params |
| POST | `/api/v1/curl/request` | `client.curl.execute({...})` | Full-featured request execution |

### Essential `execute` Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `url` | string | Yes | Target URL |
| `method` | string | No | HTTP method |
| `headers` | object | No | Request headers |
| `json` | object | No | JSON request body |
| `data` | string | No | Raw request body |
| `mode` | string | No | `sync` or `async` |
| `session_id` | string | No | Cookie session identifier |
| `save` | boolean | No | Save response to storage |
| `save_path` | string | No | Storage path for saved file |
| `timeout` | integer | No | Request timeout in seconds |
| `follow_redirects` | boolean | No | Follow HTTP redirects |
| `bearer_token` | string | No | Authorization bearer token |

### Job States

`pending` → `running` → `completed` | `failed` | `cancelled`

### Resource Management

| Resource | List | Get | Delete | Special |
|----------|------|-----|--------|---------|
| Jobs | `jobs.list()` | `jobs.get(id)` | `jobs.cancel(id)` | `jobs.getResult(id)` |
| Schedules | `schedules.list()` | `schedules.get(id)` | `schedules.delete(id)` | `schedules.toggle(id, data)` |
| Sessions | `sessions.list()` | `sessions.get(id)` | `sessions.delete(id)` | `sessions.getCookies(id)` |
| Storage | `storage.list()` | `storage.getFile(path)` | `storage.deleteFile(path)` | — |

### Events & Monitoring

| Endpoint | SDK Call | Protocol |
|----------|----------|----------|
| `/api/v1/curl/sse` | `client.curl.events.sseJobEvents()` | SSE |
| `/api/v1/curl/ws` | `client.curl.events.streamWs()` | WebSocket |
| `/api/v1/curl/channel` | `client.curl.events.wsRequestChannel()` | WebSocket |
| `/api/v1/curl/health` | `client.curl.health.check()` | HTTP (unauthenticated) |
| `/metrics` | `client.curl.ops.metrics()` | Prometheus |
```