REST API ExecModule
Overview
RestApiModule executes one bounded HTTP request from a ValkyrAI workflow. Version 2.0 replaces the legacy open-ended client contract with complete Workflow Studio metadata, destination-bound IntegrationAccount authentication, explicit write confirmation, bounded bodies and timeouts, safe read retries, single-attempt writes, disabled redirects, allowlisted response headers, sanitized target reporting, and credential-safe failures.
The canonical module identifier is:
com.valkyrlabs.workflow.modules.rest.RestApiModule
This direct HTTP client does not claim OpenAPI operation resolution, schema validation, browser cookies, streaming, multipart upload, automatic pagination, or automatic compensation. Use a provider-specific ExecModule when one exists; it can enforce a narrower resource and permission contract.
Usage
- Prefer a provider-specific ExecModule when ValkyrAI has one.
- For authenticated calls, create a least-privilege
IntegrationAccountwith statusREADY. - Set
IntegrationAccount.accountIdto the exact allowed HTTPS base URL, including a path prefix when credentials should be limited below that path. - Put the bearer/API key in
apiKeyorpassword; Basic authentication usesusernameandpassword. - Configure
method,url,authMode, expected statuses, timeout, and response bound. - Set
confirmWrite: trueforPOST,PUT,PATCH, orDELETE. - After a write failure, reconcile provider state before manually retrying because delivery can be ambiguous.
Inputs
| Input | Required | Description |
|---|---|---|
method | Yes | GET, HEAD, OPTIONS, POST, PUT, PATCH, or DELETE. Default: GET. |
url | Yes | Absolute HTTPS URL, or relative path resolved against a configured base URL. HTTP is accepted only for loopback testing. |
body | No | String or JSON-compatible body, serialized to at most 1 MiB UTF-8. |
headers | No | Up to 32 non-secret string headers; each value is capped at 4 KiB. |
authMode | No | none, bearer, api_key, or basic. Default: none. |
confirmWrite | For writes | Must be true for POST, PUT, PATCH, or DELETE. |
idempotencyKey | No | Optional caller correlation key, capped at 256 bytes. A durable workflow provider key takes precedence. |
expectedStatus | No | Up to 32 accepted HTTP statuses. Default: 200, 201, 202, 204. |
timeoutMs | No | Call timeout from 1,000 through 60,000 ms. Default: 10,000. |
maxResponseBytes | No | Response-body limit from 1 KiB through 5 MiB. Default: 1 MiB. |
maxAttempts | No | Read-only attempt cap from 1 through 3. Writes always use one attempt. |
parseJson | No | Parse JSON content types and JSON-looking response bodies. Default: true. |
responsePath | No | Dotted path selected from an object response after bounded JSON parsing. |
failOnError | No | Throw a generic sanitized exception after safe error output is recorded. Default: true. |
Legacy configuration aliases endpoint, payload, retries, storeResponseAs, storeHeadersAs, lifecycleStage, and stageStatus remain available where they do not weaken the v2 safety contract. retries is interpreted as the bounded read-attempt cap; it never enables write retries.
Raw username, password, apiKey, api_key, accessToken, token, secret, authorization, cookie, and client-secret fields are rejected. Caller-provided Authorization, Proxy-Authorization, X-API-Key, cookie, host, content-length, transfer, upgrade, and other hop-by-hop headers are also rejected.
Outputs
| Output | Description |
|---|---|
status | success or error. |
method | Normalized HTTP method. |
lastHttpStatus | Provider HTTP status when a response was received. |
lastHttpUrl | Sanitized scheme, host, port, and path. Query parameters and user information are never returned. |
lastHttpDurationMs | Duration of the final attempt. |
attempts | Number of HTTP attempts consumed. |
restResponse | Bounded parsed or textual response body, or selected responsePath value. |
responseHeaders | Allowlisted bounded response headers such as content type, ETag, retry-after, request IDs, and idempotency receipt. |
externalReceiptRef | Bounded provider idempotency receipt or response receipt identifier when available. |
retryable | Whether a read-only failure may be retried after checking the destination. |
error | Safe object containing code, message, retryable, and optional httpStatus. |
The legacy restError alias contains the same safe error object. Custom storeResponseAs and storeHeadersAs keys receive only the same bounded response and allowlisted header data.
IntegrationAccount Requirements
Authenticated requests require a bound IntegrationAccount with:
- status exactly
READY; accountIdcontaining the exact HTTPS base URL authorized to receive the credential;- a path prefix in
accountIdwhen access should be constrained below a provider subpath; apiKeyorpasswordforbearerandapi_keymodes;usernameandpasswordforbasicmode;- provider permissions limited to the resources and actions the workflow needs.
The requested URL must use the same scheme, host, and effective port as accountId, and its normalized path must remain within the bound path prefix. This prevents a workflow edit from redirecting the account credential to another origin or unrelated provider path.
authMode: none does not read IntegrationAccount secrets. Query-string credentials are unsupported in every mode.
Configuration
| Configuration | Default | Constraint |
|---|---|---|
authAccount | None | Required and READY for authenticated calls. |
authMode | none | none, bearer, api_key, or basic. |
method | GET | Seven-method allowlist. |
url | None | Required absolute HTTPS URL or relative path. |
baseUrl | Runtime default | Used only for relative unauthenticated calls; authenticated calls bind to IntegrationAccount.accountId. |
headers | {} | Bounded non-secret string map. |
body | None | At most 1 MiB serialized. |
confirmWrite | false | Required for every write method. |
idempotencyKey | None | Optional caller correlation key. |
expectedStatus | [200,201,202,204] | Bounded list of statuses from 100 through 599. |
parseJson | true | JSON parsing remains inside the response cap. |
responsePath | None | Optional bounded dotted selector. |
storeResponseAs | None | Optional safe, non-reserved output key. |
storeHeadersAs | None | Optional safe, non-reserved output key. |
timeoutMs | 10000 | 1,000–60,000 ms. |
maxResponseBytes | 1048576 | 1,024–5,242,880 bytes. |
maxAttempts | 1 | 1–3 for reads; fixed to one for writes. |
failOnError | true | Exception text contains only the normalized error code. |
Operations
The module exposes one operation: issue one HTTP request.
Read-only methods are GET, HEAD, and OPTIONS. They may retry only network failures and statuses 429, 502, 503, and 504, never exceeding maxAttempts. Backoff is bounded and redirects remain disabled.
Write methods are POST, PUT, PATCH, and DELETE. They require confirmWrite: true and always use one transport attempt even when maxAttempts or legacy retries is greater than one. An Idempotency-Key helps provider-side reconciliation only when that provider supports it; it does not make every HTTP write intrinsically idempotent.
Request and response bodies are fully bounded. The client disables OkHttp connection-failure retries and both same-scheme and SSL redirects. Response headers are allowlisted; cookies and arbitrary provider headers never enter workflow state.
Errors and Failure Modes
| Code | Meaning | Recovery |
|---|---|---|
VALIDATION_ERROR | The method, URL, credential placement, account binding, header, body, confirmation, timeout, response cap, or output key is invalid. | Correct the named field. No unsafe request was sent. |
HTTP_STATUS_ERROR | The provider returned a status outside expectedStatus. | For reads, retry only when retryable is true. For writes, reconcile provider state before a manual retry. |
NETWORK_ERROR | Connection, TLS, timeout, or transport failed. | Check the provider and account. Reconcile a write because delivery may be ambiguous. |
RESPONSE_TOO_LARGE | Declared or streamed body exceeded maxResponseBytes. | Narrow the provider response or raise the cap within 5 MiB. The oversized body is discarded. |
RETRY_INTERRUPTED | A bounded read retry was interrupted. | Preserve interruption and retry only after the workflow is healthy. |
EXECUTION_ERROR | An unexpected local runtime failure occurred. | Inspect sanitized runtime logs; do not assume the request was unsent. |
Provider response error bodies and exception messages are not copied into failure output. Credential-bearing query parameters, headers, cookies, user information, and redirect targets are not reported.
Example
Create one order using a bearer credential stored in a destination-bound IntegrationAccount:
{
"method": "POST",
"url": "https://api.example.com/v1/orders",
"authMode": "bearer",
"confirmWrite": true,
"idempotencyKey": "order-ord-42",
"headers": {
"Accept": "application/json",
"Content-Type": "application/json"
},
"body": {
"orderId": "ord-42",
"total": 125.00,
"currency": "USD"
},
"expectedStatus": [201],
"timeoutMs": 10000,
"maxResponseBytes": 1048576,
"maxAttempts": 3,
"failOnError": false
}
Expected shape after one provider-confirmed request:
{
"status": "success",
"method": "POST",
"lastHttpStatus": 201,
"lastHttpUrl": "https://api.example.com/v1/orders",
"lastHttpDurationMs": 184,
"attempts": 1,
"retryable": false,
"restResponse": {
"id": "ord-42",
"state": "accepted"
},
"externalReceiptRef": "ord-42"
}
maxAttempts: 3 does not change the write behavior: the request still uses one attempt.
Notes
- Pagination is provider-specific and not automatic. Model page or cursor inputs explicitly, then bound the workflow loop separately.
- Rate limits surface as unexpected status
429. Read-only calls may use bounded attempts; writes remain single-attempt. - Idempotency depends on the provider honoring the selected key. Repeated workflow execution can still duplicate a side effect when the provider ignores it.
- URL query values may be sent, but credential-like query parameter names are rejected and the query is removed from
lastHttpUrl. - Relative authenticated URLs resolve against
IntegrationAccount.accountId. Relative unauthenticated URLs use configuredbaseUrlor the runtime default. - HTTP is accepted only for loopback tests. Production destinations must use HTTPS.
- The deterministic tests use a loopback HTTP server. They verify request construction, transient read retries, single-attempt writes, explicit confirmation, destination-bound bearer auth, raw-secret rejection, response caps, redirect refusal, secret-safe failures, annotation discovery, and metadata serialization. Live provider TLS, authentication, rate limits, and API-specific behavior remain deployment-time boundaries.