Install
openclaw skills install @psyb0t/proxqGo, Redis-backed async HTTP proxy queue (built on asynq). POST any HTTP request (any method, any path, any body) to a configured upstream, get a job ID back instantly (202), a worker forwards it later, you poll GET /__jobs/{id} for status (queued/running/completed/failed) and GET /__jobs/{id}/content for the replayed upstream response (status/headers/body). DELETE /__jobs/{id} cancels. Path-prefix routing to multiple upstreams, per-upstream timeout/retries/pathFilter, optional response caching (memory or Redis LRU), automatic direct-proxy bypass for WebSocket/chunked/large-body requests. No built-in auth. Use when the user wants to turn a slow/unreliable backend into a fire-and-forget async API, decouple a client from upstream latency, relay webhooks with retries, or queue heavy uploads/processing jobs behind short-timeout reverse proxies.
openclaw skills install @psyb0t/proxqThe honey badger of HTTP proxies. POST a request, get a job ID back instantly, come back later for the goods. "I'll get back to you" as a service — every HTTP request becomes an async job in a Redis-backed queue (via asynq).
For installation, configuration, and container setup, see references/setup.md.
upstreams[].url is configured (and, via directProxyMode/prefix stripping, whatever path/query you tack onto it). Never point an upstream at internal/admin services you wouldn't otherwise expose, and never let untrusted callers choose the upstream prefix or URL.PROXQ_URL can submit jobs, poll any job ID, and cancel any job ID (job IDs are UUIDv4 but there is no ownership check). Front it with a reverse proxy doing auth (basic auth, mTLS, an API gateway) or bind it to loopback/an internal network only — do not expose a bare proxq instance to the open internet.upstreams[].url to point only at backends you control or explicitly trust. proxq forwards the full original request (method, headers, body) plus X-Forwarded-For/X-Real-IP/X-Forwarded-Proto — treat the upstream config the same way you'd treat a reverse-proxy target list.DELETE /__jobs/{id} is destructive & irreversible, with no ownership check. It best-effort cancels an in-flight job and deletes its task record — no undo, and (per the no-auth point above) proxq does not verify who submitted the job, so any caller who can reach the instance and knows/guesses a job ID can cancel it, not just its submitter. An agent must NEVER call it unless the user explicitly asked to cancel that exact job; confirm the specific job ID first (the one you just submitted, or the one the user named); never enumerate job IDs and bulk-cancel. On a shared/multi-tenant instance this can cancel another caller's in-flight job — treat cancellation as admin-only unless you're certain the job is yours.pathFilter, slow paths get queued.directProxyMode in setup.md), so don't expect a job ID for them.Point at a running instance:
export PROXQ_URL=http://localhost:8080
All job-management endpoints live under jobsPath (default /__jobs). Every response proxq itself generates (not proxied from upstream) carries X-Proxq-Source: proxq — that's how you distinguish a proxq-origin response (job not ready, no upstream match, proxq error) from a real upstream response replayed verbatim.
Any request that doesn't hit a job endpoint gets routed by longest-prefix match to a configured upstream and queued (unless it qualifies for direct-proxy bypass — see setup.md).
curl -s -X POST "$PROXQ_URL/api/heavy-computation" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer upstream-token" \
-d '{"data": "lots of it"}'
# 202 Accepted, X-Proxq-Source: proxq
# {"jobId": "550e8400-e29b-41d4-a716-446655440000"}
If no upstream prefix matches the request path: 502 Bad Gateway, X-Proxq-Source: proxq.
Optional per-request override header: X-Proxq-Timeout: <go-duration> (e.g. X-Proxq-Timeout: 30s) overrides the upstream's configured timeout for that one request. Invalid value → 400 Bad Request.
curl -s "$PROXQ_URL/__jobs/550e8400-e29b-41d4-a716-446655440000"
{"id": "550e8400-...", "status": "completed", "completedAt": "2025-01-01T00:00:00Z"}
Failed job includes error:
{"id": "550e8400-...", "status": "failed", "error": "forward request: dial tcp: connection refused"}
status is one of queued (pending/scheduled/aggregating), running (active, or waiting on a retry), completed (done — response stored, even if upstream returned 4xx/5xx), failed (transport broke and retries are exhausted). Unknown job ID → 404 Not Found, X-Proxq-Source: proxq, body {"code":"NOT_FOUND","message":"Not found"}.
Replays the upstream response exactly — status code, headers, body — as if you'd called upstream directly.
curl -si "$PROXQ_URL/__jobs/550e8400-e29b-41d4-a716-446655440000/content"
HTTP/1.1 200 OK
Content-Type: application/json
X-Custom-Header: from-upstream
{"result": "done"}
If upstream returned a 404, you get 404 back too — but without X-Proxq-Source (it's a real upstream response). If the job isn't done yet, or doesn't exist: 404 Not Found with X-Proxq-Source: proxq. That header is the whole disambiguation trick — present means "proxq talking", absent means "upstream talking".
curl -s -X DELETE "$PROXQ_URL/__jobs/550e8400-e29b-41d4-a716-446655440000"
# {"status": "cancelled"}
Best-effort: attempts to stop in-flight processing, then deletes the task record. Unknown job ID → 404 Not Found, X-Proxq-Source: proxq.
Destructive & irreversible. DELETE /__jobs/{id} cancels the job and deletes its record with no undo, and there is no ownership check — any job ID you can supply gets cancelled, whether or not it's one you submitted. An agent must NEVER call this unless the user explicitly asked to cancel that exact job; confirm the specific job ID first; never enumerate-then-bulk-cancel. On a shared/multi-tenant instance this can disrupt other callers' in-flight jobs.
JOB_ID=$(curl -s -X POST "$PROXQ_URL/api/report" -d '{}' | jq -r .jobId)
while :; do
STATUS=$(curl -s "$PROXQ_URL/__jobs/$JOB_ID" | jq -r .status)
case "$STATUS" in
completed) curl -s "$PROXQ_URL/__jobs/$JOB_ID/content" | jq; break ;;
failed) echo "job failed" >&2; break ;;
*) sleep 2 ;;
esac
done
For upstream routing rules (prefix matching/stripping), direct-proxy bypass conditions, caching behavior, and the full config reference (env vars, docker run/compose), see references/setup.md.