Skip to main content

Documentation Index

Fetch the complete documentation index at: https://platform.docs.zenoo.com/llms.txt

Use this file to discover all available pages before exploring further.

HTTP Connector Overview

The HTTP connector enables you to make HTTP(S) requests directly from the DSL, supporting a wide range of features for integration, automation, and external API communication.
Related Connector Documentation
The HTTP connector can be used standalone for direct response handling or within an exchange for unified error handling and retries. Below is a summary of its main features: Jump to a section for details, or read on for a full guide to all features.

HTTP connector

A built-in HTTP connector facilitates making HTTP calls directly from the DSL. It is possible to reference and use context attributes in a connector definition. The common use-cases include URL and body generation, authentication headers, etc. An HTTP connector response is automatically converted into a context attribute based on the content type. There is a built-in support for JSON and XML content types.

Usage Patterns

Standalone HTTP Usage

Use HTTP directly when you need granular control over response handling, such as branching logic based on specific status codes:
// Direct HTTP usage with custom response handling
http('user-validation') {
    url 'https://api.validation.com/users'
    method 'POST'
    jsonBody user: payload.user
}.onStatus(400) {
    // Handle validation errors specifically
    decision('VALIDATION_FAILED', it)
}.onStatus([429, 503]) {
    // Handle rate limiting and service unavailable
}.onSuccess {
    // Continue with successful response
    success(it.data)
}

Exchange-wrapped HTTP Usage

Use HTTP within an exchange for unified error handling, retries, and monitoring:
// HTTP within exchange for standardized handling
exchange('user-validation') {
    http {
        url 'https://api.validation.com/users'
        method 'POST'
        jsonBody user: payload.user
    }
    timeout 30
    fixedBackoffRetry {
        retry 3
        backoff 5
    }
}
When to use standalone HTTP:
  • Need different handling for specific status codes (400, 401, 404, etc.)
  • Complex response branching logic
  • Custom retry strategies per status code
  • Immediate response processing without standard error wrapping
When to use exchange-wrapped HTTP:
  • Standard error handling and retries are sufficient
  • Want consistent monitoring and metrics
  • Need timeout configuration
  • Prefer unified connector interface

Named HTTP Connectors

You can assign a name to an HTTP connector invocation using the syntax:
http('get-clients') {
    url 'https://api.example.com/clients'
    method 'GET'
}

Why use a name?

  • Logging: Named HTTP connectors make logs more readable and easier to search, as the name appears in execution logs.
  • Metrics: Names are used as identifiers in metrics, making it easier to track the performance and usage of specific API calls.
  • Troubleshooting: When errors occur, having a descriptive name helps quickly identify which external call failed.
  • Billing: If you track API usage for billing or cost allocation, using names allows for more granular reporting.

Default Name

If you do not provide a name, the connector will use the request URL as the default name in logs and metrics:
http {
    url 'https://api.example.com/clients'
    method 'GET'
}
In this case, the name will be set to the value of the url property.
Tip: Use descriptive names for important or frequently used HTTP connectors to improve observability and maintainability.

Error Handling and Status Codes

The HTTP connector provides several ways to handle responses and errors:

Response Handlers

You can use response handlers to manage different outcomes of your HTTP requests:
  • onStatus(statusCode): Executes a block if the response status code matches the given value. Useful for handling specific error or success codes.
  • onStatus([statusCode1, statusCode2, …]): Executes a block if the response status code matches any of the values in the array. Useful for handling multiple related status codes.
  • onStatus(range): Executes a block if the response status code falls within the given range (e.g., 400..499 for client errors, 500..599 for server errors).
  • onResponse: Executes a block for any response, giving you access to the full response entity (headers, status, payload).
  • onSuccess: Executes a block if the request is successful (typically for 2xx status codes).
  • onError: Executes a block if the request results in an error (typically for 4xx or 5xx status codes).
Examples: Handle a specific status code (e.g., 400 Bad Request):
http {
  url "${url}/create"
  method 'POST'
  jsonBody request
}.onStatus(400) {
    // 'it' contains the response payload
    match(it.code_error in [1108, 2001, 3001, 3020]) {
        decision('INCORECT_CPF', it)
    }
    error(it)
}
Handle multiple specific status codes using an array:
http {
  url "${url}/create"
  method 'POST'
  jsonBody request
}.onStatus([502, 503, 504]) {
    // Handle server errors that might be temporary
    log "Server temporarily unavailable: ${it}"
    delay(30)
    retry()
}.onStatus([400, 401, 403]) {
    // Handle client errors that shouldn't be retried
    decision('CLIENT_ERROR', it)
}
Handle status code ranges:
http {
  url "${url}/create"
  method 'POST'
  jsonBody request
}.onStatus(400..499) {
    // Handle all client errors (4xx)
    log "Client error occurred: ${it.status}"
    decision('CLIENT_ERROR', it)
}.onStatus(500..599) {
    // Handle all server errors (5xx) - these might be retryable
    log "Server error occurred: ${it.status}"
    delay(10)
    retry()
}
Handle any response and branch on status:
http {
  url "${url}/create"
  method 'POST'
  jsonBody request
}.onResponse {
  match(it.status == 201) {
      success(it.headers.Location)
  }
  error(it.payload)
}
Handle all successful responses:
http {
  url 'https://api.example.com/data'
}.onSuccess {
  // 'it' contains the successful response
  log "Success: ${it}"
  // process success
}
Handle all error responses:
http {
  url 'https://api.example.com/data'
}.onError {
  // 'it' contains the error response
  log "Error: ${it}"
  // process error
}
You can chain multiple handlers for fine-grained control over error and success cases. Only the first matching handler is executed.

GET requests

Making an HTTP GET is as simple as providing a request url
http {
    url 'https://request-url'
}
An url can be generated using Groovy GString and context attributes. An example below queries GitHub repositories using a keyword attribute.
http {
  url "https://api.github.com/search/repositories?q=topic:${keyword}"
}

Query Parameters

Query parameters can be added to HTTP requests using the queryParam method. This is particularly useful when you need to:
  • Add dynamic parameters from context attributes
  • Maintain clean, readable URL construction
  • Properly encode parameter values
Basic usage:
http {
    url 'https://api.example.com/search'
    queryParam 'q', 'searchTerm'
    queryParam 'page', '1'
    queryParam 'limit', '10'
}
Using context attributes:
http {
    url 'https://api.example.com/users'
    queryParam 'userId', user.id
    queryParam 'status', user.status
}
Dynamic parameter values with GString:
http {
    url 'https://api.example.com/notify'
    queryParam 'To', "${countryCode}${phoneNumber}"
    queryParam 'Channel', 'sms'
}
Note: Query parameters are automatically URL-encoded unless you use disabledEncoding(). See URL Encoding for more details.

Headers

The header method allows you to add custom HTTP headers to your request. Headers are commonly used for:
  • Custom authentication tokens
  • API keys
  • Content negotiation
  • Tracking and correlation IDs
Basic usage:
http {
    url 'https://api.example.com/data'
    header 'X-API-Key', apiKey
    header 'X-Request-ID', requestId
}
Using context attributes:
http {
    url 'https://api.example.com/secure'
    header 'X-Auth-Token', session.authToken
    header 'X-User-ID', user.id
}
Multiple headers:
http {
    url 'https://api.example.com/resource'
    header 'Accept', 'application/json'
    header 'Accept-Language', 'en-US'
    header 'X-Correlation-ID', correlationId
    method 'GET'
}
Headers with multiple values:
http {
    url 'https://api.example.com/resource'
    header 'Accept': ['application/json', 'application/xml']
    header 'X-Custom-Header', 'value'
}
Using map syntax:
http {
    url 'https://api.example.com/resource'
    header 'X-API-Key': apiKey, 'X-Client-ID': clientId
}
Tip: For standard authentication, use the dedicated methods: basicAuth for Basic authentication (see Authorization) or bearerAuth for Bearer tokens instead of setting headers manually.
Note: Headers can also be configured at the transport level for reuse across multiple requests. Connector-level headers override transport-level headers with the same name.

POST requests

HTTP POST request has the method set to POST.

JSON body

The request body is set using the payload expression result. The payload expression can reference and use available context attributes. Retryable error forces connector to try again even on 404 NOT_FOUND response from server. This can be useful in subsequent call to easily poll for object until it is ready.
http {
  url "${middleware.url}/api/v1/client"
  method 'POST'
  body """whatever"""
  retryableError 404
}
A JSON request body is specified using jsonBody together with application/json content type.
http {
    url 'http://localhost'
    method 'POST'
    jsonBody firstname: client.firstname, lastname: client.lastname
}

JSON body with Base64-encoded files

It is possible to specify a JSON request body using descriptors. The corresponding files are encoded using Base64.
http {
    url 'http://localhost'
    method 'POST'
    jsonBody id: [front: base64(upload.idFront), back: base64(upload.idBack)]
}

HTTP body with binary data

Some API endpoints require sending binary data in HTTP body. This is supported by rawBody. You can send content of a file by passing a file descriptor to it. If the parameter is a string, it’s send as it is, maps and lists are rendered as json.
http {
    url 'http://localhost'
    method 'POST'
    rawBody upload.idFront
}

Form data

The formData specifies an HTTP request using a form data using application/x-www-form-urlencoded content type.
http {
    url 'http://localhost'
    method 'POST'
    formData 'data1', content1
    formData 'data2', content2
}

Multipart data

The HTTP connector supports sending multipart/form-data requests with various content types using the following methods:
  • multipartFile(String name, Attribute descriptor): Adds a file part to the request. The descriptor should be an Attribute that represents a FileDescriptor. The content type is set according to the provided file descriptor.
  • multipartText(String name, String content): Adds a text part to the request. The content type is automatically set to text/plain.
  • multipartJson(String name, Object content): Adds a JSON part to the request. The content can be an Attribute or any object that can be serialized to JSON (e.g., a map, a list, or a custom object). The content type is automatically set to application/json.
  • multiPart(String name, String contentType, Object value): Adds a multipart entry with the specified content type. The contentType should be one of the following: file (application/octet-stream), text (text/plain), or json (application/json).
Examples: File Entry using multipartFile:
http {
    url 'http://localhost'
    method 'POST'
    multipartFile 'document', upload.idFront // upload.idFront is an Attribute containing a FileDescriptor
}
Text Entry using multipartText:
http {
    url 'http://localhost'
    method 'POST'
    multipartText 'name', 'John Doe'
}
JSON Entry using multipartJson:
http {
    url 'http://localhost'
    method 'POST'
    multipartJson 'metadata', [type: 'contract', version: '2.0'] // Using a map
}
JSON Entry with Attribute using multipartJson:
http {
    url 'http://localhost'
    method 'POST'
    multipartJson 'metadata', myAttribute //  Attribute
}
Using multiPart:
// File Entry
http {
    url 'http://localhost'
    method 'POST'
    multiPart 'file', 'file', reader.get("document") // reader.get("document") is an Attribute containing a FileDescriptor
}
// Text Entry
http {
    url 'http://localhost'
    method 'POST'
    multiPart 'text', 'text', 'some text'
}
// JSON Entry (using an `Attribute`):
http {
    url 'http://localhost'
    method 'POST'
    multiPart 'json', 'json', myAttribute // myAttribute is an Attribute
}
// JSON Entry (using a map):
http {
    url 'http://localhost'
    method 'POST'
    multiPart 'json', 'json', [key: 'value'] // The map will be converted to an Attribute internally
}
Mixed Content:
http {
    url 'http://localhost'
    method 'POST'
    multipartFile 'document', upload.idFront // Using multipartFile
    multipartText 'description', 'Important document' // Using multipartText
    multipartJson 'metadata', [type: 'contract', version: '2.0'] // Using multipartJson
}
Important Considerations: For multipartFile, make sure the Attribute you provide actually contains a FileDescriptor. For multipartJson, the content can be an Attribute or any object serializable to JSON. The multiPart method provides a more generic way to add multipart entries, but it requires you to specify the contentType string explicitly. The multiPart method infers the entry type (file, text, or json) based on the provided contentType and the type of value. Optionally, it is possible to use a JSON builder, see JsonBuilder
http {
    url 'http://localhost'
    method 'POST'
    jsonBody {
      client {
        firstName client.firstname
        lastName client.lastname
      }
    }
}

JWT in body

The request body is specified using jwtBody. The payload expression can reference and use available context attributes. In addition to payload it necessary to define secret which is a shared secret with the other service. alg is optional and defaults to HS256. Possible values are “HS256”, “HS384” and “HS512"".
http {
  url "${thirdparty.url}/webhook"
  method 'POST'
  jwtBody {
      payload """whatever"""
      secret "secret"
      alg "HS512"
  }
}

JWT body with Base64-encoded files

It is possible to specify a JWT request body using descriptors. The corresponding files are encoded using Base64.
http {
    url 'http://localhost'
    method 'POST'
    jwtBody {
        payload([front: base64(upload.idFront), back: base64(upload.idBack)])
        secret "secret"
    }
}

Request method

The method specifies an HTTP request method. If omitted, the default method is GET. The method can be one of the following:
  • DELETE
  • GET
  • HEAD
  • OPTIONS
  • PATCH
  • POST
  • PUT
  • TRACE
http {
    url "/api/files/cache/${uuid}"
    method 'DELETE'
}

Content type

The contentType specifies an HTTP request Content-Type header.
http {
    url 'http://localhost'
    contentType 'APPLICATION_JSON_VALUE'
    method 'POST'
    body payload
}

URL Encoding

Disable URL encoding in the request to avoid double encoding.
http {
    url 'http://localhost?param=%2Fencoded%2Bparam'
    method 'POST'
    disabledEncoding()
}

Retries

By default, the HTTP connector will automatically retry requests for the following status codes:
  • 408 (Request Timeout)
  • 425 (Too Early)
  • 429 (Too Many Requests)
  • 500 (Internal Server Error)
  • 502 (Bad Gateway)
  • 503 (Service Unavailable)
  • 504 (Gateway Timeout)
You can customize which status codes are considered retryable using the retryableError and unretryableError options:
http {
    url 'https://api.example.com/resource'
    retryableError 404   // Add 404 to the retryable list
    unretryableError 500 // Remove 500 from the retryable list
}
  • Use retryableError <code> to add a status code to the retryable list.
  • Use unretryableError <code> to remove a status code from the retryable list.
This allows you to fine-tune retry behavior for your specific use case.

Authorization

Basic authentication

The basicAuth specifies an HTTP basic authentication credentials.
http {
    url 'http://localhost'
    basicAuth 'user', 'password'
}

Bearer authentication

The bearerAuth specifies a Bearer Authentication token used in Authentication HTTP header. unretryableError makes connector not to retry the request on specified response status code which might become handy if there’s non-standard server/gateway behaviour.
http {
    url "${api.url}/verify"
    bearerAuth access_token
    unretryableError 500
}

Transports

A transport is a reusable configuration for HTTP connections, allowing you to define a base URL and authentication method (such as Basic or Bearer/OAuth2). You can reference a transport by name in your HTTP connector, making it easy to share connection settings across multiple requests.

Defining a Transport

Transports are defined separately and can include a base URL and authentication. Example definitions: Basic Authentication Transport:
transport('myTransport') {
    baseUrl 'https://api.example.com'
    auth {
        basic {
            username 'user'
            password 'pass'
        }
    }
}
Bearer (OAuth2) Authentication Transport:
transport('myBearerTransport') {
    baseUrl 'https://api.example.com'
    auth {
        bearer {
            url 'https://auth.example.com/oauth/token'
            type 'client_credentials'  // or 'password'
            scope 'read write'         // optional
            username 'myuser'          // required for 'password' grant type
            password 'mypassword'      // required for 'password' grant type
            clientCredentials 'clientid:clientsecret'  // required for 'client_credentials' grant type
        }
    }
}
Transport with Headers:
transport('myTransportWithHeaders') {
    baseUrl 'https://api.example.com'
    header 'X-API-Version', '2.0'
    header 'X-Client-ID', config('client.id')
    header 'Accept', 'application/json'
}
Transport with Multiple Headers and Authentication:
transport('fullTransport') {
    baseUrl 'https://api.example.com'
    header 'X-API-Version', '2.0'
    header 'X-Client-ID': 'my-client-123'
    header 'Accept': ['application/json', 'application/xml']
    auth {
        bearer {
            url 'https://auth.example.com/oauth/token'
            clientCredentials config('oauth.credentials')
        }
    }
}
Note:
  • The url can be relative to the transport’s baseUrl
  • The authentication method defined in the transport will be applied automatically
  • You can use context attributes in transport definitions for dynamic configuration
  • The default grant type for bearer is password if not specified
  • Transport headers are applied as default headers to all requests using that transport
  • Headers can be defined using single values, lists, or map syntax
  • Connector-level headers override transport-level headers with the same name

Using a Transport in HTTP Connector

To use a transport, reference it by name in your HTTP connector:
http {
    transport 'myTransport'
    url '/endpoint' // relative to baseUrl
    method 'GET'
}

Best Practices

  1. Error Handling:
    • Always handle expected error cases using onStatus handlers
    • Use retryableError and unretryableError to customize retry behavior
    • Consider using transports for consistent error handling across multiple requests
  2. Authentication:
    • Use transports for reusable authentication configurations
    • Keep sensitive credentials in context attributes or secure storage
    • Consider token expiration and refresh mechanisms for OAuth2
  3. Request Configuration:
    • Use context attributes for dynamic URLs and parameters
    • Set appropriate timeouts for different types of requests
    • Use appropriate content types for different body types
  4. File Handling:
    • Use appropriate multipart methods for file uploads
    • Consider file size limits and content types
    • Use Base64 encoding when required by the API
  5. Response Handling:
    • Use appropriate response handlers for different status codes
    • Handle both success and error cases
    • Consider response size and content type