Skip to content

HTTP gateway

POST /v1/gateway/http decides an HTTP call and, when the decision allows it, forwards the call to a registered route. Routes are registered in the app. The SDKs do not create routes and do not wrap this endpoint.

The response is the same decision body as POST /v1/events, plus gateway. gateway.upstream is null when the call was not forwarded. gateway.upstream.status is an integer or null. It is null when the upstream call failed before a response.

Terminal window
curl -X POST https://api.vulnify.io/v1/gateway/http \
-H "Authorization: Bearer $VULNIFY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"routeId": "ROUTE_ID",
"action": "READ_DATA",
"method": "GET",
"path": "/v1/customers?limit=50",
"resource": "Customer Database"
}'

X-API-Key may replace the bearer header. Without routeId the gateway only decides.

OpenAPI 0.3.0 (https://api.vulnify.io/openapi.json). Parameters.
NameInRequiredTypeDescription
Idempotency-KeyheadernostringRetries with the same key (per organization and endpoint, 24 hours) return the stored decision instead of deciding again. The replay always includes finalDecision, derived from the stored decision and the current review.

Same action fields as POST /v1/events, plus the registered route and the path to append.

OpenAPI 0.3.0 (https://api.vulnify.io/openapi.json). Request body.
FieldTypeRequiredDescription
agentstring (max length 100)noAgent name as registered in Vulnify.
agentIdstring (uuid)noAgent id. Use this or `agent`.
resourcestring (max length 100)noResource name as registered in Vulnify.
resourceIdstring (uuid)noResource id. Use this or `resource`.
destinationINTERNAL | EXTERNAL_EMAIL | EXTERNAL_APIno
recordsAffectedinteger (0–100000000)no
contentstring (max length 100000)noOptional text scanned for sensitive data. Scanned in memory and never stored.
actionREAD_DATA | WRITE_DATA | DELETE_DATA | EXPORT_DATA | SEND_EMAILyes
routeIdstring (uuid)noRegistered HTTP route. Without it the gateway only decides.
pathstring (max length 1000)noPath and query appended to the route base URL. Must start with /.
methodGET | POST | PUT | PATCH | DELETEnoDefaults to GET.
bodyJSON valuenoJSON value forwarded upstream when method is not GET.

path must start with /. method defaults to GET. body is forwarded upstream when method is not GET. The JSON body of this request must be at most 200 KB.

Decision: { id, decision, finalDecision, evaluatedDecision, monitored, riskLevel, riskScore, reasons, policy, dlpFindings, lgpdCategories, review, quotaExceeded, sandbox }. decision is the stored outcome and does not change when a review is resolved. finalDecision is the effective outcome (ALLOW after approval, BLOCK after denial or expiry, REVIEW while pending) and is always present, including an idempotent replay. quotaExceeded flags a plan overrun without blocking; sandbox is true for TEST keys.

OpenAPI 0.3.0 (https://api.vulnify.io/openapi.json). Response body.
FieldTypeRequiredDescription
idstring (uuid)yes
decisionALLOW | REVIEW | BLOCKyesDecision recorded on the event. It does not change when a review is resolved.
finalDecisionALLOW | REVIEW | BLOCKyesEffective outcome. Equals `decision` when there is no review. REVIEW while a review is pending. ALLOW after approval, BLOCK after denial or expiry. Always present, including an idempotent replay of a response stored before this field existed: the replay derives it from the stored decision and the current review.
evaluatedDecisionALLOW | REVIEW | BLOCKyesWhat full enforcement would have decided. Differs from `decision` in monitor mode.
monitoredbooleanyes
riskLevelLOW | MEDIUM | HIGH | CRITICALyes
riskScoreinteger (0–100)yes
reasonsarray of stringyes
policyobject or nullyes
policy.idstring (uuid)yes
policy.namestringyes
dlpFindingsarray of stringyesSensitive-data types found in `content`.
lgpdCategoriesarray of IDENTIFICATION | CONTACT | LOCATION | FINANCIAL | HEALTH | COMPANY | CREDENTIALSyesLGPD categories derived from `dlpFindings`.
reviewobject or nullyesNull when the event has no human review.
review.statusPENDING | APPROVED | DENIED | EXPIREDyes
review.expiresAtstring or null (date-time)yes
review.decidedAtstring or null (date-time)yes
review.notestring or nullyes
quotaExceededbooleanyesPlan quota is over the limit. The decision is still made.
sandboxbooleanyesTrue when the event was created with a TEST key.
gatewayobjectno
gateway.protocolHTTPyes
gateway.routeIdstring or null (uuid)no
gateway.proxiedbooleanyes
gateway.credentialIdstring or null (uuid)no
gateway.upstreamobject or nullnoNull when the call was not forwarded. `status` is null when the upstream call failed before a response.
gateway.upstream.statusinteger or nullyesUpstream HTTP status, or null when the call failed before a response.
gateway.upstream.truncatedBodystringyesUpstream body, truncated to 2 KB. Injected secrets are redacted.
gateway.upstream.errorstring or nullyes
gateway.upstream.latencyMsintegeryes
OpenAPI 0.3.0 (https://api.vulnify.io/openapi.json). Response headers.
HeaderDescription
X-Request-IdRequest id for support and log correlation (a caller-sent plain id is kept).
x-api-versionMachine API contract (v1).
x-vulnify-versionAPI build (SemVer, see /changelog).
Idempotent-Replay"true" when the body is a stored replay

gateway.upstream.truncatedBody is the upstream body truncated to 2 KB, with injected secrets redacted. gateway.credentialId is the credential used for that injection, or null.

OpenAPI 0.3.0 (https://api.vulnify.io/openapi.json). Error responses.
StatusMeaning
400Validation error (agent/agentId and resource/resourceId are required)
401Missing, invalid, revoked or expired API key
403Key bound to another agent, or caller IP not in the key allowlist
404Unknown agent, resource or gateway route
413JSON body larger than 200 KB
429Rate limit exceeded

A 413 is a client error. Do not treat it as an outage or as an allow. See Errors.

A route can attach a credential from an API integration. The app injects that credential into the upstream call only after ALLOW, and only if the upstream host is in the integration’s allowed hosts. The agent never sees the credential. Gateway routes do not accept a Slack webhook or a ticket integration as that credential.

Allowed hosts are configured on the integration, up to 20, as host names such as api.example.com or *.example.com. Credential material the app can store for an HTTP integration includes an API key header (the form defaults the header name to X-Api-Key), a query parameter (default name api_key), an Authorization scheme (default Bearer), a username and secret, or an OAuth 2.0 client-credentials token URL whose host is one of the allowed hosts.

Calls through /v1/gateway/http and /v1/gateway/mcp appear in the app’s gateway call log, and from there in the audit log. A TEST key is marked as a test key on that log.