Skip to content
Hoody.com

Proxy Hooks

Manage MITM hook scripts that intercept container traffic before it reaches the real upstream service. Hooks are stored per-service, evaluated in declared order (first-match-wins), and every write is ETag-gated with If-Match: file:v. The service names logs, proxy, workspaces, and cdp are reject-listed and cannot be targeted.

GET /api/v1/containers/{id}/proxy/hooks

Returns every hook grouped by service, together with the current file_version and ETag.

NameInTypeRequiredDescription
idpathstringYesContainer ID
{
"statusCode": 200,
"message": "Proxy hooks listed successfully",
"data": {
"hooks": {
"api": [
{
"id": "01hz3x5k2n4q8w7vrgc6t1m4bz",
"position": 0,
"match": {
"method": "POST",
"path": "/v1/login",
"headers": {
"x-debug": "true"
}
},
"script": {
"path": "/workspace/hooks/login-trace.js"
},
"timeout": 1500,
"applies_to": {
"groups": ["qa", "preview"]
}
}
]
},
"file_version": 42,
"etag": "file:v42"
}
}
await client.api.proxyHooks.listContainerProxyHooks(id)
Terminal window
curl -X GET "https://api.hoody.icu/api/v1/containers/cnt_abc123/proxy/hooks" \
-H "Authorization: Bearer <token>"

GET /api/v1/containers/{id}/proxy/hooks/{service}

Returns the ordered hook array for a single service. First-match-wins within this array.

NameInTypeRequiredDescription
idpathstringYesContainer ID
servicepathstringYesService name
{
"statusCode": 200,
"message": "Service hooks listed successfully",
"data": {
"service": "api",
"hooks": [
{
"id": "01hz3x5k2n4q8w7vrgc6t1m4bz",
"position": 0,
"match": {
"method": "POST",
"path": "/v1/login",
"headers": {
"x-debug": "true"
}
},
"script": {
"path": "/workspace/hooks/login-trace.js"
},
"timeout": 1500,
"applies_to": {
"groups": ["qa", "preview"]
}
}
],
"file_version": 42,
"etag": "file:v42"
}
}
await client.api.proxyHooks.listContainerProxyServiceHooks(id, service)
Terminal window
curl -X GET "https://api.hoody.icu/api/v1/containers/cnt_abc123/proxy/hooks/api" \
-H "Authorization: Bearer <token>"

GET /api/v1/containers/{id}/proxy/hooks/{service}/{hookId}

Returns one hook by id, including the current file version and ETag.

NameInTypeRequiredDescription
idpathstringYesContainer ID
servicepathstringYesService name
hookIdpathstringYes26-char Crockford base32 ULID (lowercase)
{
"statusCode": 200,
"message": "Hook retrieved successfully",
"data": {
"hook": {
"id": "01hz3x5k2n4q8w7vrgc6t1m4bz",
"position": 0,
"match": {
"method": "POST",
"path": "/v1/login",
"headers": {
"x-debug": "true"
}
},
"script": {
"path": "/workspace/hooks/login-trace.js"
},
"timeout": 1500,
"applies_to": {
"groups": ["qa", "preview"]
}
},
"file_version": 42,
"etag": "file:v42"
}
}
await client.api.proxyHooks.getContainerProxyHook(id, service, hookId)
Terminal window
curl -X GET "https://api.hoody.icu/api/v1/containers/cnt_abc123/proxy/hooks/api/01hz3x5k2n4q8w7vrgc6t1m4bz" \
-H "Authorization: Bearer <token>"

POST /api/v1/containers/{id}/proxy/hooks/{service}

Append a new hook to a service, or insert at a specific position. Omit position to append to the end of the array. Requires If-Match.

NameInTypeRequiredDescription
idpathstringYesContainer ID
servicepathstringYesService name
if-matchheaderstringNofile:v ETag precondition
FieldTypeRequiredDescription
matchobjectYesRequest matcher — see fields below
scriptobjectYesScript reference — path is required
timeoutintegerNoExecution timeout in milliseconds (1 to 30000)
applies_toobjectNoTargeting — groups array of group names
positionintegerNoZero-indexed insertion position

match fields:

FieldTypeRequiredDescription
methodstring or array of stringsNoA single HTTP method (*, GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS) or an array of methods
pathstringNoURL path matcher, up to 257 characters
headersobjectNoMap of header name to expected value

script fields:

FieldTypeRequiredDescription
pathstringYesFilesystem path to the hook script
subdomainstringNoOptional subdomain binding
execIdstringNoOptional pre-existing execution id
{
"match": {
"method": "GET",
"path": "/v1/users/*",
"headers": {
"x-trace": "1"
}
},
"script": {
"path": "/workspace/hooks/user-trace.js"
},
"timeout": 1000,
"applies_to": {
"groups": ["qa"]
}
}
{
"statusCode": 201,
"message": "Hook created successfully",
"data": {
"hook": {
"id": "01hz3x5k2n4q8w7vrgc6t1m4bz",
"position": 1,
"match": {
"method": "GET",
"path": "/v1/users/*",
"headers": {
"x-trace": "1"
}
},
"script": {
"path": "/workspace/hooks/user-trace.js"
},
"timeout": 1000,
"applies_to": {
"groups": ["qa"]
}
},
"file_version": 43,
"etag": "file:v43"
}
}
await client.api.proxyHooks.addContainerProxyHook(
id,
service,
{
match: { method: "GET", path: "/v1/users/*", headers: { "x-trace": "1" } },
script: { path: "/workspace/hooks/user-trace.js" },
timeout: 1000,
applies_to: { groups: ["qa"] }
},
{ ifMatch: "file:v42" }
)
Terminal window
curl -X POST "https://api.hoody.icu/api/v1/containers/cnt_abc123/proxy/hooks/api" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-H "If-Match: file:v42" \
-d '{
"match": {
"method": "GET",
"path": "/v1/users/*",
"headers": { "x-trace": "1" }
},
"script": { "path": "/workspace/hooks/user-trace.js" },
"timeout": 1000,
"applies_to": { "groups": ["qa"] }
}'

PUT /api/v1/containers/{id}/proxy/hooks/{service}/{hookId}

Full-replace a hook preserving its id and position. Requires If-Match.

NameInTypeRequiredDescription
idpathstringYesContainer ID
servicepathstringYesService name
hookIdpathstringYes26-char Crockford base32 ULID (lowercase)
if-matchheaderstringNofile:v ETag precondition
FieldTypeRequiredDescription
matchobjectYesRequest matcher — see fields below
scriptobjectYesScript reference — path is required
timeoutintegerNoExecution timeout in milliseconds (1 to 30000)
applies_toobjectNoTargeting — groups array of group names
positionintegerNoZero-indexed insertion position (POST only)

match fields:

FieldTypeRequiredDescription
methodstring or array of stringsNoA single HTTP method (*, GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS) or an array of methods
pathstringNoURL path matcher, up to 257 characters
headersobjectNoMap of header name to expected value

script fields:

FieldTypeRequiredDescription
pathstringYesFilesystem path to the hook script
subdomainstringNoOptional subdomain binding
execIdstringNoOptional pre-existing execution id
{
"match": {
"method": "GET",
"path": "/v1/users/*",
"headers": {
"x-trace": "1"
}
},
"script": {
"path": "/workspace/hooks/user-trace.js"
},
"timeout": 1000,
"applies_to": {
"groups": ["qa"]
}
}
{
"statusCode": 200,
"message": "Hook updated successfully",
"data": {
"hook": {
"id": "01hz3x5k2n4q8w7vrgc6t1m4bz",
"position": 0,
"match": {
"method": "GET",
"path": "/v1/users/*",
"headers": {
"x-trace": "1"
}
},
"script": {
"path": "/workspace/hooks/user-trace.js"
},
"timeout": 1000,
"applies_to": {
"groups": ["qa"]
}
},
"file_version": 44,
"etag": "file:v44"
}
}
await client.api.proxyHooks.updateContainerProxyHook(
id,
service,
hookId,
{
match: { method: "GET", path: "/v1/users/*", headers: { "x-trace": "1" } },
script: { path: "/workspace/hooks/user-trace.js" },
timeout: 1000,
applies_to: { groups: ["qa"] }
},
{ ifMatch: "file:v43" }
)
Terminal window
curl -X PUT "https://api.hoody.icu/api/v1/containers/cnt_abc123/proxy/hooks/api/01hz3x5k2n4q8w7vrgc6t1m4bz" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-H "If-Match: file:v43" \
-d '{
"match": {
"method": "GET",
"path": "/v1/users/*",
"headers": { "x-trace": "1" }
},
"script": { "path": "/workspace/hooks/user-trace.js" },
"timeout": 1000,
"applies_to": { "groups": ["qa"] }
}'

PATCH /api/v1/containers/{id}/proxy/hooks/{service}/{hookId}/position

Atomic reposition within the service array. Requires If-Match.

NameInTypeRequiredDescription
idpathstringYesContainer ID
servicepathstringYesService name
hookIdpathstringYes26-char Crockford base32 ULID (lowercase)
if-matchheaderstringNofile:v ETag precondition
FieldTypeRequiredDescription
positionintegerYesZero-indexed destination position within the service array
{
"position": 2
}
{
"statusCode": 200,
"message": "Hook moved successfully",
"data": {
"hook": {
"id": "01hz3x5k2n4q8w7vrgc6t1m4bz",
"position": 2,
"match": {
"method": "POST",
"path": "/v1/login",
"headers": {
"x-debug": "true"
}
},
"script": {
"path": "/workspace/hooks/login-trace.js"
},
"timeout": 1500,
"applies_to": {
"groups": ["qa", "preview"]
}
},
"file_version": 45,
"etag": "file:v45"
}
}
await client.api.proxyHooks.moveContainerProxyHook(
id,
service,
hookId,
{ position: 2 },
{ ifMatch: "file:v44" }
)
Terminal window
curl -X PATCH "https://api.hoody.icu/api/v1/containers/cnt_abc123/proxy/hooks/api/01hz3x5k2n4q8w7vrgc6t1m4bz/position" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-H "If-Match: file:v44" \
-d '{ "position": 2 }'

DELETE /api/v1/containers/{id}/proxy/hooks/{service}

Remove every hook under the named service. Requires If-Match.

NameInTypeRequiredDescription
idpathstringYesContainer ID
servicepathstringYesService name
if-matchheaderstringNofile:v ETag precondition
{
"statusCode": 200,
"message": "Service hooks cleared successfully",
"data": {
"removed": 3,
"file_version": 46,
"etag": "file:v46"
}
}
await client.api.proxyHooks.clearContainerProxyServiceHooks(id, service, { ifMatch: "file:v45" })
Terminal window
curl -X DELETE "https://api.hoody.icu/api/v1/containers/cnt_abc123/proxy/hooks/api" \
-H "Authorization: Bearer <token>" \
-H "If-Match: file:v45"

DELETE /api/v1/containers/{id}/proxy/hooks/{service}/{hookId}

Delete a single hook. Requires If-Match.

NameInTypeRequiredDescription
idpathstringYesContainer ID
servicepathstringYesService name
hookIdpathstringYes26-char Crockford base32 ULID (lowercase)
if-matchheaderstringNofile:v ETag precondition
{
"statusCode": 200,
"message": "Hook removed successfully",
"data": {
"file_version": 47,
"etag": "file:v47"
}
}
await client.api.proxyHooks.removeContainerProxyHook(id, service, hookId, { ifMatch: "file:v46" })
Terminal window
curl -X DELETE "https://api.hoody.icu/api/v1/containers/cnt_abc123/proxy/hooks/api/01hz3x5k2n4q8w7vrgc6t1m4bz" \
-H "Authorization: Bearer <token>" \
-H "If-Match: file:v46"